ServerRemoteControl

ServerRemoteControl.java v1.2
多版本兼容的远程服务器控制插件,支持HTTP API、公私钥验证、服务器重启等功能
作者: immml

命令列表

  • src显示插件帮助信息
  • srcreload重载插件配置

权限列表

  • servercontrol.admin插件管理权限
package com.example.servercontrol;

import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.scheduler.BukkitRunnable;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * @pluginName ServerRemoteControl
 * @author immml
 * @version 1.2
 * @description 多版本兼容的远程服务器控制插件,支持HTTP API、公私钥验证、服务器重启等功能
 * [command]src|显示插件帮助信息[/command]
 * [command]srcreload|重载插件配置[/command]
 * [Permission]servercontrol.admin|插件管理权限[/Permission]
 */
public class ServerRemoteControl extends JavaPlugin {
    
    private HttpServer httpServer;
    private FileConfiguration config;
    private File configFile;
    private PrivateKey privateKey;
    private PublicKey publicKey;
    private Executor threadPool;
    
    @Override
    public void onEnable() {
        // 初始化配置
        saveDefaultConfig();
        config = getConfig();
        configFile = new File(getDataFolder(), "config.yml");
        
        // 创建线程池
        threadPool = Executors.newFixedThreadPool(config.getInt("server.threads", 4));
        
        // 加载密钥
        loadKeys();
        
        // 启动HTTP服务器
        startHttpServer();
        
        getLogger().info("服务器远程控制插件已启用 - 版本 1.2");
        getLogger().info("HTTP服务运行在端口: " + config.getInt("server.port"));
    }
    
    @Override
    public void onDisable() {
        // 关闭HTTP服务器
        if (httpServer != null) {
            httpServer.stop(0);
            getLogger().info("HTTP服务器已关闭");
        }
        getLogger().info("服务器远程控制插件已禁用");
    }
    
    private void loadKeys() {
        try {
            // 加载或生成密钥
            String privateKeyStr = config.getString("security.private_key");
            String publicKeyStr = config.getString("security.public_key");
            
            if (privateKeyStr == null || privateKeyStr.isEmpty() || 
                publicKeyStr == null || publicKeyStr.isEmpty()) {
                generateNewKeys();
                return;
            }
            
            // 加载私钥
            byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr);
            PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            privateKey = keyFactory.generatePrivate(privateKeySpec);
            
            // 加载公钥
            byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyStr);
            X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
            publicKey = keyFactory.generatePublic(publicKeySpec);
            
