你的目标
希望 outpre 增加一条 LOGIN-state 桥接路径,专门用于 Forge 玩家或所有需要 LOGIN-state 协商的客户端。即:
- 不发 ServerLoginSuccessPacket,客户端保持 LOGIN state
- 在 LOGIN state 下转发 ModList 与 Channels 协商插件包(LoginPluginRequest 与 LoginPluginResponse)到 outpre-auth 后端
- 协商完成后,outpre-auth 那边发出的 LoginSuccess 经 Ambassador 的 OutboundSuccessHolder 放行
- 玩家进入 PLAY,outpre 后续逻辑(认证、onVerified、connectVerifiedPlayerToTarget)继续
当前环境
- Velocity 3.4.0-566(也试过 3.5.0-SNAPSHOT-594)
- HyperZoneLogin 26.4.6-RC-29a32a89
- Ambassador 1.5.3-beta(含本地 instanceof 修复)
- Forge 1.20.1(47.4.0)
- 客户端 mod 数约 200个
插件版本与下载渠道
HyperZoneLogin-26.4.6-RC-29a32a89-all
服务器插件列表
velocity:
Ambassador-Velocity-1.5.3-beta-all
HyperZoneLogin 26.4.6-RC-29a32a89
CMIV-1.0.2.3
Mohist:
YukiNoaAPI-3.0.1
vault-1.6
ProtocolLib
item-nbt-api-plugin-2.15.7
CMILib1.5.9.3
LagFixer
Banit-1.2.1
PlayerTitle-5.0.0-beta.3
LuckPerms-Bukkit-5.5.42
CMI-9.8.6.6
SuperLobbyDeluxe-4.7.7-shaded
GeneralFix-1.1.7
具体问题
背景
在 Velocity 上同时运行 HyperZoneLogin 26.4.6-RC-29a32a89 与 Ambassador 1.5.3-beta 时,Forge 1.20.1 客户端无法完成登入。outpre 模式启用,outpre-auth 后端是 Forge 1.20.1 服务器。
现象
服务端日志(outpre 模式下):
[INFO] <player> entered outpre pre-registration flow
[INFO] OutPreBackendBridge has connected
[INFO] <player> has disconnected
[INFO] OutPreBackendBridge has disconnected
[INFO] 条目 mojang: 玩家 <player> 认证成功
服务端视角认证流程是成功的,没有 ERROR。
客户端日志在等约 50 秒后断开:
[Netty Client IO] [NetworkRegistry]: Channels [touhou_little_maid:network, ..., twilightforest:channel] rejected vanilla connections
客户端认为收到的是 vanilla 服务器响应,所以拒绝连接。
根因分析
经过对 outpre 源码、Ambassador 源码、Forge 协议三方对照,这是协议层面的设计冲突。
Forge 协议要求的握手顺序(Ambassador 实现)
Client 发握手包(含 FML marker) 给 Server
Client 收到 ModList 请求 在 LOGIN state
Client 回 ModList 在 LOGIN state
Client 收到 Channels 协商 在 LOGIN state
Client 收到 ServerLoginSuccess ModList 协商通过后才发
Client 进入 PLAY state
Ambassador 在客户端 pipeline 装了 OutboundSuccessHolder(velocity/client/OutboundSuccessHolder.java),作用是截留 ServerLoginSuccessPacket,直到 Forge ModList 协商完毕才放行。
outpre 的设计顺序(OutPreAuthSessionHandler)
OutPreAuthSessionHandler.kt 第 144 至 148 行:
val success = ServerLoginSuccessPacket()
mcConnection.write(success)
loginState = State.SUCCESS_SENT
outpre 的核心是先把客户端推到 PLAY 或 CONFIG state,然后桥接 PLAY/CONFIG state 包到 outpre-auth。
物理冲突
- Ambassador 期望客户端停留在 LOGIN state 完成 ModList 协商,LoginSuccess 被 OutboundSuccessHolder 拦下;
- outpre 期望立刻发 LoginSuccess 把客户端推到 PLAY 或 CONFIG state,才能启动 Bridge SessionHandler;
- 同装时 outpre 写出的 LoginSuccess 被 Ambassador 拦截,客户端永远停在 LOGIN state 等 ModList,50 秒后超时断开。
OutPreClientBridgeSessionHandler 完全建立在 PLAY 或 CONFIG state 之上(StateRegistry.PLAY 与 StateRegistry.CONFIG 分支),没有 LOGIN state 桥接路径。
已尝试的修复(均不成立)
- 改 Ambassador 让 OutPreBackendBridge 走 instanceof 放行:编译通过、ClassCast 不再抛,但 backend 不再发 FML marker、不接管 ModList 协商,客户端协议拒绝。
- 加 ServerConnection 接口 instanceof 走 marker:marker 发出去了,但 Ambassador 的 ForgeLoginWrapperHandler 和 VelocityForgeBackendConnectionPhase 强依赖 VelocityServerConnection.getPhase() 和 setConnectionPhase,OutPreBackendBridge 没实现这些方法。
- 让 Forge 玩家跳过 outpre:会破坏 outpre 对 Forge 玩家的认证。
你已经看过哪些文档
全部看过
其他补充
已验证可用的临时方案
切换到 backend 模式(作者在 config-comments 里写的退路:推荐 outpre 模式,若有问题请使用 backend 模式)。
具体步骤(零代码改动):
- core.conf 把 v-server.mode 从 outpre 改为 backend
- core.conf 设置 v-server.backend.fallback-auth-server 为一个 Velocity 注册的 Forge 认证服服务器名(比如 verify)
- velocity.toml 的 [servers] 段加一行注册这个服务器,比如 verify = "127.0.0.1:25581"
- velocity.toml 的 try 列表不要包含这个 verify 服务器
为什么有效:backend 模式走 Velocity 标准 PlayerChooseInitialServerEvent + createConnectionRequest 流程,不在 LOGIN state 做任何 hack。Ambassador 完整接管 Forge ModList 协商,客户端正常进入 verify 服后再走 yggdrasil 认证。验证通过后 BackendAuthHoldListener.connectVerifiedPlayerToTarget 跨服到 post-auth-default-server。
实测日志线索:
Using backend auth hold server 'verify'
[server connection] HZL... -> verify has connected
条目 mojang: 玩家 <player> 认证成功
玩家 <player> 通过 Yggdrasil 验证,Entry: mojang
代价:玩家会先进 verify 服,再被自动跳走,不像 outpre 那样隐形过渡。但功能完整、Forge 协议兼容、Ambassador 跨服转发正常。
你的目标
希望 outpre 增加一条 LOGIN-state 桥接路径,专门用于 Forge 玩家或所有需要 LOGIN-state 协商的客户端。即:
当前环境
插件版本与下载渠道
HyperZoneLogin-26.4.6-RC-29a32a89-all
服务器插件列表
velocity:
Ambassador-Velocity-1.5.3-beta-all
HyperZoneLogin 26.4.6-RC-29a32a89
CMIV-1.0.2.3
Mohist:
YukiNoaAPI-3.0.1
vault-1.6
ProtocolLib
item-nbt-api-plugin-2.15.7
CMILib1.5.9.3
LagFixer
Banit-1.2.1
PlayerTitle-5.0.0-beta.3
LuckPerms-Bukkit-5.5.42
CMI-9.8.6.6
SuperLobbyDeluxe-4.7.7-shaded
GeneralFix-1.1.7
具体问题
背景
在 Velocity 上同时运行 HyperZoneLogin 26.4.6-RC-29a32a89 与 Ambassador 1.5.3-beta 时,Forge 1.20.1 客户端无法完成登入。outpre 模式启用,outpre-auth 后端是 Forge 1.20.1 服务器。
现象
服务端日志(outpre 模式下):
服务端视角认证流程是成功的,没有 ERROR。
客户端日志在等约 50 秒后断开:
客户端认为收到的是 vanilla 服务器响应,所以拒绝连接。
根因分析
经过对 outpre 源码、Ambassador 源码、Forge 协议三方对照,这是协议层面的设计冲突。
Forge 协议要求的握手顺序(Ambassador 实现)
Ambassador 在客户端 pipeline 装了 OutboundSuccessHolder(velocity/client/OutboundSuccessHolder.java),作用是截留 ServerLoginSuccessPacket,直到 Forge ModList 协商完毕才放行。
outpre 的设计顺序(OutPreAuthSessionHandler)
OutPreAuthSessionHandler.kt 第 144 至 148 行:
outpre 的核心是先把客户端推到 PLAY 或 CONFIG state,然后桥接 PLAY/CONFIG state 包到 outpre-auth。
物理冲突
OutPreClientBridgeSessionHandler 完全建立在 PLAY 或 CONFIG state 之上(StateRegistry.PLAY 与 StateRegistry.CONFIG 分支),没有 LOGIN state 桥接路径。
已尝试的修复(均不成立)
你已经看过哪些文档
全部看过
其他补充
已验证可用的临时方案
切换到 backend 模式(作者在 config-comments 里写的退路:推荐 outpre 模式,若有问题请使用 backend 模式)。
具体步骤(零代码改动):
为什么有效:backend 模式走 Velocity 标准 PlayerChooseInitialServerEvent + createConnectionRequest 流程,不在 LOGIN state 做任何 hack。Ambassador 完整接管 Forge ModList 协商,客户端正常进入 verify 服后再走 yggdrasil 认证。验证通过后 BackendAuthHoldListener.connectVerifiedPlayerToTarget 跨服到 post-auth-default-server。
实测日志线索:
代价:玩家会先进 verify 服,再被自动跳走,不像 outpre 那样隐形过渡。但功能完整、Forge 协议兼容、Ambassador 跨服转发正常。