docs: consolidate Chinese documentation

This commit is contained in:
layue13 committed 2026-09-22 03:57:47 +08:00
1 parent 9fa6a782a6
commit 8063f6eb1f
25 files changed
+151 -852

No files matched your search

+148 -5
View File
@@ -1,14 +1,157 @@
# StrataProxy
StrataProxy 是 Java 25 Minecraft 后端代理。它负责连接接入、后端路由、健康检查、认证/转发协议、Minecraft zlib 压缩和必要的 Forge/Bungee 兼容;运行时后端可由代理插件或受限的 Bukkit 后端适配器注册。
StrataProxy 是一个 Java 25 的 Minecraft 后端代理。它只做代理该做的事:接受玩家连接、选择可用后端、维持后端健康状态,并处理认证、转发、压缩及必要的 Forge/Bungee 协议兼容。
没有 Admin HTTP API、管理 CLI、数据包分析/抓取或 Prometheus/Grafana 资源。Bukkit 适配器仅拥有后端注册、心跳与下线权限,不是动态运维控制面。
项目没有管理后台、HTTP Admin API、管理 CLI、查询协议、数据包分析或监控面板。运行期扩缩容通过代理插件 API 或受限的 Bukkit 后端适配器完成;两者都只能操作后端注册表,不能管理代理进程或读取玩家数据。
## 工程与产物
工程只有两个 Gradle 模块:
- `proxy-core`:可运行的代理、配置、网络转发与插件加载器。
- `proxy-plugin-api`:第三方代理插件唯一需要依赖的公开 API。
`integrations/bukkit-backend-agent` 是独立的 Java 8 Bukkit 插件;它以 Spigot 1.8 API 编译,只使用 Bukkit 1.7.10 已有的基础 API,因此可用于 1.7.10 Bukkit 派生服务端。
## 构建与启动
需要 JDK 25。执行:
```powershell
.\gradlew.bat --no-daemon check :proxy-core:installDist
.\proxy-core\build\install\strataproxy\bin\strataproxy.bat --validate-config .\proxy-core\build\install\strataproxy\config\strataproxy.yml
```
工程只保留两个模块:`proxy-core` 是完整的代理运行时(启动、配置、插件加载与网络转发),`proxy-plugin-api` 是唯一稳定的第三方插件契约。
运行目录位于 `proxy-core\build\install\strataproxy\`。先复制默认配置并校验:
中文文档从 [docs/README.md](docs/README.md) 开始。
```powershell
Copy-Item proxy-core\src\main\resources\config\strataproxy.yml .\strataproxy.yml
.\proxy-core\build\install\strataproxy\bin\strataproxy.bat --config .\strataproxy.yml --validate-config
.\proxy-core\build\install\strataproxy\bin\strataproxy.bat --config .\strataproxy.yml
```
`network.bind` 是玩家入口,`servers` 至少需要一个静态后端;若启用了 `backendAgent`,可以只通过动态注册提供后端。每次修改 YAML 后重启代理。
## 最小配置
下面是一个本机大厅的最小可用配置。其余字段可保留默认值:
```yaml
network:
bind: "0.0.0.0:25577"
registry:
staticServers: true
healthCheckEnabled: true
healthCheckMode: "minecraft-status"
servers:
- name: "lobby-1"
address: "127.0.0.1:25565"
tags: ["lobby"]
weight: 100
softCapacity: 500
hardCapacity: 600
drainMode: false
```
常用配置边界:
- `healthCheckMode: tcp` 只检查 TCP 可连接;`minecraft-status` 还要求后端能正常回复服务器列表状态。
- `forwarding.mode` 可选 `none`、`velocity-modern`、`bungee-legacy`、`bungee-guard`。使用 `velocity-modern` 或 `bungee-guard` 时必须同时设置 `forwarding.secret`,并在后端配置相同密钥。
- `registry.persistenceEnabled: true` 会将标记为持久化的动态后端写入 `registry.persistencePath`;临时后端不会跨重启保留。
- `network.proxyProtocol` 只能用于可信的 HAProxy 或负载均衡器之后,并且上游确实发送 PROXY protocol v1。
## 动态后端:Bukkit 服务端
官方 Bukkit 适配器用于让一个 Bukkit 服务端在启动时注册、定期发送心跳,并在停止时注销。代理端先启用一个仅监听本机的受限端点:
```yaml
backendAgent:
enabled: true
bind: "127.0.0.1:25578"
sharedSecret: "替换为至少 32 个字符的随机密钥"
heartbeatTimeout: "30s"
```
构建适配器:
```powershell
.\gradlew.bat -p integrations\bukkit-backend-agent clean jar
```
将 `integrations\bukkit-backend-agent\build\libs\strataproxy-bukkit-backend-agent-*.jar` 放入 Bukkit 的 `plugins/` 目录。首次启动后编辑 `plugins/StrataProxyBackendAgent/config.yml`:
```yaml
proxy:
host: "127.0.0.1"
port: 25578
sharedSecret: "与代理 backendAgent.sharedSecret 完全相同的至少 32 字符随机密钥"
backend:
name: "survival-1"
address: "127.0.0.1:25565"
tags: ["survival"]
weight: 100
persistent: false
heartbeatSeconds: 10
unregisterOnDisable: true
```
`sharedSecret` 两端必须相同,且不能保留示例中的占位符;配置错误时插件会拒绝启动。端点只接受注册、心跳和注销,心跳超时后代理会移除该后端。建议保持 `bind` 在环回地址,或以防火墙限制到可信 Bukkit 主机。
## 代理插件
代理会从配置文件同级的 `plugins/` 目录加载插件 JAR。插件仅依赖 `dev.strataproxy:proxy-plugin-api`,不要依赖 `proxy-core` 的实现类。
本仓库开发时可先发布 API 到本机 Maven:
```powershell
.\gradlew.bat :proxy-plugin-api:publishToMavenLocal
```
插件项目依赖:
```kotlin
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compileOnly("dev.strataproxy:proxy-plugin-api:0.1.0-SNAPSHOT")
}
```
JAR 根目录需要 `strataproxy-plugin.properties`:
```properties
id=example
name=Example Plugin
version=1.0.0
main=com.example.ExamplePlugin
```
入口实现 `ProxyPlugin`。`PluginContext` 提供命令、事件、玩家查询/转服、后端查询/注册、调度器和插件日志。动态后端默认是临时的;选择 `ServerPersistence.PERSISTENT` 才会写入持久化注册表。插件只能修改自己创建的后端,不能覆盖 YAML 静态后端或其他插件的后端。
```java
public final class ExamplePlugin implements ProxyPlugin {
@Override
public void onLoad(PluginContext context) {
context.events().subscribe(ProxyStartedEvent.class,
event -> context.logger().info("Example plugin ready"));
}
}
```
不要在事件回调或命令处理中阻塞 Netty 线程;耗时操作请交给 `context.scheduler()`。代理内置玩家命令为 `/server <server>`、`/hub`、`/lobby`、`/servers` 和 `/glist`。
## 发布
`release` 会生成代理发行包、插件 API JAR、SBOM、校验和及元数据:
```powershell
.\gradlew.bat --no-daemon release
```
`publish` 会向已配置的 Gitea Maven 仓库发布 `proxy-plugin-api` 和 Bukkit 后端适配器。CI 使用同一套 Gradle 任务;不要把密钥写入配置样例或提交到仓库。
+3 -13
View File
@@ -127,16 +127,6 @@ gradle.projectsEvaluated {
)
into(releaseStaging)
from("README.md")
from("docs") {
into("docs")
exclude("history/**")
}
from("deployment") {
into("deployment")
}
from("examples") {
into("examples")
}
from("proxy-core/src/main/resources/config") {
into("config")
}
@@ -178,7 +168,7 @@ gradle.projectsEvaluated {
metadataFile.set(releaseMetadata)
sbomFile.set(releaseSbom)
checksumFile.set(releaseChecksum)
chineseReadme.set(layout.projectDirectory.file("docs/README.md"))
chineseReadme.set(layout.projectDirectory.file("README.md"))
runtimeArtifacts.from(app.configurations.getByName("runtimeClasspath"))
}
@@ -480,8 +470,8 @@ abstract class ReleaseAuditTask : DefaultTask() {
val sbom = sbomFile.get().asFile.readText()
val checksums = checksumFile.get().asFile.readText()
val chineseDocs = chineseReadme.get().asFile.readText()
listOf("StrataProxy 文档", "快速开始", "运维说明", "架构").forEach { token ->
require(chineseDocs.contains(token)) { "Chinese documentation index missing $token" }
listOf("StrataProxy", "构建与启动", "动态后端:Bukkit 服务端", "代理插件").forEach { token ->
require(chineseDocs.contains(token)) { "Chinese documentation missing $token" }
}
listOf(
"\"name\": \"strataproxy-",
-9
View File
@@ -1,9 +0,0 @@
# 部署
将 `proxy-core` 的 `installDist` 产物和 YAML 配置部署到目标主机即可。示例 systemd 与容器文件只暴露 Minecraft 监听端口 `25577`,注册表持久化路径通过 `STRATAPROXY_DATA_DIR` 控制。
```bash
./gradlew --no-daemon :proxy-core:installDist
```
启动前执行 `strataproxy --validate-config /path/to/strataproxy.yml`。生产环境不包含管理端口、指标端点或数据包抓取功能。
-21
View File
@@ -1,21 +0,0 @@
ARG JAVA_IMAGE=eclipse-temurin:25-jdk
ARG RUNTIME_IMAGE=eclipse-temurin:25-jre
FROM ${JAVA_IMAGE} AS build
WORKDIR /workspace
COPY . .
RUN ./gradlew --no-daemon :proxy-core:installDist
FROM ${RUNTIME_IMAGE}
RUN useradd --system --home-dir /opt/strataproxy --shell /usr/sbin/nologin strataproxy
WORKDIR /opt/strataproxy
COPY --from=build /workspace/proxy-core/build/install/strataproxy/ /opt/strataproxy/
RUN mkdir -p /opt/strataproxy/data \
&& cp /opt/strataproxy/config/strataproxy-production.yml /opt/strataproxy/config/strataproxy.yml \
&& chown -R strataproxy:strataproxy /opt/strataproxy
USER strataproxy
EXPOSE 25577
VOLUME ["/opt/strataproxy/data"]
ENV JAVA_OPTS="-Xms2g -Xmx2g -XX:MaxDirectMemorySize=2g -XX:+UseZGC -XX:+AlwaysPreTouch -XX:+ExitOnOutOfMemoryError -Dio.netty.leakDetection.level=disabled"
ENTRYPOINT ["/opt/strataproxy/bin/strataproxy"]
CMD ["/opt/strataproxy/config/strataproxy.yml"]
@@ -1,46 +0,0 @@
# 本地 Minecraft 1.7.10 后端池
此目录提供用于测试 StrataProxy 的本地三后端 Minecraft 1.7.10 环境脚本和配置。
## 后端
| 名称 | 地址 | 路由主机 | 标签 |
| --- | --- | --- | --- |
| lobby-1 | 127.0.0.1:25565 | lobby.local | lobby, mc-1.7.10 |
| survival-1 | 127.0.0.1:25566 | survival.local | survival, mc-1.7.10 |
| minigame-1 | 127.0.0.1:25567 | minigame.local | minigame, mc-1.7.10 |
脚本会按需从 Mojang 版本元数据下载服务端 jar 到 `bin/minecraft_server.1.7.10.jar`,并校验 SHA-1:
```text
952438ac4e01b4d115c5fc38f891710c4941df29
```
## 启动
先阅读并接受 Minecraft EULA;确认接受后运行:
```powershell
.\deployment\local-minecraft-1.7.10\start-backends.ps1 -AcceptEula
```
使用本地 1.7.10 配置启动 StrataProxy:
```powershell
.\deployment\local-minecraft-1.7.10\start-proxy.ps1
```
Minecraft 1.7.10 客户端连接:
```text
127.0.0.1:25577
```
## 检查与停止
```powershell
.\deployment\local-minecraft-1.7.10\status.ps1
.\deployment\local-minecraft-1.7.10\stop-backends.ps1
```
各后端的标准输出和错误日志写在自己的后端目录中。Git 有意忽略生成的运行时文件:`bin/`、`backend-*/`、`*.pid` 和 `*.log`。
@@ -1,121 +0,0 @@
param(
[switch] $AcceptEula,
[string] $JavaExe = "java",
[string] $Xms = "256M",
[string] $Xmx = "768M"
)
$ErrorActionPreference = "Stop"
$Root = Split-Path -Parent $MyInvocation.MyCommand.Path
$Bin = Join-Path $Root "bin"
$Jar = Join-Path $Root "bin\minecraft_server.1.7.10.jar"
$ExpectedServerSha1 = "952438ac4e01b4d115c5fc38f891710c4941df29"
function Get-ServerJar {
if (Test-Path -LiteralPath $Jar) {
$ActualSha1 = (Get-FileHash -LiteralPath $Jar -Algorithm SHA1).Hash.ToLowerInvariant()
if ($ActualSha1 -ne $ExpectedServerSha1) {
throw "Server jar SHA-1 mismatch: expected $ExpectedServerSha1, got $ActualSha1"
}
return
}
New-Item -ItemType Directory -Path $Bin -Force | Out-Null
$Manifest = Invoke-RestMethod -Uri "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json"
$Version = $Manifest.versions | Where-Object { $_.id -eq "1.7.10" } | Select-Object -First 1
if (-not $Version) {
throw "Could not find Minecraft 1.7.10 in Mojang version manifest"
}
$VersionMetadata = Invoke-RestMethod -Uri $Version.url
$ServerUrl = $VersionMetadata.downloads.server.url
if (-not $ServerUrl) {
throw "Minecraft 1.7.10 manifest does not include a server download"
}
$TempJar = "$Jar.download"
Invoke-WebRequest -Uri $ServerUrl -OutFile $TempJar
$ActualSha1 = (Get-FileHash -LiteralPath $TempJar -Algorithm SHA1).Hash.ToLowerInvariant()
if ($ActualSha1 -ne $ExpectedServerSha1) {
Remove-Item -LiteralPath $TempJar -Force
throw "Downloaded server jar SHA-1 mismatch: expected $ExpectedServerSha1, got $ActualSha1"
}
Move-Item -LiteralPath $TempJar -Destination $Jar -Force
Write-Host "Downloaded Minecraft 1.7.10 server jar to $Jar"
}
function Ensure-BackendConfig {
param(
[string] $Dir,
[string] $Name,
[int] $Port
)
New-Item -ItemType Directory -Path $Dir -Force | Out-Null
$ServerProperties = Join-Path $Dir "server.properties"
if (Test-Path -LiteralPath $ServerProperties) {
return
}
$Properties = @(
"server-ip=127.0.0.1",
"server-port=$Port",
"online-mode=false",
"enable-query=false",
"enable-rcon=false",
"motd=StrataProxy $Name",
"max-players=100",
"spawn-protection=0",
"view-distance=6",
"difficulty=1",
"gamemode=0",
"white-list=false",
"generate-structures=false"
)
Set-Content -LiteralPath $ServerProperties -Value $Properties -Encoding ASCII
}
$Backends = @(
@{ Name = "backend-lobby-1"; Port = 25565 },
@{ Name = "backend-survival-1"; Port = 25566 },
@{ Name = "backend-minigame-1"; Port = 25567 }
)
Get-ServerJar
foreach ($Backend in $Backends) {
$Dir = Join-Path $Root $Backend.Name
Ensure-BackendConfig -Dir $Dir -Name $Backend.Name -Port $Backend.Port
$Eula = Join-Path $Dir "eula.txt"
$PidFile = Join-Path $Dir "server.pid"
$OutLog = Join-Path $Dir "stdout.log"
$ErrLog = Join-Path $Dir "stderr.log"
if (-not (Test-Path -LiteralPath $Eula)) {
Set-Content -LiteralPath $Eula -Value "eula=false" -Encoding ASCII
}
if ($AcceptEula) {
Set-Content -LiteralPath $Eula -Value "eula=true" -Encoding ASCII
}
if (-not (Select-String -LiteralPath $Eula -Pattern "^eula=true$" -Quiet)) {
throw "EULA is not accepted for $($Backend.Name). Read Minecraft EULA, then rerun with -AcceptEula if you accept it."
}
if (Test-Path -LiteralPath $PidFile) {
$ExistingPid = (Get-Content -LiteralPath $PidFile -Raw).Trim()
if ($ExistingPid -and (Get-Process -Id ([int] $ExistingPid) -ErrorAction SilentlyContinue)) {
Write-Host "$($Backend.Name) already running on port $($Backend.Port), pid $ExistingPid"
continue
}
}
$Args = @("-Xms$Xms", "-Xmx$Xmx", "-jar", $Jar, "nogui")
$Process = Start-Process -FilePath $JavaExe `
-ArgumentList $Args `
-WorkingDirectory $Dir `
-RedirectStandardOutput $OutLog `
-RedirectStandardError $ErrLog `
-WindowStyle Hidden `
-PassThru
Set-Content -LiteralPath $PidFile -Value $Process.Id -Encoding ASCII
Write-Host "Started $($Backend.Name) on 127.0.0.1:$($Backend.Port), pid $($Process.Id)"
}
@@ -1,21 +0,0 @@
param(
[string] $JavaHome = "C:\Program Files\Zulu\zulu-25"
)
$ErrorActionPreference = "Stop"
$Root = Split-Path -Parent $MyInvocation.MyCommand.Path
$Repo = Split-Path -Parent (Split-Path -Parent $Root)
$Config = Join-Path $Root "strataproxy-1.7.10-local.yml"
$Proxy = Join-Path $Repo "proxy-core\build\install\strataproxy\bin\strataproxy.bat"
if (-not (Test-Path -LiteralPath $Proxy)) {
Push-Location $Repo
try {
$env:JAVA_HOME = $JavaHome
& .\gradlew.bat --no-daemon :proxy-core:installDist
} finally {
Pop-Location
}
}
& $Proxy $Config
@@ -1,36 +0,0 @@
$ErrorActionPreference = "Stop"
$Backends = @(
@{ Name = "lobby-1"; Port = 25565 },
@{ Name = "survival-1"; Port = 25566 },
@{ Name = "minigame-1"; Port = 25567 }
)
function Test-Port {
param(
[string] $HostName,
[int] $Port,
[int] $TimeoutMillis = 500
)
$Client = [System.Net.Sockets.TcpClient]::new()
try {
$Connect = $Client.BeginConnect($HostName, $Port, $null, $null)
if (-not $Connect.AsyncWaitHandle.WaitOne($TimeoutMillis)) {
return $false
}
$Client.EndConnect($Connect)
return $true
} catch {
return $false
} finally {
$Client.Close()
}
}
foreach ($Backend in $Backends) {
$State = if (Test-Port -HostName "127.0.0.1" -Port $Backend.Port) { "open" } else { "closed" }
Write-Host "$($Backend.Name) 127.0.0.1:$($Backend.Port) $State"
}
$ProxyState = if (Test-Port -HostName "127.0.0.1" -Port 25577) { "open" } else { "closed" }
Write-Host "strataproxy 127.0.0.1:25577 $ProxyState"
@@ -1,25 +0,0 @@
$ErrorActionPreference = "Stop"
$Root = Split-Path -Parent $MyInvocation.MyCommand.Path
$Backends = @("backend-lobby-1", "backend-survival-1", "backend-minigame-1")
foreach ($Backend in $Backends) {
$PidFile = Join-Path (Join-Path $Root $Backend) "server.pid"
if (-not (Test-Path -LiteralPath $PidFile)) {
Write-Host "$Backend is not tracked"
continue
}
$PidText = (Get-Content -LiteralPath $PidFile -Raw).Trim()
if (-not $PidText) {
Remove-Item -LiteralPath $PidFile -Force
Write-Host "$Backend has an empty pid file"
continue
}
$Process = Get-Process -Id ([int] $PidText) -ErrorAction SilentlyContinue
if ($Process) {
Stop-Process -Id $Process.Id -Force
Write-Host "Stopped $Backend, pid $PidText"
} else {
Write-Host "$Backend pid $PidText is not running"
}
Remove-Item -LiteralPath $PidFile -Force
}
@@ -1,109 +0,0 @@
network:
bind: "0.0.0.0:25577"
workerThreads: 0
nativeTransport: true
maxFrameBytes: "8mb"
connectTimeoutMillis: 5000
writeBufferLow: "4mb"
writeBufferHigh: "16mb"
maxConnections: 10000
maxConnectionsPerAddress: 200
maxNewConnectionsPerSecond: 0
maxNewConnectionsPerAddressPerSecond: 0
initialHandshakeTimeoutMillis: 5000
proxyProtocol: false
registry:
staticServers: true
persistenceEnabled: false
persistencePath: "data/registry.json"
healthCheckEnabled: true
healthCheckInterval: "5s"
healthCheckTimeout: "2s"
healthCheckMode: "minecraft-status"
compression:
mode: off
minThreshold: 256
maxThreshold: 8192
cpuGuard: 0.75
rewriteEnabled: false
rewriteMaxEventLoopDelayMillis: 25
observability:
flushIntervalSeconds: 5
status:
enabled: true
motd: "StrataProxy local 1.7.10 pool"
protocolName: "Minecraft 1.7.10"
protocolVersion: 5
maxPlayers: 300
favicon: ""
faviconPath: ""
samplePlayers: []
auth:
onlineMode: false
rsaKeyBits: 1024
verifyTokenBytes: 4
sessionVerification: false
sessionVerificationTimeout: "5s"
forwarding:
mode: "none"
secret: ""
native:
enabled: true
autoDetect: true
preferNativeTransport: true
requireNativeTransport: false
preferOpenSslTls: false
preferNativeCompression: false
disabledFeatures: []
forcedFeatures: []
servers:
- name: "lobby-1"
address: "127.0.0.1:25565"
tags: ["lobby", "mc-1.7.10"]
capabilities: []
protocolRange: "5"
weight: 100
softCapacity: 80
hardCapacity: 100
drainMode: false
metadata:
group: "lobby"
host: "lobby.local"
route: "lobby"
version: "1.7.10"
- name: "survival-1"
address: "127.0.0.1:25566"
tags: ["survival", "mc-1.7.10"]
capabilities: []
protocolRange: "5"
weight: 100
softCapacity: 80
hardCapacity: 100
drainMode: false
metadata:
group: "survival"
host: "survival.local"
route: "survival"
version: "1.7.10"
- name: "minigame-1"
address: "127.0.0.1:25567"
tags: ["minigame", "mc-1.7.10"]
capabilities: []
protocolRange: "5"
weight: 100
softCapacity: 80
hardCapacity: 100
drainMode: false
metadata:
group: "minigame"
host: "minigame.local"
route: "minigame"
version: "1.7.10"
-4
View File
@@ -1,4 +0,0 @@
# JVM options read by the Gradle application start script.
JAVA_OPTS="-Xms2g -Xmx2g -XX:MaxDirectMemorySize=2g -XX:+UseZGC -XX:+AlwaysPreTouch -XX:+ExitOnOutOfMemoryError -Dio.netty.leakDetection.level=disabled"
STRATAPROXY_DATA_DIR="/var/lib/strataproxy"
-28
View File
@@ -1,28 +0,0 @@
[Unit]
Description=StrataProxy Minecraft Proxy
Documentation=file:/opt/strataproxy/deployment/README.md
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=strataproxy
Group=strataproxy
WorkingDirectory=/opt/strataproxy
EnvironmentFile=/etc/strataproxy/strataproxy.env
ExecStart=/opt/strataproxy/bin/strataproxy /opt/strataproxy/config/strataproxy-production.yml
Restart=on-failure
RestartSec=5s
TimeoutStopSec=30s
LimitNOFILE=1048576
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/strataproxy /var/log/strataproxy
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
SuccessExitStatus=130 143
[Install]
WantedBy=multi-user.target
-13
View File
@@ -1,13 +0,0 @@
# StrataProxy 文档
| 目标 | 文档 |
| --- | --- |
| 本地启动 | [快速开始](quick-start.md) |
| YAML 参数 | [配置说明](configuration.md) |
| 部署与检查 | [运维说明](operations-manual.md) |
| 插件与游戏内命令 | [插件开发](plugins.md) |
| Bukkit 后端自动注册 | [Bukkit 适配器](../integrations/bukkit-backend-agent/README.md) |
| 模块边界 | [架构](architecture.md) |
| 协议支持范围 | [协议矩阵](protocol-version-matrix.md) |
文档只描述当前代码。Admin、数据包抓取、包异常分析和 Prometheus 端点均已删除。
-16
View File
@@ -1,16 +0,0 @@
# 架构与模块边界
```text
第三方插件 ──> proxy-plugin-api
│
proxy-core
```
| 工程 | 职责 |
| --- | --- |
| `proxy-core` | 完整代理运行时:启动、YAML 配置、插件加载、Minecraft 协议、Netty 转发、路由、注册表、健康检查与兼容处理 |
| `proxy-plugin-api` | 唯一稳定的第三方插件契约 |
模块边界只按稳定契约划分,不按内部实现策略拆分。没有 `proxy-admin-cli` 或 HTTP 管理面;运行时后端只由已加载插件通过公开 API 管理。
`integrations/bukkit-backend-agent/` 是独立的 Java 8 Bukkit 适配器工程,不属于代理运行时模块。它只能通过受限的 `backendAgent` 端点注册、续租和下线自己的后端,不能作为管理 API 使用。
-50
View File
@@ -1,50 +0,0 @@
# 配置说明
配置文件是 YAML。未知字段会被忽略,因此升级后应删除已经不再支持的 `admin`、`packetAnalysis` 和旧的 `observability` 子字段。
```yaml
network:
bind: "0.0.0.0:25577"
maxFrameBytes: "8mb"
maxConnections: 10000
registry:
staticServers: true
persistenceEnabled: true
persistencePath: "data/registry.json"
healthCheckEnabled: true
healthCheckMode: "tcp"
compression:
mode: adaptive
minThreshold: 256
maxThreshold: 8192
observability:
flushIntervalSeconds: 5
servers:
- name: "lobby-1"
address: "127.0.0.1:25565"
tags: ["lobby"]
protocolRange: "any"
weight: 100
```
`network` 控制监听、帧上限和连接限制;`registry` 控制静态后端、健康检查和本地持久化;`compression` 控制 Minecraft 压缩协商与重写;`observability.flushIntervalSeconds` 仅控制后端已观测负载写回间隔。
`status` 控制服务器列表响应,`auth` 控制 online-mode 登录校验,`forwarding` 可选 `none`、`velocity-modern`、`bungee-legacy` 或 `bungee-guard`,`native` 控制 Netty 原生传输偏好。完整默认值见 [strataproxy.yml](../proxy-core/src/main/resources/config/strataproxy.yml)。
## Bukkit 后端自动注册
`backendAgent` 默认关闭,且不是 Admin API。启用后只允许持有共享密钥的后端适配器注册、发送心跳或下线自己的服务:
```yaml
backendAgent:
enabled: true
bind: "127.0.0.1:25578"
sharedSecret: "至少32字符的随机密钥"
heartbeatTimeout: "30s"
```
若 Bukkit 服务在另一台受保护的主机,可把 `bind` 改为内网地址,并通过防火墙只允许该主机访问。适配器位于 [`integrations/bukkit-backend-agent`](../integrations/bukkit-backend-agent),其 `config.yml` 的密钥必须完全一致;心跳超时后代理会移除该适配器注册的后端。
-37
View File
@@ -1,37 +0,0 @@
# JavaDoc 参考
本文说明项目源码级 JavaDoc 的组织方式,以及如何在本地生成 API 参考文档。
## 生成
```powershell
.\gradlew.bat javadoc
```
生成的 HTML 会写入各模块的 `build/docs/javadoc` 目录,例如:
- `proxy-core/build/docs/javadoc`
- `proxy-plugin-api/build/docs/javadoc`
大规模修改注释后,建议强制重跑,避免 Gradle up-to-date 状态隐藏文档问题:
```powershell
.\gradlew.bat --rerun-tasks javadoc
```
## 文档范围
本项目的 JavaDoc 重点覆盖公开契约:
- `proxy-core`:代理启动、YAML 配置、插件加载、后端服务器描述、路由、Minecraft codec、Netty 生命周期和运行时策略。这些包是代理内部实现,不单独作为 API 发布。
- `proxy-plugin-api`:插件生命周期、命令、事件、调度器、玩家服务和服务器服务契约。
包私有 relay handler 和底层实现辅助类只在行为不直观、或影响公开契约时补充注释。
## 维护规则
- 公开接口、公开 record、公开 enum、公开 class 和公开构造器都应有 JavaDoc。
- record 的 JavaDoc 应说明字段语义、单位和默认行为。
- 返回 Netty `ByteBuf` 所有权的方法必须说明由谁释放。
- 跨模块传播的诊断字符串、拒绝原因、阈值和超时单位应在边界处说明。
- 正式用户文档统一维护在 `docs/` 根目录;历史验证资料仅保存在源码仓库中,不随发布包分发。
-14
View File
@@ -1,14 +0,0 @@
# 运维说明
生产运行只需要代理进程、一个 YAML 配置文件和已声明的后端。后端增删、权重或排空状态通过配置变更和重启完成。
```powershell
.\strataproxy.bat --config .\strataproxy.yml --validate-config
.\strataproxy.bat --config .\strataproxy.yml
```
上线前确认监听端口可达、每个后端的 TCP 或 Minecraft status 健康检查正常,并根据需要启用 online-mode 和受保护的转发密钥。
本项目不提供 Admin HTTP API、Prometheus 指标端点、数据包抓取或运行时后端管理。
容器和 systemd 示例见 [deployment](../deployment/README.md)。
-108
View File
@@ -1,108 +0,0 @@
# 插件和游戏内命令
StrataProxy 会从当前配置文件旁边的 `plugins/` 目录加载代理插件。插件 jar 是可选的;目录不存在时代理照常启动。
公开插件接口是 `dev.strataproxy:proxy-plugin-api` artifact。插件项目只应该依赖这个 API;`proxy-core` 是内部实现细节。
在本仓库本地开发时,先发布 API 到本机 Maven:
```powershell
.\gradlew.bat :proxy-plugin-api:publishToMavenLocal
```
插件项目使用:
```kotlin
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compileOnly("dev.strataproxy:proxy-plugin-api:0.1.0-SNAPSHOT")
}
```
## 内置玩家命令
这些命令由代理拦截,不会继续转发到后端服务器:
```text
/server <server>
/hub
/lobby
/servers
/glist
```
`/server`、`/hub` 和 `/lobby` 使用与受支持 BungeeCord `Connect` plugin message 相同的类 Bungee 后端重登录转服流程。
## 插件入口
插件 jar 可以在 jar 根目录放置 `strataproxy-plugin.properties`:
```properties
id=example
name=Example Plugin
version=1.0.0
main=com.example.ExamplePlugin
```
也可以使用标准 Java `ServiceLoader`,提供 `dev.strataproxy.plugin.ProxyPlugin` provider。
插件可以通过 `PluginContext` 注册命令、订阅事件、查询玩家和服务器、注册动态后端、调度异步任务、发起玩家转服。插件代码不要阻塞 Netty event loop。
## 动态后端注册
插件可通过 `context.servers().register(...)` 注册运行期后端,用于服务发现、房间服、副本服或外部编排系统同步。默认注册是 `ServerPersistence.EPHEMERAL`,只影响当前运行中的 proxy;需要重启后保留时,显式使用 `ServerPersistence.PERSISTENT`。
插件注册的 server 会自动写入 owner metadata。默认情况下,插件只能替换、删除或切换 drain 自己注册的 server,不能覆盖 YAML 静态 server 或其他插件注册的 server。
```java
var registration = new ServerRegistration(
"arena-1",
new InetSocketAddress("127.0.0.1", 25570),
Set.of("arena"),
Set.of("modern-forwarding"),
ServerProtocolRange.any(),
100,
80,
100,
false,
Map.of("group", "arena"),
ServerPersistence.EPHEMERAL);
context.servers().register(registration).thenAccept(result -> {
if (!result.success()) {
context.logger().warn("Failed to register backend: {} {}", result.outcome(), result.message());
}
});
```
能力名使用和配置文件一致的语义,大小写不敏感,`-` 会按 `_` 规范化,例如 `modern-forwarding` 会映射到 `MODERN_FORWARDING`。
## 最小插件
```java
public final class HelloPlugin implements ProxyPlugin {
@Override
public void onLoad(PluginContext context) {
context.commands().register(new CommandSpec(
"hello",
List.of(),
"",
"Send a hello message.",
command -> CompletableFuture.completedFuture(
CommandResult.ok("Hello, " + command.source().name() + "."))));
}
}
```
仓库里提供了独立示例 `examples/hello-plugin`。本地发布 API 后可以构建:
```powershell
.\gradlew.bat :proxy-plugin-api:publishToMavenLocal
.\gradlew.bat -p examples/hello-plugin build
```
把生成的插件 jar 放到当前 StrataProxy 配置文件旁边的 `plugins/` 目录,然后重启代理。
-70
View File
@@ -1,70 +0,0 @@
# 协议版本能力矩阵
这张表是 StrataProxy 的 Minecraft 协议兼容契约。以后新增版本时,必须同时更新 `MinecraftProtocolProfile`、测试和这份文档。
状态含义:
- `已支持`:已经进入 `MinecraftProtocolProfile`,并有针对性测试覆盖。
- `部分支持`:有部分行为,但不能宣称完整多版本生产兼容。
- `未支持`:不能对外宣称稳定兼容;必须先补 profile 和测试。
## 当前运行矩阵
| Minecraft Java 版本 | Protocol | 状态 | 登录压缩 | 上行命令包 | Custom Payload 格式 | 后端切服 | Legacy Forge/FML |
| --- | ---: | --- | --- | --- | --- | --- | --- |
| 1.7.10 | 5 | 已支持当前 1.7.10/legacy 切服目标 | 不启用 vanilla login compression | `0x01` chat message | 上行 unsigned-short 长度,下行 varshort 长度 | Respawn 切服;Forge 安全路径为 Join Game + Respawn | 部分支持:REGISTER 跟踪、reset 注入、握手观测/gate、非 FML payload 排队、plugin-channel replay |
| 1.8.x | 47 | 已支持当前压缩和 legacy 切服目标 | 支持 Login Set Compression `0x03` | `0x01` chat message | 上下行都是 remaining bytes | 支持压缩/未压缩 Respawn 切服;Forge 安全路径为 Join Game + Respawn | 部分支持:压缩握手观测/gate、压缩 race suppression、压缩 REGISTER replay |
| 1.20.1 | 763 | 部分支持 | 支持 | `0x03`、`0x04`、`0x05` | 只在已有 sampler 覆盖范围内支持现代上行 payload | 不支持 legacy backend replacement | 不支持 legacy Forge/FML 状态机 |
| 其他 1.9+ 版本 | 各不相同 | 未支持 | 不能只按版本号推断 | 未定义 | 未定义 | 未支持 | 未支持 |
| 当前/latest Java 版本 | 各不相同 | 未支持,直到补 profile | 必须从目标版本协议数据读取 | 必须建模 | 必须建模 | 必须建模 | 现代模组加载器需要单独状态机 |
重要压缩规则:protocol 5 不启用 vanilla 登录压缩。旧代理里存在 1.7.x play-state 压缩包的历史实现,但 StrataProxy 不能把它当作普通 login compression。
## 新版本必须补齐的 Profile 字段
| 字段 | 原因 |
| --- | --- |
| protocol number | 路由过滤和 status response 需要 |
| login packet id | Login Success、Disconnect、Encryption Request、Set Compression 都依赖它 |
| compression framing | 登录协商后 packet 是否压缩以及如何解码 |
| serverbound command packet id | `/server` 等代理命令必须被代理吃掉,不能发给后端 |
| custom payload packet id | Forge/Fabric/Bungee/Velocity plugin message 都依赖它 |
| custom payload 长度格式 | 1.7.10 和 1.8 不同,现代版本又是另一套 payload 语义 |
| Join Game / Respawn id 和布局 | 切服时要安全转换新后端首个 Join Game |
| player list / scoreboard / team 清理包 | 安全切服需要清客户端状态 |
| Forge/mod-loader 握手阶段 | 大型模组服切服需要 reset、registry 和 payload 排队 |
| forwarding/login plugin 行为 | Velocity modern、Bungee legacy 身份转发都和版本有关 |
## 每个版本的测试门槛
| 门槛 | 1.7.10 | 1.8.x | 1.20.1 | 新版本 |
| --- | --- | --- | --- | --- |
| 命令拦截 | 已覆盖 | 通过 legacy profile 路径覆盖 | 已覆盖 | 必须覆盖 |
| 真实 TCP 后端切服 | 已覆盖 protocol 5 `/server` | 已有压缩切服单元覆盖 | 未覆盖 | 宣称切服支持前必须覆盖 |
| 登录压缩 | 明确禁用 | 已覆盖 | 已覆盖现代路径 | 必须覆盖 |
| custom payload 解析 | 已覆盖 | 已覆盖压缩/未压缩 | 部分覆盖 | 必须覆盖 |
| Forge 握手跟踪 | 已覆盖 | 已覆盖压缩/未压缩 | 不适用 | 按 Forge/Fabric 设计补 |
| Forge race suppression | 已覆盖 | 已覆盖压缩/未压缩 | 不适用 | legacy Forge 适用时必须覆盖 |
| plugin channel REGISTER replay | 已覆盖 | 已覆盖压缩/未压缩 | 不适用 | 需要 replay 的版本必须覆盖 |
| 真实大型模组服验收 | 尚未证明 | 尚未证明 | 不适用 | 生产宣称前必须验收 |
## 实现规则
新增版本时,不要直接在 relay handler 里散落 packet id。先扩展 `MinecraftProtocolProfile`,再让 relay/controller 通过 profile 能力工作。如果一个行为无法用 profile 表达,说明应该提取专门状态机,而不是继续在 relay 里加版本判断。
## 后续路线
1. 抽出明确的 `ConnectionStateMachine`:登录、压缩、play、mod-loader 阶段。
2. 抽出 `TransferOrchestrator`:切服生命周期、初始 clientbound 包、回滚和指标。
3. `MinecraftProtocolProfile` 只保留数据:packet id、格式、布局和能力开关。
4. 按版本组补 profile 和测试,再宣称支持:
- 1.9-1.12.2:legacy Forge 时代。
- 1.13-1.16.5:扁平化与 namespaced payload 过渡。
- 1.17-1.18.2:现代网络变更。
- 1.19-1.20.6:签名聊天、登录/configuration 过渡。
- 1.21+ 和 26.x 当前版本。
## 参考
- Minecraft Wiki 的 protocol version 数据说明,现代构建可从 client/server jar 的 `version.json` 读取 `protocol_version`。
- Mojang 已宣布 Java Edition 从 2026 年开始使用 `26.x` 年份式版本号;因此版本名和 protocol number 必须分开建模。
-23
View File
@@ -1,23 +0,0 @@
# 快速开始
StrataProxy 是面向 Minecraft 的静态后端代理:监听客户端连接、按路由选择后端,并处理认证、转发协议、压缩和 Forge/Bungee 兼容所需的协议帧。
## 构建与校验
```powershell
.\gradlew.bat --no-daemon check :proxy-core:installDist
```
代理程序在 `proxy-core\build\install\strataproxy\`。
## 配置并启动
```powershell
Copy-Item proxy-core\src\main\resources\config\strataproxy.yml .\strataproxy.yml
.\proxy-core\build\install\strataproxy\bin\strataproxy.bat --config .\strataproxy.yml --validate-config
.\proxy-core\build\install\strataproxy\bin\strataproxy.bat --config .\strataproxy.yml
```
至少配置一个 `servers` 后端。修改 YAML 后重启代理生效;没有 HTTP 管理端口或管理令牌。动态后端仅由已加载插件通过 API 注册、下线或切换 drain 状态。
详见[配置说明](configuration.md)和[运维说明](operations-manual.md)。
-17
View File
@@ -1,17 +0,0 @@
plugins {
`java-library`
}
group = "example.strataproxy"
version = "1.0.0"
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(25))
}
}
dependencies {
compileOnly("dev.strataproxy:proxy-plugin-api:0.1.0-SNAPSHOT")
compileOnly("org.slf4j:slf4j-api:2.0.18")
}
-16
View File
@@ -1,16 +0,0 @@
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenLocal()
mavenCentral()
}
}
rootProject.name = "strataproxy-hello-plugin"
@@ -1,37 +0,0 @@
package example.strataproxy.hello;
import dev.strataproxy.plugin.PluginContext;
import dev.strataproxy.plugin.ProxyPlugin;
import dev.strataproxy.plugin.command.CommandResult;
import dev.strataproxy.plugin.command.CommandSpec;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public final class HelloPlugin implements ProxyPlugin {
/**
* Creates HelloPlugin.
*/
public HelloPlugin() {
}
private PluginContext context;
@Override
/** Provides on load. */
public void onLoad(PluginContext context) {
this.context = context;
context.commands().register(new CommandSpec(
"hello",
List.of(),
"",
"Send a hello message.",
command -> CompletableFuture.completedFuture(CommandResult.ok("Hello, " + command.source().name() + "."))));
}
@Override
/** Provides on enable. */
public void onEnable() {
context.logger().info("Hello plugin enabled");
}
}
@@ -1,4 +0,0 @@
id=hello
name=Hello Plugin
version=1.0.0
main=example.strataproxy.hello.HelloPlugin
@@ -1,9 +0,0 @@
# StrataProxy Bukkit 后端适配器
这是独立的 Java 8 Bukkit 插件,不属于 StrataProxy 的 Gradle 多模块工程。它以 Bukkit/Spigot 1.8 API 编译,但只调用 Bukkit 1.7.10 已有的基础 API,因此可运行在 1.7.10 Bukkit 派生端。它在启动时向代理的 `backendAgent` 端点注册本服务器,随后发送心跳;关闭时可主动下线。
先在代理配置启用 `backendAgent`,并设置至少 32 字符的随机 `sharedSecret`。然后将同一密钥写入本插件 `config.yml`,按实际服务器修改 `backend.name`、`backend.address`、标签、容量和持久化选项。
构建:`gradle jar`。将生成的 `build/libs/strataproxy-bukkit-backend-agent-*.jar` 放入 Bukkit 的 `plugins/` 目录后重启。
该端点只接受注册、心跳和下线,不能读取玩家、执行命令或修改其他代理设置。