fix(felis): 启动失败的服可在面板重试或停止,玩家入服时直接告知启动失败
This commit is contained in:
28 files changed
+775
-75
No files matched your search
@@ -20,12 +20,14 @@ import java.util.Optional;
|
||||
* backend or the wake lever.
|
||||
*
|
||||
* <p>This is the read-only, phase-aware subset of the responsibility: the MOTD is
|
||||
* synthesized from the server's lifecycle (online / starting / sleeping) and its
|
||||
* cached player counts. Mirroring each backend's <em>own</em> MOTD string (by
|
||||
* pinging ready servers in the background and caching the result) is a richer
|
||||
* synthesized from the server's lifecycle (online / starting / start failed /
|
||||
* sleeping) and its cached player counts. Mirroring each backend's <em>own</em> MOTD
|
||||
* string (by pinging ready servers in the background and caching the result) is a richer
|
||||
* variant deferred to a later slice; nothing here ever pings a sleeping backend.
|
||||
*/
|
||||
public final class MotdResponder {
|
||||
private static final String PHASE_FAILED = "Failed";
|
||||
|
||||
private final ServerRegistry registry;
|
||||
|
||||
MotdResponder(ServerRegistry registry) {
|
||||
@@ -55,21 +57,33 @@ public final class MotdResponder {
|
||||
}
|
||||
|
||||
// The server-list ping carries no client locale, so the MOTD status uses the
|
||||
// both-languages-in-one-line pattern the modded /link clients share.
|
||||
private static String statusLine(ServerView v) {
|
||||
// both-languages-in-one-line pattern the modded /link clients share. A start
|
||||
// that failed still holds desiredState Running, so it is read first: joining a
|
||||
// server whose retries are spent wakes nothing, and one between retries is
|
||||
// waiting out a backoff, which a "starting…" line hid behind a queue that ran out.
|
||||
static String statusLine(ServerView v) {
|
||||
if (v.ready()) {
|
||||
return "在线 / online";
|
||||
}
|
||||
if (v.startGaveUp()) {
|
||||
return "启动失败,等服主处理 / failed to start — the owner has to restart it";
|
||||
}
|
||||
if (PHASE_FAILED.equals(v.phase())) {
|
||||
return "启动超时,稍后自动重试 / start timed out — retrying shortly";
|
||||
}
|
||||
if ("Running".equals(v.desiredState())) {
|
||||
return "启动中… / starting…";
|
||||
}
|
||||
return "休眠中,加入即唤醒 / sleeping — join to wake";
|
||||
}
|
||||
|
||||
private static NamedTextColor statusColor(ServerView v) {
|
||||
static NamedTextColor statusColor(ServerView v) {
|
||||
if (v.ready()) {
|
||||
return NamedTextColor.GREEN;
|
||||
}
|
||||
if (v.startGaveUp()) {
|
||||
return NamedTextColor.RED;
|
||||
}
|
||||
if ("Running".equals(v.desiredState())) {
|
||||
return NamedTextColor.YELLOW;
|
||||
}
|
||||
|
||||
@@ -505,11 +505,7 @@ public final class WaitingRouter {
|
||||
// stopped the server — it says so and returns false.
|
||||
private boolean stillComing(Player player, boolean zh, Waiter w, ServerView status, long now) {
|
||||
if (status.startGaveUp()) {
|
||||
player.sendMessage(Component.text(
|
||||
zh ? "「" + w.serverName + "」启动失败,自动重试也已用完。服主可以在面板查看日志后重新启动。"
|
||||
: "« " + w.serverName + " » failed to start and its automatic retries are spent. "
|
||||
+ "The owner can check its log in the panel and start it again.",
|
||||
NamedTextColor.RED));
|
||||
tellStartFailed(player, zh, w.serverName);
|
||||
return false;
|
||||
}
|
||||
if (DESIRED_STOPPED.equalsIgnoreCase(status.desiredState())) {
|
||||
@@ -655,6 +651,12 @@ public final class WaitingRouter {
|
||||
NamedTextColor.YELLOW));
|
||||
return;
|
||||
}
|
||||
if ("start_failed".equals(e.errorCode())) {
|
||||
// The last start failed with its retries spent. The join does
|
||||
// not buy another round of them, so there is nothing to wait for.
|
||||
tellStartFailed(player, zh, serverName);
|
||||
return;
|
||||
}
|
||||
logWakeFailure(player, serverName, zh, e);
|
||||
return;
|
||||
case 429:
|
||||
@@ -688,6 +690,16 @@ public final class WaitingRouter {
|
||||
waiting.put(id, new Waiter(serverName, clock.getAsLong(), fromMenu));
|
||||
}
|
||||
|
||||
// The server's start failed and its automatic retries are spent: nothing more is
|
||||
// coming until a person starts it again from the panel.
|
||||
private static void tellStartFailed(Player player, boolean zh, String serverName) {
|
||||
player.sendMessage(Component.text(
|
||||
zh ? "「" + serverName + "」启动失败,自动重试也已用完。服主可以在面板查看日志后重新启动。"
|
||||
: "« " + serverName + " » failed to start and its automatic retries are spent. "
|
||||
+ "The owner can check its log in the panel and start it again.",
|
||||
NamedTextColor.RED));
|
||||
}
|
||||
|
||||
private void logWakeFailure(Player player, String serverName, boolean zh, LinkException e) {
|
||||
log.warn("Felis: wake {} failed (status={}): {}", serverName, e.statusCode(), e.getMessage());
|
||||
player.sendMessage(Component.text(
|
||||
|
||||
@@ -2,6 +2,8 @@ package best.lolicon.felis.velocity;
|
||||
|
||||
import best.lolicon.felis.link.ServerView;
|
||||
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -139,6 +141,24 @@ public final class ServerRegistryTest {
|
||||
assertAddr("a non-numeric port is not a port", "backend:x", 25565,
|
||||
ServerRegistry.parseAddress("backend:x"));
|
||||
|
||||
// The server-list MOTD reads a failed start before desiredState Running,
|
||||
// which a failed start still holds.
|
||||
assertEq("motd: up", "在线 / online", MotdResponder.statusLine(up("a", "a", "10.43.0.1:25565")));
|
||||
assertEq("motd: starting", "启动中… / starting…", MotdResponder.statusLine(
|
||||
new ServerView("a", "a", "Starting", false, "ownerOnly", "Running", "fallback", "login", 0, 0)));
|
||||
ServerView backoff = new ServerView("a", "a", "Failed", false, "ownerOnly", "Running",
|
||||
"fallback", "login", 0, 0, false, 1, false);
|
||||
assertEq("motd: between retries", "启动超时,稍后自动重试 / start timed out — retrying shortly",
|
||||
MotdResponder.statusLine(backoff));
|
||||
assertEq("motd: between retries is yellow", NamedTextColor.YELLOW, MotdResponder.statusColor(backoff));
|
||||
ServerView gaveUp = new ServerView("a", "a", "Failed", false, "ownerOnly", "Running",
|
||||
"fallback", "login", 0, 0, false, 3, true);
|
||||
assertEq("motd: retries spent", "启动失败,等服主处理 / failed to start — the owner has to restart it",
|
||||
MotdResponder.statusLine(gaveUp));
|
||||
assertEq("motd: retries spent is red", NamedTextColor.RED, MotdResponder.statusColor(gaveUp));
|
||||
assertEq("motd: sleeping", "休眠中,加入即唤醒 / sleeping — join to wake",
|
||||
MotdResponder.statusLine(down("a", "a")));
|
||||
|
||||
System.out.println("ServerRegistryTest OK (" + checks + " checks)");
|
||||
}
|
||||
|
||||
|
||||
@@ -219,6 +219,7 @@ public final class WaitingRouterTest {
|
||||
String[][] cases = {
|
||||
{"403 forbidden", "You're not allowed to start « gamma »", null},
|
||||
{"409 maintenance_in_progress", "« gamma » is under maintenance", null},
|
||||
{"409 start_failed", "« gamma » failed to start and its automatic retries are spent", null},
|
||||
{"409 conflict", "Couldn't start « gamma » right now", "wake gamma failed (status=409)"},
|
||||
{"503 at_capacity", "The cluster is at capacity right now", null},
|
||||
{"503 unavailable", "Couldn't start « gamma » right now", "wake gamma failed (status=503)"},
|
||||
|
||||
Reference in new issue
Block a user