mirror of
https://github.com/System-End/Discord-MC-Chat.git
synced 2026-04-19 20:55:13 +00:00
184 lines
13 KiB
Markdown
184 lines
13 KiB
Markdown
# Discord-MC-Chat (DMCC) 重构设计文档
|
||
|
||
## 1. 项目概述
|
||
|
||
Discord-MC-Chat (DMCC) 是一个 Minecraft 模组,旨在为 Discord 和 Minecraft 服务器之间建立一个功能强大、可高度定制的双向通信桥梁。
|
||
|
||
本次重构的核心目标是实现一个**统一的、基于“服务端-客户端 (Server-Client)”的通信架构**
|
||
。在此架构下,所有运行模式都将复用同一套核心逻辑,以达到最大程度的代码复用、架构一致性和未来的可扩展性。
|
||
|
||
项目初期将优先实现对 **NeoForge 1.21.10** 的兼容。但为了未来能够无缝支持 Fabric,整体架构设计严格遵循平台无关原则,所有核心代码中
|
||
**不得含有任何启动器专属的调用**,仅通过 Mixin 进行注入。
|
||
|
||
## 2. 核心功能需求
|
||
|
||
### 2.1 通信功能
|
||
|
||
- **双向消息转发**: 在 Discord 和 Minecraft 之间实时转发聊天消息。
|
||
- **格式兼容**: 支持 Markdown、Discord 表情符号、提及和超链接在两个平台间的自动转换。
|
||
- **Webhook 支持**: 可选通过 Webhook 以玩家头像发送 Minecraft 消息到 Discord。
|
||
- **自定义格式**: 允许用户高度自定义所有消息的显示格式。
|
||
|
||
### 2.2 事件通知
|
||
|
||
- **玩家事件**: 玩家加入/离开服务器、死亡、获得进度/成就。
|
||
- **服务器事件**: 服务器启动/关闭。
|
||
- **性能监控**: 当服务器 MSPT (每 tick 毫秒数) 超过阈值时,在 Discord 发出警告。
|
||
|
||
### 2.3 管理功能
|
||
|
||
- 通过 Discord 和游戏内命令查询服务器状态。
|
||
- 通过 Discord 和游戏内命令执行 Minecraft 控制台命令。
|
||
- 查看和过滤服务器日志。
|
||
- 重载 DMCC 配置文件。
|
||
- 远程启动/关闭/重启子服务器(仅限多服务器模式)。
|
||
|
||
### 2.4 账户链接功能
|
||
|
||
- **身份绑定**: 支持将 Minecraft 玩家账户与 Discord 账户进行一对一关联。
|
||
- **角色颜色同步**: 游戏内聊天消息显示与玩家 Discord 角色的颜色一致。
|
||
- **跨平台提及**: 在 Discord 中`@`用户时,对应的 Minecraft 玩家会收到游戏内提醒。
|
||
- **权限同步**: 可选基于 Discord 角色自动授予玩家游戏内的 OP 权限。
|
||
|
||
## 3. 系统架构
|
||
|
||
### 3.1 统一的“服务端-客户端 (Server-Client)”架构
|
||
|
||
DMCC 所有运行模式都基于一个统一的通信模型,该模型包含两个核心组件:
|
||
|
||
1. **服务端 (Server)**: 整个系统的“大脑”。它作为后台服务运行,是**唯一**负责与 Discord API (通过 JDA)
|
||
直接通信的组件。它处理所有核心逻辑,如消息格式化、命令解析和权限验证。**此组件在任何情况下都不得包含任何 `net.minecraft`
|
||
的导入(反射除外)**,以确保其可以在没有 Minecraft 环境的情况下独立运行。
|
||
2. **客户端 (Client)**: 部署在每个 Minecraft 服务器上的“触手”。它作为 Minecraft 模组运行,负责捕获游戏内的所有事件,将其发送给
|
||
**服务端 (Server)**。并执行来自 **服务端 (Server)** 的指令和本地命令。
|
||
|
||
两者之间通过基于 **Netty** 的 TCP 协议进行通信,使用 **JSON** 对数据包进行序列化和反序列化,并采用**哈希质询-响应机制**
|
||
进行安全认证。
|
||
|
||
### 3.2 运行模式与部署
|
||
|
||
1. **单体服务器模式 (`single_server`)**: 在后台**同时启动一个内部服务端和一个内部客户端**。内部客户端自动通过本地回环地址连接到内部服务端。这是为单个
|
||
Minecraft 服务器提供的开箱即用的解决方案。
|
||
2. **多服务器-客户端模式 (`multi_server_client`)**: **只启动一个客户端**,此客户端会连接到一个外部独立运行的服务端。用于将多个
|
||
Minecraft 服务器连接到一个中央服务端。
|
||
3. **独立模式 (`standalone`)**: **只启动一个服务端**,监听网络端口,等待一个或多个外部客户端连接。作为多服务器架构的中央“大脑”而存在。
|
||
|
||
### 3.3 配置文件策略
|
||
|
||
1. **`mode.yml`**: 用户首先在此文件选择一种运行模式。若在非 Minecraft 环境下直接运行 JAR,则自动认定为 `standalone` 模式。
|
||
2. **`config.yml`**: DMCC 会根据选择的模式,从内部模板生成一份对应的 `config.yml`。此配置文件将被严格验证。`standalone`
|
||
模式首次启动时,会自动在 `config.yml` 中生成一个高强度的 `shared_secret`,用户需将其手动同步到所有 `client` 的配置文件中。
|
||
|
||
## 4. 网络协议与安全
|
||
|
||
### 4.1 哈希质询-响应认证机制
|
||
|
||
为保证通信安全,防止共享密钥在传输中被窃取及防止重放攻击,系统采用一次性的哈希质询-响应机制进行认证。
|
||
|
||
**核心协议包 (`Packets.java`):**
|
||
|
||
```java
|
||
// 客户端发起连接,表明身份和版本
|
||
public record ClientHello(String serverName, String version) implements Packet {
|
||
}
|
||
|
||
// 服务端收到ClientHello后,发送随机质询
|
||
public record ServerChallenge(String challenge) implements Packet {
|
||
}
|
||
|
||
// 客户端收到质询后,计算并发送响应
|
||
public record ClientResponse(String responseHash) implements Packet {
|
||
}
|
||
|
||
// 服务端验证通过后,发送最终的成功响应
|
||
public record HandshakeSuccess(String messageKey, String language) implements Packet {
|
||
}
|
||
|
||
// 任何阶段失败,都可以发送一个失败响应
|
||
public record HandshakeFailure(String messageKey) implements Packet {
|
||
}
|
||
```
|
||
|
||
**认证流程:**
|
||
|
||
1. **客户端发起连接**: `Client` 连接成功后,立即发送 `ClientHello` 包,包含其在 `config.yml` 中配置的 `serverName` 和自身的
|
||
DMCC 版本号。
|
||
2. **服务端版本验证与质询**: `Server` 收到 `ClientHello` 后:
|
||
a. **版本检查**: 对比 `Client` 版本和自身版本。如果不兼容,立即返回
|
||
`HandshakeFailure("handshake.error.invalid_version")` 并断开连接。
|
||
b. **生成质询**: 如果版本兼容,`Server` 生成一个唯一的、一次性的随机字符串作为 `challenge`,并暂存于内存中。
|
||
c. **发送质询**: `Server` 向 `Client` 发送 `ServerChallenge` 包。
|
||
3. **客户端计算响应**: `Client` 收到 `ServerChallenge` 后:
|
||
a. 从 `config.yml` 读取 `shared_secret`。
|
||
b. 使用 **HMAC-SHA256** 算法计算哈希值: `responseHash = hmac_sha256(key = shared_secret, data = challenge)`。
|
||
c. 向 `Server` 发送 `ClientResponse` 包。
|
||
4. **服务端验证响应**: `Server` 收到 `ClientResponse` 后:
|
||
a. 取出内存中为该 `Client` 暂存的 `challenge`。
|
||
b. 以完全相同的方式计算出 `expectedResponseHash`。
|
||
c. **比对哈希**: 如果不一致,返回 `HandshakeFailure("handshake.error.authentication_failed")` 并断开连接。
|
||
5. **握手成功**: 如果哈希一致,认证通过。`Server` 发送 `HandshakeSuccess` 包,其中包含配置的全局 `language`。
|
||
|
||
### 4.2 全局语言同步
|
||
|
||
为确保所有实例的输出信息语言统一,系统采用握手时同步的策略:
|
||
|
||
1. `Client` 启动时,默认加载 `en_us` 语言文件。
|
||
2. 当 `Client` 收到 `Server` 发来的 `HandshakeSuccess` 包后,它会解析出 `language` 字段(如 `zh_cn`)。
|
||
3. 如果该语言与当前加载的语言不同,`Client` 会调用 `I18nManager.load("zh_cn")` 重新加载对应的语言文件。
|
||
4. 从此,该 `Client` 生成的所有消息都将使用与 `Server` 统一的语言。
|
||
|
||
## 5. 命令系统设计
|
||
|
||
### 5.1 统一命令入口与异步处理
|
||
|
||
- **`CommandSource` 抽象接口**: 所有命令处理逻辑都将面向一个 `CommandSource`
|
||
接口,该接口封装了命令来源的特有信息(如来源名称、权限检查、响应方法),使其与具体实现(玩家、控制台、Discord用户)解耦。
|
||
- **异步执行**: 所有可能耗时的命令(如 `reload`, `start`, `stats`)都将异步执行,并返回一个
|
||
`CompletableFuture<CommandResult>`,避免阻塞主线程。
|
||
|
||
### 5.2 命令可用性矩阵
|
||
|
||
| 命令 | 权限 (默认) | `multi_server_client` | `standalone` (终端) | `single_server` | Discord | 说明与行为差异 |
|
||
|:----------|:-----------|:----------------------|:------------------|:----------------|:--------|:-----------------------------------------------------------------------------------------------------------|
|
||
| `help` | `everyone` | ✅ | ✅ | ✅ | ✅ | 动态显示当前环境可用且有权执行的命令。 |
|
||
| `info` | `everyone` | ✅ | ✅ | ✅ | ✅ | **行为**: `client` 只显示自身状态。`server` 端显示全局信息,包括所有 `client` 的摘要。 |
|
||
| `update` | `everyone` | ✅ | ✅ | ✅ | ✅ | **本地执行**: 每个实例独立检查自身DMCC的版本更新。此命令**不**进行跨实例转发。 |
|
||
| `stats` | `everyone` | ✅ | ✅ | ✅ | ✅ | **服务端聚合**: `client` 收到命令后,将**命令请求**转发给 `server`。`server` 再向所有 `client` **实时请求**统计数据,聚合后将结果返回给最初的 `client`。 |
|
||
| `console` | `admin` | ✅ | ❌ | ✅ | ✅ | 执行**所在MC服务器**的命令。`standalone` 无此命令。在Discord执行时,对 `standalone` 模式相当于 `execute`。 |
|
||
| `execute` | `admin` | ✅ | ✅ | ❌ | ✅ | **`multi-server` 核心**: 用于远程执行命令。`single_server` 无此命令。`client` 端执行此命令时,会将请求转发给 `server` 处理。 |
|
||
| `log` | `admin` | ✅ | ✅ | ✅ | ✅ | `client` 可获取自身日志,`server` 可获取自身日志。可通过 `execute` 获取远程日志。 |
|
||
| `restart` | `admin` | ✅ | ✅ | ✅ | ✅ | **应用内重启**: 通过 `shutdown()` + `init()` 实现,无需重启JVM。`standalone` 模式下,Bot会短暂离线后重连。 |
|
||
| `start` | `admin` | ❌ | ✅ | ❌ | ✅ | **`standalone` 独有**: 启动 `config.yml` 中定义的子服务器进程。 |
|
||
| `stop` | `admin` | ✅ | ✅ | ✅ | ✅ | 停止**当前DMCC实例**。高危操作,建议增加二次确认。 |
|
||
|
||
### 5.3 关键命令逻辑详解
|
||
|
||
#### `stats` (纯实时查询模型)
|
||
|
||
1. **转发请求**: `client` 收到命令后,封装 `ForwardedCommandPacket` 发送给 `server`。
|
||
2. **广播数据请求**: `server` 收到请求后,**立即**向所有 `client` 广播 `StatsRequestPacket`。
|
||
3. **响应数据**: 各 `client` 查询本地统计数据,并回传 `StatsDataPacket`。
|
||
4. **聚合与返回**: `server` 在短暂超时时间内聚合所有响应,生成排行榜,并通过 `CommandResponsePacket` 定向返回给最初请求的
|
||
`client`。
|
||
|
||
#### `update` (本地独立检查模型)
|
||
|
||
- 此命令**始终在接收到命令的实例上本地执行**。
|
||
- 逻辑(获取自身版本、请求 GitHub API、比较版本)位于 `:common` 模块,`client` 和 `server` 均可调用。
|
||
- 返回的更新信息将明确指出这是一个**手动操作**,需要管理员在所有实例(`standalone` 和所有 `client`)上手动替换 `.jar` 文件。
|
||
|
||
#### `start` (事件驱动反馈模型)
|
||
|
||
1. 管理员在 `standalone` 或 Discord 执行 `/start SMP`。
|
||
2. `standalone` 回复临时消息(“正在启动...”)并执行预设的 `start_command`。
|
||
3. 子服务器的 DMCC `client` 启动并自动连接 `standalone`。
|
||
4. `standalone` 将“新客户端注册成功”的内部事件作为该服务器成功启动的信号。
|
||
5. `standalone` 更新之前的临时消息为最终的成功状态(“服务器 SMP 已成功启动并连接!”)。
|
||
|
||
## 6. 权限管理模型 (双轨制)
|
||
|
||
1. **原生继承模型 (Minecraft -> DMCC)**: 游戏内 OP 等级高于 `minecraft_op_level_requirement` 的玩家,自动成为“DMCC
|
||
管理员”。这是权限的基础来源。
|
||
2. **角色同步模型 (Discord -> Minecraft)**: 可选功能。可配置 Discord 角色到游戏内 OP 等级的映射。`Server` 组件会周期性检查并
|
||
**通过向 `Client` 发送指令**来自动授予/撤销玩家的游戏内 OP 权限。一旦玩家因此获得足够 OP 等级,也将通过路径一成为“DMCC
|
||
管理员”。
|