mirror of
https://github.com/jie65535/gc-opencommand-plugin.git
synced 2025-06-02 17:49:12 +08:00
Add multi server run console command
This commit is contained in:
parent
37dcd0f1a7
commit
4d08acd084
195
README.md
195
README.md
@ -5,8 +5,10 @@
|
||||
一个为第三方客户端开放GC命令执行接口的插件
|
||||
|
||||
## 服务端安装
|
||||
|
||||
1. 在 [Release](https://github.com/jie65535/gc-opencommand-plugin/releases) 下载 `jar`
|
||||
2. 放入 `plugins` 文件夹即可
|
||||
|
||||
> 注意,如果出现以下错误:
|
||||
> ```log
|
||||
> INFO:PluginManager Enabling plugin: opencommand-plugin
|
||||
@ -17,24 +19,28 @@
|
||||
> 请使用v1.2.1版本插件,因为该报错表示你的服务端是旧版!
|
||||
|
||||
## 控制台连接
|
||||
|
||||
1. 首次启动时,会在 `plugins` 目录下生成一个 `opencommand-plugin` 目录,打开并编辑 `config.json`
|
||||
2. 设置 `consoleToken` 的值为你的连接秘钥,建议使用至少32字符的长随机字符串。
|
||||
3. 重新启动服务端即可生效配置
|
||||
4. 在客户端中选择控制台身份,并填写你的 `consoleToken` 即可以控制台身份运行指令
|
||||
|
||||
## 构建说明
|
||||
|
||||
1. 克隆仓库
|
||||
2. 在目录下新建 `lib` 目录
|
||||
3. 将 `grasscutter-1.1.x-dev.jar` 放入 `lib` 目录
|
||||
4. `gradle build`
|
||||
|
||||
## 玩家使用流程
|
||||
|
||||
1. 在客户端中填写服务地址,确认是否支持
|
||||
2. 填写UID,发送验证码
|
||||
3. 将游戏内收到的**4位整数验证码**填入客户端校验
|
||||
4. 享受便利!
|
||||
|
||||
## 客户端请求流程
|
||||
|
||||
1. `ping` 确认是否支持 `opencommand` 插件
|
||||
2. `sendCode` 向指定玩家发送验证码(1分钟内不允许重发),保存返回的 `token`
|
||||
3. 使用 `token` 和**4位整数验证码**发送 `verify` 校验
|
||||
@ -43,6 +49,7 @@
|
||||
---
|
||||
|
||||
## `config.json`
|
||||
|
||||
```json
|
||||
{
|
||||
// 控制台连接令牌
|
||||
@ -58,114 +65,210 @@
|
||||
// 多服务器通信密钥
|
||||
"socketToken": "",
|
||||
// 多服务器Dispatch地址
|
||||
"socketHost": "127.0.0.1"
|
||||
"socketHost": "127.0.0.1",
|
||||
// 多服务器显示名称
|
||||
"socketDisplayName": ""
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## API `/opencommand/api`
|
||||
|
||||
示例
|
||||
|
||||
```
|
||||
https://127.0.0.1/opencommand/api
|
||||
```
|
||||
|
||||
### Request 请求
|
||||
|
||||
```java
|
||||
public final class JsonRequest {
|
||||
public String token = "";
|
||||
public String action = "";
|
||||
public String server = "";
|
||||
public Object data = null;
|
||||
}
|
||||
```
|
||||
|
||||
### Response 响应
|
||||
|
||||
```java
|
||||
public final class JsonResponse {
|
||||
public int retcode = 200;
|
||||
public String message = "success";
|
||||
public String message = "Success";
|
||||
public Object data;
|
||||
}
|
||||
```
|
||||
|
||||
### Actions 动作
|
||||
|
||||
#### `测试连接`
|
||||
|
||||
##### Request
|
||||
|
||||
| 请求参数 | 请求数据 | 类型 |
|
||||
| ------- | --------- | ------ |
|
||||
| action | `ping` |`String`|
|
||||
| 请求参数 | 请求数据 | 类型 |
|
||||
|--------|--------|----------|
|
||||
| action | `ping` | `String` |
|
||||
|
||||
##### Response
|
||||
|
||||
| 返回参数 | 返回数据 | 类型 |
|
||||
| ------- | --------- | ------ |
|
||||
| retcode | `200` |`String`|
|
||||
| message | `success` |`String`|
|
||||
| data | `null` |`null` |
|
||||
| 返回参数 | 返回数据 | 类型 |
|
||||
|---------|-----------|----------|
|
||||
| retcode | `200` | `Int` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `null` | `null` |
|
||||
|
||||
|
||||
#### `获取在线玩家`
|
||||
|
||||
##### Request
|
||||
|
||||
| 请求参数 | 请求数据 | 类型 |
|
||||
|--------|----------|----------|
|
||||
| action | `online` | `String` |
|
||||
|
||||
##### Response
|
||||
|
||||
| 返回参数 | 返回数据 | 类型 |
|
||||
|---------|---------------------------------|--------------|
|
||||
| retcode | `200` | `Int` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `{"count": 0, playerList": []}` | `JsonObject` |
|
||||
|
||||
#### `发送验证码`
|
||||
|
||||
##### Request
|
||||
|
||||
| 请求参数 | 请求数据 | 类型 |
|
||||
| ------- | --------- | ------ |
|
||||
| action | `sendCode`|`String`|
|
||||
| data | `uid` |`Int` |
|
||||
| 请求参数 | 请求数据 | 类型 |
|
||||
|--------|------------|----------|
|
||||
| action | `sendCode` | `String` |
|
||||
| data | `uid` | `Int` |
|
||||
|
||||
##### Response
|
||||
|
||||
| 返回参数 | 返回数据 | 类型 |
|
||||
| ------- | --------- | ------ |
|
||||
| retcode | `200` |`String`|
|
||||
| message | `success` |`String`|
|
||||
| data | `token` |`String`|
|
||||
|
||||
| 返回参数 | 返回数据 | 类型 |
|
||||
|---------|-----------|----------|
|
||||
| retcode | `200` | `Int` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `token` | `String` |
|
||||
|
||||
#### `验证验证码`
|
||||
|
||||
##### Request
|
||||
|
||||
| 请求参数 | 请求数据 | 类型 |
|
||||
| ------- | --------- | ------ |
|
||||
| action | `verify` |`String`|
|
||||
| token | `token` |`String`|
|
||||
| data | `code` |`Int` |
|
||||
| 请求参数 | 请求数据 | 类型 |
|
||||
|--------|----------|----------|
|
||||
| action | `verify` | `String` |
|
||||
| token | `token` | `String` |
|
||||
| data | `code` | `Int` |
|
||||
|
||||
##### Response
|
||||
|
||||
成功
|
||||
|
||||
| 返回参数 | 返回数据 | 类型 |
|
||||
| ------- | --------- | ------ |
|
||||
| retcode | `200` |`String`|
|
||||
| message | `success` |`String`|
|
||||
| data | `null` | `null` |
|
||||
| 返回参数 | 返回数据 | 类型 |
|
||||
|---------|-----------|----------|
|
||||
| retcode | `200` | `Int` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `null` | `null` |
|
||||
|
||||
失败
|
||||
|
||||
| 返回参数 | 返回数据 | 类型 |
|
||||
| ------- | -------------------- | ------ |
|
||||
| retcode | `400` |`String`|
|
||||
| message | `Verification failed`|`String`|
|
||||
| data | `null` |`null` |
|
||||
| 返回参数 | 返回数据 | 类型 |
|
||||
|---------|-----------------------|----------|
|
||||
| retcode | `400` | `Int` |
|
||||
| message | `Verification failed` | `String` |
|
||||
| data | `null` | `null` |
|
||||
|
||||
#### `执行命令`
|
||||
|
||||
##### Request
|
||||
|
||||
| 请求参数 | 请求数据 | 类型 |
|
||||
| ------- | ----------- | ------ |
|
||||
| action | `command` |`String`|
|
||||
| token | `token` |`String`|
|
||||
| data | `command` |`String`|
|
||||
| 请求参数 | 请求数据 | 类型 |
|
||||
|--------|-----------|----------|
|
||||
| action | `command` | `String` |
|
||||
| token | `token` | `String` |
|
||||
| data | `command` | `String` |
|
||||
|
||||
##### Response
|
||||
|
||||
成功
|
||||
|
||||
| 返回参数 | 返回数据 | 类型 |
|
||||
| ------- | ---------------- | ------ |
|
||||
| retcode | `200` |`String`|
|
||||
| message | `success` |`String`|
|
||||
| data | `Command return` |`String`|
|
||||
| 返回参数 | 返回数据 | 类型 |
|
||||
|---------|------------------|----------|
|
||||
| retcode | `200` | `Int` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `Command return` | `String` |
|
||||
|
||||
### 执行控制台命令
|
||||
|
||||
#### `获取运行模式`
|
||||
|
||||
##### Request
|
||||
|
||||
| 请求参数 | 请求数据 | 类型 |
|
||||
|--------|-----------|----------|
|
||||
| action | `runmode` | `String` |
|
||||
| token | `token` | `String` |
|
||||
|
||||
##### Response
|
||||
|
||||
成功
|
||||
|
||||
| 返回参数 | 返回数据 | 类型 |
|
||||
|---------|-----------------------|----------|
|
||||
| retcode | `200` | `Int` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `1 (多服务器) / 0 (单服务器)` | `Int` |
|
||||
|
||||
#### `获取多服务器列表`
|
||||
|
||||
##### Request
|
||||
|
||||
| 请求参数 | 请求数据 | 类型 |
|
||||
|--------|----------|----------|
|
||||
| action | `server` | `String` |
|
||||
| token | `token` | `String` |
|
||||
|
||||
##### Response
|
||||
|
||||
成功
|
||||
|
||||
| 返回参数 | 返回数据 | 类型 |
|
||||
|---------|-----------|--------------|
|
||||
| retcode | `200` | `Int` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `{}` | `JsonObject` |
|
||||
|
||||
```json
|
||||
{
|
||||
"retcode": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
// 服务器 UUID
|
||||
"13d82d0d-c7d9-47dd-830c-76588006ef6e": "2.8.0 服务器",
|
||||
"e6b83224-a761-4023-be57-e054c5bb823a": "2.8.0 开发服务器"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `执行命令`
|
||||
|
||||
##### Request
|
||||
|
||||
| 请求参数 | 请求数据 | 类型 |
|
||||
|--------|-----------|----------|
|
||||
| action | `command` | `String` |
|
||||
| token | `token` | `String` |
|
||||
| server | `UUID` | `String` |
|
||||
| data | `command` | `String` |
|
||||
|
||||
##### Response
|
||||
|
||||
成功
|
||||
|
||||
| 返回参数 | 返回数据 | 类型 |
|
||||
|---------|------------------|----------|
|
||||
| retcode | `200` | `Int` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `Command return` | `String` |
|
176
README_en-US.md
176
README_en-US.md
@ -60,6 +60,7 @@ https://127.0.0.1/opencommand/api
|
||||
public final class JsonRequest {
|
||||
public String token = "";
|
||||
public String action = "";
|
||||
public Seting server = "";
|
||||
public Object data = null;
|
||||
}
|
||||
```
|
||||
@ -68,7 +69,7 @@ public final class JsonRequest {
|
||||
```java
|
||||
public final class JsonResponse {
|
||||
public int retcode = 200;
|
||||
public String message = "success";
|
||||
public String message = "Success";
|
||||
public Object data;
|
||||
}
|
||||
```
|
||||
@ -78,79 +79,168 @@ public final class JsonResponse {
|
||||
|
||||
##### Request
|
||||
|
||||
| Request | Request data | type |
|
||||
| ------- | -------------- | ------ |
|
||||
| action | `ping` |`String`|
|
||||
| Request | Request data | type |
|
||||
|---------|--------------|----------|
|
||||
| action | `ping` | `String` |
|
||||
|
||||
##### Response
|
||||
|
||||
| Response | Response data | type |
|
||||
| -------- | --------------- | ------ |
|
||||
| retcode | `200` |`String`|
|
||||
| message | `success` |`String`|
|
||||
| data | `null` |`null` |
|
||||
| Response | Response data | type |
|
||||
|----------|---------------|----------|
|
||||
| retcode | `200` | `String` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `null` | `null` |
|
||||
|
||||
#### `Get online players`
|
||||
|
||||
##### Request
|
||||
|
||||
| Request | Request data | type |
|
||||
|---------|--------------|----------|
|
||||
| action | `online` | `String` |
|
||||
|
||||
##### Response
|
||||
|
||||
| Response | Response data | type |
|
||||
|----------|---------------------------------|--------------|
|
||||
| retcode | `200` | `String` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `{"count": 0, playerList": []}` | `JsonObject` |
|
||||
|
||||
#### `Send code`
|
||||
|
||||
##### Request
|
||||
|
||||
| Request | Request data | type |
|
||||
| ------- | -------------- | ------ |
|
||||
| action | `sendCode` |`String`|
|
||||
| data | `uid` |`Int` |
|
||||
| Request | Request data | type |
|
||||
|---------|--------------|----------|
|
||||
| action | `sendCode` | `String` |
|
||||
| data | `uid` | `Int` |
|
||||
|
||||
##### Response
|
||||
|
||||
| Response | Response data | type |
|
||||
| -------- | --------------- | ------ |
|
||||
| retcode | `200` |`String`|
|
||||
| message | `success` |`String`|
|
||||
| data | `token` |`String`|
|
||||
| Response | Response data | type |
|
||||
|----------|---------------|----------|
|
||||
| retcode | `200` | `String` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `token` | `String` |
|
||||
|
||||
#### `Verify code`
|
||||
|
||||
##### Request
|
||||
|
||||
| Request | Request data | type |
|
||||
| ------- | -------------- | ------ |
|
||||
| action | `verify` |`String`|
|
||||
| token | `token` |`String`|
|
||||
| data | `code` |`Int` |
|
||||
| Request | Request data | type |
|
||||
|---------|--------------|----------|
|
||||
| action | `verify` | `String` |
|
||||
| token | `token` | `String` |
|
||||
| data | `code` | `Int` |
|
||||
|
||||
##### Response
|
||||
|
||||
Success
|
||||
|
||||
| Response | Response data | type |
|
||||
| -------- | -------------- | ------ |
|
||||
| retcode | `200` |`String`|
|
||||
| message | `success` |`String`|
|
||||
| data | `null` | `null` |
|
||||
| Response | Response data | type |
|
||||
|----------|---------------|----------|
|
||||
| retcode | `200` | `String` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `null` | `null` |
|
||||
|
||||
Failed
|
||||
|
||||
| Response | Response data | type |
|
||||
| -------- | -------------------- | ------ |
|
||||
| retcode | `400` |`String`|
|
||||
| message | `Verification failed`|`String`|
|
||||
| data | `null` |`null` |
|
||||
| Response | Response data | type |
|
||||
|----------|-----------------------|----------|
|
||||
| retcode | `400` | `String` |
|
||||
| message | `Verification failed` | `String` |
|
||||
| data | `null` | `null` |
|
||||
|
||||
#### `Run command`
|
||||
|
||||
##### Request
|
||||
|
||||
| Request | Request data | type |
|
||||
| ------- | -------------- | ------ |
|
||||
| action | `command` |`String`|
|
||||
| token | `token` |`String`|
|
||||
| data | `command` |`String`|
|
||||
| Request | Request data | type |
|
||||
|---------|--------------|----------|
|
||||
| action | `command` | `String` |
|
||||
| token | `token` | `String` |
|
||||
| data | `command` | `String` |
|
||||
|
||||
##### Response
|
||||
|
||||
Success
|
||||
|
||||
| Response | Response data | type |
|
||||
| -------- | ------------------ | ------ |
|
||||
| retcode | `200` |`String`|
|
||||
| message | `success` |`String`|
|
||||
| data | `Command return` |`String`|
|
||||
| Response | Response data | type |
|
||||
|----------|------------------|----------|
|
||||
| retcode | `200` | `String` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `Command return` | `String` |
|
||||
|
||||
### Run console command
|
||||
|
||||
#### `Get run mode`
|
||||
|
||||
##### Request
|
||||
|
||||
| Request | Request data | Type |
|
||||
|---------|--------------|----------|
|
||||
| action | `runmode` | `String` |
|
||||
| token | `token` | `String` |
|
||||
|
||||
##### Response
|
||||
|
||||
Success
|
||||
|
||||
| Request | Response data | Type |
|
||||
|---------|----------------------------------------|----------|
|
||||
| retcode | `200` | `Int` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `1 (Multi server) / 0 (Single server)` | `Int` |
|
||||
|
||||
#### `Get mulit server list`
|
||||
|
||||
##### Request
|
||||
|
||||
| Request | Request data | Type |
|
||||
|---------|--------------|----------|
|
||||
| action | `server` | `String` |
|
||||
| token | `token` | `String` |
|
||||
|
||||
##### Response
|
||||
|
||||
Success
|
||||
|
||||
| Request | Response data | Type |
|
||||
|---------|---------------|--------------|
|
||||
| retcode | `200` | `Int` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `{}` | `JsonObject` |
|
||||
|
||||
```json
|
||||
{
|
||||
"retcode": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
// Server UUID
|
||||
"13d82d0d-c7d9-47dd-830c-76588006ef6e": "2.8.0 Server",
|
||||
"e6b83224-a761-4023-be57-e054c5bb823a": "2.8.0 Dev server"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `Run command`
|
||||
|
||||
##### Request
|
||||
|
||||
| Request | Request data | Type |
|
||||
|---------|--------------|----------|
|
||||
| action | `command` | `String` |
|
||||
| token | `token` | `String` |
|
||||
| server | `UUID` | `String` |
|
||||
| data | `command` | `String` |
|
||||
|
||||
##### Response
|
||||
|
||||
Success
|
||||
|
||||
| Request | Response data | Type |
|
||||
|---------|------------------|----------|
|
||||
| retcode | `200` | `Int` |
|
||||
| message | `Success` | `String` |
|
||||
| data | `Command return` | `String` |
|
@ -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());
|
||||
|
@ -25,4 +25,5 @@ public class OpenCommandConfig {
|
||||
public int socketPort = 5746;
|
||||
public String socketToken = "";
|
||||
public String socketHost = "127.0.0.1";
|
||||
public String socketDisplayName = "";
|
||||
}
|
||||
|
@ -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")) {
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
// 等待客户端连接
|
||||
|
@ -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) {
|
||||
|
@ -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 + "]";
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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 + "]";
|
||||
}
|
||||
}
|
||||
|
@ -6,5 +6,6 @@ public enum PacketEnum {
|
||||
Player,
|
||||
HttpPacket,
|
||||
AuthPacket,
|
||||
RunConsoleCommand,
|
||||
HeartBeat
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user