Add multi server run console command

This commit is contained in:
方块君
2022-07-26 19:14:49 +08:00
parent 37dcd0f1a7
commit 4d08acd084
19 changed files with 509 additions and 149 deletions

View File

@@ -15,9 +15,11 @@ import java.util.ArrayList;
public final class EventListeners {
private static MessageHandler consoleMessageHandler;
public static void setConsoleMessageHandler(MessageHandler handler) {
consoleMessageHandler = handler;
}
public static void onCommandResponse(ReceiveCommandFeedbackEvent event) {
if (consoleMessageHandler != null && event.getPlayer() == null) {
consoleMessageHandler.setMessage(event.getMessage());

View File

@@ -25,4 +25,5 @@ public class OpenCommandConfig {
public int socketPort = 5746;
public String socketToken = "";
public String socketHost = "127.0.0.1";
public String socketDisplayName = "";
}

View File

@@ -19,6 +19,7 @@ package com.github.jie65535.opencommand;
import com.github.jie65535.opencommand.json.JsonRequest;
import com.github.jie65535.opencommand.json.JsonResponse;
import com.github.jie65535.opencommand.socket.SocketData;
import emu.grasscutter.command.CommandMap;
import emu.grasscutter.server.http.Router;
import emu.grasscutter.utils.Crypto;
@@ -31,6 +32,7 @@ import io.javalin.Javalin;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@@ -85,6 +87,11 @@ public final class OpenCommandHandler implements Router {
} else if (req.action.equals("ping")) {
response.json(new JsonResponse());
return;
} else if (req.action.equals("online")) {
var p = new ArrayList<String>();
plugin.getServer().getPlayers().forEach((uid, player) -> p.add(player.getNickname()));
response.json(new JsonResponse(200, "Success", new SocketData.OnlinePlayer(p)));
return;
}
// token is required
@@ -118,6 +125,9 @@ public final class OpenCommandHandler implements Router {
}
}
return;
} else if (req.action.equals("runmode")) {
response.json(new JsonResponse(200, "Success", 0));
return;
}
} else if (codes.containsKey(req.token)) {
if (req.action.equals("verify")) {

View File

@@ -23,6 +23,7 @@ import com.github.jie65535.opencommand.socket.SocketData;
import com.github.jie65535.opencommand.socket.SocketDataWait;
import com.github.jie65535.opencommand.socket.SocketServer;
import com.github.jie65535.opencommand.socket.packet.HttpPacket;
import com.github.jie65535.opencommand.socket.packet.RunConsoleCommand;
import com.github.jie65535.opencommand.socket.packet.player.Player;
import com.github.jie65535.opencommand.socket.packet.player.PlayerEnum;
import emu.grasscutter.command.CommandMap;
@@ -92,6 +93,9 @@ public final class OpenCommandOnlyHttpHandler implements Router {
} else if (req.action.equals("ping")) {
response.json(new JsonResponse());
return;
} else if (req.action.equals("online")) {
response.json(new JsonResponse(200, "Success", SocketData.getOnlinePlayer()));
return;
}
// token is required
@@ -110,20 +114,40 @@ public final class OpenCommandOnlyHttpHandler implements Router {
response.json(new JsonResponse());
return;
} else if (req.action.equals("command")) {
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (plugin) {
try {
plugin.getLogger().info(String.format("IP: %s run command in console > %s", request.ip(), req.data));
var resultCollector = new MessageHandler();
EventListeners.setConsoleMessageHandler(resultCollector);
CommandMap.getInstance().invoke(null, null, req.data.toString());
response.json(new JsonResponse(resultCollector.getMessage()));
} catch (Exception e) {
plugin.getLogger().warn("Run command failed.", e);
EventListeners.setConsoleMessageHandler(null);
response.json(new JsonResponse(500, "error", e.getLocalizedMessage()));
}
var server = SocketServer.getClientInfoByUuid(req.server);
if (server == null) {
response.json(new JsonResponse(404, "Server Not Found."));
return;
}
plugin.getLogger().info(String.format("IP: %s run command in console > %s", request.ip(), req.data));
var wait = new SocketDataWait<HttpPacket>(2000L) {
@Override
public void run() {
}
@Override
public HttpPacket initData(HttpPacket data) {
return data;
}
@Override
public void timeout() {
}
};
SocketServer.sendPacketAndWait(server.ip, new RunConsoleCommand(req.data.toString()), wait);
var data = wait.getData();
if (data == null) {
response.json(new JsonResponse(408, "Timeout"));
return;
}
response.json(new JsonResponse(data.code, data.message, data.data));
return;
} else if (req.action.equals("server")) {
response.json(new JsonResponse(200, "Success", SocketServer.getOnlineClient()));
return;
} else if (req.action.equals("runmode")) {
response.json(new JsonResponse(200, "Success", 1));
return;
}
} else if (codes.containsKey(req.token)) {
@@ -143,7 +167,8 @@ public final class OpenCommandOnlyHttpHandler implements Router {
if (req.action.equals("command")) {
SocketDataWait<HttpPacket> socketDataWait = new SocketDataWait<>(1000L * 10L) {
@Override
public void run() {}
public void run() {
}
@Override
public HttpPacket initData(HttpPacket data) {
@@ -152,7 +177,6 @@ public final class OpenCommandOnlyHttpHandler implements Router {
@Override
public void timeout() {
response.json(new JsonResponse(408, "Wait server timeout"));
}
};
@@ -173,7 +197,7 @@ public final class OpenCommandOnlyHttpHandler implements Router {
HttpPacket httpPacket = socketDataWait.getData();
if (httpPacket == null) {
response.json(new JsonResponse(500, "error", "Server connect failed."));
response.json(new JsonResponse(500, "error", "Wait timeout"));
return;
}
response.json(new JsonResponse(httpPacket.code, httpPacket.message));

View File

@@ -35,7 +35,10 @@ import java.io.IOException;
public final class OpenCommandPlugin extends Plugin {
private static OpenCommandPlugin instance;
public static OpenCommandPlugin getInstance() { return instance; }
public static OpenCommandPlugin getInstance() {
return instance;
}
private OpenCommandConfig config;
@@ -82,19 +85,19 @@ public final class OpenCommandPlugin extends Plugin {
var configFile = new File(getDataFolder(), "config.json");
if (!configFile.exists()) {
config = new OpenCommandConfig();
try (var file = new FileWriter(configFile)){
try (var file = new FileWriter(configFile)) {
file.write(Grasscutter.getGsonFactory().toJson(config));
} catch (IOException e) {
getLogger().error("Unable to write to config file.");
getLogger().error("[OpenCommand] Unable to write to config file.");
} catch (Exception e) {
getLogger().error("Unable to save config file.");
getLogger().error("[OpenCommand] Unable to save config file.");
}
} else {
try (var file = new FileReader(configFile)) {
config = Grasscutter.getGsonFactory().fromJson(file, OpenCommandConfig.class);
} catch (Exception exception) {
config = new OpenCommandConfig();
getLogger().error("There was an error while trying to load the configuration from config.json. Please make sure that there are no syntax errors. If you want to start with a default configuration, delete your existing config.json.");
getLogger().error("[OpenCommand] There was an error while trying to load the configuration from config.json. Please make sure that there are no syntax errors. If you want to start with a default configuration, delete your existing config.json.");
}
}
// 启动Socket
@@ -110,7 +113,7 @@ public final class OpenCommandPlugin extends Plugin {
try {
SocketServer.startServer();
} catch (IOException e) {
getLogger().error("Unable to start socket server.", e);
getLogger().error("[OpenCommand] Unable to start socket server.", e);
}
}
}

View File

@@ -20,5 +20,6 @@ package com.github.jie65535.opencommand.json;
public final class JsonRequest {
public String token = "";
public String action = "";
public String server = "";
public Object data = null;
}

View File

@@ -19,7 +19,7 @@ package com.github.jie65535.opencommand.json;
public final class JsonResponse {
public int retcode = 200;
public String message = "success";
public String message = "Success";
public Object data;
public JsonResponse() {

View File

@@ -0,0 +1,14 @@
package com.github.jie65535.opencommand.socket;
public class ClientInfo {
public final String uuid;
public final SocketServer.ClientThread clientThread;
public final String ip;
public ClientInfo(String uuid, String ip, SocketServer.ClientThread clientThread) {
this.uuid = uuid;
this.clientThread = clientThread;
this.ip = ip;
}
}

View File

@@ -1,5 +1,6 @@
package com.github.jie65535.opencommand.socket;
import com.github.jie65535.opencommand.EventListeners;
import com.github.jie65535.opencommand.OpenCommandConfig;
import com.github.jie65535.opencommand.OpenCommandPlugin;
import com.github.jie65535.opencommand.socket.packet.*;
@@ -33,6 +34,14 @@ public class SocketClient {
// 连接服务器
public static void connectServer() {
if (connect) return;
if (clientThread != null) {
mLogger.warn("[OpenCommand] Retry connecting to the server after 15 seconds");
try {
Thread.sleep(15000);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
OpenCommandConfig config = OpenCommandPlugin.getInstance().getConfig();
mLogger = OpenCommandPlugin.getInstance().getLogger();
clientThread = new ClientThread(config.socketHost, config.socketPort);
@@ -160,6 +169,24 @@ public class SocketClient {
}
}
break;
case RunConsoleCommand:
var consoleCommand = Grasscutter.getGsonFactory().fromJson(packet.data, RunConsoleCommand.class);
var plugin = OpenCommandPlugin.getInstance();
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (plugin) {
try {
var resultCollector = new MessageHandler();
EventListeners.setConsoleMessageHandler(resultCollector);
CommandMap.getInstance().invoke(null, null, consoleCommand.command);
sendPacket(new HttpPacket(resultCollector.getMessage()), packet.packetID);
} catch (Exception e) {
mLogger.warn("[OpenCommand] Run command failed.", e);
EventListeners.setConsoleMessageHandler(null);
sendPacket(new HttpPacket(500, "error", e.getLocalizedMessage()), packet.packetID);
} finally {
EventListeners.setConsoleMessageHandler(null);
}
}
}
} catch (Throwable e) {
e.printStackTrace();
@@ -206,18 +233,12 @@ public class SocketClient {
socket = new Socket(ip, port);
os = socket.getOutputStream();
mLogger.info("[OpenCommand]Connect to server: " + ip + ":" + port);
SocketClient.sendPacket(new AuthPacket(OpenCommandPlugin.getInstance().getConfig().socketToken));
mLogger.info("[OpenCommand] Connect to server: " + ip + ":" + port);
SocketClient.sendPacket(new AuthPacket(OpenCommandPlugin.getInstance().getConfig().socketToken, OpenCommandPlugin.getInstance().getConfig().socketDisplayName));
receiveThread = new ReceiveThread(socket);
} catch (IOException e) {
connect = false;
mLogger.warn("[OpenCommand]Connect to server failed: " + ip + ":" + port);
mLogger.warn("[OpenCommand] Retry connecting to the server after 15 seconds");
try {
Thread.sleep(15000);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
mLogger.warn("[OpenCommand] Connect to server failed: " + ip + ":" + port);
connectServer();
}
}

View File

@@ -2,6 +2,7 @@ package com.github.jie65535.opencommand.socket;
import com.github.jie65535.opencommand.socket.packet.player.PlayerList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicReference;
@@ -27,4 +28,21 @@ public class SocketData {
});
return ret.get();
}
public static OnlinePlayer getOnlinePlayer() {
ArrayList<String> player = new ArrayList<>();
playerList.forEach((address, playerMap) -> playerMap.playerMap.forEach((uid, name) -> player.add(name)));
return new OnlinePlayer(player);
}
public static class OnlinePlayer {
public int count;
public ArrayList<String> playerList;
public OnlinePlayer(ArrayList<String> playerList) {
this.playerList = playerList;
this.count = playerList.size();
}
}
}

View File

@@ -14,16 +14,34 @@ import java.net.Socket;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
// Socket 服务器
public class SocketServer {
// 客户端超时时间
private static final int TIMEOUT = 5000;
private static final HashMap<String, ClientThread> clientList = new HashMap<>();
private static final HashMap<String, ClientInfo> clientList = new HashMap<>();
private static final HashMap<String, Integer> clientTimeout = new HashMap<>();
private static Logger mLogger;
public static HashMap<String, String> getOnlineClient() {
HashMap<String, String> onlineClient = new HashMap<>();
for (var key : clientList.entrySet()) {
onlineClient.put(key.getValue().uuid, key.getValue().clientThread.getDisplayName());
}
return onlineClient;
}
public static ClientInfo getClientInfoByUuid(String uuid) {
for (var key : clientList.entrySet()) {
if (key.getValue().uuid.equals(uuid)) {
return key.getValue();
}
}
return null;
}
public static void startServer() throws IOException {
int port = OpenCommandPlugin.getInstance().getConfig().socketPort;
mLogger = OpenCommandPlugin.getInstance().getLogger();
@@ -34,7 +52,7 @@ public class SocketServer {
// 向全部客户端发送数据
public static boolean sendAllPacket(BasePacket packet) {
var p = SocketUtils.getPacket(packet);
HashMap<String, ClientThread> old = (HashMap<String, ClientThread>) clientList.clone();
HashMap<String, ClientThread> old = (HashMap<String, ClientThread>) clientList.clone();
for (var client : old.entrySet()) {
if (!client.getValue().sendPacket(p)) {
mLogger.warn("[OpenCommand] Send packet to client {} failed", client.getKey());
@@ -49,7 +67,21 @@ public class SocketServer {
var p = SocketUtils.getPacket(packet);
var client = clientList.get(address);
if (client != null) {
if (client.sendPacket(p)) {
if (client.clientThread.sendPacket(p)) {
return true;
}
mLogger.warn("[OpenCommand] Send packet to client {} failed", address);
clientList.remove(address);
}
return false;
}
public static boolean sendPacketAndWait(String address, BasePacket packet, SocketDataWait<?> wait) {
var p = SocketUtils.getPacketAndPackID(packet);
var client = clientList.get(address);
if (client != null) {
wait.uid = p.get(0);
if (client.clientThread.sendPacket(p.get(1), wait)) {
return true;
}
mLogger.warn("[OpenCommand] Send packet to client {} failed", address);
@@ -66,7 +98,7 @@ public class SocketServer {
var client = clientList.get(clientID);
if (client != null) {
socketDataWait.uid = p.get(0);
if (!client.sendPacket(p.get(1), socketDataWait)) {
if (!client.clientThread.sendPacket(p.get(1), socketDataWait)) {
mLogger.warn("[OpenCommand] Send packet to client {} failed", clientID);
clientList.remove(clientID);
return false;
@@ -97,13 +129,14 @@ public class SocketServer {
}
// 客户端数据包处理
private static class ClientThread extends Thread {
static class ClientThread extends Thread {
private final Socket socket;
private InputStream is;
private OutputStream os;
private final String address;
private final String token;
private boolean auth = false;
private String displayName = "";
private final HashMap<String, SocketDataWait<?>> socketDataWaitList = new HashMap<>();
@@ -147,11 +180,12 @@ public class SocketServer {
String data = SocketUtils.readString(is);
Packet packet = Grasscutter.getGsonFactory().fromJson(data, Packet.class);
if (packet.type == PacketEnum.AuthPacket) {
AuthPacket authPacket = Grasscutter.getGsonFactory().fromJson(data, AuthPacket.class);
AuthPacket authPacket = Grasscutter.getGsonFactory().fromJson(packet.data, AuthPacket.class);
if (authPacket.token.equals(token)) {
auth = true;
mLogger.info("[OpenCommand] Client {} auth success", address);
clientList.put(address, this);
displayName = authPacket.displayName;
mLogger.info("[OpenCommand] Client {} auth success, name: {}", address, displayName);
clientList.put(address, new ClientInfo(UUID.randomUUID().toString(), address, this));
clientTimeout.put(address, 0);
} else {
mLogger.warn("[OpenCommand] Client {} auth failed", address);
@@ -196,6 +230,10 @@ public class SocketServer {
}
}
}
public String getDisplayName() {
return displayName;
}
}
// 等待客户端连接

View File

@@ -17,12 +17,12 @@ public class SocketUtils {
/**
* 获取打包后的数据包
*
* @param bPacket 数据包
* @return 打包后的数据包
*/
public static String getPacket(BasePacket bPacket) {
Packet packet = new Packet();
packet.token = OpenCommandPlugin.getInstance().getConfig().socketToken;
packet.type = bPacket.getType();
packet.data = bPacket.getPacket();
packet.packetID = UUID.randomUUID().toString();
@@ -31,12 +31,12 @@ public class SocketUtils {
/**
* 获取打包后的数据包
*
* @param bPacket BasePacket
* @return list[0] 是包ID, list[1] 是数据包
*/
public static List<String> getPacketAndPackID(BasePacket bPacket) {
Packet packet = new Packet();
packet.token = OpenCommandPlugin.getInstance().getConfig().socketToken;
packet.type = bPacket.getType();
packet.data = bPacket.getPacket();
packet.packetID = UUID.randomUUID().toString();
@@ -49,13 +49,13 @@ public class SocketUtils {
/**
* 获取打包后的数据包
* @param bPacket 数据包
*
* @param bPacket 数据包
* @param packetID 数据包ID
* @return 打包后的数据包
*/
public static String getPacketAndPackID(BasePacket bPacket, String packetID) {
Packet packet = new Packet();
packet.token = OpenCommandPlugin.getInstance().getConfig().socketToken;
packet.type = bPacket.getType();
packet.data = bPacket.getPacket();
packet.packetID = packetID;
@@ -64,6 +64,7 @@ public class SocketUtils {
/**
* 读整数
*
* @param is 输入流
* @return 整数
*/
@@ -77,32 +78,34 @@ public class SocketUtils {
e.printStackTrace();
}
return values[0]<<24 | values[1]<<16 | values[2]<<8 | values[3];
return values[0] << 24 | values[1] << 16 | values[2] << 8 | values[3];
}
/**
* 写整数
* @param os 输出流
*
* @param os 输出流
* @param value 整数
*/
public static void writeInt(OutputStream os, int value) {
int[] values = new int[4];
values[0] = (value>>24)&0xFF;
values[1] = (value>>16)&0xFF;
values[2] = (value>>8)&0xFF;
values[3] = (value)&0xFF;
values[0] = (value >> 24) & 0xFF;
values[1] = (value >> 16) & 0xFF;
values[2] = (value >> 8) & 0xFF;
values[3] = (value) & 0xFF;
try{
try {
for (int i = 0; i < 4; i++) {
os.write(values[i]);
}
}catch (IOException e){
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 读字符串
*
* @param is 输入流
* @return 字符串
*/
@@ -120,15 +123,16 @@ public class SocketUtils {
/**
* 写字符串
*
* @param os 输出流
* @param s 字符串
* @param s 字符串
* @return 是否成功
*/
public static boolean writeString(OutputStream os,String s) {
public static boolean writeString(OutputStream os, String s) {
try {
byte[] bytes = s.getBytes();
int len = bytes.length;
writeInt(os,len);
writeInt(os, len);
os.write(bytes);
return true;
} catch (IOException e) {

View File

@@ -4,8 +4,10 @@ import emu.grasscutter.Grasscutter;
public class AuthPacket extends BasePacket {
public String token;
public String displayName;
public AuthPacket(String token) {
public AuthPacket(String token, String displayName) {
this.displayName = displayName;
this.token = token;
}
@@ -18,4 +20,9 @@ public class AuthPacket extends BasePacket {
public PacketEnum getType() {
return PacketEnum.AuthPacket;
}
@Override
public String toString() {
return "AuthPacket [token=" + token + ", displayName=" + displayName + "]";
}
}

View File

@@ -4,8 +4,8 @@ import emu.grasscutter.Grasscutter;
// http返回数据
public class HttpPacket extends BasePacket {
public int code;
public String message;
public int code = 200;
public String message = "Success";
public String data;
public HttpPacket(int code, String message, String data) {
@@ -18,6 +18,9 @@ public class HttpPacket extends BasePacket {
this.code = code;
this.message = message;
}
public HttpPacket(String data) {
this.data = data;
}
@Override
public String getPacket() {

View File

@@ -2,13 +2,12 @@ package com.github.jie65535.opencommand.socket.packet;
// 数据包结构
public class Packet {
public String token;
public PacketEnum type;
public String data;
public String packetID;
@Override
public String toString() {
return "Packet [token=" + token + ", type=" + type + ", data=" + data + ", packetID=" + packetID + "]";
return "Packet [type=" + type + ", data=" + data + ", packetID=" + packetID + "]";
}
}

View File

@@ -6,5 +6,6 @@ public enum PacketEnum {
Player,
HttpPacket,
AuthPacket,
RunConsoleCommand,
HeartBeat
}

View File

@@ -0,0 +1,21 @@
package com.github.jie65535.opencommand.socket.packet;
import emu.grasscutter.Grasscutter;
public class RunConsoleCommand extends BasePacket {
public String command;
public RunConsoleCommand(String command) {
this.command = command;
}
@Override
public String getPacket() {
return Grasscutter.getGsonFactory().toJson(this);
}
@Override
public PacketEnum getType() {
return PacketEnum.RunConsoleCommand;
}
}