Compare commits
No commits in common. "cba3248484cd57d7bb2e63c686b5b252781d11de" and "49a5dd76e7465550a12a8c97d43b6bf6ebb02993" have entirely different histories.
cba3248484
...
49a5dd76e7
12 changed files with 262 additions and 345 deletions
|
@ -1,8 +1,6 @@
|
|||
# VTools
|
||||
Tools for Velocity proxy server.
|
||||
|
||||
add telegram/auto shutdown support for spec server.
|
||||
|
||||
## Commands
|
||||
|Command|What is does?|Permission|
|
||||
|-------|-------------|----------|
|
||||
|
|
|
@ -1,223 +0,0 @@
|
|||
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,9 +1,8 @@
|
|||
package com.alpt.vtools.listeners;
|
||||
package de.strifel.VTools;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import de.strifel.VTools.VTools;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -14,26 +13,18 @@ import java.util.Collection;
|
|||
|
||||
public class OnlinePlayerQueryService {
|
||||
|
||||
public static OnlinePlayerQueryService INSTANCE;
|
||||
|
||||
private final VTools plugin;
|
||||
|
||||
private OnlinePlayerQueryService(VTools plugin) {
|
||||
public OnlinePlayerQueryService(VTools plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
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"));
|
||||
public void register() {
|
||||
try {
|
||||
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", port), 0);
|
||||
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 17611), 0);
|
||||
|
||||
server.createContext("/api/getOnlinePlayers", exchange -> {
|
||||
Collection<Player> players = plugin.getServer().getAllPlayers();
|
191
src/main/java/de/strifel/VTools/ServerCloser.java
Normal file
191
src/main/java/de/strifel/VTools/ServerCloser.java
Normal file
|
@ -0,0 +1,191 @@
|
|||
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,7 +1,5 @@
|
|||
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.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.plugin.Plugin;
|
||||
|
@ -11,17 +9,11 @@ import de.strifel.VTools.commands.*;
|
|||
import de.strifel.VTools.listeners.*;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.slf4j.Logger;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
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.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 {
|
||||
private final ProxyServer server;
|
||||
public final Logger logger;
|
||||
|
@ -50,46 +42,11 @@ public class VTools {
|
|||
server.getCommandManager().register("tps", new CommandTp(server), "jump");
|
||||
server.getCommandManager().register("server", new CommandServer(server), "serverv");
|
||||
server.getCommandManager().register("servers", new CommandServers(server), "allservers");
|
||||
loadConfig();
|
||||
new TGBridge(this).register();
|
||||
new PlayerStatus(this).register();
|
||||
new GlobalChat(this).register();
|
||||
ServerCloser.createInstance(this);
|
||||
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);
|
||||
}
|
||||
new ServerCloser(this).register();
|
||||
new OnlinePlayerQueryService(this).register();
|
||||
}
|
||||
|
||||
public ProxyServer getServer() {
|
||||
|
|
|
@ -54,7 +54,7 @@ public class CommandGlobalChat implements SimpleCommand {
|
|||
|
||||
@Override
|
||||
public List<String> suggest(Invocation invocation) {
|
||||
return new ArrayList<>();
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package de.strifel.VTools.commands;
|
||||
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.command.SimpleCommand;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
|
@ -18,6 +19,7 @@ public class CommandRestart implements SimpleCommand {
|
|||
|
||||
@Override
|
||||
public void execute(SimpleCommand.Invocation invocation) {
|
||||
CommandSource commandSource = invocation.source();
|
||||
String[] strings = invocation.arguments();
|
||||
|
||||
if (strings.length > 0) {
|
||||
|
@ -31,7 +33,7 @@ public class CommandRestart implements SimpleCommand {
|
|||
|
||||
@Override
|
||||
public List<String> suggest(SimpleCommand.Invocation invocation) {
|
||||
return new ArrayList<>();
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -9,7 +9,10 @@ import com.velocitypowered.api.proxy.ServerConnection;
|
|||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -36,20 +39,23 @@ public class CommandSend implements SimpleCommand {
|
|||
for (Player player : server.getAllPlayers()) {
|
||||
oPlayer.add(player);
|
||||
}
|
||||
} else if (strings[0].equals("current")) {
|
||||
}
|
||||
else if (strings[0].equals("current")) {
|
||||
if (commandSource instanceof Player) {
|
||||
Player playerSource = (Player) commandSource;
|
||||
Player playerSource = (Player)commandSource;
|
||||
Optional<ServerConnection> conn = playerSource.getCurrentServer();
|
||||
if (conn.isPresent()) {
|
||||
for (Player player : conn.get().getServer().getPlayersConnected()) {
|
||||
oPlayer.add(player);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
commandSource.sendMessage(Component.text("Command is only for players.").color(COLOR_RED));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Optional<Player> p = server.getPlayer(strings[0]);
|
||||
if (p.isPresent()) {
|
||||
oPlayer.add(p.get());
|
||||
|
@ -90,7 +96,7 @@ public class CommandSend implements SimpleCommand {
|
|||
String sendResults = results.isEmpty() ? "nothing" : results.entrySet().stream().map(
|
||||
entry -> String.format("%s : %d", entry.getKey(), entry.getValue())
|
||||
).collect(Collectors.joining("\n"));
|
||||
commandSource.sendMessage(Component.text("Send Results:%n%s".formatted(sendResults)).color(COLOR_YELLOW));
|
||||
commandSource.sendMessage(Component.text(String.format("Send Results:\n%s", sendResults)).color(COLOR_YELLOW));
|
||||
}).start();
|
||||
} else {
|
||||
commandSource.sendMessage(Component.text("The server or user does not exist!").color(COLOR_RED));
|
||||
|
@ -102,23 +108,21 @@ public class CommandSend implements SimpleCommand {
|
|||
|
||||
public List<String> suggest(SimpleCommand.Invocation invocation) {
|
||||
String[] currentArgs = invocation.arguments();
|
||||
switch (currentArgs.length) {
|
||||
case 0, 1 -> {
|
||||
List<String> arg = new ArrayList<>(server.getPlayerCount() + 2);
|
||||
|
||||
List<String> arg = new ArrayList<String>();
|
||||
if (currentArgs.length <= 1) {
|
||||
arg.add("all");
|
||||
arg.add("current");
|
||||
for (Player player : server.getAllPlayers()) {
|
||||
arg.add(player.getUsername());
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
case 2 -> {
|
||||
return server.getAllServers().stream().map(s -> s.getServerInfo().getName()).toList();
|
||||
}
|
||||
default -> {
|
||||
return new ArrayList<>(0);
|
||||
} else if (currentArgs.length == 2) {
|
||||
for (RegisteredServer server : server.getAllServers()) {
|
||||
arg.add(server.getServerInfo().getName());
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
public boolean hasPermission(SimpleCommand.Invocation invocation) {
|
||||
|
|
|
@ -34,7 +34,7 @@ public class CommandServers implements SimpleCommand {
|
|||
|
||||
@Override
|
||||
public List<String> suggest(SimpleCommand.Invocation invocation) {
|
||||
return new ArrayList<>();
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,11 +26,11 @@ public class CommandTp implements SimpleCommand {
|
|||
CommandSource commandSource = commandInvocation.source();
|
||||
String[] strings = commandInvocation.arguments();
|
||||
|
||||
if (commandSource instanceof Player playerCommandSource) {
|
||||
if (commandSource instanceof Player) {
|
||||
if (strings.length == 1) {
|
||||
Optional<Player> player = server.getPlayer(strings[0]);
|
||||
if (player.isPresent()) {
|
||||
player.get().getCurrentServer().ifPresent(serverConnection -> playerCommandSource.createConnectionRequest(serverConnection.getServer()).fireAndForget());
|
||||
player.get().getCurrentServer().ifPresent(serverConnection -> ((Player) commandSource).createConnectionRequest(serverConnection.getServer()).fireAndForget());
|
||||
commandSource.sendMessage(Component.text("Connecting to the server of " + strings[0]).color(COLOR_YELLOW));
|
||||
} else {
|
||||
commandSource.sendMessage(Component.text("Player does not exists.").color(COLOR_RED));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.alpt.vtools.utils;
|
||||
package de.strifel.VTools.listeners;
|
||||
|
||||
import com.pengrad.telegrambot.model.Message;
|
||||
import com.pengrad.telegrambot.model.MessageEntity;
|
|
@ -1,7 +1,5 @@
|
|||
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.GsonBuilder;
|
||||
import com.pengrad.telegrambot.Callback;
|
||||
|
@ -29,8 +27,13 @@ import de.strifel.VTools.VTools;
|
|||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
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.concurrent.LinkedBlockingQueue;
|
||||
import java.util.function.BiConsumer;
|
||||
|
@ -74,9 +77,24 @@ public class TGBridge {
|
|||
}
|
||||
|
||||
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) {
|
||||
CHAT_ID = Long.parseLong(plugin.getConfigOrDefault("chat_id", "0"));
|
||||
TOKEN = plugin.getConfigOrDefault("token", "");
|
||||
this.CHAT_ID = Long.parseLong(config.getOrDefault("chat_id", "0"));
|
||||
this.TOKEN = config.getOrDefault("token", "");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.logger.error("parsing config", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,10 +138,9 @@ public class TGBridge {
|
|||
if (message.text() != null && !message.text().isEmpty()) {
|
||||
String msgText = message.text();
|
||||
if (msgText.startsWith("/")) {
|
||||
String[] s = msgText.split("((@\\w+bot)|(@\\w+bot)?[\t \n]+)", 2);
|
||||
String[] s = msgText.split("(@[A-Za-z0-9_]bot)?[\t \n]+", 2);
|
||||
String command = s[0];
|
||||
@Nullable String arg = s.length == 2 ? s[1] : null;
|
||||
System.out.println(command);
|
||||
switch (command) {
|
||||
case "/list" -> outbound(genOnlineStatus(), ParseMode.MarkdownV2);
|
||||
case "/setpin" -> {
|
||||
|
@ -171,22 +188,6 @@ public class TGBridge {
|
|||
}
|
||||
}, 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 -> {}
|
||||
}
|
||||
}
|
||||
|
@ -306,7 +307,7 @@ public class TGBridge {
|
|||
}
|
||||
String result = String.join("\n", out);
|
||||
if (shutdownCountMinutes < 0) return result;
|
||||
if (shutdownCountMinutes == 0 || PROXY_SHUT_DOWN) return "server already shutdown\\.%n%s".formatted(result);
|
||||
if (shutdownCountMinutes == 0) return "server is shutdown\\.%n%s".formatted(result);
|
||||
return "server will shutdown after %s minute\\.%n%s".formatted(shutdownCountMinutes, result);
|
||||
}
|
||||
|
||||
|
@ -430,14 +431,14 @@ public class TGBridge {
|
|||
} catch (InterruptedException ignored) {}
|
||||
if (oldestRequest == null) {
|
||||
plugin.logger.warn("updateRequests.take() return a null value, why?");
|
||||
sleep(10000);
|
||||
sleep(1000);
|
||||
continue;
|
||||
}
|
||||
updateRequests.clear();
|
||||
|
||||
if (!updateOnlineStatus()) {
|
||||
updateRequests.add(oldestRequest); // 更新失败 回去吧您内
|
||||
sleep(10000);
|
||||
sleep(1000);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
@ -478,8 +479,7 @@ public class TGBridge {
|
|||
}
|
||||
|
||||
protected boolean setOnlineStatusNotAvailable() {
|
||||
return updateOnlineStatus();
|
||||
// return editOnlineStatusMessage("(proxy already shutdown)");
|
||||
return editOnlineStatusMessage("(proxy already shutdown)");
|
||||
}
|
||||
|
||||
private static final Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
@ -490,11 +490,8 @@ public class TGBridge {
|
|||
}
|
||||
BaseResponse response;
|
||||
try {
|
||||
response = bot.execute(new EditMessageText(CHAT_ID, ONLINE_STATUS_MESSAGE_ID, markdownText).parseMode(ParseMode.MarkdownV2).disableWebPagePreview(true));
|
||||
response = bot.execute(new EditMessageText(CHAT_ID, ONLINE_STATUS_MESSAGE_ID, markdownText).parseMode(ParseMode.MarkdownV2));
|
||||
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);
|
||||
plugin.logger.warn("update failed: {}", responseJSON);
|
||||
}
|
||||
|
@ -529,7 +526,7 @@ public class TGBridge {
|
|||
protected JoinLeftAnnounceMessage(String firstMessage) {
|
||||
text = new StringBuilder(firstMessage);
|
||||
time = System.currentTimeMillis();
|
||||
timeMinute = time / MINUTE;
|
||||
timeMinute = time/MINUTE;
|
||||
sendAnnounceMessage();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue