diff --git a/src/main/java/de/strifel/VTools/ServerCloser.java b/src/main/java/de/strifel/VTools/ServerCloser.java index fe9a3c4..574bcdf 100644 --- a/src/main/java/de/strifel/VTools/ServerCloser.java +++ b/src/main/java/de/strifel/VTools/ServerCloser.java @@ -3,7 +3,6 @@ package de.strifel.VTools; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.player.ServerConnectedEvent; -import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; import de.strifel.VTools.listeners.TGBridge; import okhttp3.*; @@ -14,13 +13,13 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collection; import java.util.Map; import java.util.Timer; import java.util.TimerTask; public class ServerCloser { private static final long INACTIVITY_TIMEOUT_MILLISECOND = 15L * 60 * 1000; // 15 minutes + private static final long MINUTE = 60L * 1000; // 15 minutes private static final OkHttpClient CLIENT = new OkHttpClient(); private String apiUrl; @@ -67,7 +66,8 @@ public class ServerCloser { for (int i = 0; i < 3; i++) { httpSuccess = executeAzure(); if (httpSuccess) { - TGBridge.log("关机命令发送成功,正在关闭 pymcd。"); + TGBridge.log("ServerCloser: 向azure发送关机命令成功,正在关闭 pymcd."); + TGBridge.setShuttingDown(0); break; } } @@ -76,12 +76,14 @@ public class ServerCloser { } else { try { Runtime.getRuntime().exec("pkill pymcd"); - } catch (IOException ignored) {} -// plugin.getServer().shutdown(); + } catch (IOException e) { + TGBridge.error("关闭 pymcd 时出现问题:" + e.getMessage()); + } } } private boolean firstInit; + private int countDown = -1; public void register() { server.getEventManager().register(plugin, this); @@ -122,30 +124,62 @@ public class ServerCloser { } private void update() { - Collection players = server.getAllPlayers(); + var hasPlayer = !server.getAllPlayers().isEmpty(); - if (!players.isEmpty()) { + if (hasPlayer) { plugin.logger.info("ServerCloser: 有玩家在线,干掉任何可能的关机计时器。"); closeServerTimer.cancel(); + countDown = -1; + TGBridge.setShuttingDown(-1); return; } + initCountdown(); + } + + private void initCountdown() { + countDown = firstInit ? 60 : 15; + firstInit = false; + startCountdown(); + } + + private void startCountdown() { + TGBridge.setShuttingDown(countDown); Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override + @SuppressWarnings("java:S1199") public void run() { - if (server.getAllPlayers().isEmpty()) { - plugin.logger.info("ServerCloser: 即将关机。"); - close(); - } else { - plugin.logger.error("ServerCloser: 定时器到点时发现服务器有人。这不应发生,因为定时器本应该被打断。"); - TGBridge.error("ServerCloser: #bug @NaAlOH4 定时器到点时发现服务器有人。这不应发生,因为定时器本应该被打断。"); - } - closeServerTimer.cancel(); - } - }, firstInit ? (4 * INACTIVITY_TIMEOUT_MILLISECOND) : INACTIVITY_TIMEOUT_MILLISECOND); - plugin.logger.info("ServerCloser: 将在 {} 后自动关机。", firstInit ? "1h" : "15min"); + { + countDown--; + if (countDown < 0) { + TGBridge.error("ServerCloser: #bug @NaAlOH4 计时器写炸了"); + closeServerTimer.cancel(); + return; + } - firstInit = false; + if (server.getAllPlayers().isEmpty()) { + switch (countDown) { + case 1 -> { + TGBridge.log("服务器即将在一分钟后关机"); + startCountdown(); + } + case 0 -> { + plugin.logger.info("ServerCloser: 距离上一个玩家离开已经过了%s,即将关机。".formatted(firstInit ? "一小时" : "15分钟")); + TGBridge.log("ServerCloser: 距离上一个玩家离开已经过了%s,即将关机。".formatted(firstInit ? "一小时" : "15分钟")); + close(); + closeServerTimer.cancel(); + } + default -> startCountdown(); + } + } else { + plugin.logger.error("ServerCloser: 定时器到点时发现服务器有人。这不应发生,因为定时器本应该被打断。"); + TGBridge.error("ServerCloser: #bug @NaAlOH4 定时器到点时发现服务器有人。这不应发生,因为定时器本应该被打断。"); + closeServerTimer.cancel(); + } + } + } + }, MINUTE); + plugin.logger.info("ServerCloser: 将在 {} 分钟后自动关机。", countDown); synchronized (lock) { closeServerTimer.cancel(); @@ -153,4 +187,5 @@ public class ServerCloser { } } + } diff --git a/src/main/java/de/strifel/VTools/listeners/TGBridge.java b/src/main/java/de/strifel/VTools/listeners/TGBridge.java index 01003dc..7c11bbd 100644 --- a/src/main/java/de/strifel/VTools/listeners/TGBridge.java +++ b/src/main/java/de/strifel/VTools/listeners/TGBridge.java @@ -1,5 +1,7 @@ package de.strifel.VTools.listeners; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.pengrad.telegrambot.Callback; import com.pengrad.telegrambot.TelegramBot; import com.pengrad.telegrambot.UpdatesListener; @@ -40,6 +42,7 @@ import java.util.stream.Collectors; public class TGBridge { private final VTools plugin; private final ProxyServer server; + @SuppressWarnings("java:S3008") protected static TGBridge INSTANCE = null; private TelegramBot bot; @@ -56,7 +59,11 @@ public class TGBridge { */ private String pinNote; + @SuppressWarnings("java:S3010") public TGBridge(@NotNull VTools plugin) { + if (INSTANCE != null) { + throw new IllegalStateException(); + } INSTANCE = this; this.plugin = plugin; this.server = plugin.getServer(); @@ -100,11 +107,12 @@ public class TGBridge { for (Update update : updates) { try { - if (update == null || update.message() == null) break; + if (update == null || update.message() == null) continue; Message message = update.message(); - if (message.chat() == null || message.chat().id() != CHAT_ID || message.from() == null) { - break; - } + if (message.chat() == null || message.chat().id() != CHAT_ID || message.from() == null) continue; + + currentAnnounce.abort(); + String text = ""; Message replyTo = message.replyToMessage(); if (replyTo != null) { @@ -117,7 +125,7 @@ public class TGBridge { if (replyType != null) { replyText += replyType; if (replyTo.caption() != null) { - replyText += " \"" + replyTo.caption()+"\""; + replyText += " \"" + replyTo.caption() + "\""; } } if (replyText.equals("")) { @@ -141,15 +149,15 @@ public class TGBridge { usage: use "/setpin" reply a message that from the bot to set that message to pin-message, or use "/setpin " to update current pinned message."""); - break; + continue; } ONLINE_STATUS_MESSAGE_ID = replyTo.messageId(); updateOnlineStatus(); - break; + continue; } if (replyTo != null && (replyTo.from() == null || replyTo.messageId() <= 0)) { outbound("must reply a message that from the bot (or reply nothing)."); - break; + continue; } String markdownString = MarkdownString.markdownString(message); @@ -157,7 +165,7 @@ public class TGBridge { String shouldBeCommand = markdownString.substring(0, "/setpin ".length()); if (!shouldBeCommand.matches("/setpin[\t \n]")) { outbound("\"/setpin\" must be plain text."); - break; + continue; } @@ -179,6 +187,7 @@ public class TGBridge { } }, ParseMode.MarkdownV2 ); + default -> {} } } text += msgText; @@ -255,17 +264,29 @@ public class TGBridge { */ private String genPinMessage() { return (pinNote != null && pinNote.length() != 0) ? - genOnlineStatus() + "\n" + DIVIDER + pinNote : + genOnlineStatus() + "\n\n" + DIVIDER + pinNote : genOnlineStatus(); } /** * @return markdown escaped str */ + + private int shutdownCountMinutes = -1; + + public static void setShuttingDown(int minute) { + INSTANCE.shutdownCountMinutes = minute; + INSTANCE.updateOnlineStatus(); + } + private String genOnlineStatus() { ArrayList out = new ArrayList<>(); - String fmt = server.getAllPlayers().size() > 1 ? "%d players are currently connected to the proxy\\." : "%d player is currently connected to the proxy\\."; - out.add(String.format(fmt, server.getAllPlayers().size())); + int playerCount = server.getAllPlayers().size(); + out.add(switch (playerCount) { + case 0 -> "nobody here\\."; + case 1 -> "only one player online\\."; + default -> playerCount + " players online\\."; + }); List registeredServers = new ArrayList<>(server.getAllServers()); for (RegisteredServer registeredServer : registeredServers) { LinkedList onServer = new LinkedList<>(); @@ -283,7 +304,10 @@ public class TGBridge { ); } } - return String.join("\n", out); + String result = String.join("\n", out); + if (shutdownCountMinutes < 0) return result; + if (shutdownCountMinutes == 0) return "server is shutdown\\.%n%s".formatted(result); + return "server will shutdown after %s minute\\.%n%s".formatted(shutdownCountMinutes, result); } protected void tgInbound(User user, String content) { @@ -310,6 +334,7 @@ public class TGBridge { public static void error(String context) { INSTANCE.outbound("*" + MarkdownString.escapeStr(context) + "*", ParseMode.MarkdownV2); } + public static void log(String context) { INSTANCE.outbound("_" + MarkdownString.escapeStr(context) + "_", ParseMode.MarkdownV2); } @@ -353,14 +378,12 @@ public class TGBridge { @Subscribe public void onServerConnected(ServerConnectedEvent event) { - if (event.getPreviousServer().isEmpty()) { - if (!event.getPlayer().hasPermission("vtools.globalchat.bypassbridge.join")) { - String username = event.getPlayer().getUsername(); - if (lastDisconnect.equals(username)) { - lastDisconnect = ""; - } - joinLeftAnnounce(String.format("%s joined the proxy", username)); + if (event.getPreviousServer().isEmpty() && !event.getPlayer().hasPermission("vtools.globalchat.bypassbridge.join")) { + String username = event.getPlayer().getUsername(); + if (lastDisconnect.equals(username)) { + lastDisconnect = ""; } + joinLeftAnnounce(String.format("%s joined the proxy", username)); } updateRequests.add(new UpdateRequest()); } @@ -393,6 +416,7 @@ public class TGBridge { private LinkedBlockingQueue updateRequests = new LinkedBlockingQueue<>(); + @SuppressWarnings("java:S3776") private void initUpdateThread() { new Thread(() -> { while (true) { @@ -406,15 +430,14 @@ public class TGBridge { } catch (InterruptedException ignored) {} if (oldestRequest == null) { plugin.logger.warn("updateRequests.take() return a null value, why?"); - try { - Thread.sleep(1000); - } catch (InterruptedException ignored) {} + sleep(1000); continue; } updateRequests.clear(); if (!updateOnlineStatus()) { updateRequests.add(oldestRequest); // 更新失败 回去吧您内 + sleep(1000); } } }).start(); @@ -444,6 +467,12 @@ public class TGBridge { }).start(); } + private static void sleep(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException ignored) {} + } + protected boolean updateOnlineStatus() { return editOnlineStatusMessage(genPinMessage()); } @@ -452,6 +481,8 @@ public class TGBridge { return editOnlineStatusMessage("(proxy already shutdown)"); } + private static final Gson prettyGson = new GsonBuilder().setPrettyPrinting().create(); + protected boolean editOnlineStatusMessage(String markdownText) { if (ONLINE_STATUS_MESSAGE_ID < 1) { return true; @@ -459,8 +490,16 @@ public class TGBridge { BaseResponse response; try { response = bot.execute(new EditMessageText(CHAT_ID, ONLINE_STATUS_MESSAGE_ID, markdownText).parseMode(ParseMode.MarkdownV2)); - } catch (RuntimeException e) {return false;} - return response != null && response.isOk(); + if (!response.isOk()) { + String responseJSON = prettyGson.toJson(response); + plugin.logger.warn("update failed: {}", responseJSON); + } + return response.isOk(); + } catch (RuntimeException e) { + e.printStackTrace(); + return false; + } + } private class JoinLeftAnnounceMessage { @@ -468,13 +507,16 @@ public class TGBridge { private long time; private StringBuilder text; + private boolean abort = false; boolean isValid() { - if (messageId < 1) { - return false; - } + if (abort || messageId < 1) return false; long dt = System.currentTimeMillis() - time; - return dt <= 60_000 && dt >= 0; + return dt <= 30_000 && dt >= 0; + } + + private void abort() { + abort = true; } protected JoinLeftAnnounceMessage(String firstMessage) { @@ -496,7 +538,7 @@ public class TGBridge { messageId = -1; return; } - if (response.isOk() == false) { + if (!response.isOk()) { messageId = -1; return; }