mirror of
https://github.com/layue13/MoonBridge.git
synced 2026-10-25 18:46:18 +00:00
Merge pull request #4 from layue13/refactor/build-and-modules
重构构建与模块:后端 5 合 2、LuckPerms 可选构建、统一 release
This commit is contained in:
99 files changed
+374
-516
No files matched your search
@@ -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,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
|
||||
@@ -6,33 +6,40 @@ 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
|
||||
|
||||
**Session threading.** `ProxySessionListener` owns the listener, event loops and the UUID→session index. Each `Session` (one per player connection) confines its mutable state to the frontend event loop, and its backend channels are created on that same loop (`SessionChannels.backendBootstrap` enforces it). Login verification, backend DNS and plugin callbacks run off that loop and re-enter it by posting a task; results are checked against the current connection identity so a stale async result cannot act on a reconnected player. Plugin work goes through bounded pools that reject when full.
|
||||
|
||||
**Session layout.** `core.session` holds `Session` (the Netty handler and lifecycle owner) plus the pieces it delegates to: `FrontendLogin` (handshake, status, encryption, Mojang verification), `InitialRouter` (placement decision and first backend dial), `BackendHandshakes`. `core.session.transfer` is `TransferCoordinator`, which reaches the session only through the `TransferHost` port (Session supplies it as an adapter) and owns all transfer state. `core.session.play` has the per-frame PLAY handlers and relay start-up buffering; `core.session.channel` has backend dialing, pipeline lookups and `clientAddress` (the player IP: a trusted forwarded address or the socket peer; use it instead of `remoteAddress()`). `core.websocket` is the optional WebSocket frontend; after the upgrade the pipeline behind `WebSocketFrameAdapter` is the same as for TCP. Dependencies point one way: `session` -> `transfer` -> `play`/`channel`.
|
||||
**Session layout.** `core.session` holds `Session` (the Netty handler and lifecycle owner) plus the pieces it delegates to: `FrontendLogin` (handshake, status, encryption, Mojang verification), `InitialRouter` (placement decision and first backend dial), `BackendHandshakes`. `core.session.transfer` is `TransferCoordinator`, which reaches the session only through the `TransferHost` port (Session supplies it as an adapter) and owns all transfer state. `core.session.play` has the PLAY data path (`RawRelay`, the per-frame handlers, relay start-up buffering); `core.session.channel` has `NetworkTransport`, backend dialing, pipeline lookups and `clientAddress` (the player IP: a trusted forwarded address or the socket peer; use it instead of `remoteAddress()`). `core.websocket` is the optional WebSocket frontend; after the upgrade the pipeline behind `WebSocketFrameAdapter` is the same as for TCP. Dependencies point one way: `session` -> `transfer` -> `play`/`channel`.
|
||||
|
||||
**Data path.** After login, `RawRelay` forwards already-framed PLAY frames between the two channels without copying. Per-frame concerns are separate inbound handlers (`KeepAliveBridge`, `PlayerCommandInterceptor`, `TabCompletionBridge`, `TransferFrameHandler`) in front of the relay. Backpressure works by pausing reads on the source when the peer is not writable; the relay flushes once per read batch (`channelReadComplete`).
|
||||
|
||||
|
||||
@@ -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-迁移);历史验收记录保留当时的名称与构件路径。
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
import java.io.DataInputStream
|
||||
import java.util.jar.JarFile
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(25))
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
options.release.set(8)
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":messaging-api"))
|
||||
// Consumers compile with their server API; the running server supplies Bukkit.
|
||||
compileOnly("org.spigotmc:spigot-api:1.8-R0.1-SNAPSHOT") { isTransitive = false }
|
||||
}
|
||||
|
||||
val verifyApiJar = tasks.register("verifyApiJar") {
|
||||
group = "verification"
|
||||
dependsOn(tasks.jar)
|
||||
val apiJar = tasks.jar.flatMap { it.archiveFile }
|
||||
inputs.file(apiJar)
|
||||
doLast {
|
||||
JarFile(apiJar.get().asFile).use { archive ->
|
||||
val entries = archive.entries().asSequence().toList()
|
||||
val classes = entries.filter { it.name.endsWith(".class") }
|
||||
check(classes.map { it.name }.toSet() == setOf(
|
||||
"dev/moonbridge/bukkit/BackendPlayerSession.class",
|
||||
"dev/moonbridge/bukkit/BukkitMessagingService.class",
|
||||
"dev/moonbridge/bukkit/BukkitSessionService.class")) {
|
||||
"Bukkit API artifact must contain only the public service contract"
|
||||
}
|
||||
check(entries.none { it.name == "plugin.yml" }) { "API is not a deployable plugin" }
|
||||
classes.forEach { entry ->
|
||||
DataInputStream(archive.getInputStream(entry)).use { input ->
|
||||
input.readInt()
|
||||
input.readUnsignedShort()
|
||||
check(input.readUnsignedShort() == 52) { "Bukkit API must target Java 8" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("check") { dependsOn(verifyApiJar) }
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("bukkitApi") {
|
||||
from(components["java"])
|
||||
artifactId = "backend-bukkit-api"
|
||||
pom {
|
||||
name.set("MoonBridge Bukkit API")
|
||||
description.set("Java 8 service contract for plugins using the shared MoonBridge backend host.")
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
name = "giteaPackages"
|
||||
url = uri(providers.environmentVariable("MAVEN_URL")
|
||||
.orElse("https://git.nest.potatolab.uk:8443/api/packages/layue13/maven").get())
|
||||
credentials {
|
||||
username = providers.environmentVariable("MAVEN_USER").orElse("NONE").get()
|
||||
password = providers.environmentVariable("MAVEN_PASSWORD").orElse("NONE").get()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
import java.io.DataInputStream
|
||||
import java.util.jar.JarFile
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(25))
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
options.release.set(8)
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":backend-bukkit-api"))
|
||||
implementation(project(":backend-channel-client"))
|
||||
compileOnly("org.spigotmc:spigot-api:1.8-R0.1-SNAPSHOT") { isTransitive = false }
|
||||
testImplementation("org.spigotmc:spigot-api:1.8-R0.1-SNAPSHOT") { isTransitive = false }
|
||||
// Plugin's legacy API signature references Ebean; needed by the test proxy only.
|
||||
testRuntimeOnly("org.avaje:ebean:2.8.1") { isTransitive = false }
|
||||
testImplementation(platform("org.junit:junit-bom:5.14.1"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach { useJUnitPlatform() }
|
||||
|
||||
// One provider owns the API classes, codec and transport. Consumers use compileOnly
|
||||
// and plugin.yml depend, so Bukkit resolves the same service class identity.
|
||||
tasks.jar {
|
||||
archiveBaseName.set("moonbridge-backend-bukkit")
|
||||
dependsOn(configurations.runtimeClasspath)
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
from({ configurations.runtimeClasspath.get().filter { it.extension == "jar" }.map { zipTree(it) } })
|
||||
exclude("META-INF/*.SF", "META-INF/*.RSA", "META-INF/*.DSA")
|
||||
}
|
||||
|
||||
val verifyHostJar = tasks.register("verifyHostJar") {
|
||||
group = "verification"
|
||||
dependsOn(tasks.jar)
|
||||
val hostJar = tasks.jar.flatMap { it.archiveFile }
|
||||
inputs.file(hostJar)
|
||||
doLast {
|
||||
JarFile(hostJar.get().asFile).use { archive ->
|
||||
val entries = archive.entries().asSequence().toList()
|
||||
val names = entries.map { it.name }
|
||||
listOf("plugin.yml", "config.yml",
|
||||
"dev/moonbridge/bukkit/BukkitMessagingService.class",
|
||||
"dev/moonbridge/bukkit/BukkitSessionService.class",
|
||||
"dev/moonbridge/bukkit/BackendPlayerSession.class",
|
||||
"dev/moonbridge/messaging/Message.class",
|
||||
"dev/moonbridge/messaging/internal/LocalMessaging.class",
|
||||
"dev/moonbridge/messaging/protocol/MessageCodec.class",
|
||||
"dev/moonbridge/messaging/session/ForwardedSessionProof.class",
|
||||
"dev/moonbridge/messaging/session/ForwardedSessionProof\$Claims.class",
|
||||
"dev/moonbridge/backendchannel/BackendChannelClient.class",
|
||||
"dev/moonbridge/backendchannel/Wire\$RegisteredIdentity.class").forEach {
|
||||
check(names.count { name -> name == it } == 1) { "Host jar must contain exactly one $it" }
|
||||
}
|
||||
check(names.none { it.startsWith("org/bukkit/") }) { "Bukkit API must remain server-provided" }
|
||||
entries.filter { it.name.endsWith(".class") }.forEach { entry ->
|
||||
DataInputStream(archive.getInputStream(entry)).use { input ->
|
||||
input.readInt()
|
||||
input.readUnsignedShort()
|
||||
check(input.readUnsignedShort() <= 52) { "Host class is newer than Java 8: ${entry.name}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("check") { dependsOn(verifyHostJar) }
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("bukkitHost") {
|
||||
from(components["java"])
|
||||
artifactId = "backend-bukkit"
|
||||
pom {
|
||||
name.set("MoonBridge Backend Host")
|
||||
description.set("Java 8 shared channel messaging service for Bukkit and Uranium plugins.")
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
name = "giteaPackages"
|
||||
url = uri(providers.environmentVariable("MAVEN_URL")
|
||||
.orElse("https://git.nest.potatolab.uk:8443/api/packages/layue13/maven").get())
|
||||
credentials {
|
||||
username = providers.environmentVariable("MAVEN_USER").orElse("NONE").get()
|
||||
password = providers.environmentVariable("MAVEN_PASSWORD").orElse("NONE").get()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(25))
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
options.release.set(8)
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":messaging-api"))
|
||||
api(project(":messaging-protocol"))
|
||||
testImplementation(platform("org.junit:junit-bom:5.14.1"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("backendChannelClient") {
|
||||
from(components["java"])
|
||||
artifactId = "backend-channel-client"
|
||||
pom {
|
||||
name.set("MoonBridge Backend Channel Client")
|
||||
description.set("Java 8-compatible backend control channel SDK.")
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
name = "giteaPackages"
|
||||
url = uri(providers.environmentVariable("MAVEN_URL")
|
||||
.orElse("https://git.nest.potatolab.uk:8443/api/packages/layue13/maven").get())
|
||||
credentials {
|
||||
username = providers.environmentVariable("MAVEN_USER").orElse("NONE").get()
|
||||
password = providers.environmentVariable("MAVEN_PASSWORD").orElse("NONE").get()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import java.io.DataInputStream
|
||||
import java.util.jar.JarFile
|
||||
|
||||
plugins {
|
||||
id("moonbridge.java8-library-conventions")
|
||||
id("moonbridge.publishing-conventions")
|
||||
}
|
||||
|
||||
val spigotApi = "org.spigotmc:spigot-api:1.8-R0.1-SNAPSHOT"
|
||||
|
||||
dependencies {
|
||||
api(project(":messaging"))
|
||||
// The running server supplies Bukkit; business plugins compile against their own server API.
|
||||
compileOnly(spigotApi) { isTransitive = false }
|
||||
testImplementation(spigotApi) { isTransitive = false }
|
||||
// Plugin's legacy API signature references Ebean; needed by the test proxy only.
|
||||
testRuntimeOnly("org.avaje:ebean:2.8.1") { isTransitive = false }
|
||||
}
|
||||
|
||||
moonbridgePublication {
|
||||
artifactId.set("backend")
|
||||
displayName.set("MoonBridge Backend")
|
||||
description.set("Java 8 backend side of MoonBridge: the shared Bukkit host plugin (MoonBridgeBackend), its "
|
||||
+ "service API and the control channel client for other hosts.")
|
||||
}
|
||||
|
||||
// The jar is the deployable Bukkit plugin and the one copy of the messaging classes on a server.
|
||||
// Business plugins use it compileOnly plus plugin.yml depend, so Bukkit resolves one service class identity.
|
||||
tasks.jar {
|
||||
archiveBaseName.set("moonbridge-backend")
|
||||
dependsOn(configurations.runtimeClasspath)
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
from({ configurations.runtimeClasspath.get().filter { it.extension == "jar" }.map { zipTree(it) } })
|
||||
exclude("META-INF/*.SF", "META-INF/*.RSA", "META-INF/*.DSA")
|
||||
}
|
||||
|
||||
val verifyBackendJar = tasks.register("verifyBackendJar") {
|
||||
group = "verification"
|
||||
dependsOn(tasks.jar)
|
||||
val backendJar = tasks.jar.flatMap { it.archiveFile }
|
||||
inputs.file(backendJar)
|
||||
doLast {
|
||||
JarFile(backendJar.get().asFile).use { archive ->
|
||||
val entries = archive.entries().asSequence().toList()
|
||||
val names = entries.map { it.name }
|
||||
listOf("plugin.yml", "config.yml",
|
||||
"dev/moonbridge/bukkit/BukkitMessagingService.class",
|
||||
"dev/moonbridge/bukkit/BukkitSessionService.class",
|
||||
"dev/moonbridge/bukkit/BackendPlayerSession.class",
|
||||
"dev/moonbridge/messaging/Message.class",
|
||||
"dev/moonbridge/messaging/internal/LocalMessaging.class",
|
||||
"dev/moonbridge/messaging/protocol/MessageCodec.class",
|
||||
"dev/moonbridge/messaging/session/ForwardedSessionProof.class",
|
||||
"dev/moonbridge/messaging/session/ForwardedSessionProof\$Claims.class",
|
||||
"dev/moonbridge/backendchannel/BackendChannelClient.class",
|
||||
"dev/moonbridge/backendchannel/Wire\$RegisteredIdentity.class").forEach {
|
||||
check(names.count { name -> name == it } == 1) { "Backend jar must contain exactly one $it" }
|
||||
}
|
||||
check(names.none { it.startsWith("org/bukkit/") }) { "Bukkit API must remain server-provided" }
|
||||
entries.filter { it.name.endsWith(".class") }.forEach { entry ->
|
||||
DataInputStream(archive.getInputStream(entry)).use { input ->
|
||||
input.readInt()
|
||||
input.readUnsignedShort()
|
||||
check(input.readUnsignedShort() <= 52) { "Backend class is newer than Java 8: ${entry.name}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("check") { dependsOn(verifyBackendJar) }
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -49,8 +49,8 @@ public final class ProxySessionBenchmark {
|
||||
int commandInterceptor = intArg(args, "command-interceptor", 1);
|
||||
int burst = intArg(args, "burst", 1);
|
||||
String mode = modeArg(args);
|
||||
var transport = dev.moonbridge.core.net.NetworkTransport.select(
|
||||
dev.moonbridge.core.net.NetworkTransport.Preference.valueOf(
|
||||
var transport = dev.moonbridge.core.session.channel.NetworkTransport.select(
|
||||
dev.moonbridge.core.session.channel.NetworkTransport.Preference.valueOf(
|
||||
stringArg(args, "transport", "auto").toUpperCase(java.util.Locale.ROOT)));
|
||||
boolean afterTransfer = mode.equals("post-transfer");
|
||||
if (connections < 1 || connections > 32 || messages < 1 || messages > 100_000
|
||||
|
||||
@@ -5,7 +5,7 @@ import dev.moonbridge.core.session.play.KeepAliveBridge;
|
||||
import dev.moonbridge.core.protocol.MinecraftFrameDecoder;
|
||||
import dev.moonbridge.core.protocol.ProtocolProfile;
|
||||
import dev.moonbridge.core.protocol.ProtocolVarInt;
|
||||
import dev.moonbridge.core.relay.RawRelay;
|
||||
import dev.moonbridge.core.session.play.RawRelay;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
+37
-5
@@ -10,15 +10,47 @@ allprojects {
|
||||
version = releaseVersion
|
||||
}
|
||||
|
||||
val luckPerms = findProject(":luckperms-moonbridge")
|
||||
val published = listOfNotNull(":proxy-plugin-api", ":messaging", ":backend", luckPerms?.path)
|
||||
|
||||
tasks.register("check") {
|
||||
group = "verification"
|
||||
dependsOn(":proxy-plugin-api:check", ":messaging-api:check", ":messaging-protocol:check",
|
||||
":backend-channel-client:check", ":backend-bukkit-api:check", ":backend-bukkit:check", ":proxy-core:check",
|
||||
":luckperms-moonbridge:check")
|
||||
description = "Runs every module's checks (add -Pluckperms to include the LuckPerms plugin)."
|
||||
dependsOn(subprojects.map { "${it.path}:check" })
|
||||
}
|
||||
|
||||
tasks.register("publish") {
|
||||
group = "publishing"
|
||||
dependsOn(":proxy-plugin-api:publish", ":messaging-api:publish", ":messaging-protocol:publish",
|
||||
":backend-channel-client:publish", ":backend-bukkit-api:publish", ":backend-bukkit:publish", ":luckperms-moonbridge: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") }
|
||||
}
|
||||
}
|
||||
@@ -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 构件和发行包的区别 |
|
||||
|
||||
## 当前可引用的验证记录
|
||||
|
||||
@@ -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) 追踪。
|
||||
@@ -30,7 +31,7 @@ flowchart LR
|
||||
F --> G[PLAY / Forge 握手与转发]
|
||||
```
|
||||
|
||||
普通转发沿用协议 5 帧边界,不重新编码普通帧;同一次 socket 读取解码出的帧逐个写入对端,在读取完成时合并为一次 flush,读取之外转发的帧由每批一次的事件循环任务补发 flush。前后端通道绑定同一 Netty EventLoop,会话控制状态在该循环串行变更。Keep Alive ID 由代理转换并跟踪未完成请求;必要的玩家实体 ID 数据包在转服后改写。通道不可写时通过暂停对端读取施加背压。实现见 [`Session`](../proxy-core/src/main/java/dev/moonbridge/core/session/Session.java)、[`RawRelay`](../proxy-core/src/main/java/dev/moonbridge/core/relay/RawRelay.java) 与 [`KeepAliveBridge`](../proxy-core/src/main/java/dev/moonbridge/core/session/play/KeepAliveBridge.java)。
|
||||
普通转发沿用协议 5 帧边界,不重新编码普通帧;同一次 socket 读取解码出的帧逐个写入对端,在读取完成时合并为一次 flush,读取之外转发的帧由每批一次的事件循环任务补发 flush。前后端通道绑定同一 Netty EventLoop,会话控制状态在该循环串行变更。Keep Alive ID 由代理转换并跟踪未完成请求;必要的玩家实体 ID 数据包在转服后改写。通道不可写时通过暂停对端读取施加背压。实现见 [`Session`](../proxy-core/src/main/java/dev/moonbridge/core/session/Session.java)、[`RawRelay`](../proxy-core/src/main/java/dev/moonbridge/core/session/play/RawRelay.java) 与 [`KeepAliveBridge`](../proxy-core/src/main/java/dev/moonbridge/core/session/play/KeepAliveBridge.java)。
|
||||
|
||||
## 后端目录与发现所有权
|
||||
|
||||
@@ -69,10 +70,11 @@ flowchart LR
|
||||
|---|---|
|
||||
| `core.session` | `ProxySessionListener`(监听与会话索引);`Session`(每个玩家连接一个,Netty handler 与生命周期)及其委托:`FrontendLogin`(握手、状态、加密、Mojang 验证)、`InitialRouter`(放置决策与首个后端拨号)、`ClientControl`(代发聊天与有序断开)、`BackendHandshakes` |
|
||||
| `core.session.transfer` | `TransferCoordinator` 转服状态机,经 `TransferHost` 接口读取会话状态,自己持有全部转服状态 |
|
||||
| `core.session.play` | PLAY 阶段逐帧 handler(Keep Alive、命令拦截、Tab 补全)与中继启动缓冲 |
|
||||
| `core.session.channel` | 后端拨号、pipeline 查找辅助与玩家地址(`clientAddress`) |
|
||||
| `core.session.play` | PLAY 阶段数据通路:`RawRelay` 帧转发热路径、逐帧 handler(Keep Alive、命令拦截、Tab 补全)与中继启动缓冲 |
|
||||
| `core.session.channel` | `NetworkTransport`(epoll/kqueue/NIO 选择)、后端拨号、pipeline 查找辅助与玩家地址(`clientAddress`) |
|
||||
| `core.websocket` | WebSocket 前端:升级、可信代理真实 IP(`ClientAddressPolicy`)、二进制消息与字节流互转;不依赖 `session` |
|
||||
| `core.relay` | `RawRelay` 帧转发热路径 |
|
||||
| `core.auth` | 在线认证与加密、Mojang 会话验证、Bungee 旧版身份转发(`BungeeLegacyForwarding`) |
|
||||
| `core.event` | 会话调用插件运行时的端口:`EventDispatcher`、转服准备、`PermissionService` |
|
||||
| `core.plugin` | `PluginHost` 生命周期,分出 `EventRouter`、`CommandService`、`PlacementService`、`PluginContextImpl` |
|
||||
| `core.control` | 后端控制通道:`BackendControlService` 接入,`LeaseRegistry`、`InboundRouter`、`BackendExchange`、`OutboundWriter` |
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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><published-version></version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -4,7 +4,7 @@ import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
plugins {
|
||||
id("moonbridge.java-library-conventions")
|
||||
id("com.gradleup.shadow") version "9.6.1"
|
||||
`maven-publish`
|
||||
id("moonbridge.publishing-conventions")
|
||||
}
|
||||
|
||||
val hostBridgeCompileClasspath = configurations.create("hostBridgeCompileClasspath") {
|
||||
@@ -146,15 +146,4 @@ publishing {
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
name = "giteaPackages"
|
||||
url = uri(providers.environmentVariable("MAVEN_URL")
|
||||
.orElse("https://git.nest.potatolab.uk:8443/api/packages/layue13/maven").get())
|
||||
credentials {
|
||||
username = providers.environmentVariable("MAVEN_USER").orElse("NONE").get()
|
||||
password = providers.environmentVariable("MAVEN_PASSWORD").orElse("NONE").get()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(25))
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
options.release.set(8)
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(platform("org.junit:junit-bom:5.14.1"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("messagingApi") {
|
||||
from(components["java"])
|
||||
artifactId = "messaging-api"
|
||||
pom {
|
||||
name.set("MoonBridge Messaging API")
|
||||
description.set("Java 8-compatible shared message envelope and channel API.")
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
name = "giteaPackages"
|
||||
url = uri(providers.environmentVariable("MAVEN_URL")
|
||||
.orElse("https://git.nest.potatolab.uk:8443/api/packages/layue13/maven").get())
|
||||
credentials {
|
||||
username = providers.environmentVariable("MAVEN_USER").orElse("NONE").get()
|
||||
password = providers.environmentVariable("MAVEN_PASSWORD").orElse("NONE").get()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(25))
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
options.release.set(8)
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":messaging-api"))
|
||||
testImplementation(platform("org.junit:junit-bom:5.14.1"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("messagingProtocol") {
|
||||
from(components["java"])
|
||||
artifactId = "messaging-protocol"
|
||||
pom {
|
||||
name.set("MoonBridge Messaging Protocol")
|
||||
description.set("Java 8-compatible message wire codec shared by proxy and backend clients.")
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
name = "giteaPackages"
|
||||
url = uri(providers.environmentVariable("MAVEN_URL")
|
||||
.orElse("https://git.nest.potatolab.uk:8443/api/packages/layue13/maven").get())
|
||||
credentials {
|
||||
username = providers.environmentVariable("MAVEN_USER").orElse("NONE").get()
|
||||
password = providers.environmentVariable("MAVEN_PASSWORD").orElse("NONE").get()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
plugins {
|
||||
id("moonbridge.java8-library-conventions")
|
||||
id("moonbridge.publishing-conventions")
|
||||
}
|
||||
|
||||
moonbridgePublication {
|
||||
artifactId.set("messaging")
|
||||
displayName.set("MoonBridge Messaging")
|
||||
description.set("Java 8-compatible message envelope, channel API and wire codec shared by the proxy and backends.")
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -8,6 +8,14 @@ gradlePlugin {
|
||||
id = "moonbridge.java-library-conventions"
|
||||
implementationClass = "MoonBridgeJavaLibraryConventionsPlugin"
|
||||
}
|
||||
register("java8LibraryConventions") {
|
||||
id = "moonbridge.java8-library-conventions"
|
||||
implementationClass = "MoonBridgeJava8LibraryConventionsPlugin"
|
||||
}
|
||||
register("publishingConventions") {
|
||||
id = "moonbridge.publishing-conventions"
|
||||
implementationClass = "MoonBridgePublishingConventionsPlugin"
|
||||
}
|
||||
register("applicationConventions") {
|
||||
id = "moonbridge.application-conventions"
|
||||
implementationClass = "MoonBridgeApplicationConventionsPlugin"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
|
||||
/** Libraries loaded by Java 8 backends (Uranium and similar): compiled with --release 8. */
|
||||
class MoonBridgeJava8LibraryConventionsPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
project.plugins.apply(MoonBridgeJavaLibraryConventionsPlugin::class.java)
|
||||
project.tasks.withType<JavaCompile>().configureEach {
|
||||
options.release.set(8)
|
||||
options.encoding = "UTF-8"
|
||||
// JDK 25 warns that --release 8 is obsolete; targeting Java 8 is the point of this convention.
|
||||
options.compilerArgs.add("-Xlint:-options")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.publish.PublishingExtension
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.create
|
||||
import org.gradle.kotlin.dsl.findByType
|
||||
|
||||
/** Name and description of a module's standard Maven publication (its jar, sources and javadoc). */
|
||||
abstract class MoonBridgePublicationExtension {
|
||||
abstract val artifactId: Property<String>
|
||||
abstract val displayName: Property<String>
|
||||
abstract val description: Property<String>
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the shared package repository. A module that sets `moonbridgePublication.artifactId` also gets the
|
||||
* standard publication; a module with an unusual artifact (the LuckPerms plugin) declares its own instead.
|
||||
*/
|
||||
class MoonBridgePublishingConventionsPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
project.plugins.apply(MavenPublishPlugin::class.java)
|
||||
val publication = project.extensions.create<MoonBridgePublicationExtension>("moonbridgePublication")
|
||||
project.extensions.configure<PublishingExtension> {
|
||||
repositories.maven {
|
||||
name = "giteaPackages"
|
||||
url = project.uri(project.providers.environmentVariable("MAVEN_URL")
|
||||
.orElse("https://git.nest.potatolab.uk:8443/api/packages/layue13/maven").get())
|
||||
credentials {
|
||||
username = project.providers.environmentVariable("MAVEN_USER").orElse("NONE").get()
|
||||
password = project.providers.environmentVariable("MAVEN_PASSWORD").orElse("NONE").get()
|
||||
}
|
||||
}
|
||||
}
|
||||
project.afterEvaluate {
|
||||
if (!publication.artifactId.isPresent) return@afterEvaluate
|
||||
project.extensions.findByType<JavaPluginExtension>()?.apply {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
project.extensions.configure<PublishingExtension> {
|
||||
publications.create<MavenPublication>("maven") {
|
||||
from(project.components.getByName("java"))
|
||||
artifactId = publication.artifactId.get()
|
||||
pom {
|
||||
name.set(publication.displayName)
|
||||
description.set(publication.description)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
-35
@@ -7,7 +7,7 @@ plugins {
|
||||
val runtimeJavaHome = System.getProperty("java.home")
|
||||
|
||||
dependencies {
|
||||
implementation(project(":messaging-protocol"))
|
||||
implementation(project(":messaging"))
|
||||
// The plugin contract is the only stable external boundary. Everything else
|
||||
// in this project remains an implementation package of the proxy core.
|
||||
api(project(":proxy-plugin-api"))
|
||||
@@ -32,7 +32,7 @@ dependencies {
|
||||
implementation(libs.findLibrary("adventure-gson").get())
|
||||
implementation(libs.findLibrary("adventure-plain").get())
|
||||
runtimeOnly(libs.findLibrary("logback-classic").get())
|
||||
testImplementation(project(":backend-channel-client"))
|
||||
testImplementation(project(":backend"))
|
||||
testImplementation(libs.findLibrary("logback-classic").get())
|
||||
testImplementation(libs.findLibrary("netty-pkitesting").get())
|
||||
}
|
||||
@@ -86,13 +86,6 @@ val installedDistVersionSmokeTest = tasks.register<Exec>("installedDistVersionSm
|
||||
}
|
||||
}
|
||||
|
||||
val compilePermissionAcceptance = tasks.register<JavaCompile>("compilePermissionAcceptance") {
|
||||
source(rootProject.file("smoke/PermissionAcceptance.java"))
|
||||
classpath = sourceSets.main.get().runtimeClasspath
|
||||
destinationDirectory.set(layout.buildDirectory.dir("classes/permissionAcceptance"))
|
||||
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") {
|
||||
@@ -104,21 +97,33 @@ val compileBenchmarks = tasks.register<JavaCompile>("compileBenchmarks") {
|
||||
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."
|
||||
dependsOn(tasks.named("installDist"), compilePermissionAcceptance)
|
||||
classpath = sourceSets.main.get().runtimeClasspath + files(compilePermissionAcceptance.flatMap { it.destinationDirectory })
|
||||
mainClass.set("PermissionAcceptance")
|
||||
args(layout.buildDirectory.dir("install/moonbridge").get().asFile.absolutePath,
|
||||
layout.buildDirectory.dir("permission-acceptance").get().asFile.absolutePath)
|
||||
// permissionAcceptance needs the LuckPerms plugin, which is only built with -Pluckperms.
|
||||
val luckPerms = rootProject.findProject(":luckperms-moonbridge")
|
||||
if (luckPerms != null) {
|
||||
val compilePermissionAcceptance = tasks.register<JavaCompile>("compilePermissionAcceptance") {
|
||||
source(rootProject.file("smoke/PermissionAcceptance.java"))
|
||||
classpath = sourceSets.main.get().runtimeClasspath
|
||||
destinationDirectory.set(layout.buildDirectory.dir("classes/permissionAcceptance"))
|
||||
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."
|
||||
dependsOn(tasks.named("installDist"), compilePermissionAcceptance)
|
||||
classpath = sourceSets.main.get().runtimeClasspath + files(compilePermissionAcceptance.flatMap { it.destinationDirectory })
|
||||
mainClass.set("PermissionAcceptance")
|
||||
args(layout.buildDirectory.dir("install/moonbridge").get().asFile.absolutePath,
|
||||
layout.buildDirectory.dir("permission-acceptance").get().asFile.absolutePath)
|
||||
}
|
||||
|
||||
tasks.named("check") { dependsOn(permissionAcceptance) }
|
||||
}
|
||||
|
||||
tasks.named("check") {
|
||||
dependsOn(installedDistSmokeTest)
|
||||
dependsOn(installedDistHelpSmokeTest)
|
||||
dependsOn(installedDistVersionSmokeTest)
|
||||
dependsOn(permissionAcceptance)
|
||||
dependsOn(compileBenchmarks)
|
||||
}
|
||||
|
||||
@@ -147,8 +152,10 @@ distributions {
|
||||
main {
|
||||
contents {
|
||||
from(rootProject.file("README.md"))
|
||||
from(project(":luckperms-moonbridge").tasks.named("pluginJar")) {
|
||||
into("plugins")
|
||||
if (luckPerms != null) {
|
||||
from(luckPerms.tasks.named("pluginJar")) {
|
||||
into("plugins")
|
||||
}
|
||||
}
|
||||
from(rootProject.file("docs")) {
|
||||
into("docs")
|
||||
@@ -162,21 +169,8 @@ distributions {
|
||||
from("src/main/resources/config") {
|
||||
into("config")
|
||||
}
|
||||
from(project(":backend-channel-client").tasks.named("jar")) {
|
||||
into("backend-client")
|
||||
}
|
||||
from(project(":messaging-api").tasks.named("jar")) {
|
||||
into("backend-client")
|
||||
}
|
||||
from(project(":messaging-protocol").tasks.named("jar")) {
|
||||
into("backend-client")
|
||||
}
|
||||
from(project(":backend-bukkit").tasks.named("jar")) {
|
||||
into("backend-host")
|
||||
}
|
||||
from(listOf(project(":backend-bukkit-api").tasks.named("jar"),
|
||||
project(":messaging-api").tasks.named("jar"))) {
|
||||
into("backend-api")
|
||||
from(project(":backend").tasks.named("jar")) {
|
||||
into("backend")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package dev.moonbridge.app;
|
||||
|
||||
import dev.moonbridge.core.net.NetworkTransport;
|
||||
import dev.moonbridge.core.session.channel.NetworkTransport;
|
||||
import dev.moonbridge.core.backend.InMemoryBackendCatalog;
|
||||
import dev.moonbridge.core.control.BackendControlService;
|
||||
import dev.moonbridge.core.auth.MojangSessionVerifier;
|
||||
|
||||
+1
-3
@@ -1,11 +1,9 @@
|
||||
package dev.moonbridge.core.forwarding;
|
||||
package dev.moonbridge.core.auth;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import dev.moonbridge.core.auth.ProfileProperty;
|
||||
import dev.moonbridge.core.auth.VerifiedProfile;
|
||||
import dev.moonbridge.core.protocol.MinecraftHandshake;
|
||||
import dev.moonbridge.core.protocol.ProtocolProfile;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package dev.moonbridge.core.permission;
|
||||
package dev.moonbridge.core.event;
|
||||
|
||||
import dev.moonbridge.api.PlayerIdentity;
|
||||
import dev.moonbridge.api.PlayerView;
|
||||
@@ -13,7 +13,7 @@ import dev.moonbridge.api.Players;
|
||||
import dev.moonbridge.api.Plugin;
|
||||
import dev.moonbridge.api.permission.PermissionResult;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import dev.moonbridge.core.permission.PermissionService;
|
||||
import dev.moonbridge.core.event.PermissionService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package dev.moonbridge.core.plugin;
|
||||
import dev.moonbridge.messaging.internal.LocalMessaging;
|
||||
import dev.moonbridge.api.Players;
|
||||
import dev.moonbridge.core.backend.BackendCatalog;
|
||||
import dev.moonbridge.core.permission.PermissionService;
|
||||
import dev.moonbridge.core.event.PermissionService;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import dev.moonbridge.core.backend.BackendCatalog;
|
||||
import dev.moonbridge.core.backend.BackendOwner;
|
||||
import dev.moonbridge.core.control.BackendChannelTransport;
|
||||
import dev.moonbridge.core.event.EventDispatcher;
|
||||
import dev.moonbridge.core.permission.PermissionService;
|
||||
import dev.moonbridge.core.event.PermissionService;
|
||||
import dev.moonbridge.core.event.TransferPreparation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -3,7 +3,7 @@ package dev.moonbridge.core.session;
|
||||
import dev.moonbridge.core.session.channel.SessionChannels;
|
||||
import dev.moonbridge.api.PlayerIdentity;
|
||||
import dev.moonbridge.core.auth.VerifiedProfile;
|
||||
import dev.moonbridge.core.forwarding.BungeeLegacyForwarding;
|
||||
import dev.moonbridge.core.auth.BungeeLegacyForwarding;
|
||||
import dev.moonbridge.core.protocol.MinecraftHandshake;
|
||||
import dev.moonbridge.core.protocol.ProtocolProfile;
|
||||
import dev.moonbridge.messaging.session.ForwardedSessionProof;
|
||||
|
||||
@@ -5,7 +5,7 @@ import dev.moonbridge.api.MessageResult;
|
||||
import dev.moonbridge.core.protocol.Minecraft1710PlayPackets;
|
||||
import dev.moonbridge.core.protocol.MinecraftLoginDisconnect;
|
||||
import dev.moonbridge.core.protocol.MinecraftText;
|
||||
import dev.moonbridge.core.relay.RawRelay;
|
||||
import dev.moonbridge.core.session.play.RawRelay;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
@@ -10,7 +10,7 @@ import dev.moonbridge.api.event.ConnectionAdmissionEvent;
|
||||
import dev.moonbridge.api.event.PlayerDisconnectedEvent;
|
||||
import dev.moonbridge.api.event.ServerConnectedEvent;
|
||||
import dev.moonbridge.core.event.EventDispatcher;
|
||||
import dev.moonbridge.core.permission.PermissionService;
|
||||
import dev.moonbridge.core.event.PermissionService;
|
||||
import dev.moonbridge.api.PlayerIdentity;
|
||||
import dev.moonbridge.api.PlayerView;
|
||||
import dev.moonbridge.api.MessageResult;
|
||||
@@ -21,7 +21,7 @@ import dev.moonbridge.api.TransferStatus;
|
||||
import dev.moonbridge.core.auth.MinecraftEncryptionRequest;
|
||||
import dev.moonbridge.core.auth.SessionVerifier;
|
||||
import dev.moonbridge.core.backend.BackendCatalog;
|
||||
import dev.moonbridge.core.net.NetworkTransport;
|
||||
import dev.moonbridge.core.session.channel.NetworkTransport;
|
||||
import dev.moonbridge.core.protocol.ServerListStatus;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
@@ -25,10 +25,10 @@ import dev.moonbridge.core.protocol.LoginStart;
|
||||
import dev.moonbridge.core.protocol.MinecraftLoginSuccess;
|
||||
import dev.moonbridge.core.protocol.ProtocolProfile;
|
||||
import dev.moonbridge.core.protocol.ProtocolVarInt;
|
||||
import dev.moonbridge.core.relay.RawRelay;
|
||||
import dev.moonbridge.core.session.play.RawRelay;
|
||||
import dev.moonbridge.core.backend.BackendCatalog;
|
||||
import dev.moonbridge.core.event.TransferPreparation;
|
||||
import dev.moonbridge.core.net.NetworkTransport;
|
||||
import dev.moonbridge.core.session.channel.NetworkTransport;
|
||||
import io.netty.resolver.AddressResolverGroup;
|
||||
import java.util.function.BiConsumer;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package dev.moonbridge.core.net;
|
||||
package dev.moonbridge.core.session.channel;
|
||||
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.IoEventLoop;
|
||||
@@ -1,6 +1,5 @@
|
||||
package dev.moonbridge.core.session.channel;
|
||||
|
||||
import dev.moonbridge.core.net.NetworkTransport;
|
||||
import dev.moonbridge.core.protocol.MinecraftFrameDecoder;
|
||||
import dev.moonbridge.core.protocol.ProtocolProfile;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package dev.moonbridge.core.session.play;
|
||||
|
||||
import dev.moonbridge.core.relay.RawRelay;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package dev.moonbridge.core.relay;
|
||||
package dev.moonbridge.core.session.play;
|
||||
|
||||
import dev.moonbridge.core.protocol.ProtocolProfile;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
@@ -1,7 +1,6 @@
|
||||
package dev.moonbridge.core.session.play;
|
||||
|
||||
import dev.moonbridge.core.protocol.MinecraftTabCompletion;
|
||||
import dev.moonbridge.core.relay.RawRelay;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
@@ -4,7 +4,7 @@ import dev.moonbridge.api.TransferResult;
|
||||
import dev.moonbridge.api.event.TransferContext;
|
||||
import dev.moonbridge.core.backend.BackendView;
|
||||
import dev.moonbridge.core.event.TransferPreparation;
|
||||
import dev.moonbridge.core.relay.RawRelay;
|
||||
import dev.moonbridge.core.session.play.RawRelay;
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import dev.moonbridge.core.protocol.ByteBufs;
|
||||
import dev.moonbridge.core.protocol.LoginStart;
|
||||
import dev.moonbridge.core.protocol.ProtocolProfile;
|
||||
import dev.moonbridge.core.protocol.Minecraft1710PlayPackets;
|
||||
import dev.moonbridge.core.relay.RawRelay;
|
||||
import dev.moonbridge.core.session.play.RawRelay;
|
||||
import dev.moonbridge.core.session.channel.SessionChannels;
|
||||
import dev.moonbridge.core.session.play.KeepAliveBridge;
|
||||
import dev.moonbridge.core.session.play.PlayObservation;
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import dev.moonbridge.core.protocol.Minecraft1710EntityIds;
|
||||
import dev.moonbridge.core.protocol.Minecraft1710PlayPackets;
|
||||
import dev.moonbridge.core.protocol.ProtocolProfile;
|
||||
import dev.moonbridge.core.protocol.ProtocolVarInt;
|
||||
import dev.moonbridge.core.relay.RawRelay;
|
||||
import dev.moonbridge.core.session.play.RawRelay;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
@@ -5,8 +5,8 @@ import dev.moonbridge.api.PlayerView;
|
||||
import dev.moonbridge.core.backend.BackendCatalog;
|
||||
import dev.moonbridge.core.backend.BackendView;
|
||||
import dev.moonbridge.core.event.TransferPreparation;
|
||||
import dev.moonbridge.core.net.NetworkTransport;
|
||||
import dev.moonbridge.core.relay.RawRelay;
|
||||
import dev.moonbridge.core.session.channel.NetworkTransport;
|
||||
import dev.moonbridge.core.session.play.RawRelay;
|
||||
import dev.moonbridge.core.session.play.KeepAliveBridge;
|
||||
import dev.moonbridge.core.session.play.PlayObservation;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
+1
-3
@@ -1,8 +1,6 @@
|
||||
package dev.moonbridge.core.forwarding;
|
||||
package dev.moonbridge.core.auth;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import dev.moonbridge.core.auth.ProfileProperty;
|
||||
import dev.moonbridge.core.auth.VerifiedProfile;
|
||||
import dev.moonbridge.core.protocol.MinecraftHandshake;
|
||||
import dev.moonbridge.core.protocol.ProtocolProfile;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package dev.moonbridge.core.permission;
|
||||
package dev.moonbridge.core.event;
|
||||
|
||||
import dev.moonbridge.api.PlayerIdentity;
|
||||
import dev.moonbridge.api.PlayerView;
|
||||
@@ -14,7 +14,7 @@ import dev.moonbridge.core.backend.BackendOwner;
|
||||
import dev.moonbridge.core.backend.BackendRegistration;
|
||||
import dev.moonbridge.core.backend.InMemoryBackendCatalog;
|
||||
import dev.moonbridge.core.event.EventDispatcher;
|
||||
import dev.moonbridge.core.permission.PermissionService;
|
||||
import dev.moonbridge.core.event.PermissionService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
@@ -2384,7 +2384,7 @@ final class ProxySessionListenerTest {
|
||||
backendDone.get(5, TimeUnit.SECONDS);
|
||||
var relayField = Session.class.getDeclaredField("relay");
|
||||
relayField.setAccessible(true);
|
||||
var relay = (dev.moonbridge.core.relay.RawRelay.Link) relayField.get(session);
|
||||
var relay = (dev.moonbridge.core.session.play.RawRelay.Link) relayField.get(session);
|
||||
assertTrue(relay.hasPendingClientboundWrites());
|
||||
backendChannel.eventLoop().submit(() -> backendChannel.pipeline()
|
||||
.fireExceptionCaught(new IOException("backend read failed"))).get(5, TimeUnit.SECONDS);
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package dev.moonbridge.core.net;
|
||||
package dev.moonbridge.core.session.channel;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
@@ -1,6 +1,5 @@
|
||||
package dev.moonbridge.core.session.play;
|
||||
|
||||
import dev.moonbridge.core.relay.RawRelay;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package dev.moonbridge.core.relay;
|
||||
package dev.moonbridge.core.session.play;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package dev.moonbridge.core.relay;
|
||||
package dev.moonbridge.core.session.play;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
+1
-1
@@ -4,7 +4,7 @@ import dev.moonbridge.core.session.play.PlayObservation;
|
||||
|
||||
import dev.moonbridge.core.protocol.Minecraft1710PlayPackets;
|
||||
import dev.moonbridge.core.protocol.ProtocolVarInt;
|
||||
import dev.moonbridge.core.relay.RawRelay;
|
||||
import dev.moonbridge.core.session.play.RawRelay;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
@@ -1,41 +1,17 @@
|
||||
plugins {
|
||||
id("moonbridge.java-library-conventions")
|
||||
`maven-publish`
|
||||
id("moonbridge.publishing-conventions")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":messaging-api"))
|
||||
api(project(":messaging"))
|
||||
api(libs.findLibrary("slf4j-api").get())
|
||||
api(libs.findLibrary("adventure-api").get())
|
||||
api(libs.findLibrary("adventure-minimessage").get())
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("pluginApi") {
|
||||
from(components["java"])
|
||||
artifactId = "proxy-plugin-api"
|
||||
pom {
|
||||
name.set("MoonBridge Plugin API")
|
||||
description.set("Stable API for MoonBridge proxy-side plugins.")
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
name = "giteaPackages"
|
||||
url = uri(providers.environmentVariable("MAVEN_URL")
|
||||
.orElse("https://git.nest.potatolab.uk:8443/api/packages/layue13/maven")
|
||||
.get())
|
||||
credentials {
|
||||
username = providers.environmentVariable("MAVEN_USER").orElse("NONE").get()
|
||||
password = providers.environmentVariable("MAVEN_PASSWORD").orElse("NONE").get()
|
||||
}
|
||||
}
|
||||
}
|
||||
moonbridgePublication {
|
||||
artifactId.set("proxy-plugin-api")
|
||||
displayName.set("MoonBridge Plugin API")
|
||||
description.set("Stable API for MoonBridge proxy-side plugins.")
|
||||
}
|
||||
+12
-8
@@ -27,15 +27,19 @@ dependencyResolutionManagement {
|
||||
|
||||
rootProject.name = "moonbridge"
|
||||
|
||||
includeBuild("luckperms-moonbridge/upstream-build")
|
||||
|
||||
include(
|
||||
"proxy-core",
|
||||
"proxy-plugin-api",
|
||||
"backend-channel-client",
|
||||
"messaging-api",
|
||||
"messaging-protocol",
|
||||
"backend-bukkit",
|
||||
"backend-bukkit-api",
|
||||
"luckperms-moonbridge",
|
||||
"messaging",
|
||||
"backend",
|
||||
)
|
||||
|
||||
// The LuckPerms platform plugin compiles LuckPerms itself from the vendor/luckperms submodule, which is
|
||||
// slow and pulls a large dependency tree, so it is opt-in: pass -Pluckperms (CI and release builds do).
|
||||
if (providers.gradleProperty("luckperms").map { it != "false" }.getOrElse(false)) {
|
||||
check(file("vendor/luckperms/build.gradle").isFile) {
|
||||
"-Pluckperms needs the LuckPerms sources: run git submodule update --init --recursive"
|
||||
}
|
||||
includeBuild("luckperms-moonbridge/upstream-build")
|
||||
include("luckperms-moonbridge")
|
||||
}
|
||||
@@ -16,8 +16,8 @@ if ($serverJars.Count -ne 1) { throw 'Expected exactly one Uranium server JAR.'
|
||||
$serverJar = $serverJars[0]
|
||||
$serverHash = (Get-FileHash -LiteralPath $serverJar.FullName -Algorithm SHA256).Hash
|
||||
$dist = Join-Path $repo 'proxy-core/build/install/moonbridge'
|
||||
$hostJars = @(Get-ChildItem (Join-Path $dist 'backend-host') -Filter 'moonbridge-backend-bukkit-*.jar')
|
||||
if ($hostJars.Count -ne 1) { throw 'Build the renamed host and installDist first.' }
|
||||
$hostJars = @(Get-ChildItem (Join-Path $dist 'backend') -Filter 'moonbridge-backend-*.jar')
|
||||
if ($hostJars.Count -ne 1) { throw 'Build the backend jar and installDist first.' }
|
||||
$run = Join-Path $repo ('build/backend-uranium-' + [guid]::NewGuid().ToString('N'))
|
||||
New-Item -ItemType Directory -Force $run | Out-Null
|
||||
Write-Output "ACCEPTANCE_DIRECTORY=$run"
|
||||
@@ -43,8 +43,8 @@ $probeClasses = Join-Path $run 'probe-classes'
|
||||
$proxyClasses = Join-Path $run 'proxy-classes'
|
||||
New-Item -ItemType Directory -Force $controlDir, $proxyPlugins, $probeClasses, $proxyClasses | Out-Null
|
||||
$proxyClasspath = Join-Path $dist 'lib/*'
|
||||
# Business plugins compile against public API artifacts only, never the host/transport.
|
||||
$backendApiClasspath = Join-Path $dist 'backend-api/*'
|
||||
# Business plugins compile against the backend jar (compileOnly) and depend on MoonBridgeBackend at runtime.
|
||||
$backendApiClasspath = Join-Path $dist 'backend/*'
|
||||
& (Join-Path $Java8Home 'bin/javac.exe') -cp "$backendApiClasspath;$BukkitApiJar" -d $probeClasses `
|
||||
(Join-Path $PSScriptRoot 'BackendAcceptancePlugin.java') (Join-Path $PSScriptRoot 'BackendObserverPlugin.java')
|
||||
if ($LASTEXITCODE -ne 0) { throw 'Bukkit probe compilation failed.' }
|
||||
|
||||
@@ -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.'
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user