            getLogger().info("密钥加载成功");
            
        } catch (Exception e) {
            getLogger().warning("密钥加载失败,生成新密钥: " + e.getMessage());
            generateNewKeys();
        }
    }
    
    private void generateNewKeys() {
        try {
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
            keyGen.initialize(2048);
            KeyPair keyPair = keyGen.generateKeyPair();
            
            privateKey = keyPair.getPrivate();
            publicKey = keyPair.getPublic();
            
            // 保存到配置
            config.set("security.private_key", Base64.getEncoder().encodeToString(privateKey.getEncoded()));
            config.set("security.public_key", Base64.getEncoder().encodeToString(publicKey.getEncoded()));
            saveConfig();
            
            getLogger().info("新密钥对已生成并保存");
            
        } catch (Exception e) {
            getLogger().severe("生成密钥失败: " + e.getMessage());
        }
    }
    
    private void startHttpServer() {
        try {
            int port = config.getInt("server.port", 8080);
            httpServer = HttpServer.create(new InetSocketAddress(port), 0);
            
            // 创建API处理器
            httpServer.createContext("/api/server", new ApiHandler());
            
            httpServer.setExecutor(threadPool);
            httpServer.start();
            
            getLogger().info("HTTP API服务器已启动在端口 " + port);
            
        } catch (IOException e) {
            getLogger().severe("启动HTTP服务器失败: " + e.getMessage());
        }
    }
    
    private void reloadConfiguration() {
        try {
            config = YamlConfiguration.loadConfiguration(configFile);
            getLogger().info("配置重载成功");
            
            // 重启HTTP服务器
            if (httpServer != null) {
                httpServer.stop(0);
            }
            startHttpServer();
            
        } catch (Exception e) {
            getLogger().warning("重载配置失败: " + e.getMessage());
        }
    }
    
    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        if (!sender.hasPermission("servercontrol.admin")) {
            sender.sendMessage("§c你没有权限使用此命令!");
            return true;
        }
        
        if (command.getName().equalsIgnoreCase("src")) {
            if (args.length == 0) {
                showHelp(sender);
                return true;
            }
            
            switch (args[0].toLowerCase()) {
                case "reload":
                    reloadConfiguration();
                    sender.sendMessage("§a配置已重载!");
                    break;
                case "info":
                    showServerInfo(sender);
                    break;
                case "keys":
                    showKeyInfo(sender);
                    break;
                default:
                    showHelp(sender);
            }
            return true;
        }
        
        if (command.getName().equalsIgnoreCase("srcreload")) {
            reloadConfiguration();
            sender.sendMessage("§a配置已重载!");
            return true;
        }
        
        return false;
    }
    
    private void showHelp(CommandSender sender) {
        sender.sendMessage("§6§l服务器远程控制插件 - 帮助");
        sender.sendMessage("§e/src help §7- 显示此帮助信息");
        sender.sendMessage("§e/src reload §7- 重载插件配置");
        sender.sendMessage("§e/src info §7- 显示服务器信息");
        sender.sendMessage("§e/src keys §7- 显示密钥信息");
        sender.sendMessage("§e/srcreload §7- 快速重载配置");
        sender.sendMessage("§6API端口: §e" + config.getInt("server.port"));
    }
    
    private void showServerInfo(CommandSender sender) {
        sender.sendMessage("§6§l服务器信息");
        sender.sendMessage("§eAPI端口: §7" + config.getInt("server.port"));
        sender.sendMessage("§e在线玩家: §7" + Bukkit.getOnlinePlayers().size());
        sender.sendMessage("§e最大玩家: §7" + Bukkit.getMaxPlayers());
        sender.sendMessage("§e服务器版本: §7" + Bukkit.getVersion());
    }
    
    private void showKeyInfo(CommandSender sender) {
        sender.sendMessage("§6§l密钥信息");
        sender.sendMessage("§e公钥长度: §7" + (publicKey != null ? publicKey.getEncoded().length + " bytes" : "未加载"));
        sender.sendMessage("§e私钥长度: §7" + (privateKey != null ? privateKey.getEncoded().length + " bytes" : "未加载"));
        sender.sendMessage("§6配置文件中查看完整密钥");
    }
    
    // API请求处理器
    private class ApiHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange exchange) throws IOException {
            if (!"POST".equalsIgnoreCase(exchange.getRequestMethod())) {
                sendErrorResponse(exchange, 405, "Method Not Allowed");
                return;
            }
            
            try {
                // 读取请求体
                String requestBody = readRequestBody(exchange);
                
                // 验证签名
                if (!verifySignature(requestBody, exchange)) {
                    sendErrorResponse(exchange, 401, "Invalid Signature");
                    return;
                }
                
                // 处理API请求
                processApiRequest(exchange, requestBody);
                
            } catch (Exception e) {
                getLogger().warning("处理API请求时出错: " + e.getMessage());
                sendErrorResponse(exchange, 500, "Internal Server Error");
            }
        }
        
        private String readRequestBody(HttpExchange exchange) throws IOException {
            InputStream is = exchange.getRequestBody();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
            StringBuilder body = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                body.append(line);
            }
            return body.toString();
        }
        
        private boolean verifySignature(String requestBody, HttpExchange exchange) {
            try {
                // 从头部获取签名
                String signature = exchange.getRequestHeaders().getFirst("X-Signature");
                if (signature == null) {
                    return false;
                }
                
                // 验证签名
                Signature sig = Signature.getInstance("SHA256withRSA");
                sig.initVerify(publicKey);
                sig.update(requestBody.getBytes(StandardCharsets.UTF_8));
                
                byte[] signatureBytes = Base64.getDecoder().decode(signature);
                return sig.verify(signatureBytes);
                
            } catch (Exception e) {
                getLogger().warning("签名验证失败: " + e.getMessage());
                return false;
            }
        }
        
        private void processApiRequest(HttpExchange exchange, String requestBody) throws IOException {
            // 简单的JSON解析(避免外部依赖)
            Map<String, String> requestData = parseSimpleJson(requestBody);
            String action = requestData.get("action");
            String data = requestData.get("data");
            
            if (action == null) {
                sendErrorResponse(exchange, 400, "Missing action");
                return;
            }
            
            String response;
            switch (action.toLowerCase()) {
                case "execute":
                    response = executeCommand(data);
                    break;
                case "info":
                    response = getServerInfo();
                    break;
                case "players":
                    response = getOnlinePlayers();
                    break;
                case "restart":
                    response = scheduleRestart(data);
                    break;
                case "broadcast":
                    response = broadcastMessage(data);
                    break;
                default:
                    sendErrorResponse(exchange, 400, "Unknown action");
                    return;
            }
            
            sendSuccessResponse(exchange, response);
        }
        
        private Map<String, String> parseSimpleJson(String json) {
            Map<String, String> result = new HashMap<>();
            // 简单解析 {"key":"value"} 格式
            json = json.replaceAll("[{}\"]", "").trim();
            String[] pairs = json.split(",");
            for (String pair : pairs) {
                String[] keyValue = pair.split(":", 2);
                if (keyValue.length == 2) {
                    result.put(keyValue[0].trim(), keyValue[1].trim());
                }
            }
            return result;
        }
        
        private String executeCommand(String command) {
            if (command == null || command.isEmpty()) {
                return "Error: No command specified";
            }
            
            try {
                boolean result = Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command);
                return result ? "Command executed successfully" : "Command execution failed";
            } catch (Exception e) {
                return "Error: " + e.getMessage();
            }
        }
        
        private String getServerInfo() {
            return String.format(
                "Server: %s, Version: %s, Players: %d/%d, TPS: %.2f",
                Bukkit.getName(),
                Bukkit.getVersion(),
                Bukkit.getOnlinePlayers().size(),
                Bukkit.getMaxPlayers(),
                getTps()
            );
        }
        
        private double getTps() {
            // 简单的TPS获取(Paper服务器)
            try {
                double[] tps = Bukkit.getTPS();
                return tps[0]; // 最近1分钟的TPS
            } catch (Exception e) {
                return 20.0; // 默认值
            }
        }
        
        private String getOnlinePlayers() {
            StringBuilder sb = new StringBuilder();
            Bukkit.getOnlinePlayers().forEach(player -> 
                sb.append(player.getName()).append(",")
            );
            return sb.length() > 0 ? sb.substring(0, sb.length() - 1) : "No players online";
        }
        
        private String scheduleRestart(String delayStr) {
            try {
                int delay = delayStr != null ? Integer.parseInt(delayStr) : 10;
                delay = Math.max(5, Math.min(delay, 300)); // 限制5-300秒
                
                new BukkitRunnable() {
                    @Override
                    public void run() {
                        Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "restart");
                    }
                }.runTaskLater(ServerRemoteControl.this, delay * 20L);
                
                return String.format("Restart scheduled in %d seconds", delay);
                
            } catch (NumberFormatException e) {
                return "Error: Invalid delay format";
            }
        }
        
        private String broadcastMessage(String message) {
            if (message == null || message.isEmpty()) {
                return "Error: No message specified";
            }
            
            Bukkit.broadcastMessage("§6[广播] §f" + message);
            return "Message broadcasted";
        }
        
        private void sendSuccessResponse(HttpExchange exchange, String response) throws IOException {
            exchange.getResponseHeaders().set("Content-Type", "application/json; charset=UTF-8");
            String jsonResponse = "{\"status\":\"success\",\"data\":\"" + response.replace("\"", "\\\"") + "\"}";
            exchange.sendResponseHeaders(200, jsonResponse.getBytes(StandardCharsets.UTF_8).length);
            try (OutputStream os = exchange.getResponseBody()) {
                os.write(jsonResponse.getBytes(StandardCharsets.UTF_8));
            }
        }
        
        private void sendErrorResponse(HttpExchange exchange, int code, String message) throws IOException {
            exchange.getResponseHeaders().set("Content-Type", "application/json; charset=UTF-8");
            String jsonResponse = "{\"status\":\"error\",\"message\":\"" + message + "\"}";
            exchange.sendResponseHeaders(code, jsonResponse.getBytes(StandardCharsets.UTF_8).length);
            try (OutputStream os = exchange.getResponseBody()) {
                os.write(jsonResponse.getBytes(StandardCharsets.UTF_8));
            }
        }
    }
}
上一篇: NSM下一篇: MenuBackpack

举报内容

意见反馈