ServerRemoteControl.java v1.2
多版本兼容的远程服务器控制插件,支持HTTP API、公私钥验证、服务器重启等功能
命令列表
- src显示插件帮助信息
- srcreload重载插件配置
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));
}
}
}
}