mirror of
https://github.com/layue13/MoonBridge.git
synced 2026-10-25 18:46:18 +00:00
docs: 修正 CI 引用、拆出历史验证记录并统一中文文档
- CLAUDE.md、testing.md、publishing.md 的 .gitea 路径改为实际的 .github/workflows; 说明 verify.yml 在 PR 上运行 check - testing.md 中带日期的逐轮结果迁到 docs/records/ - README 精简为简介、快速开始和入口;配置与迁移细节只保留在 operations.md - channel-messaging-v2.md 更名为 channel-messaging-contract.md,与 transfer-coordination.md 一起译为中文 - benchmarks/results 中两个记录改为日期前缀命名 - 新增 compileBenchmarks 并挂到 check,防止 benchmarks/ 静默失效 - .gitignore 删除不存在的 deployment/ 条目 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGL9BudzWm4dUQgBsQw4Qr
This commit is contained in:
16 files changed
+148
-137
No files matched your search
+2
-5
@@ -19,8 +19,5 @@ replay_pid*
|
||||
|
||||
data/
|
||||
*.invalid-*
|
||||
deployment/performance/results/
|
||||
deployment/local-minecraft-1.7.10/bin/
|
||||
deployment/local-minecraft-1.7.10/backend-*/
|
||||
deployment/local-minecraft-1.7.10/*.pid
|
||||
deployment/local-minecraft-1.7.10/*.log
|
||||
|
||||
.kotlin/
|
||||
@@ -18,7 +18,7 @@ bash ./gradlew :proxy-core:installDist # runnable distribution in
|
||||
|
||||
The slow tests are the session integration tests (`ProxySessionListenerTest`, `SessionTransferTest`, `InitialRoutingTest`, `SourceReleaseTransferTest`); iterate with `--tests` and run `check` once before pushing.
|
||||
|
||||
`.gitea/workflows/ci.yml` runs `check` and publishes Maven modules only on pushes to `main`. A GitHub PR has no CI, so a local `check` is the only verification before merge.
|
||||
`.github/workflows/verify.yml` runs `check` on every pull request; `.github/workflows/ci.yml` runs `check :proxy-core:distZip` and publishes the Maven modules on pushes to `main`. Run `check` locally before pushing anyway: it is ~7 minutes and a red PR costs a round trip.
|
||||
|
||||
## Modules
|
||||
|
||||
|
||||
@@ -12,39 +12,28 @@
|
||||
- [后端 Bukkit 插件接入](docs/backend-channel-design.md#后端插件接入):共享宿主、API 依赖与 Channel 示例。
|
||||
- [发布与依赖版本](docs/publishing.md)、[架构](docs/architecture.md)与[验证方法](docs/testing.md)。
|
||||
|
||||
## 本机运行
|
||||
## 快速开始
|
||||
|
||||
需要 JDK 25。发行包不包含 Minecraft 后端;要完成玩家登录,需启动一个 Minecraft 1.7.10 后端,并让 `initialRouting.servers` 中至少一个名称能解析到它。
|
||||
需要 JDK 25。发行包不包含 Minecraft 后端;要完成玩家登录,需要一个 Minecraft 1.7.10 后端,且 `initialRouting.servers` 中至少一个名称能解析到它(随附配置为 `lobby` → `127.0.0.1:25565`)。
|
||||
|
||||
```powershell
|
||||
```sh
|
||||
git submodule update --init --recursive
|
||||
.\gradlew.bat check :proxy-core:installDist
|
||||
.\proxy-core\build\install\moonbridge\bin\moonbridge.bat --config .\proxy-core\build\install\moonbridge\config\moonbridge.yml
|
||||
bash ./gradlew check :proxy-core:installDist # Windows: .\gradlew.bat check :proxy-core:installDist
|
||||
cd proxy-core/build/install/moonbridge
|
||||
bin/moonbridge --validate-config config/moonbridge.yml # Windows: bin\moonbridge.bat
|
||||
bin/moonbridge --config config/moonbridge.yml
|
||||
```
|
||||
|
||||
默认配置为 `OFFLINE`,仅监听 `127.0.0.1:25577`,后端为 `127.0.0.1:25565`。公开部署使用 `ONLINE_BUNGEE`,并限制玩家直连后端。详见[运行配置](docs/operations.md)。
|
||||
|
||||
随附配置的入口候选为 `lobby`,地址为 `127.0.0.1:25565`。如果后端名称或地址不同,请编辑发行包 `config/moonbridge.yml` 中的 `backends` 和 `initialRouting.servers`,并确保候选列表按想要的顺序列出明确入口。空候选列表在没有选服插件时会拒绝登录。
|
||||
|
||||
若运行已解压的发行包,从发行包目录启动:
|
||||
|
||||
```powershell
|
||||
.\bin\moonbridge.bat --validate-config .\config\moonbridge.yml
|
||||
.\bin\moonbridge.bat --config .\config\moonbridge.yml
|
||||
```
|
||||
|
||||
在 Linux/macOS 上将 `moonbridge.bat` 换成 `moonbridge`。发行包根目录包含 README、`docs/` 和按记录日期归档的 `smoke/results/`、`benchmarks/results/`;历史记录只证明各自写明的提交和环境。
|
||||
默认配置为 `OFFLINE`,仅监听 `127.0.0.1:25577`。公开部署使用 `ONLINE_BUNGEE` 并限制玩家直连后端;认证、监听、入口候选和后端地址见[运行配置](docs/operations.md)。发行包根目录附带 `docs/` 与按日期归档的 `smoke/results/`、`benchmarks/results/`。
|
||||
|
||||
## 后端插件开发
|
||||
|
||||
服务器安装 `moonbridge-backend-bukkit` JAR(插件名 `MoonBridgeBackend`)。业务插件对 `backend-bukkit-api` 使用 `compileOnly` / `provided`,并通过 `BukkitMessagingService` 获取共享消息服务;无需依赖宿主实现或创建网络连接。源码当前的 `0.1.0-SNAPSHOT` 仅是本地开发构件版本;发布依赖应使用发布工作流提供的不可变版本。API 与完整示例见[后端插件接入](docs/backend-channel-design.md#后端插件接入)。
|
||||
|
||||
本次改名统一使用 `dev.moonbridge` 包名、`moonbridge` 启动命令和 `moonbridge.yml` 配置文件。现有插件需要重新编译,Bukkit 插件依赖声明和宿主配置目录改用 `MoonBridgeBackend`。历史验收记录保留当时的名称与构件路径。
|
||||
从 StrataProxy 升级见[迁移说明](docs/operations.md#从-strataproxy-迁移);历史验收记录保留当时的名称与构件路径。
|
||||
|
||||
## 验证状态
|
||||
|
||||
`MoonBridgeBackend` 后端宿主与独立 API 已通过两个真实 Uranium / Java 8 服务端的注册、通信、主线程回调、插件停用、重连及注销/租约验收,见[改名与独立 API 验收记录](smoke/results/2026-09-27-moonbridge-api.md)。这是无玩家、隔离服务端上的功能验收,不代表生产容量测试。
|
||||
当前可引用的验收记录及其覆盖范围见[文档索引](docs/README.md#当前可引用的验证记录)和[验证方法](docs/testing.md)。各记录只证明其注明的提交和环境,不代表生产容量。已知 Uranium 候选服登录停滞见[记录](smoke/results/2026-09-26-uranium-login-stalls.md)及 [Uranium #585](https://git.nest.potatolab.uk:8443/TDLM/Uranium/issues/585)。
|
||||
|
||||
最新初次路由专项记录包含自动化测试,以及通过实际 `ProxyMain` 连接真实 Java 8 Uranium 后端的协议探针结果;未进行本分支的完整模组包玩家视觉验收,见[路由验收记录](smoke/results/2026-09-27-initial-routing.md)。2026-09-26 Prism 记录是当日代码和环境下的历史真实客户端及配对测量,不应视作当前路由改动的完整模组包证明,见[历史验收记录](benchmarks/results/2026-09-26-prism-pack.md)。生产容量仍需按部署环境测量。已知 Uranium 候选服登录停滞的调查见[记录](smoke/results/2026-09-26-uranium-login-stalls.md)及 [Uranium #585](https://git.nest.potatolab.uk:8443/TDLM/Uranium/issues/585)。
|
||||
|
||||
生产权限存储统一使用 MariaDB 13.0.2;默认配置、已有数据边界与实际双实例验收入口见 [MariaDB 权限数据库](docs/mariadb.md)。
|
||||
生产权限存储统一使用 MariaDB 13.0.2;见 [MariaDB 权限数据库](docs/mariadb.md)。
|
||||
File renamed without changes.
File renamed without changes.
+1
-1
@@ -20,7 +20,7 @@
|
||||
| [玩家操作与部署完善设计](player-operations.md) | 消息、主动断开、服务器列表配置与验收边界 |
|
||||
| [运行配置](operations.md) | 认证、接入限制、超时、启动和部署边界 |
|
||||
| [后端 Channel 消息服务](backend-channel-design.md) | 共享宿主、跨后端路由、消息身份与交付语义 |
|
||||
| [Channel 消息设计契约](channel-messaging-v2.md) | 从参与者、寻址、响应与失败语义推导 API 和验收条件 |
|
||||
| [Channel 消息设计契约](channel-messaging-contract.md) | 从参与者、寻址、响应与失败语义推导 API 和验收条件 |
|
||||
| [验证方法](testing.md) | 自动化测试、烟测、真实客户端与合成性能测量 |
|
||||
| [发布与依赖版本](publishing.md) | 本地 SNAPSHOT、CI 不可变版本、Maven 构件和发行包的区别 |
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
业务插件通过命名 channel 与 proxy 或指定 backend 通信。每个后端由一个宿主持有认证控制连接,多个插件共享;proxy 内置路由,后端之间通信不需要另写代理转发插件,也不依赖在线玩家。
|
||||
|
||||
模型推导、失败语义及验收条件见 [设计契约](channel-messaging-v2.md)。消息服务与 Minecraft PLAY 转发独立;现有后端注册、心跳租约和游戏地址目录继续由控制服务维护。
|
||||
模型推导、失败语义及验收条件见 [设计契约](channel-messaging-contract.md)。消息服务与 Minecraft PLAY 转发独立;现有后端注册、心跳租约和游戏地址目录继续由控制服务维护。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# Channel 消息设计契约
|
||||
|
||||
## 问题与验收标准
|
||||
|
||||
后端 A 上的插件必须能通过具名 channel 通知或查询代理插件或后端 B。插件不得自行建立网络连接、分发凭据、实现代理中继插件,也不得依赖在线玩家。同一后端内的多个插件共享一条已认证连接。两端收到的都是不可变消息,带框架分配的标识和经过认证的来源。
|
||||
|
||||
验收要求在没有玩家的情况下进行真实 socket 测试:后端/代理与后端/后端请求;通知扇出;并发响应与各自请求正确匹配;插件独立停用;有界过载;包含排队时间的超时;断线不重放;旧连接隔离。由一个 Java 8 Bukkit 宿主构件提供共享服务。单元测试和 socket 测试不能代替在运行中的 Forge 整合包里的部署验收。
|
||||
|
||||
## 模型推导
|
||||
|
||||
1. 连接认证的是后端进程,而不是进程内的每个插件。因此代理根据已认证连接推导来源。插件标签可用于本地生命周期归属,但不是单独认证的远端主体。
|
||||
2. channel 标识业务通信域及其权限边界;destination 标识接收节点。若将两者合并,应用将被迫把后端名编码进 channel,或自建路由协议。
|
||||
3. 通知没有业务响应,可以有多个订阅者;请求在目的节点上需要恰好一个负责的处理器。分开的 `subscribe` 与 `onRequest` 注册消除了多重回复的歧义;同一节点/channel 注册第二个请求处理器会失败。
|
||||
4. 网络故障可能发生在执行之后、回复之前。超时和 socket 写入成功都无法证明业务操作是否已执行。消息仅在线投递,队列有界,不做跨连接自动重放。持久状态和幂等性由应用负责。
|
||||
5. 消息标识在跨代理转发和日志中都有用,它独立于传输操作临时的回执关联。每条消息(包括通知和回复)都有一个 UUID;回复指向请求的 UUID。路由保留标识;普通调用无需应用生成关联字段。
|
||||
6. 后端游戏线程不得执行 socket I/O,也不得等待远端回复。宿主提供回调的执行器。超时从提交时开始计时,覆盖排队、分派和等待;已过期的排队消息不得在之后发送。
|
||||
|
||||
## 公开模型
|
||||
|
||||
`Endpoint` 是代理或某个具名后端。`Message` 包含 `id`、`kind`(`EVENT`、`REQUEST`、`REPLY`)、`channel`、已认证的 `source`、`target`、`replyTo`,以及防御性复制的二进制 `payload`。事件 target 为 null 表示向订阅者发布;请求和回复总是只有一个 target。只有回复带 `replyTo`。业务消息类型和 schema 版本可编码在 payload 中,它们不是传输层 kind。连接代次保持内部。
|
||||
|
||||
每个插件获得一个归其所有的 `Messaging` 作用域,并按名称取得 `MessageChannel`。`subscribe` 接收事件,`onRequest` 异步生成回复,`send` 向单个端点发送事件,`request` 取得一条完整回复消息,`publish` 扇出事件。关闭插件作用域只撤销该插件的注册和未决调用。关闭后保留的句柄不能再创建新工作。
|
||||
|
||||
`send` 返回包含消息 ID 和结果的回执。`ACCEPTED` 表示目的节点已将通知接入其有界处理器分派,不表示业务代码已完成。`NO_SUBSCRIBER`、`NOT_CONNECTED`、`BACKPRESSURED`、`REJECTED`、`TIMED_OUT` 和 `FAILED` 相互区分。超时或未知失败不能证明接收方未执行该操作。请求在无处理器、过载、超时、断线、拒绝或处理器失败时以类型化错误失败。
|
||||
|
||||
`publish` 访问代理以及当前已连接且有权接收该 channel 的每个后端(包括发布者自身)。每个节点分派给其当前的本地订阅者。结果对每个候选节点各含一项(包括 `NO_SUBSCRIBER`);它不是跨节点的原子事务。首个实现刻意不引入复制的订阅目录及其注册/重连竞态。一次发布最多接受 256 个候选节点(含代理);超过上限会在分派前拒绝整个发布。直接发送和请求没有这一扇出限制。将来的优化可以引入带确认的订阅路由,而不改变公开语义。
|
||||
|
||||
## 所有权与传输
|
||||
|
||||
代理通过 `PluginContext` 提供消息服务。单个 Java 8 Bukkit 桥接插件持有后端客户端,并通过 Bukkit 的 `ServicesManager` 发布服务。使用方插件声明对桥接插件的依赖,并以 provided/compile-only 依赖使用其 API;不得 shade 另外一份服务 API 或客户端。桥接插件提供游戏线程回调执行,并在 `PluginDisableEvent` 时自动关闭作用域。
|
||||
|
||||
现有的认证注册、公告游戏地址校验、心跳租约和后端目录所有权仍是传输基础。后端到后端的流量经过代理内置路由器。路由既不改变游戏连接,也不需要代理业务插件。
|
||||
|
||||
协议版本 2 增加共享的有界消息编解码器。逐跳操作 ID 关联投递回执和传输响应;端到端消息 UUID 和回复引用在转发中保留。代理根据已认证连接覆盖或校验来源,并将未决调用绑定到确切连接。接收权限与发送权限分别检查。版本 1 的对端必须一并升级;不兼容的版本会明确失败,而不是互相解码对方的帧。
|
||||
|
||||
## 验证边界
|
||||
|
||||
实现在合入当前 main 时必须保留富文本/命令补全测试。必需的消息测试覆盖:两个独立后端客户端与代理、事件订阅者与唯一请求处理器、正确的来源和回复标识、调用方修改 payload、畸形/有界协议帧、停滞的处理器、慢速 socket 读取方、插件停用,以及断线/重连。打包的桥接插件必须只包含一份 Java 8 API/客户端实现,并排除 Bukkit 类。
|
||||
|
||||
实际 Bukkit/Uranium 加载、真实服务器调度下的主线程行为和生产容量属于单独的验收证据,不得从编译或隔离的 socket 测试推断。
|
||||
@@ -1,40 +0,0 @@
|
||||
# Channel messaging: contract and rationale
|
||||
|
||||
## Problem and acceptance criteria
|
||||
|
||||
A plugin on backend A must be able to notify or query a proxy plugin or backend B using a named channel. Plugins must not create network connections, distribute credentials, implement proxy relay plugins, or depend on a connected player. Multiple plugins in one backend share one authenticated connection. Both ends receive an immutable message with a framework-assigned identity and an authenticated source.
|
||||
|
||||
Acceptance requires real socket tests with no players: backend/proxy and backend/backend requests; notification fan-out; concurrent responses matched to their requests; independent plugin disable; bounded overload; timeout including queueing; disconnect without replay; and old-connection isolation. A Java 8 Bukkit host artifact provides the shared service. Unit tests and socket tests do not establish deployment acceptance in a running Forge pack.
|
||||
|
||||
## Deriving the model
|
||||
|
||||
1. A connection authenticates a backend process, not each plugin in that process. The proxy therefore derives the source from the authenticated connection. A plugin label is useful for local lifecycle ownership, but is not a separately authenticated remote principal.
|
||||
2. A channel identifies a business communication domain and its permission boundary. A destination identifies the receiving node. Conflating these would force applications to encode backend names in channels or build their own routing protocol.
|
||||
3. A notification has no business response and may have several subscribers. A request needs exactly one responsible handler at its destination. Separate `subscribe` and `onRequest` registrations remove the multiple-reply ambiguity; registering a second request handler for the same node/channel fails.
|
||||
4. Network failure can occur after execution but before a reply. Neither a timeout nor a successful socket write proves whether the business operation executed. Messaging is live-only, with bounded queues and no automatic cross-connection replay. Durable state and idempotency belong to the application.
|
||||
5. A message identity is useful across proxy hops and in logs. It is independent of a transport operation's temporary receipt correlation. Every message, including a notification and a reply, gets a UUID. A reply points to the request UUID. Routing preserves identities; applications do not generate correlation fields for ordinary calls.
|
||||
6. The backend game thread must not perform socket I/O or wait for remote replies. The host supplies the executor for callbacks. Timeouts start at submission and cover queueing, dispatch, and waiting; expired queued messages must not be transmitted later.
|
||||
|
||||
## Public model
|
||||
|
||||
`Endpoint` is either the proxy or a named backend. `Message` contains `id`, `kind` (`EVENT`, `REQUEST`, `REPLY`), `channel`, authenticated `source`, `target`, `replyTo`, and a defensively copied binary `payload`. A null event target means publication to subscribers; requests and replies always have one target. Only replies have `replyTo`. Business message types and schema versions can be encoded in the payload; they are not transport kinds. Connection generations remain internal.
|
||||
|
||||
Each plugin gets an owned `Messaging` scope and obtains a `MessageChannel` by name. `subscribe` receives events, `onRequest` produces an asynchronous reply, `send` sends an event to one endpoint, `request` obtains one full reply message, and `publish` fans out an event. Closing a plugin scope revokes only that plugin's registrations and pending calls. Retained handles cannot create new work after close.
|
||||
|
||||
`send` returns a receipt containing the message ID and a result. `ACCEPTED` means that the destination node accepted the notification into its bounded handler dispatch; it does not mean that business code finished. `NO_SUBSCRIBER`, `NOT_CONNECTED`, `BACKPRESSURED`, `REJECTED`, `TIMED_OUT`, and `FAILED` are distinct. A timeout or unknown failure cannot prove that the recipient did not execute the operation. A request fails with a typed error for no handler, overload, timeout, disconnect, rejection, or handler failure.
|
||||
|
||||
`publish` visits the proxy and each currently connected backend authorized to receive the channel, including the publisher. Each node dispatches to its current local subscribers. The result contains one result per candidate node, including `NO_SUBSCRIBER`; it is not an atomic transaction across nodes. This first implementation deliberately avoids a replicated subscription directory and its registration/reconnect races. A publication accepts at most 256 candidate nodes including the proxy; exceeding that limit rejects the whole publication before dispatch. Direct sends and requests do not have this fan-out limit. A future optimization may introduce acknowledged subscription routing without changing the public semantics.
|
||||
|
||||
## Ownership and transport
|
||||
|
||||
The proxy provides the messaging service through `PluginContext`. A single Java 8 Bukkit bridge owns the backend client and publishes a service through Bukkit's `ServicesManager`. Consumer plugins declare a dependency on the bridge and use its API with a provided/compile-only dependency. They must not shade separate copies of the service API or client. The bridge supplies game-thread callback execution and automatically closes scopes on `PluginDisableEvent`.
|
||||
|
||||
The existing authenticated registration, advertised game address validation, heartbeat lease, and backend directory ownership remain the transport foundation. Backend-to-backend traffic travels through the proxy's built-in router. Routing neither changes game connections nor requires a proxy business plugin.
|
||||
|
||||
Protocol version 2 adds a shared bounded message codec. Per-hop operation IDs correlate delivery receipts and transport responses; end-to-end message UUIDs and reply references survive forwarding. The proxy overwrites or validates the source against the authenticated connection and binds pending calls to the exact connection. Receiving permissions are checked independently of sending permissions. Version 1 peers must be upgraded together; incompatible versions fail explicitly rather than decoding each other's frames.
|
||||
|
||||
## Verification boundaries
|
||||
|
||||
The implementation must retain the rich-text/command-completion tests while integrating current main. Required messaging tests cover two independent backend clients and the proxy, event subscribers versus a unique request handler, correct source and reply identities, caller payload mutation, malformed/bounded protocol frames, stalled handlers, slow socket readers, plugin disable, and disconnect/reconnect. A packaged bridge must contain the Java 8 API/client implementation once and exclude Bukkit classes.
|
||||
|
||||
Actual Bukkit/Uranium loading, main-thread behavior under real server scheduling, and production capacity are separate acceptance evidence and must not be inferred from compilation or isolated socket tests.
|
||||
+2
-2
@@ -4,13 +4,13 @@ MoonBridge 当前区分本地开发版本、CI 发布的不可变开发构件和
|
||||
|
||||
## CI 发布流程
|
||||
|
||||
`.gitea/workflows/ci.yml` 在推送 `main` 或手动触发时运行。工作流以 CI run ID、attempt 和提交的前 12 位 SHA 生成版本 `0.1.0-dev.<run-id>.<attempt>.<commit>`,然后按顺序运行:
|
||||
`.github/workflows/ci.yml` 在推送 `main` 或手动触发时运行。工作流以 CI run ID、attempt 和提交的前 12 位 SHA 生成版本 `0.1.0-dev.<run-id>.<attempt>.<commit>`,然后按顺序运行:
|
||||
|
||||
1. 使用 JDK 25 运行 `gradlew check :proxy-core:distZip`。
|
||||
2. 将 JAR 和发行 ZIP 上传为名为 `moonbridge-development` 的 CI 工件,保留 30 天。
|
||||
3. 执行 `gradlew publish`,发布七个 Maven 模块。
|
||||
|
||||
Maven 发布需要 Gitea Actions secrets `MAVEN_USER` 和 `MAVEN_PASSWORD`。验证构建和发行 ZIP 的步骤先运行;凭据缺失会使发布步骤失败。CI 成功状态是发布结果的依据;CI 工件下载和 Maven 包仓库是不同交付物。工作流不创建 Git Release,也不表示已有稳定版本。
|
||||
Maven 发布需要 CI secrets `MAVEN_USER` 和 `MAVEN_PASSWORD`。验证构建和发行 ZIP 的步骤先运行;凭据缺失会使发布步骤失败。CI 成功状态是发布结果的依据;CI 工件下载和 Maven 包仓库是不同交付物。工作流不创建 Git Release,也不表示已有稳定版本。
|
||||
|
||||
本地 Gradle 默认版本为 `0.1.0-SNAPSHOT`。如需本地构建另一个版本,可在 Gradle 命令中传入 `-PreleaseVersion=<version>`;不要将本地 SNAPSHOT 当作不可变的 Maven 发布版本。
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# 2026-09-26 验证记录
|
||||
|
||||
本文件从 [验证方法](../testing.md) 迁出,保留当时的结果原文;数字与结论只对应记录注明的实现和日期。
|
||||
|
||||
### 玩家操作与服务器列表验证记录(2026-09-26)
|
||||
|
||||
完成[玩家操作设计](../player-operations.md)的首批实现后,运行 `./gradlew.bat check --offline` 通过。最终报告共 194 项测试(API 7、核心 174、DNS 6、Agent 7),失败、错误和跳过均为 0;安装包帮助、版本和配置校验通过。
|
||||
|
||||
本轮新增或扩展的验证包括:完整连接身份校验;PLAY 消息与普通包连续转发;未就绪、连接不可写及 64 条未完成消息上限;消息排空后主动断开;重复断开与调用方取消;准入/选服等待期间的 LOGIN 断开;Login Success 写入未完成时的 PLAY 包顺序;断开写入失败或停滞时的有界清理;转服暂停期间断开的候选后端与索引释放。在线协议测试用实际 RSA/AES 链路核对加密消息与断开,身份验证器仍为注入实现。
|
||||
|
||||
服务器列表测试核对默认/自定义 MOTD、展示人数与实时在线人数、非 BMP 字符、JSON 转义、完整响应长度、PNG 文件/尺寸/路径校验,以及经真实 TCP 管线的 STATUS 与 Ping/Pong。插件宿主关闭时先终止事件派发再撤销订阅,避免排队准入链跳过已撤销策略而返回允许。文档相对链接检查通过。
|
||||
|
||||
本轮没有使用真实 Mojang 登录会话、目标整合包或生产负载;没有进行性能对比。远端 CI 的执行结果单独记录在 PR 中,本地通过不表示 Runner 或部署环境已验收。
|
||||
|
||||
### 统一事件模型验证记录(2026-09-26)
|
||||
|
||||
统一为 `Event<R>` / `EventListener<E,R>` 后运行 `./gradlew.bat check --offline`。最终报告共 178 项测试(API 7、核心 158、DNS 6、Agent 7),失败、错误和跳过均为 0;安装包帮助、版本和配置校验通过。
|
||||
|
||||
本轮覆盖统一订阅的类型推断、准入的允许/拒绝/空结果/异常/超时/过载、异步通知按监听器和事件顺序完成、通知超时后的队列推进、空通知 stage 的异常隔离,以及取消后停止监听器链、取消回调离开调用线程、宿主关闭后迟到 stage 的取消。既有合成 TCP 登录、身份验证顺序、转服提交/断线通知、命令与普通 PLAY 转发回归通过。文档相对链接检查通过。
|
||||
|
||||
本轮没有运行目标整合包、真实 Mojang 在线服务或事件吞吐基准。测试中的在线身份验证器为注入实现;这些结果验证功能与并发边界,不能推出性能收益、零开销或生产容量。
|
||||
### 历史类型化事件验证记录(2026-09-26)
|
||||
|
||||
以下结果针对当时的事件 API 与派发实现。运行 `./gradlew.bat check --offline` 的报告共 174 项测试(API 7、核心 154、DNS 6、Agent 7),失败、错误和跳过均为 0;安装包帮助、版本和配置校验通过。这些数字只对应当时的实现;当前统一 `Event<R>` / `EventListener<E,R>` 模型的验证见上一节。
|
||||
|
||||
当时的覆盖包括认证前/认证后拒绝、加密断开消息、异步等待与取消、超时和容量上限,启动阶段注册与冻结、异步准入期间注销订阅、通知顺序/异常隔离/有界队列、启动失败时撤销订阅。合成 TCP 流程还核对首次进入后端、转服提交与断线通知,以及失败/同服转服和重复关闭不会多发通知。普通 PLAY 聊天与代理命令回归通过。
|
||||
|
||||
这轮旧模型验证没有运行吞吐对比或目标整合包测试;在线认证测试使用注入的会话验证器。它不能推出统一事件系统无开销或已达到生产容量。
|
||||
|
||||
### 历史访问检查验证记录(2026-09-26)
|
||||
|
||||
以下记录验证的是 2026-09-26 当时基于专用访问钩子的实现。它是历史结果;类型化事件 API 的验证见上一节。
|
||||
|
||||
新增访问检查时先运行 `./gradlew.bat check --offline --rerun-tasks`;最后的并发清理修正后,再运行 `./gradlew.bat check --offline`,重新执行受影响的核心测试与发行包烟测。最终报告共 167 项测试(API 6、核心 148、DNS 6、Agent 7),失败、错误和跳过均为 0;安装包帮助、版本与核心配置校验通过。
|
||||
|
||||
新增覆盖包括协议解析前的 IP 拒绝、身份建立后的玩家拒绝、异步等待期间的数据回放、加密登录断开消息、断线取消、超时、异常、工作队列与未决 stage 容量上限,以及进入 PLAY 后不重复调用访问检查。在线身份测试使用注入的会话验证器,未连接真实 Mojang 服务。本次未测量接入吞吐或真实数据库延迟;以下基准均保留各自的历史版本与验证范围。
|
||||
@@ -0,0 +1,11 @@
|
||||
# 2026-09-27 富文本与命令补全验证
|
||||
|
||||
本文件从 [验证方法](../testing.md) 迁出,保留当时的结果原文;数字与结论只对应记录注明的实现和日期。
|
||||
|
||||
### 富文本与命令补全验证(2026-09-27)
|
||||
|
||||
实现设计见[消息与命令补全](../messages-and-completion.md)。本地 `gradlew check` 通过:217 项测试(API 7、核心 197、DNS 6、Agent 7),失败、错误、跳过均为 0;发行包帮助、版本、配置校验通过。
|
||||
|
||||
新增验证包含组件构造与旧版 JSON 字段、模板变量作为文字插入、消息深度/节点/字节限制;根命令与后端候选合并、参数补全、原始后端回复透传;超时、迟到回复隔离、队列上限、转服代次切换、插件注销/关闭与取消回调线程。真实 TCP 测试同时核对富文本命令回复和补全,转服测试核对切换前后的补全上下文。
|
||||
|
||||
此外用 Java 8 加载本机 Uranium 的实际组件解析器读取当前发行包生成的 JSON,核对正文、点击命令和悬浮文字通过。未启动真实客户端进行视觉/鼠标交互验收;没有测量本次改动的吞吐收益。最终远端 CI 按对应 PR 检查项记录。
|
||||
+6
-40
@@ -14,7 +14,7 @@ sh smoke/container-channel.sh
|
||||
|
||||
`smoke/container-network.ps1` 使用 Docker bridge 中的独立代理、后端和客户端容器,依次检查静态配置和控制通道注册、登录及 PLAY 转发;控制通道模式还验证注销后已连接会话继续转发。PowerShell 7 和 Docker Desktop 是运行前提。`smoke/container-channel.sh` 在 Unix shell 下验证控制通道的同一跨容器路径。2026-09-25 的[容器网络烟测记录](../smoke/results/2026-09-25-container-network.md)是旧实现的历史结果,不代表当前通道已在那次运行中验证。容器烟测不能代替跨物理主机或目标 Forge 整合包实测。
|
||||
|
||||
`.gitea/workflows/ci.yml` 在 `main` 推送或手动触发时先执行 `gradlew check :proxy-core:distZip`,再上传 CI 工件并发布 Maven 模块。发布步骤要求 `MAVEN_USER` 与 `MAVEN_PASSWORD` secrets;本地 `check` 不能证明远端 Maven 发布成功。版本规则、七个坐标和发行 ZIP 见[发布与依赖版本](publishing.md)。
|
||||
`.github/workflows/verify.yml` 在每个 Pull Request 上运行 `gradlew check`。`.github/workflows/ci.yml` 在 `main` 推送或手动触发时先执行 `gradlew check :proxy-core:distZip`,再上传 CI 工件并发布 Maven 模块。发布步骤要求 `MAVEN_USER` 与 `MAVEN_PASSWORD` secrets;本地 `check` 不能证明远端 Maven 发布成功。版本规则、七个坐标和发行 ZIP 见[发布与依赖版本](publishing.md)。
|
||||
|
||||
`check` 包含 `permissionAcceptance`:使用发行包中的实际 LuckPerms JAR、隔离插件类加载器和临时 H2 数据库,验证原生命令、授权/拒绝/未设置、组继承、临时节点到期、后端上下文、连接代次隔离及关闭后重新打开数据库。输出位于 `proxy-core/build/permission-acceptance/run-*/`,成功标记为 `PERMISSION_ACCEPTANCE_PASS`。它使用 API 玩家探针,不连接真实 Minecraft 客户端,也不证明外部 SQL/Redis 联调成功;真实会话的权限准备和释放由 TCP 生命周期测试另行覆盖。
|
||||
|
||||
@@ -24,38 +24,12 @@ sh smoke/container-channel.sh
|
||||
|
||||
安装包转服烟测可加 `-DebugSession`,在本次复制的 Uranium 配置中开启登录阶段日志,并将代理会话的连接、登录写入和失败路径 DEBUG 日志写入所打印运行目录的 `proxy.stdout.log`。加 `-TraceBackend` 会在代理与两台 Uranium 之间放置本地 TCP 中继,并将每个方向的前 512 字节记录到运行目录的 `tap-old.log` 和 `tap-new.log`;这一诊断选项会改变连接时序。初次登录 EOF 的复现实验见 [运行记录](../smoke/results/2026-09-26-initial-login-repeat.md);增强日志和中继后捕获的 Uranium 登录停滞及源码假设见 [登录停滞记录](../smoke/results/2026-09-26-uranium-login-stalls.md)。
|
||||
|
||||
## 玩家操作与服务器列表验证记录(2026-09-26)
|
||||
## 历史验证记录
|
||||
|
||||
完成[玩家操作设计](player-operations.md)的首批实现后,运行 `./gradlew.bat check --offline` 通过。最终报告共 194 项测试(API 7、核心 174、DNS 6、Agent 7),失败、错误和跳过均为 0;安装包帮助、版本和配置校验通过。
|
||||
带日期的逐轮验证结果放在 [`records/`](records/),与 `smoke/results/`、`benchmarks/results/` 一样只证明各自注明的提交和环境:
|
||||
|
||||
本轮新增或扩展的验证包括:完整连接身份校验;PLAY 消息与普通包连续转发;未就绪、连接不可写及 64 条未完成消息上限;消息排空后主动断开;重复断开与调用方取消;准入/选服等待期间的 LOGIN 断开;Login Success 写入未完成时的 PLAY 包顺序;断开写入失败或停滞时的有界清理;转服暂停期间断开的候选后端与索引释放。在线协议测试用实际 RSA/AES 链路核对加密消息与断开,身份验证器仍为注入实现。
|
||||
|
||||
服务器列表测试核对默认/自定义 MOTD、展示人数与实时在线人数、非 BMP 字符、JSON 转义、完整响应长度、PNG 文件/尺寸/路径校验,以及经真实 TCP 管线的 STATUS 与 Ping/Pong。插件宿主关闭时先终止事件派发再撤销订阅,避免排队准入链跳过已撤销策略而返回允许。文档相对链接检查通过。
|
||||
|
||||
本轮没有使用真实 Mojang 登录会话、目标整合包或生产负载;没有进行性能对比。远端 CI 的执行结果单独记录在 PR 中,本地通过不表示 Runner 或部署环境已验收。
|
||||
|
||||
## 统一事件模型验证记录(2026-09-26)
|
||||
|
||||
统一为 `Event<R>` / `EventListener<E,R>` 后运行 `./gradlew.bat check --offline`。最终报告共 178 项测试(API 7、核心 158、DNS 6、Agent 7),失败、错误和跳过均为 0;安装包帮助、版本和配置校验通过。
|
||||
|
||||
本轮覆盖统一订阅的类型推断、准入的允许/拒绝/空结果/异常/超时/过载、异步通知按监听器和事件顺序完成、通知超时后的队列推进、空通知 stage 的异常隔离,以及取消后停止监听器链、取消回调离开调用线程、宿主关闭后迟到 stage 的取消。既有合成 TCP 登录、身份验证顺序、转服提交/断线通知、命令与普通 PLAY 转发回归通过。文档相对链接检查通过。
|
||||
|
||||
本轮没有运行目标整合包、真实 Mojang 在线服务或事件吞吐基准。测试中的在线身份验证器为注入实现;这些结果验证功能与并发边界,不能推出性能收益、零开销或生产容量。
|
||||
## 历史类型化事件验证记录(2026-09-26)
|
||||
|
||||
以下结果针对当时的事件 API 与派发实现。运行 `./gradlew.bat check --offline` 的报告共 174 项测试(API 7、核心 154、DNS 6、Agent 7),失败、错误和跳过均为 0;安装包帮助、版本和配置校验通过。这些数字只对应当时的实现;当前统一 `Event<R>` / `EventListener<E,R>` 模型的验证见上一节。
|
||||
|
||||
当时的覆盖包括认证前/认证后拒绝、加密断开消息、异步等待与取消、超时和容量上限,启动阶段注册与冻结、异步准入期间注销订阅、通知顺序/异常隔离/有界队列、启动失败时撤销订阅。合成 TCP 流程还核对首次进入后端、转服提交与断线通知,以及失败/同服转服和重复关闭不会多发通知。普通 PLAY 聊天与代理命令回归通过。
|
||||
|
||||
这轮旧模型验证没有运行吞吐对比或目标整合包测试;在线认证测试使用注入的会话验证器。它不能推出统一事件系统无开销或已达到生产容量。
|
||||
|
||||
## 历史访问检查验证记录(2026-09-26)
|
||||
|
||||
以下记录验证的是 2026-09-26 当时基于专用访问钩子的实现。它是历史结果;类型化事件 API 的验证见上一节。
|
||||
|
||||
新增访问检查时先运行 `./gradlew.bat check --offline --rerun-tasks`;最后的并发清理修正后,再运行 `./gradlew.bat check --offline`,重新执行受影响的核心测试与发行包烟测。最终报告共 167 项测试(API 6、核心 148、DNS 6、Agent 7),失败、错误和跳过均为 0;安装包帮助、版本与核心配置校验通过。
|
||||
|
||||
新增覆盖包括协议解析前的 IP 拒绝、身份建立后的玩家拒绝、异步等待期间的数据回放、加密登录断开消息、断线取消、超时、异常、工作队列与未决 stage 容量上限,以及进入 PLAY 后不重复调用访问检查。在线身份测试使用注入的会话验证器,未连接真实 Mojang 服务。本次未测量接入吞吐或真实数据库延迟;以下基准均保留各自的历史版本与验证范围。
|
||||
- [2026-09-26 验证记录](records/2026-09-26-validation-log.md):玩家操作与服务器列表、统一事件模型、历史类型化事件与访问检查。
|
||||
- [2026-09-27 富文本与命令补全验证](records/2026-09-27-rich-text-completion.md)。
|
||||
|
||||
## 泄漏闸门与变异检查
|
||||
|
||||
@@ -112,15 +86,7 @@ sh smoke/container-channel.sh
|
||||
|
||||
`-Burst N`(直接运行 Java 时为 `--burst N`)让每次往返由客户端一次写出 N 个帧、模拟后端一次 flush 回声,接近一个服务器 tick 内的多个小 PLAY 包;此时 Window 按批计数。输出另含代理 I/O 线程每帧 CPU 时间,以及在 Linux 上读取 `/proc/net/snmp` 得到的全机 TCP 发送段数/帧(含 ACK,其他平台显示 `n/a`)。[2026-10-05 批量 flush 记录](../benchmarks/results/2026-10-05-relay-batched-flush.md)用它比较逐帧 flush 与按读取批次 flush。`-Transport auto|nio|epoll|kqueue`(`--transport`)选择代理使用的网络传输层,默认 `auto`;[2026-10-05 传输层记录](../benchmarks/results/2026-10-05-network-transport.md)比较了 epoll 与 NIO。
|
||||
|
||||
原 stop-and-wait 小样本见 `benchmarks/results/proxy-session-benchmark-smoke-2026-09-25.md`;Window 参数的小样本和 Window=1/16 重复测量见 `benchmarks/results/2026-09-25-proxy-session-window.md`。这些是环回网络上的合成帧对照,不能代表 Forge 整合包、真实后端或跨主机部署的性能。
|
||||
|
||||
## 富文本与命令补全验证(2026-09-27)
|
||||
|
||||
实现设计见[消息与命令补全](messages-and-completion.md)。本地 `gradlew check` 通过:217 项测试(API 7、核心 197、DNS 6、Agent 7),失败、错误、跳过均为 0;发行包帮助、版本、配置校验通过。
|
||||
|
||||
新增验证包含组件构造与旧版 JSON 字段、模板变量作为文字插入、消息深度/节点/字节限制;根命令与后端候选合并、参数补全、原始后端回复透传;超时、迟到回复隔离、队列上限、转服代次切换、插件注销/关闭与取消回调线程。真实 TCP 测试同时核对富文本命令回复和补全,转服测试核对切换前后的补全上下文。
|
||||
|
||||
此外用 Java 8 加载本机 Uranium 的实际组件解析器读取当前发行包生成的 JSON,核对正文、点击命令和悬浮文字通过。未启动真实客户端进行视觉/鼠标交互验收;没有测量本次改动的吞吐收益。最终远端 CI 按对应 PR 检查项记录。
|
||||
原 stop-and-wait 小样本见 `benchmarks/results/2026-09-25-proxy-session-benchmark-smoke.md`;Window 参数的小样本和 Window=1/16 重复测量见 `benchmarks/results/2026-09-25-proxy-session-window.md`。这些是环回网络上的合成帧对照,不能代表 Forge 整合包、真实后端或跨主机部署的性能。
|
||||
|
||||
## 后端宿主真实服务端验收
|
||||
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
# Transfer coordination
|
||||
# 转服协调
|
||||
|
||||
## Contract
|
||||
## 契约
|
||||
|
||||
Transfer coordination is optional. Without `TransferPreparingEvent` subscribers, transfers use the existing parallel login and cutover path. Ordinary PLAY forwarding does not dispatch or inspect transfer events.
|
||||
转服协调是可选的。没有 `TransferPreparingEvent` 订阅者时,转服沿用现有的并行登录与切换路径。普通 PLAY 转发不派发也不检查转服事件。
|
||||
|
||||
After the destination TCP connection succeeds, before any destination Minecraft login bytes are written, the proxy calls the selected preparation listeners off its I/O thread. The source remains connected and usable. Listeners return `allow()`, `deny(reason)`, or `releaseSource(afterSourceClosed)`. Denial stops the transfer while preserving the source. All listeners are considered in registration order until denial; an allow cannot erase an earlier source-release requirement.
|
||||
目标 TCP 连接成功后、写入任何目标 Minecraft 登录字节之前,代理在 I/O 线程之外调用选定的准备监听器。源服保持连接且可用。监听器返回 `allow()`、`deny(reason)` 或 `releaseSource(afterSourceClosed)`。拒绝会停止转服并保留源服。所有监听器按注册顺序依次考虑,直至遇到拒绝;allow 不能抹去先前的源服释放要求。
|
||||
|
||||
Preparation should validate and record reversible intent. It must not take a final snapshot or clear live player state: the source can still process gameplay. A plugin that records intent must reconcile abandoned intent independently; notification delivery is not a persistence guarantee.
|
||||
准备阶段应校验并记录可撤销的意图,不得做最终快照或清除在线玩家状态:源服仍可能处理玩法。记录意图的插件必须独立对账被放弃的意图;通知投递不是持久化保证。
|
||||
|
||||
If any listener requests source release, the proxy stops forwarding new source-world operations, drains already accepted network writes, detaches the source relay, and closes the source connection. It then invokes only the callbacks attached to the selected source-release decisions, in registration order, off the I/O thread. Each callback is a `Supplier<CompletionStage<Void>>` bound to the preparation that selected it; it can capture that preparation's immutable context. There is no separate source-close event or independently subscribable notification. Socket closure does not mean the backend has processed logout or committed a save. Each required callback defines and confirms its own durable boundary through successful asynchronous completion.
|
||||
若任一监听器请求释放源服,代理停止转发新的源服世界操作,排空已接受的网络写入,摘除源服 relay 并关闭源服连接。随后仅按注册顺序、在 I/O 线程之外调用附加在所选源服释放决定上的回调。每个回调都是绑定到选择它的那次准备的 `Supplier<CompletionStage<Void>>`,可以捕获该准备的不可变上下文。没有单独的源服关闭事件或可独立订阅的通知。socket 关闭不表示后端已处理登出或完成存档。每个必需回调通过异步成功完成来定义并确认自己的持久化边界。
|
||||
|
||||
Only after all required callbacks succeed does the proxy send the destination Handshake and LoginStart. It checks that the attempt, frontend connection, destination connection and destination registration generation are still current. Source-world packets received during the gap are discarded, never queued for replay into the destination. Existing Forge negotiation continues to permit control traffic and reject gameplay until its world boundary is ready.
|
||||
只有所有必需回调都成功后,代理才发送目标 Handshake 和 LoginStart。它会检查本次尝试、前端连接、目标连接和目标注册代次仍是当前的。间隙期间收到的源服世界数据包会被丢弃,绝不排队重放到目标。现有 Forge 协商继续放行控制流量,并在其世界边界就绪前拒绝玩法流量。
|
||||
|
||||
## Identity and lifetime
|
||||
## 标识与生命周期
|
||||
|
||||
`TransferContext` is an immutable snapshot containing a unique transfer ID, exact player/frontend connection identity, source and destination server snapshots, catalog registration generations, authenticated backend instance epochs (zero for static backends without an instance binding), and a total deadline. It exposes no channels, buffers, mutable sessions, database concepts or player inventory logic.
|
||||
`TransferContext` 是不可变快照,包含唯一的转服 ID、精确的玩家/前端连接身份、源服和目标服务器快照、目录注册代次、经认证的后端实例 epoch(没有实例绑定的静态后端为零)以及总期限。它不暴露通道、缓冲区、可变会话、数据库概念或玩家背包逻辑。
|
||||
|
||||
The listener cohort is selected once per attempt. Every required source-release callback remains bound to its original registration and plugin owner. Revocation, plugin shutdown, null results, exception, timeout or overload fails the transfer; it cannot silently shrink the required cohort. Late completions cannot resume a closed or replaced attempt.
|
||||
监听器集合在每次尝试中只选定一次。每个必需的源服释放回调始终绑定到其原始注册和插件所有者。撤销、插件关闭、null 结果、异常、超时或过载都会使转服失败;它不能悄悄缩小必需的集合。迟到的完成不能恢复已关闭或已被替换的尝试。
|
||||
|
||||
Callbacks use a separate bounded dispatcher: two workers, 128 queued tasks and at most 128 pending events. Callback stages do not block I/O threads or globally serialize unrelated transfers. No subscribers means no transfer event, event future or event timer allocation. The dispatcher is created only when subscriptions exist. A slow plugin can cause its transfer to fail; it cannot create an unbounded queue.
|
||||
回调使用单独的有界分派器:两个工作线程、128 个排队任务、至多 128 个未决事件。回调 stage 不阻塞 I/O 线程,也不全局串行化互不相关的转服。没有订阅者时不分配转服事件、事件 future 或事件计时器;分派器仅在存在订阅时创建。慢插件可能导致其转服失败,但不能造成无界队列。
|
||||
|
||||
## Deadlines and failures
|
||||
## 期限与失败
|
||||
|
||||
Preparation and source-release confirmation each use the configured event deadline, including queue wait. Destination login gets its own 15-second budget after login starts for coordinated transfers. TCP connection retains its five-second budget. The existing cutover and Forge negotiation budgets remain. A total deadline bounds the entire coordinated attempt.
|
||||
准备和源服释放确认各自使用配置的事件期限,包括排队等待。对协调转服,目标登录开始后另有 15 秒预算。TCP 连接保留其 5 秒预算。现有的切换和 Forge 协商预算不变。总期限约束整个协调尝试。
|
||||
|
||||
| Failure point | Outcome |
|
||||
| 失败点 | 结果 |
|
||||
| --- | --- |
|
||||
| TCP failure, preparation denial/error/timeout | Close candidate; source continues |
|
||||
| Pause failure or partial detach | Preserve source only if its relay can be safely restored; otherwise disconnect |
|
||||
| Source released, callback error/timeout or destination failure | Disconnect frontend; never fall back to the closed source |
|
||||
| Destination registration changed | Fail this attempt; never authenticate against a replacement instance implicitly |
|
||||
| Frontend disconnect or proxy shutdown | Close candidate and release local resources; ignore late results |
|
||||
| TCP 失败,准备拒绝/出错/超时 | 关闭候选;源服继续 |
|
||||
| 暂停失败或部分摘除 | 仅当源服 relay 可安全恢复时保留源服;否则断开 |
|
||||
| 源服已释放后回调出错/超时或目标失败 | 断开前端;绝不回退到已关闭的源服 |
|
||||
| 目标注册已变化 | 本次尝试失败;绝不隐式向替换实例认证 |
|
||||
| 前端断线或代理关闭 | 关闭候选并释放本地资源;忽略迟到结果 |
|
||||
|
||||
This opt-in sequence sacrifices the normal destination-login-before-source-close fallback window. TCP reachability does not prove whitelist, authentication, mod negotiation or gameplay readiness. `TransferResult.NETWORK_READY` retains its existing meaning; it does not certify persistence by itself.
|
||||
这一可选流程放弃了常规的“先登录目标、再关闭源服”的回退窗口。TCP 可达不证明白名单、认证、mod 协商或玩法就绪。`TransferResult.NETWORK_READY` 保持原有含义,其本身不证明持久化。
|
||||
|
||||
## Acceptance
|
||||
## 验收
|
||||
|
||||
Correctness tests must establish source close before confirmation, confirmation before destination login, denial preserving the source, failure after release disconnecting, old source EOF not closing the waiting frontend, cancellation and stale callbacks, exact backend generation checks, and no old-world gameplay replay. Existing vanilla and Forge transfers must remain covered.
|
||||
正确性测试必须证明:确认前源服已关闭;目标登录前已确认;拒绝保留源服;释放后失败会断开;旧源服 EOF 不会关闭等待中的前端;取消与过期回调;精确的后端代次检查;不重放旧世界玩法。现有原版和 Forge 转服必须继续覆盖。
|
||||
|
||||
Measure ordinary PLAY forwarding against main `876de1a056a79b807a03f3877cbe6414b60e86dc`, with initial and post-transfer sessions, windows 1/16 and five repetitions. Also measure transfer throughput and p50/p99 latency at 0/1/4 participants and 1/16/64 concurrent players. Record environment, errors, allocation and retained buffering where measurable. An initial 3% default-path regression budget is a decision threshold, not a result; if measurement noise exceeds it, classify the comparison as inconclusive and improve the experiment. Passing functional tests does not establish performance superiority.
|
||||
以 main `876de1a056a79b807a03f3877cbe6414b60e86dc` 为基准测量普通 PLAY 转发,包括初始会话和转服后会话、窗口 1/16、重复五次。同时测量 0/1/4 个参与者、1/16/64 个并发玩家下的转服吞吐量和 p50/p99 延迟。在可测时记录环境、错误、分配和滞留缓冲。初始 3% 默认路径回退预算是决策阈值而非结果;若测量噪声超过该阈值,应将比较归为无定论并改进实验。功能测试通过不能证明性能更优。
|
||||
|
||||
PlayerDataSQL integration is a separate plugin concern. These events do not implement SQL ownership, backend final-save confirmation, authoritative data loading, mod synchronization or crash recovery.
|
||||
PlayerDataSQL 集成是单独的插件问题。这些事件不实现 SQL 所有权、后端最终存档确认、权威数据加载、mod 同步或崩溃恢复。
|
||||
@@ -91,6 +91,17 @@ val compilePermissionAcceptance = tasks.register<JavaCompile>("compilePermission
|
||||
options.release.set(25)
|
||||
}
|
||||
|
||||
// The benchmarks in benchmarks/ declare proxy-core packages to reach package-private classes and are
|
||||
// normally compiled by their PowerShell runners. Compiling them here keeps them building with main.
|
||||
val compileBenchmarks = tasks.register<JavaCompile>("compileBenchmarks") {
|
||||
group = "verification"
|
||||
description = "Compiles the benchmarks in benchmarks/ against proxy-core."
|
||||
source(rootProject.fileTree("benchmarks") { include("*.java") })
|
||||
classpath = sourceSets.main.get().runtimeClasspath
|
||||
destinationDirectory.set(layout.buildDirectory.dir("classes/benchmarks"))
|
||||
options.release.set(25)
|
||||
}
|
||||
|
||||
val permissionAcceptance = tasks.register<JavaExec>("permissionAcceptance") {
|
||||
group = "verification"
|
||||
description = "Loads the packaged LuckPerms plugin and verifies native permissions, commands and H2 persistence."
|
||||
@@ -106,6 +117,7 @@ tasks.named("check") {
|
||||
dependsOn(installedDistHelpSmokeTest)
|
||||
dependsOn(installedDistVersionSmokeTest)
|
||||
dependsOn(permissionAcceptance)
|
||||
dependsOn(compileBenchmarks)
|
||||
}
|
||||
|
||||
application {
|
||||
|
||||
@@ -55,4 +55,4 @@ All six pass markers were present, including `asyncBarrier=true persistenceVerif
|
||||
|
||||
## Limits
|
||||
|
||||
The synthetic protocol client is not a real Prism visual/gameplay test. The smoke callback confirms only its deliberate asynchronous barrier; it does not observe a database commit. Additional mod inventories, a production Uranium build, arbitrary plugin behavior and PlayerDataSQL crash recovery are outside this result. Performance evidence and its uncertainty are recorded separately in [the benchmark report](../../benchmarks/results/transfer-coordination-validation.md).
|
||||
The synthetic protocol client is not a real Prism visual/gameplay test. The smoke callback confirms only its deliberate asynchronous barrier; it does not observe a database commit. Additional mod inventories, a production Uranium build, arbitrary plugin behavior and PlayerDataSQL crash recovery are outside this result. Performance evidence and its uncertainty are recorded separately in [the benchmark report](../../benchmarks/results/2026-10-01-transfer-coordination-validation.md).
|
||||
Reference in new issue
Block a user