build: 统一 release 入口,CI 覆盖客户端 mod,文档同步新结构

- 根任务 release:build/release/ 下收集服务器包、后端插件 JAR 与客户端 mod
  (经 client-mod 自身 wrapper 构建,-PskipClientMod 可跳过),附安装位置说明
  docs/release-layout.md
- verify.yml:-Pluckperms check,并新增 client-mod 构建测试 job;
  ci.yml:-Pluckperms check release,上传 build/release/,发布带 -Pluckperms
- gradlew 加可执行位
- README、CLAUDE.md、架构/发布/测试/权限/后端接入文档与 smoke 脚本改用新模块、
  新坐标与发行包 backend/ 目录

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGL9BudzWm4dUQgBsQw4Qr
This commit is contained in:
Claude committed 2026-10-21 10:13:06 +00:00
1 parent f156932f1a
commit d836f6c3d3
15 files changed
+117 -50

No files matched your search

+4 -8
View File
@@ -29,18 +29,14 @@ jobs:
- name: Set development version
run: echo "ORG_GRADLE_PROJECT_releaseVersion=0.1.0-dev.${GITHUB_RUN_ID}.${GITHUB_RUN_ATTEMPT:-1}.${GITHUB_SHA:0:12}" >> "$GITHUB_ENV"
- name: Check and assemble distribution
run: bash ./gradlew --no-daemon check :proxy-core:distZip
- name: Check and assemble the release
run: bash ./gradlew --no-daemon -Pluckperms check release
- name: Upload build outputs
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882
with:
name: moonbridge-development
path: |
**/build/libs/*.jar
!luckperms-moonbridge/build/libs/*-engine.jar
!vendor/**/build/libs/*.jar
proxy-core/build/distributions/*.zip
path: build/release/
if-no-files-found: error
retention-days: 30
@@ -75,4 +71,4 @@ jobs:
fi
- name: Publish all Maven modules
run: bash ./gradlew --no-daemon publish
run: bash ./gradlew --no-daemon -Pluckperms publish
+19 -4
View File
@@ -19,7 +19,22 @@ jobs:
with:
distribution: zulu
java-version: '25'
- name: Check
run: |
chmod +x ./gradlew
./gradlew --no-daemon check
- name: Check (including the LuckPerms plugin)
run: ./gradlew --no-daemon -Pluckperms check
client-mod:
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09
with:
persist-credentials: false
- uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6
with:
distribution: zulu
java-version: '25'
- name: Build and test the 1.7.10 client mod
working-directory: client-mod
run: ./gradlew --no-daemon build
+17 -10
View File
@@ -6,27 +6,34 @@ MoonBridge is a session proxy for Minecraft 1.7.10 Forge (protocol 5): login/aut
## Build and test
JDK 25 is required (Gradle toolchain; the daemon JVM is pinned to 25). `gradlew` is not executable in a fresh Linux checkout, so use `bash ./gradlew` (`.\gradlew.bat` on Windows). The LuckPerms plugin builds from a submodule: run `git submodule update --init --recursive` first.
JDK 25 is required (Gradle toolchain; the daemon JVM is pinned to 25). Use `./gradlew` (`.\gradlew.bat` on Windows).
The LuckPerms platform plugin compiles LuckPerms from the `vendor/luckperms` submodule, which is slow and download-heavy, so it is opt-in: `-Pluckperms` includes `luckperms-moonbridge`, its tests, `permissionAcceptance` and the plugin in the distribution (run `git submodule update --init --recursive` first). CI always passes it; run it locally when touching permissions, plugin loading or the distribution layout.
```sh
bash ./gradlew check # everything CI runs, ~7 min: tests, installed-dist smoke tests, permissionAcceptance
bash ./gradlew :proxy-core:test # proxy-core tests only
bash ./gradlew :proxy-core:test --tests '*RawRelayTest' # one class
bash ./gradlew :proxy-core:test --tests '*RawRelayTest.methodName' # one method
bash ./gradlew :proxy-core:installDist # runnable distribution in proxy-core/build/install/moonbridge
./gradlew check # tests, installed-dist smoke tests, benchmark compile
./gradlew -Pluckperms check # what CI runs, ~7 min: adds the LuckPerms plugin and permissionAcceptance
./gradlew release # build/release: server zip, backend jar, client mod (-PskipClientMod to omit it)
./gradlew :proxy-core:test # proxy-core tests only
./gradlew :proxy-core:test --tests '*RawRelayTest' # one class
./gradlew :proxy-core:test --tests '*RawRelayTest.m' # one method
./gradlew :proxy-core:installDist # runnable distribution in proxy-core/build/install/moonbridge
```
The slow tests are the session integration tests (`ProxySessionListenerTest`, `SessionTransferTest`, `InitialRoutingTest`, `SourceReleaseTransferTest`); iterate with `--tests` and run `check` once before pushing.
`.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.
`.github/workflows/verify.yml` runs `-Pluckperms check` and builds `client-mod/` on every pull request; `.github/workflows/ci.yml` runs `-Pluckperms check release`, uploads `build/release/` and publishes the Maven modules on pushes to `main`. Run `-Pluckperms check` locally before pushing anyway when the change can affect it: a red PR costs a round trip.
## Modules
- `proxy-core` — the proxy runtime (`dev.moonbridge.app` = startup/config, `dev.moonbridge.core.*` = everything else). The only module that uses Netty.
- `proxy-plugin-api` — the stable plugin boundary. Plugins are discovered with `META-INF/services/dev.moonbridge.api.Plugin` and loaded in separate class loaders by `PluginHost`.
- `messaging-api`, `messaging-protocol`, `backend-channel-client`, `backend-bukkit-api`, `backend-bukkit` — backend side. These compile with `--release 8` (Java 8 backends such as Uranium) and must not pull in newer APIs.
- `luckperms-moonbridge` — native LuckPerms platform plugin, built from `vendor/luckperms` via the included build `luckperms-moonbridge/upstream-build`. `proxy-core:check` depends on it (`permissionAcceptance`).
- `proxy-build-logic` — Kotlin convention plugins shared by all modules.
- `messaging` — message envelope, channel API and wire codec shared by the proxy and backends.
- `backend` — the Bukkit/Uranium host plugin `MoonBridgeBackend` (its jar bundles `messaging`), its service API (`dev.moonbridge.bukkit`) and the control channel client (`dev.moonbridge.backendchannel`). Business plugins compile against this jar `compileOnly`.
- `messaging` and `backend` use `moonbridge.java8-library-conventions` (`--release 8`, for Java 8 backends such as Uranium) and must not pull in newer APIs.
- `luckperms-moonbridge` — native LuckPerms platform plugin, built from `vendor/luckperms` via the included build `luckperms-moonbridge/upstream-build`; only included with `-Pluckperms`.
- `client-mod/` — the 1.7.10 Forge WebSocket client mod. A separate Gradle build (GTNH template, RetroFuturaGradle, Mixins) with its own wrapper: `cd client-mod && ./gradlew build`. Its `net` package is free of Minecraft classes and uses Minecraft's bundled Netty 4.0.10.
- `proxy-build-logic` — Kotlin convention plugins: `java-library`, `java8-library`, `application`, and `publishing-conventions` (shared package repository; set `moonbridgePublication { artifactId ... }` for a standard publication).
## Architecture that spans files
+4 -3
View File
@@ -18,18 +18,19 @@
需要 JDK 25。发行包不包含 Minecraft 后端;要完成玩家登录,需要一个 Minecraft 1.7.10 后端,且 `initialRouting.servers` 中至少一个名称能解析到它(随附配置为 `lobby` → `127.0.0.1:25565`)。
```sh
git submodule update --init --recursive
bash ./gradlew check :proxy-core:installDist # Windows: .\gradlew.bat check :proxy-core:installDist
./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
```
需要全部部署物(服务器包、后端插件、客户端 mod)时运行 `./gradlew release`,说明见[发行物与安装位置](docs/release-layout.md)。LuckPerms 平台插件从 `vendor/luckperms` 子模块编译,默认不构建;需要时先 `git submodule update --init --recursive`,再加 `-Pluckperms`。
默认配置为 `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#后端插件接入)。
服务器安装 `moonbridge-backend` JAR(插件名 `MoonBridgeBackend`)。业务插件对同一个构件 `uk.potatolab.moonbridge:backend` 使用 `compileOnly` / `provided`,并通过 `BukkitMessagingService` 获取共享消息服务;无需创建网络连接。源码当前的 `0.1.0-SNAPSHOT` 仅是本地开发构件版本;发布依赖应使用发布工作流提供的不可变版本。API 与完整示例见[后端插件接入](docs/backend-channel-design.md#后端插件接入)。
从 StrataProxy 升级见[迁移说明](docs/operations.md#从-strataproxy-迁移);历史验收记录保留当时的名称与构件路径。
+30
View File
@@ -24,3 +24,33 @@ tasks.register("publish") {
description = "Publishes the Maven modules (CI passes -Pluckperms so the LuckPerms plugin is included)."
dependsOn(published.map { "$it:publish" })
}
// client-mod/ is a separate Gradle build (GTNH toolchain for Minecraft 1.7.10); release runs it through its
// own wrapper. Pass -PskipClientMod to leave it out, e.g. where the Minecraft toolchain cannot be downloaded.
val skipClientMod = providers.gradleProperty("skipClientMod").map { it != "false" }.getOrElse(false)
val windows = System.getProperty("os.name").lowercase().contains("windows")
val buildClientMod = tasks.register<Exec>("buildClientMod") {
group = "build"
description = "Builds the 1.7.10 Forge WebSocket client mod in client-mod/."
workingDir = file("client-mod")
// clean: the jar name carries the git version, so older jars would otherwise be collected too.
val gradle = if (windows) listOf("cmd", "/c", "gradlew.bat") else listOf("bash", "./gradlew")
commandLine(gradle + listOf("clean", "build"))
}
tasks.register<Sync>("release") {
group = "distribution"
description = "Collects everything a deployment needs into build/release (see docs/release-layout.md)."
into(layout.buildDirectory.dir("release"))
from(file("docs/release-layout.md")) { rename { "README.md" } }
from(project(":proxy-core").tasks.named("distZip")) { into("server") }
from(project(":backend").tasks.named("jar")) { into("backend") }
if (!skipClientMod) {
dependsOn(buildClientMod)
from(fileTree("client-mod/build/libs") {
include("moonbridgews-*.jar")
exclude("*-dev.jar", "*-sources.jar")
}) { into("client") }
}
}
+1
View File
@@ -23,6 +23,7 @@
| [后端 Channel 消息服务](backend-channel-design.md) | 共享宿主、跨后端路由、消息身份与交付语义 |
| [Channel 消息设计契约](channel-messaging-contract.md) | 从参与者、寻址、响应与失败语义推导 API 和验收条件 |
| [验证方法](testing.md) | 自动化测试、烟测、真实客户端与合成性能测量 |
| [发行物与安装位置](release-layout.md) | `gradlew release` 产出的服务器包、后端插件、客户端 mod 各装在哪里 |
| [发布与依赖版本](publishing.md) | 本地 SNAPSHOT、CI 不可变版本、Maven 构件和发行包的区别 |
## 当前可引用的验证记录
+1
View File
@@ -6,6 +6,7 @@
- `proxy-plugin-api` 定义插件外部接口:生命周期、玩家/服务器只读视图、后端注册、类型化事件、初始选服、命令和转服。核心实现位于 `proxy-core`;可选发现器位于 `plugins/`。
- `ProxyMain` 读取严格 YAML 配置,先注册静态后端,再加载配置启用的插件,连接插件选服回调,最后启动 Netty 监听器。插件以 `META-INF/services/dev.moonbridge.api.Plugin` 声明,通过独立类加载器加载。
- 后端一侧只有两个 Java 8 模块:`messaging`(消息 API 与线协议,代理与后端共用)和 `backend`(Bukkit/Uranium 共享宿主 `MoonBridgeBackend`、其服务 API 与控制通道客户端,打成一个 JAR)。`luckperms-moonbridge` 是可选的原生权限插件(`-Pluckperms` 构建);`client-mod/` 是独立构建的 1.7.10 客户端 WebSocket mod。
- 核心支持 Minecraft 1.7.10(协议 5)登录和转发。`OFFLINE` 信任客户端提交的名字;`ONLINE_BUNGEE` 在代理执行加密与会话验证,并将身份转发给受信任的旧版 Bungee 后端。
入口可从 [`ProxyMain`](../proxy-core/src/main/java/dev/moonbridge/app/ProxyMain.java)、[`ProxySessionListener`](../proxy-core/src/main/java/dev/moonbridge/core/session/ProxySessionListener.java) 和 [`PluginHost`](../proxy-core/src/main/java/dev/moonbridge/core/plugin/PluginHost.java) 追踪。
+5 -5
View File
@@ -25,7 +25,7 @@ flowchart LR
## 后端插件接入
在 Bukkit/Uranium 后端安装一次 `backend-bukkit` 生成的 `moonbridge-backend-bukkit` JAR,插件名为 `MoonBridgeBackend`。配置唯一的实例 ID、后端名称、代理控制地址、游戏地址与凭据。宿主异步建立连接,不在启动主线程等待网络。
在 Bukkit/Uranium 后端安装一次 `backend` 模块生成的 `moonbridge-backend` JAR(发行包 `backend/` 目录),插件名为 `MoonBridgeBackend`。配置唯一的实例 ID、后端名称、代理控制地址、游戏地址与凭据。宿主异步建立连接,不在启动主线程等待网络。
消费者 `plugin.yml` 声明:
@@ -33,7 +33,7 @@ flowchart LR
depend: [MoonBridgeBackend]
```
消费者依赖独立的 `uk.potatolab.moonbridge:backend-bukkit-api`,它传递依赖平台无关的 `messaging-api`。源码默认 `0.1.0-SNAPSHOT` 是本地开发版本;使用已发布构件时,必须将示例中的 `<published-version>` 替换为 CI 成功发布的准确不可变版本。`backend-bukkit` 是部署到服务器的宿主实现,`backend-channel-client` 是其他平台实现宿主时使用的传输 SDK,普通业务插件不依赖它们。发布版本及构件列表见[发布与依赖版本](publishing.md)。
消费者对 `uk.potatolab.moonbridge:backend` 使用 `compileOnly`,它就是服务器上安装的那个 JAR,传递依赖平台无关的 `messaging`。业务插件只应使用 `dev.moonbridge.bukkit` 中的 `BukkitMessagingService`、`BukkitSessionService`、`BackendPlayerSession` 和 `dev.moonbridge.messaging` 的公共 API;`dev.moonbridge.backendchannel` 是宿主内部的控制通道客户端,只有在其他平台自行实现宿主时才直接使用。源码默认 `0.1.0-SNAPSHOT` 是本地开发版本;使用已发布构件时,必须将示例中的 `<published-version>` 替换为 CI 成功发布的准确不可变版本。发布版本及构件列表见[发布与依赖版本](publishing.md)。
Gradle 示例(Bukkit API 仍由业务插件按目标服务器版本自行声明):
@@ -42,11 +42,11 @@ repositories {
maven("https://git.nest.potatolab.uk:8443/api/packages/layue13/maven")
}
dependencies {
compileOnly("uk.potatolab.moonbridge:backend-bukkit-api:<published-version>")
compileOnly("uk.potatolab.moonbridge:backend:<published-version>")
}
```
Maven 使用同一坐标并设置 `<scope>provided</scope>`。本地开发可改用 `0.1.0-SNAPSHOT`,但不要把它当作已发布版本。发行包的 `backend-api/` 也包含编译所需的 API JAR,适合隔离构建环境直接编译。本例中的 Bukkit API 仍由业务插件按目标服务器版本自行声明为 `compileOnly` / `provided`。不要把 API、SDK 或宿主实现重新打包进业务插件 JAR,也不要把 API JAR 单独放进服务器 `plugins/`;运行时由 `MoonBridgeBackend` 提供同一份 API 类和服务实现。
Maven 使用同一坐标并设置 `<scope>provided</scope>`。本地开发可改用 `0.1.0-SNAPSHOT`,但不要把它当作已发布版本。隔离构建环境也可以直接对发行包 `backend/` 中的 JAR 编译。本例中的 Bukkit API 仍由业务插件按目标服务器版本自行声明为 `compileOnly` / `provided`。不要把这个 JAR 重新打包进业务插件;运行时由服务器 `plugins/` 中的 `MoonBridgeBackend` 提供唯一一份 API 类和服务实现。
```java
import dev.moonbridge.bukkit.BukkitMessagingService;
@@ -116,7 +116,7 @@ HMAC 挑战认证本身不加密链路;跨机器使用私有加密链路或可
本次构建、244 项回归和实际 Java 8 进程的结果见 [2026-09-27 验证记录](../smoke/results/2026-09-27-channel-messaging.md)。
安装发行包的 `backend-host/` 提供可直接安装的共享宿主 JAR;`backend-client/` 提供非 Bukkit 宿主所需的 SDK、消息 API 和协议三个 JAR。运行 `:proxy-core:installDist` 后,可用实际 JDK 8 运行独立进程验收:
安装发行包的 `backend/` 提供共享宿主 JAR;它同时包含非 Bukkit 宿主所需的控制通道客户端、消息 API 和协议。运行 `:proxy-core:installDist` 后,可用实际 JDK 8 运行独立进程验收:
```powershell
.\smoke\channel-java8.ps1 -Java8Home 'C:\path\to\jdk8' -Java25Home 'C:\path\to\jdk25'
+2 -2
View File
@@ -22,7 +22,7 @@
MoonBridge 是破坏性改名,旧包名/API、启动程序和配置文件没有兼容别名。升级时将 `strataproxy` 启动程序改为 `moonbridge`、将 `strataproxy.yml` 迁移为 `moonbridge.yml`,并检查 `dev.strataproxy` Java 导入改为 `dev.moonbridge`。旧配置中的 `plugins.initialPlacementTimeoutSeconds` 改为 `initialRouting.timeoutSeconds`;首次登录入口应显式写入 `initialRouting.servers`,不要依赖注册顺序。
后端宿主插件从 `StrataProxyBackend` 改为 `MoonBridgeBackend`。更新业务插件 `plugin.yml` 的 `depend`、安装新的宿主 JAR,并将旧插件配置迁移到新宿主插件的数据目录。消费者需重新编译;已删除的 `backendChannels()` API 应迁移到 `messaging()`,Bukkit 业务插件改用 `backend-bukkit-api` 提供的 `BukkitMessagingService`。迁移后的依赖和宿主接法见[后端 Channel 接入](backend-channel-design.md#后端插件接入)。
后端宿主插件从 `StrataProxyBackend` 改为 `MoonBridgeBackend`。更新业务插件 `plugin.yml` 的 `depend`、安装新的宿主 JAR,并将旧插件配置迁移到新宿主插件的数据目录。消费者需重新编译;已删除的 `backendChannels()` API 应迁移到 `messaging()`,Bukkit 业务插件改用 `backend` 构件提供的 `BukkitMessagingService`。迁移后的依赖和宿主接法见[后端 Channel 接入](backend-channel-design.md#后端插件接入)。
## 服务器列表
@@ -74,6 +74,6 @@ Docker 容器间的监听、服务名和地址配置见[配置与部署](backend
- 登录、认证和协议:`proxy-core/src/test/java/dev/moonbridge/core/auth/` 与 `proxy-core/src/test/java/dev/moonbridge/core/protocol/`
- 会话、断开、转服与缓冲:`proxy-core/src/test/java/dev/moonbridge/core/session/`,重点包括 `SessionTransferTest`、`SessionTransferDispatchTest`、`transfer/TransferFrameBufferTest`
- 插件生命周期、访问检查和 API:`proxy-core/src/test/java/dev/moonbridge/core/plugin/PluginHostTest.java`、`proxy-core/src/test/java/dev/moonbridge/core/session/ProxySessionListenerTest.java`、`proxy-core/src/test/java/dev/moonbridge/app/ProxyConfigurationTest.java` 与 `proxy-plugin-api/src/test/`
- 控制通道:`proxy-core/src/test/java/dev/moonbridge/core/control/` 与 `backend-channel-client/src/test/`
- 控制通道:`proxy-core/src/test/java/dev/moonbridge/core/control/` 与 `backend/src/test/`
仓库另有本地 Uranium 协议探针和 Prism Forge 客户端记录,见 [`smoke/results`](../smoke/results/2026-09-25-local-uranium.md)、[转服记录](../smoke/results/2026-09-25-local-uranium-transfer.md) 和 [真实客户端记录](../smoke/results/2026-09-25-prism-forge-client.md)。这些记录支持其明确写出的单机环境、软件版本和探针路径。已知也出现过真实 Forge 客户端转服候选后端登录停滞,见[调查记录](../smoke/results/2026-09-26-uranium-login-stalls.md);已有成功样例不构成稳定性或生产验收。
+3 -2
View File
@@ -55,13 +55,14 @@ context.commands().register("island", "example.command.island", invocation -> {
## LuckPerms 平台插件
`luckperms-moonbridge` 使用固定版本的官方 LuckPerms 公共核心,并实现 MoonBridge 平台适配。上游源码在 Git 子模块 `vendor/luckperms`,构建前运行:
`luckperms-moonbridge` 使用固定版本的官方 LuckPerms 公共核心,并实现 MoonBridge 平台适配。上游源码在 Git 子模块 `vendor/luckperms`。编译它较慢且依赖很多,因此默认构建不包含该插件,需要显式开启:
```sh
git submodule update --init --recursive
./gradlew -Pluckperms check release # 构建插件、运行 permissionAcceptance,并放进发行包
```
发行包在 `plugins/` 提供插件 JAR。修改 `config/moonbridge.yml`,替换默认的 `enabled: {}`:
带 `-Pluckperms` 构建的发行包(CI 和正式发布均如此)在 `plugins/` 提供插件 JAR。修改 `config/moonbridge.yml`,替换默认的 `enabled: {}`:
```yaml
plugins:
+10 -11
View File
@@ -6,9 +6,9 @@ MoonBridge 当前区分本地开发版本、CI 发布的不可变开发构件和
`.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 模块。
1. 使用 JDK 25 运行 `gradlew -Pluckperms check release`。
2. 将 `build/release/`(服务器包、后端插件、客户端 mod,见[发行物与安装位置](release-layout.md))上传为名为 `moonbridge-development` 的 CI 工件,保留 30 天。
3. 执行 `gradlew -Pluckperms publish`,发布四个 Maven 模块。
Maven 发布需要 CI secrets `MAVEN_USER` 和 `MAVEN_PASSWORD`。验证构建和发行 ZIP 的步骤先运行;凭据缺失会使发布步骤失败。CI 成功状态是发布结果的依据;CI 工件下载和 Maven 包仓库是不同交付物。工作流不创建 Git Release,也不表示已有稳定版本。
@@ -21,14 +21,13 @@ Maven 坐标统一使用 group `uk.potatolab.moonbridge` 和 CI 所报版本:
| Artifact ID | 用途 |
| --- | --- |
| `proxy-plugin-api` | Proxy 插件 API |
| `messaging-api` | 跨节点消息公共 API |
| `messaging-protocol` | 消息传输协议 |
| `backend-channel-client` | 非 Bukkit 后端宿主使用的传输 SDK |
| `backend-bukkit-api` | Bukkit 业务插件编译时使用的服务 API |
| `backend-bukkit` | 部署在 Bukkit/Uranium 服务端的共享宿主实现 |
| `messaging` | 跨节点消息公共 API 与线协议(Java 8) |
| `backend` | 后端共享宿主 `MoonBridgeBackend`(Bukkit/Uranium 插件,Java 8),含其服务 API 与控制通道客户端;业务插件对它 `compileOnly` |
| `luckperms-moonbridge` | Proxy 原生 LuckPerms 插件;不是业务插件的编译 API |
七个模块均随同一次 CI 发布使用相同版本。业务插件通常将 `backend-bukkit-api` 和目标服务器的 Bukkit API 作为 `compileOnly` / Maven `provided` 依赖;服务器提供 Bukkit API,`MoonBridgeBackend` 宿主提供运行时消息 API 和服务。普通业务插件不应依赖宿主/传输 SDK 实现,也不应把任何 API JAR 打包进插件。非 Bukkit 宿主才需要 `backend-channel-client` 及消息 API/协议;见[后端接入说明](backend-channel-design.md)。
四个模块均随同一次 CI 发布使用相同版本。业务插件将 `backend` 和目标服务器的 Bukkit API 作为 `compileOnly` / Maven `provided` 依赖;服务器提供 Bukkit API,`MoonBridgeBackend` 宿主提供运行时消息 API 和服务。不要把 `backend` 打包进插件。非 Bukkit 宿主也使用 `backend` 中的控制通道客户端;见[后端接入说明](backend-channel-design.md)。
旧坐标 `messaging-api`、`messaging-protocol`、`backend-channel-client`、`backend-bukkit-api`、`backend-bukkit` 已停止发布:前两者合并为 `messaging`,后三者合并为 `backend`,包名与类名不变,只需替换依赖坐标并重新编译。
包仓库地址为 `https://git.nest.potatolab.uk:8443/api/packages/layue13/maven`。消费者应从成功 CI 记录中复制实际版本;以下 `<published-version>` 只是需替换的说明占位符:
@@ -38,14 +37,14 @@ repositories {
}
dependencies {
compileOnly("uk.potatolab.moonbridge:backend-bukkit-api:<published-version>")
compileOnly("uk.potatolab.moonbridge:backend:<published-version>")
}
```
```xml
<dependency>
<groupId>uk.potatolab.moonbridge</groupId>
<artifactId>backend-bukkit-api</artifactId>
<artifactId>backend</artifactId>
<version>&lt;published-version&gt;</version>
<scope>provided</scope>
</dependency>
+16
View File
@@ -0,0 +1,16 @@
# 发行物与安装位置
`bash ./gradlew release`(Windows:`.\gradlew.bat release`)把部署需要的东西收集到 `build/release/`:
| 路径 | 是什么 | 装在哪里 |
| --- | --- | --- |
| `server/moonbridge-<版本>.zip` | 代理发行包:`bin/` 启动脚本、`lib/`、`config/` 示例配置、`docs/`;带 `-Pluckperms` 构建时 `plugins/` 里含 LuckPerms 平台插件 | 代理服务器,解压后用 JDK 25 运行 `bin/moonbridge --config config/moonbridge.yml` |
| `backend/moonbridge-backend-<版本>.jar` | 后端共享宿主 `MoonBridgeBackend`(Bukkit/Uranium 插件,Java 8),内含消息 API 与控制通道客户端 | 每台后端服务器的 `plugins/`;后端业务插件对它 `compileOnly` 并在 `plugin.yml` 里 `depend: [MoonBridgeBackend]` |
| `client/moonbridgews-<版本>.jar` | 1.7.10 Forge WebSocket 客户端 mod | 玩家客户端 `mods/`,另需 UniMixins;只在使用 `ws://`/`wss://` 地址时需要 |
可选开关:
- `-Pluckperms`:同时编译 LuckPerms 平台插件(需要 `git submodule update --init --recursive`),并运行 `permissionAcceptance`。CI 和正式发布都带这个开关。
- `-PskipClientMod`:不构建客户端 mod(它需要下载 Minecraft 1.7.10 工具链)。
Maven 坐标(group `uk.potatolab.moonbridge`):`proxy-plugin-api`(代理插件 API)、`messaging`(消息 API 与协议)、`backend`(后端宿主及其 API)、`luckperms-moonbridge`(LuckPerms 平台插件)。版本与仓库见[发布与依赖版本](publishing.md)。
+4 -4
View File
@@ -5,7 +5,7 @@
```powershell
.\gradlew.bat check
.\gradlew.bat :proxy-core:installedDistSmokeTest
.\gradlew.bat :proxy-core:permissionAcceptance
.\gradlew.bat -Pluckperms :proxy-core:permissionAcceptance
.\smoke\container-network.ps1
sh smoke/container-channel.sh
```
@@ -14,9 +14,9 @@ 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 整合包实测。
`.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)。
`.github/workflows/verify.yml` 在每个 Pull Request 上运行 `gradlew -Pluckperms check`,并单独构建、测试 `client-mod/`。`.github/workflows/ci.yml` 在 `main` 推送或手动触发时先执行 `gradlew -Pluckperms check release`,再上传 `build/release/` 并发布 Maven 模块。发布步骤要求 `MAVEN_USER` 与 `MAVEN_PASSWORD` secrets;本地 `check` 不能证明远端 Maven 发布成功。版本规则、Maven 坐标和发行物 见[发布与依赖版本](publishing.md)。
`check` 包含 `permissionAcceptance`:使用发行包中的实际 LuckPerms JAR、隔离插件类加载器和临时 H2 数据库,验证原生命令、授权/拒绝/未设置、组继承、临时节点到期、后端上下文、连接代次隔离及关闭后重新打开数据库。输出位于 `proxy-core/build/permission-acceptance/run-*/`,成功标记为 `PERMISSION_ACCEPTANCE_PASS`。它使用 API 玩家探针,不连接真实 Minecraft 客户端,也不证明外部 SQL/Redis 联调成功;真实会话的权限准备和释放由 TCP 生命周期测试另行覆盖。
带 `-Pluckperms` 时 `check` 包含 `permissionAcceptance`(本地默认 `check` 不编译 LuckPerms,因此不含这一项;CI 始终开启):使用发行包中的实际 LuckPerms JAR、隔离插件类加载器和临时 H2 数据库,验证原生命令、授权/拒绝/未设置、组继承、临时节点到期、后端上下文、连接代次隔离及关闭后重新打开数据库。输出位于 `proxy-core/build/permission-acceptance/run-*/`,成功标记为 `PERMISSION_ACCEPTANCE_PASS`。它使用 API 玩家探针,不连接真实 Minecraft 客户端,也不证明外部 SQL/Redis 联调成功;真实会话的权限准备和释放由 TCP 生命周期测试另行覆盖。
如果本地已有 Uranium 1.7.10 可运行包及其编译好的 `MinecraftProtocolProbe`,可运行 `smoke/local-uranium.ps1 -BundlePath <包目录> -ProbeClassesPath <探针类目录>`。脚本复制服务端到忽略目录,启动 Java 8 后端与当前安装包,再让探针经代理完成状态查询、FML 登录、Join Game 和持续 Keep Alive,结束时停止两个进程。本地一次结果与具体前提见 [最小 Uranium 联机记录](../smoke/results/2026-09-25-local-uranium.md)。初次路由的当前 `ProxyMain` 协议探针结果另见[路由验收](../smoke/results/2026-09-27-initial-routing.md)。真实客户端记录见下段和其单独的历史验收档案;它们不应与当前路由分支的完整模组包验收混为一谈。
@@ -90,7 +90,7 @@ sh smoke/container-channel.sh
## 后端宿主真实服务端验收
`smoke/backend-uranium.ps1` 将已经接受 EULA 的 Uranium 发行包中的服务端 JAR、运行库和 EULA 复制到全新隔离目录,启动两个 Java 8 后端及实际 Java 25 Proxy。它安装 `MoonBridgeBackend`、两个独立的 Bukkit 业务插件和一个外部 Proxy 验收插件,通过正式插件 API 检查注册与消息服务;不会复制或修改原有世界、模组、插件及凭据。Bukkit 业务插件只使用发行包 `backend-api/` 中的 API JAR 和服务器 Bukkit API 编译,编译路径不包含宿主及传输 SDK,并在实际插件加载器下验证服务类身份一致。
`smoke/backend-uranium.ps1` 将已经接受 EULA 的 Uranium 发行包中的服务端 JAR、运行库和 EULA 复制到全新隔离目录,启动两个 Java 8 后端及实际 Java 25 Proxy。它安装 `MoonBridgeBackend`、两个独立的 Bukkit 业务插件和一个外部 Proxy 验收插件,通过正式插件 API 检查注册与消息服务;不会复制或修改原有世界、模组、插件及凭据。Bukkit 业务插件以发行包 `backend/` 中的 JAR 和服务器 Bukkit API 编译(compileOnly,运行时依赖 `MoonBridgeBackend`),并在实际插件加载器下验证服务类身份一致。
```powershell
.\gradlew.bat check :proxy-core:installDist
Vendored Regular → Executable
View File
File mode changed.
+1 -1
View File
@@ -7,7 +7,7 @@ $repo = Split-Path -Parent $PSScriptRoot
$fixtureClasses = Join-Path $repo 'build/channel-smoke/router'
$probeClasses = Join-Path $repo 'build/channel-smoke/probe'
$proxyClasspath = Join-Path $repo 'proxy-core/build/install/moonbridge/lib/*'
$backendClasspath = Join-Path $repo 'proxy-core/build/install/moonbridge/backend-client/*'
$backendClasspath = Join-Path $repo 'proxy-core/build/install/moonbridge/backend/*'
if (-not (Test-Path (Join-Path $repo 'proxy-core/build/install/moonbridge/lib'))) {
throw 'Run gradlew :proxy-core:installDist first.'
}