split package name and re-write server closer
This commit is contained in:
parent
1a3c8872d1
commit
cba3248484
6 changed files with 317 additions and 230 deletions
|
@ -1,8 +1,9 @@
|
||||||
package de.strifel.VTools;
|
package com.alpt.vtools.listeners;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.sun.net.httpserver.HttpServer;
|
import com.sun.net.httpserver.HttpServer;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
|
import de.strifel.VTools.VTools;
|
||||||
import org.jetbrains.annotations.TestOnly;
|
import org.jetbrains.annotations.TestOnly;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -13,18 +14,26 @@ import java.util.Collection;
|
||||||
|
|
||||||
public class OnlinePlayerQueryService {
|
public class OnlinePlayerQueryService {
|
||||||
|
|
||||||
|
public static OnlinePlayerQueryService INSTANCE;
|
||||||
private final VTools plugin;
|
private final VTools plugin;
|
||||||
|
|
||||||
public OnlinePlayerQueryService(VTools plugin) {
|
private OnlinePlayerQueryService(VTools plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Gson gson = new Gson();
|
private static final Gson gson = new Gson();
|
||||||
|
|
||||||
public void register() {
|
public static OnlinePlayerQueryService createInstance(VTools plugin) {
|
||||||
|
if (INSTANCE != null) return INSTANCE;
|
||||||
|
INSTANCE = new OnlinePlayerQueryService(plugin);
|
||||||
|
INSTANCE.register();
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void register() {
|
||||||
|
int port = Integer.parseInt(plugin.getConfigOrDefault("http_service_port", "17611"));
|
||||||
try {
|
try {
|
||||||
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 17611), 0);
|
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", port), 0);
|
||||||
|
|
||||||
server.createContext("/api/getOnlinePlayers", exchange -> {
|
server.createContext("/api/getOnlinePlayers", exchange -> {
|
||||||
Collection<Player> players = plugin.getServer().getAllPlayers();
|
Collection<Player> players = plugin.getServer().getAllPlayers();
|
223
src/main/java/com/alpt/vtools/listeners/ServerCloser.java
Normal file
223
src/main/java/com/alpt/vtools/listeners/ServerCloser.java
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
package com.alpt.vtools.listeners;
|
||||||
|
|
||||||
|
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.ProxyServer;
|
||||||
|
import de.strifel.VTools.VTools;
|
||||||
|
import de.strifel.VTools.listeners.TGBridge;
|
||||||
|
import okhttp3.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
|
||||||
|
public class ServerCloser {
|
||||||
|
private static final long MINUTE = 60L * 1000;
|
||||||
|
private static final OkHttpClient CLIENT = new OkHttpClient();
|
||||||
|
|
||||||
|
private static final String ACTION_STOP_JSON = "{\"action\":\"stop\"}";
|
||||||
|
@SuppressWarnings("java:S1068")
|
||||||
|
private static final String ACTION_START_JSON = "{\"action\":\"start\"}";
|
||||||
|
private final VTools plugin;
|
||||||
|
private final ProxyServer server;
|
||||||
|
|
||||||
|
private final LinkedBlockingQueue<Counter> lock = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
|
public static ServerCloser INSTANCE;
|
||||||
|
|
||||||
|
private ServerCloser(VTools plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.server = plugin.getServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean executeAzure() {
|
||||||
|
return executeAzure(ACTION_STOP_JSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean executeAzure(String action) {
|
||||||
|
RequestBody requestBody = RequestBody.create(action, MediaType.parse("application/json"));
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(plugin.getConfigOrDefault("azure_api_url", "https://example.com/"))
|
||||||
|
.post(requestBody)
|
||||||
|
.addHeader("Content-Type", "application/json")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (Response response = CLIENT.newCall(request).execute()) {
|
||||||
|
String body = response.body() == null ? "null" : response.body().string();
|
||||||
|
plugin.logger.info("ServerCloser: http request response: {} ({}).", body, response.code());
|
||||||
|
return (response.code() >= 200 && response.code() < 300);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void close() {
|
||||||
|
if (!INSTANCE.server.getAllPlayers().isEmpty()) {
|
||||||
|
plugin.logger.error("ServerCloser: 定时器到点时发现服务器有人。这不应发生,因为定时器本应该被打断。");
|
||||||
|
TGBridge.error("ServerCloser: #bug @NaAlOH4 定时器到点时发现服务器有人。这不应发生,因为定时器本应该被打断。");
|
||||||
|
}
|
||||||
|
boolean httpSuccess = false;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
httpSuccess = executeAzure();
|
||||||
|
if (httpSuccess) {
|
||||||
|
TGBridge.log("ServerCloser: 向azure发送关机命令成功,正在关闭 pymcd.");
|
||||||
|
TGBridge.setShuttingDown(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!httpSuccess) {
|
||||||
|
TGBridge.error("服务器关机 http 请求失效,服务器可能没有正常关闭。");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Runtime.getRuntime().exec(new String[]{"pkill", "pymcd"});
|
||||||
|
} catch (IOException e) {
|
||||||
|
TGBridge.error("关闭 pymcd 时出现问题:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean firstInit;
|
||||||
|
|
||||||
|
public static ServerCloser createInstance(VTools plugin) {
|
||||||
|
if (INSTANCE != null) return INSTANCE;
|
||||||
|
INSTANCE = new ServerCloser(plugin);
|
||||||
|
INSTANCE.server.getEventManager().register(plugin, INSTANCE);
|
||||||
|
INSTANCE.firstInit = true;
|
||||||
|
INSTANCE.update();
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("java:S106")
|
||||||
|
@Subscribe
|
||||||
|
public void onDisconnect(DisconnectEvent event) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onServerConnected(ServerConnectedEvent event) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
if (server.getAllPlayers().isEmpty()) {
|
||||||
|
initCountdown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean canceledSomething = false;
|
||||||
|
synchronized (lock) {
|
||||||
|
while (!lock.isEmpty()) {
|
||||||
|
lock.poll().cancel();
|
||||||
|
canceledSomething = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (canceledSomething) {
|
||||||
|
plugin.logger.info("ServerCloser: 有玩家在线,干掉任何可能的关机计时器。");
|
||||||
|
}
|
||||||
|
|
||||||
|
TGBridge.setShuttingDown(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initCountdown() {
|
||||||
|
startCountdown(firstInit);
|
||||||
|
firstInit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startCountdown(boolean isFirstInit) {
|
||||||
|
synchronized (lock) {
|
||||||
|
while (!lock.isEmpty()) {
|
||||||
|
lock.poll().cancel();
|
||||||
|
}
|
||||||
|
lock.add(new Counter(INSTANCE, isFirstInit ? 60 : 15).start());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void fastShutdown() {
|
||||||
|
synchronized (lock) {
|
||||||
|
while (!lock.isEmpty()) {
|
||||||
|
lock.poll().cancel();
|
||||||
|
}
|
||||||
|
lock.add(new Counter(INSTANCE, 1).start());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void slowShutdown() {
|
||||||
|
synchronized (lock) {
|
||||||
|
while (!lock.isEmpty()) {
|
||||||
|
lock.poll().cancel();
|
||||||
|
}
|
||||||
|
lock.add(new Counter(INSTANCE, 60).start());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Counter {
|
||||||
|
private final ServerCloser instance;
|
||||||
|
private final int totalMin;
|
||||||
|
private int minLeft;
|
||||||
|
private boolean canceled = false;
|
||||||
|
|
||||||
|
protected Counter(ServerCloser instance, int minute) {
|
||||||
|
this.instance = instance;
|
||||||
|
totalMin = minute;
|
||||||
|
minLeft = minute;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void cancel() {
|
||||||
|
canceled = true;
|
||||||
|
this.notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean started = false;
|
||||||
|
private final Object startLock = new Object();
|
||||||
|
|
||||||
|
protected Counter start() {
|
||||||
|
synchronized (startLock) {
|
||||||
|
if (started) return this;
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
new Thread(this::run).start();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void run() {
|
||||||
|
while (true) {
|
||||||
|
if (canceled) return;
|
||||||
|
TGBridge.setShuttingDown(minLeft);
|
||||||
|
try {
|
||||||
|
this.wait(MINUTE);
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
if (canceled) return;
|
||||||
|
if (!instance.server.getAllPlayers().isEmpty()) {
|
||||||
|
instance.plugin.logger.error("ServerCloser: 定时器发现服务器有人。这不应发生,因为定时器本应该被直接打断。");
|
||||||
|
TGBridge.error("ServerCloser: #bug @NaAlOH4 定时器发现服务器有人。这不应发生,因为定时器本应该被直接打断。");
|
||||||
|
canceled = true;
|
||||||
|
}
|
||||||
|
if (canceled) return;
|
||||||
|
minLeft--;
|
||||||
|
switch (minLeft) {
|
||||||
|
case 1 -> {
|
||||||
|
String msg = "服务器即将在一分钟后关机";
|
||||||
|
instance.plugin.logger.info(msg);
|
||||||
|
TGBridge.log(msg);
|
||||||
|
}
|
||||||
|
case 0 -> {
|
||||||
|
String msg = "ServerCloser: 距离上一个玩家离开已经过了 %s 分钟,即将关机。".formatted(totalMin);
|
||||||
|
instance.plugin.logger.info(msg);
|
||||||
|
TGBridge.log(msg);
|
||||||
|
instance.close();
|
||||||
|
canceled = true;
|
||||||
|
}
|
||||||
|
case -1 -> {
|
||||||
|
instance.plugin.logger.error("ServerCloser: 定时器写炸了。");
|
||||||
|
TGBridge.error("ServerCloser: #bug @NaAlOH4 定时器写炸了。");
|
||||||
|
canceled = true;
|
||||||
|
}
|
||||||
|
default -> instance.plugin.logger.info("服务器即将在 {} 分钟后关机", minLeft);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package de.strifel.VTools.listeners;
|
package com.alpt.vtools.utils;
|
||||||
|
|
||||||
import com.pengrad.telegrambot.model.Message;
|
import com.pengrad.telegrambot.model.Message;
|
||||||
import com.pengrad.telegrambot.model.MessageEntity;
|
import com.pengrad.telegrambot.model.MessageEntity;
|
|
@ -1,191 +0,0 @@
|
||||||
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.ProxyServer;
|
|
||||||
import de.strifel.VTools.listeners.TGBridge;
|
|
||||||
import okhttp3.*;
|
|
||||||
import org.yaml.snakeyaml.Yaml;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
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;
|
|
||||||
|
|
||||||
private static final String ACTION_STOP_JSON = "{\"action\":\"stop\"}";
|
|
||||||
@SuppressWarnings("java:S1068")
|
|
||||||
private static final String ACTION_START_JSON = "{\"action\":\"start\"}";
|
|
||||||
private final VTools plugin;
|
|
||||||
private final ProxyServer server;
|
|
||||||
|
|
||||||
private Timer closeServerTimer = new Timer();
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
|
||||||
|
|
||||||
public ServerCloser(VTools plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.server = plugin.getServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean executeAzure() {
|
|
||||||
return executeAzure(ACTION_STOP_JSON);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean executeAzure(String action) {
|
|
||||||
RequestBody requestBody = RequestBody.create(action, MediaType.parse("application/json"));
|
|
||||||
Request request = new Request.Builder()
|
|
||||||
.url(apiUrl)
|
|
||||||
.post(requestBody)
|
|
||||||
.addHeader("Content-Type", "application/json")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
try {
|
|
||||||
Response response = CLIENT.newCall(request).execute();
|
|
||||||
plugin.logger.info("ServerCloser: http request response: {} ({}).", response.body().string(), response.code());
|
|
||||||
return (response.code() >= 200 && response.code() < 300);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void close() {
|
|
||||||
boolean httpSuccess = false;
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
httpSuccess = executeAzure();
|
|
||||||
if (httpSuccess) {
|
|
||||||
TGBridge.log("ServerCloser: 向azure发送关机命令成功,正在关闭 pymcd.");
|
|
||||||
TGBridge.setShuttingDown(0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!httpSuccess) {
|
|
||||||
TGBridge.error("服务器关机 http 请求失效,服务器可能没有正常关闭。");
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
Runtime.getRuntime().exec("pkill pymcd");
|
|
||||||
} catch (IOException e) {
|
|
||||||
TGBridge.error("关闭 pymcd 时出现问题:" + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean firstInit;
|
|
||||||
private int countDown = -1;
|
|
||||||
|
|
||||||
public void register() {
|
|
||||||
server.getEventManager().register(plugin, this);
|
|
||||||
firstInit = true;
|
|
||||||
update();
|
|
||||||
loadConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void loadConfig() {
|
|
||||||
try {
|
|
||||||
File configDir = plugin.dataDirectory.toFile();
|
|
||||||
if (!configDir.exists()) {
|
|
||||||
configDir.mkdir();
|
|
||||||
}
|
|
||||||
File configFile = new File(configDir, "config.yaml");
|
|
||||||
if (!configFile.exists()) {
|
|
||||||
Files.write(Path.of(configFile.toURI()), "chat_id: \"0\"\ntoken: \"\"\n".getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
String configStr = Files.readString(Path.of(configFile.toURI()), StandardCharsets.UTF_8);
|
|
||||||
Yaml yaml = new Yaml();
|
|
||||||
Map<String, String> config = yaml.load(configStr);
|
|
||||||
apiUrl = config.getOrDefault("azure_api_url", "https://example.com/");
|
|
||||||
} catch (Exception e) {
|
|
||||||
plugin.logger.error("parsing config", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("java:S106")
|
|
||||||
@Subscribe
|
|
||||||
public void onDisconnect(DisconnectEvent event) {
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onServerConnected(ServerConnectedEvent event) {
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void update() {
|
|
||||||
var hasPlayer = !server.getAllPlayers().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() {
|
|
||||||
{
|
|
||||||
countDown--;
|
|
||||||
if (countDown < 0) {
|
|
||||||
TGBridge.error("ServerCloser: #bug @NaAlOH4 计时器写炸了");
|
|
||||||
closeServerTimer.cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
closeServerTimer = timer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,5 +1,7 @@
|
||||||
package de.strifel.VTools;
|
package de.strifel.VTools;
|
||||||
|
|
||||||
|
import com.alpt.vtools.listeners.OnlinePlayerQueryService;
|
||||||
|
import com.alpt.vtools.listeners.ServerCloser;
|
||||||
import com.velocitypowered.api.event.Subscribe;
|
import com.velocitypowered.api.event.Subscribe;
|
||||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||||
import com.velocitypowered.api.plugin.Plugin;
|
import com.velocitypowered.api.plugin.Plugin;
|
||||||
|
@ -9,11 +11,17 @@ import de.strifel.VTools.commands.*;
|
||||||
import de.strifel.VTools.listeners.*;
|
import de.strifel.VTools.listeners.*;
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Plugin(id = "vtools", name="VTools", version="1.0-SNAPSHOT", description="Some commands!", authors="unnamed")
|
@Plugin(id = "vtools", name = "VTools", version = "1.0-SNAPSHOT", description = "Some commands!", authors = "unnamed")
|
||||||
public class VTools {
|
public class VTools {
|
||||||
private final ProxyServer server;
|
private final ProxyServer server;
|
||||||
public final Logger logger;
|
public final Logger logger;
|
||||||
|
@ -42,11 +50,46 @@ public class VTools {
|
||||||
server.getCommandManager().register("tps", new CommandTp(server), "jump");
|
server.getCommandManager().register("tps", new CommandTp(server), "jump");
|
||||||
server.getCommandManager().register("server", new CommandServer(server), "serverv");
|
server.getCommandManager().register("server", new CommandServer(server), "serverv");
|
||||||
server.getCommandManager().register("servers", new CommandServers(server), "allservers");
|
server.getCommandManager().register("servers", new CommandServers(server), "allservers");
|
||||||
|
loadConfig();
|
||||||
new TGBridge(this).register();
|
new TGBridge(this).register();
|
||||||
new PlayerStatus(this).register();
|
new PlayerStatus(this).register();
|
||||||
new GlobalChat(this).register();
|
new GlobalChat(this).register();
|
||||||
new ServerCloser(this).register();
|
ServerCloser.createInstance(this);
|
||||||
new OnlinePlayerQueryService(this).register();
|
OnlinePlayerQueryService.createInstance(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> config = new HashMap<>();
|
||||||
|
|
||||||
|
public String getConfig(String k) {
|
||||||
|
return config.get(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConfigOrDefault(String k, String v) {
|
||||||
|
return config.getOrDefault(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadConfig() {
|
||||||
|
try {
|
||||||
|
File configDir = dataDirectory.toFile();
|
||||||
|
if (!configDir.exists()) {
|
||||||
|
configDir.mkdir();
|
||||||
|
}
|
||||||
|
File configFile = new File(configDir, "config.yaml");
|
||||||
|
if (!configFile.exists()) {
|
||||||
|
String defVal = """
|
||||||
|
chat_id: "0"
|
||||||
|
token: ""
|
||||||
|
azure_api_url: "https://example.com/"
|
||||||
|
http_service_port: 17611
|
||||||
|
""";
|
||||||
|
Files.write(Path.of(configFile.toURI()), defVal.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
String configStr = Files.readString(Path.of(configFile.toURI()), StandardCharsets.UTF_8);
|
||||||
|
Yaml yaml = new Yaml();
|
||||||
|
config = yaml.load(configStr);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("parsing config", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProxyServer getServer() {
|
public ProxyServer getServer() {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package de.strifel.VTools.listeners;
|
package de.strifel.VTools.listeners;
|
||||||
|
|
||||||
|
import com.alpt.vtools.listeners.ServerCloser;
|
||||||
|
import com.alpt.vtools.utils.MarkdownString;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.pengrad.telegrambot.Callback;
|
import com.pengrad.telegrambot.Callback;
|
||||||
|
@ -27,13 +29,8 @@ import de.strifel.VTools.VTools;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.yaml.snakeyaml.Yaml;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.File;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
@ -77,24 +74,9 @@ public class TGBridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadConfig() {
|
private void loadConfig() {
|
||||||
try {
|
|
||||||
File configDir = plugin.dataDirectory.toFile();
|
|
||||||
if (!configDir.exists()) {
|
|
||||||
configDir.mkdir();
|
|
||||||
}
|
|
||||||
File configFile = new File(configDir, "config.yaml");
|
|
||||||
if (!configFile.exists()) {
|
|
||||||
Files.write(Path.of(configFile.toURI()), "chat_id: \"0\"\ntoken: \"\"\n".getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
String configStr = Files.readString(Path.of(configFile.toURI()), StandardCharsets.UTF_8);
|
|
||||||
Yaml yaml = new Yaml();
|
|
||||||
Map<String, String> config = yaml.load(configStr);
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
this.CHAT_ID = Long.parseLong(config.getOrDefault("chat_id", "0"));
|
CHAT_ID = Long.parseLong(plugin.getConfigOrDefault("chat_id", "0"));
|
||||||
this.TOKEN = config.getOrDefault("token", "");
|
TOKEN = plugin.getConfigOrDefault("token", "");
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
plugin.logger.error("parsing config", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,9 +120,10 @@ public class TGBridge {
|
||||||
if (message.text() != null && !message.text().isEmpty()) {
|
if (message.text() != null && !message.text().isEmpty()) {
|
||||||
String msgText = message.text();
|
String msgText = message.text();
|
||||||
if (msgText.startsWith("/")) {
|
if (msgText.startsWith("/")) {
|
||||||
String[] s = msgText.split("(@[A-Za-z0-9_]bot)?[\t \n]+", 2);
|
String[] s = msgText.split("((@\\w+bot)|(@\\w+bot)?[\t \n]+)", 2);
|
||||||
String command = s[0];
|
String command = s[0];
|
||||||
@Nullable String arg = s.length == 2 ? s[1] : null;
|
@Nullable String arg = s.length == 2 ? s[1] : null;
|
||||||
|
System.out.println(command);
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case "/list" -> outbound(genOnlineStatus(), ParseMode.MarkdownV2);
|
case "/list" -> outbound(genOnlineStatus(), ParseMode.MarkdownV2);
|
||||||
case "/setpin" -> {
|
case "/setpin" -> {
|
||||||
|
@ -188,6 +171,22 @@ public class TGBridge {
|
||||||
}
|
}
|
||||||
}, ParseMode.MarkdownV2
|
}, ParseMode.MarkdownV2
|
||||||
);
|
);
|
||||||
|
case "/shutdown" -> {
|
||||||
|
if (server.getAllPlayers().isEmpty()) {
|
||||||
|
ServerCloser.INSTANCE.fastShutdown();
|
||||||
|
outbound("server will shutdown in 1 minute.\nuse /fuck to cancel.");
|
||||||
|
} else {
|
||||||
|
outbound("still player online, can't shutdown.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "/fuck" -> {
|
||||||
|
if (server.getAllPlayers().isEmpty()) {
|
||||||
|
ServerCloser.INSTANCE.slowShutdown();
|
||||||
|
outbound("shutdown timer has been set to 60 minutes.");
|
||||||
|
} else {
|
||||||
|
outbound("still player online, can't shutdown.");
|
||||||
|
}
|
||||||
|
}
|
||||||
default -> {}
|
default -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,7 +306,7 @@ public class TGBridge {
|
||||||
}
|
}
|
||||||
String result = String.join("\n", out);
|
String result = String.join("\n", out);
|
||||||
if (shutdownCountMinutes < 0) return result;
|
if (shutdownCountMinutes < 0) return result;
|
||||||
if (shutdownCountMinutes == 0) return "server is shutdown\\.%n%s".formatted(result);
|
if (shutdownCountMinutes == 0 || PROXY_SHUT_DOWN) return "server already shutdown\\.%n%s".formatted(result);
|
||||||
return "server will shutdown after %s minute\\.%n%s".formatted(shutdownCountMinutes, result);
|
return "server will shutdown after %s minute\\.%n%s".formatted(shutdownCountMinutes, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,14 +430,14 @@ public class TGBridge {
|
||||||
} catch (InterruptedException ignored) {}
|
} catch (InterruptedException ignored) {}
|
||||||
if (oldestRequest == null) {
|
if (oldestRequest == null) {
|
||||||
plugin.logger.warn("updateRequests.take() return a null value, why?");
|
plugin.logger.warn("updateRequests.take() return a null value, why?");
|
||||||
sleep(1000);
|
sleep(10000);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
updateRequests.clear();
|
updateRequests.clear();
|
||||||
|
|
||||||
if (!updateOnlineStatus()) {
|
if (!updateOnlineStatus()) {
|
||||||
updateRequests.add(oldestRequest); // 更新失败 回去吧您内
|
updateRequests.add(oldestRequest); // 更新失败 回去吧您内
|
||||||
sleep(1000);
|
sleep(10000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
|
@ -479,7 +478,8 @@ public class TGBridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean setOnlineStatusNotAvailable() {
|
protected boolean setOnlineStatusNotAvailable() {
|
||||||
return editOnlineStatusMessage("(proxy already shutdown)");
|
return updateOnlineStatus();
|
||||||
|
// return editOnlineStatusMessage("(proxy already shutdown)");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
|
private static final Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
|
||||||
|
@ -490,8 +490,11 @@ public class TGBridge {
|
||||||
}
|
}
|
||||||
BaseResponse response;
|
BaseResponse response;
|
||||||
try {
|
try {
|
||||||
response = bot.execute(new EditMessageText(CHAT_ID, ONLINE_STATUS_MESSAGE_ID, markdownText).parseMode(ParseMode.MarkdownV2));
|
response = bot.execute(new EditMessageText(CHAT_ID, ONLINE_STATUS_MESSAGE_ID, markdownText).parseMode(ParseMode.MarkdownV2).disableWebPagePreview(true));
|
||||||
if (!response.isOk()) {
|
if (!response.isOk()) {
|
||||||
|
if (response.description().equals("Bad Request: message is not modified: specified new message content and reply markup are exactly the same as a current content and reply markup of the message")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
String responseJSON = prettyGson.toJson(response);
|
String responseJSON = prettyGson.toJson(response);
|
||||||
plugin.logger.warn("update failed: {}", responseJSON);
|
plugin.logger.warn("update failed: {}", responseJSON);
|
||||||
}
|
}
|
||||||
|
@ -526,7 +529,7 @@ public class TGBridge {
|
||||||
protected JoinLeftAnnounceMessage(String firstMessage) {
|
protected JoinLeftAnnounceMessage(String firstMessage) {
|
||||||
text = new StringBuilder(firstMessage);
|
text = new StringBuilder(firstMessage);
|
||||||
time = System.currentTimeMillis();
|
time = System.currentTimeMillis();
|
||||||
timeMinute = time/MINUTE;
|
timeMinute = time / MINUTE;
|
||||||
sendAnnounceMessage();
|
sendAnnounceMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue