fix: 修复更新检查失效与四处运行期异常,补充单元测试 - #4
Open
mincHR549 wants to merge 7 commits into
Open
Conversation
added 7 commits
August 2, 2026 10:43
velocity 模块用的是 3.5.0-SNAPSHOT,而 velocity:injector 仍停留在 3.4.0-SNAPSHOT。injector 全靠反射操作 Velocity 内部类(StateRegistry、 InitialLoginSessionHandler、AuthSessionHandler),两个模块基于不同版本 编译时反射目标容易错位,是 issue TeamVastsea#1 那类启动崩溃的温床。 统一到 3.5.0-SNAPSHOT。
needUpgradeIgnoreSuffixes 的条件是
version.major >= major && version.minor >= minor && version.patch > patch
patch 严格递增是必要条件,因此 0.6.11 -> 0.7.0、1.2.3 -> 2.0.0 全部被
判定为不需要更新。只要下一个版本不是纯修订号递增,所有用户都收不到更新提示。
改为按 major、minor、patch、后缀等级、后缀序号逐级比较。
同时修两个会让更新检查彻底停止工作的问题:
- SemVersion.of 对非法输入抛异常。它的输入来自远程仓库的 latest 文件,
内容不可控,带一个行尾回车就会抛 NumberFormatException。现在解析失败
返回 null,并且容忍首尾空白。
- CheckUpdater.start 只捕获 IOException。底层是 scheduleAtFixedRate,
任务抛出未捕获异常后不会再被调度,一次失败就让更新检查永久停止。
现在捕获所有异常,检查逻辑抽到独立方法里。
AssignInGameFlows 在检测到游戏内用户名被占用后,会延迟 2000ms 给玩家发送 改名提示。此时玩家可能还没有真正进入服务器,或者已经断开连接, getPlayer 返回 null,直接调用 sendMessagePL 就会抛 NullPointerException。 名称冲突场景下必然触发。 加上判空,玩家不在线时记 debug 日志跳过。 顺带把 incrementString 改为包级可见以便单元测试。
PlayerHandler.register 注册的周期任务完全没有异常保护,底层 scheduleAtFixedRate 在任务抛出未捕获异常后不再调度,一次失败就会让 cache 永远不再清理,造成内存泄漏。原先的 InterruptedException 处理也不对: catch 之后继续往下走,会在被中断的线程上执行清理。 把清理逻辑抽到 clearInvalidCache,外层捕获所有异常并记录日志。
injectPlayer 只对协议版本 761(1.19.3)及以上的玩家注入 ChatSessionHandler, removePlayer 却没有同样的判断,对低版本玩家移除一个不存在的处理器会抛 NoSuchElementException。因为跑在 eventLoop().submit() 里被 Future 吞掉, 表现为静默失败。 removePlayer 补上同样的协议版本判断,并且在移除前确认处理器存在、 包一层异常捕获。裸的 761 提取为 CHAT_SESSION_MIN_PROTOCOL 常量。
项目此前没有任何测试。补上 74 个用例,覆盖四处改动风险最高、 又完全可以脱离服务器环境验证的纯函数: - SemVersion:版本号解析与升级判断,包含本次修复的次版本号、 主版本号升级判定,以及远端 latest 带行尾回车时的解析容错 - AssignInGameFlows.incrementString:用户名冲突时的候选名生成, 含进位、连续递增单调性、结果字符合法性 - MapperConfig:ChatSession 包 ID 映射的去重规则、协议版本下限、 保存与重载往返 - ValueUtil:消息变量替换、UUID 与字节数组互转、join、sha256 同时在 CI 的构建步骤前加入 test,避免回归。
CONTRIBUTING.md 原内容是占位符 "Enter file contents here", SECURITY.md 是 GitHub 默认模板且支持版本表格把所有版本都标为不支持。 - CONTRIBUTING.md:补齐构建与测试命令、模块职责说明、injector 反射 注意事项、运行期依赖的添加方式、周期任务必须捕获异常等代码约定、 新增协议版本支持的做法 - SECURITY.md:明确受支持版本、走 GitHub 私密漏洞报告通道、 报告需附带的信息(并提醒删除 accessToken 等敏感值)、处理时限, 以及不属于安全漏洞的情形 - README:修正安装步骤的编号错乱与重复项,补充测试命令, 加上贡献指南和安全策略的入口,中英文同步
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
变更摘要
本 PR 修复 5 处会在生产环境实际触发的缺陷,统一 Velocity API 版本,并为项目引入首批单元测试。共 7 个提交,按关注点拆分,可单独 review 或 revert。
修复的缺陷
1. 更新检查无法发现次版本号和主版本号升级
SemVersion.needUpgradeIgnoreSuffixes的判断条件是:patch严格递增是必要条件,因此0.6.11 -> 0.7.0、1.2.3 -> 2.0.0全部被判定为不需要更新。只要下一个版本不是纯修订号递增,所有用户都收不到更新提示。改为按 major、minor、patch、后缀等级、后缀序号逐级比较。
2. 更新检查会因单次失败永久停止
两个独立原因,都会让检查彻底失效:
SemVersion.of对非法输入抛异常。它的输入来自远程仓库的latest文件,内容不可控,带一个行尾回车就抛NumberFormatException。CheckUpdater.start只捕获IOException。底层是scheduleAtFixedRate,任务抛出未捕获异常后不再被调度。现在解析失败返回
null并容忍首尾空白,周期任务捕获所有异常。3. 用户名冲突改名提示空指针
AssignInGameFlows延迟 2000ms 给玩家发送改名提示,此时玩家可能尚未进服或已断开,getPlayer返回null后直接调用sendMessagePL抛 NPE。名称冲突场景下必然触发。已加判空,玩家不在线时记 debug 日志跳过。4. 玩家缓存清理任务会因异常永久停止
PlayerHandler.register的周期任务没有异常保护,同样受scheduleAtFixedRate语义影响,一次失败后cache永不清理,造成内存泄漏。原有的InterruptedException处理也不正确:catch 之后继续往下走,会在已被中断的线程上执行清理。5. 低版本客户端退出时静默报错
injectPlayer只对协议版本 761(1.19.3)及以上注入ChatSessionHandler,removePlayer缺少同样的判断,对低版本玩家移除不存在的处理器会抛NoSuchElementException。因为在eventLoop().submit()内被 Future 吞掉,表现为静默失败。裸的761已提取为CHAT_SESSION_MIN_PROTOCOL常量。其他改动
统一 Velocity API 版本:
velocity用3.5.0-SNAPSHOT,velocity:injector却停留在3.4.0-SNAPSHOT。injector 全靠反射操作 Velocity 内部类(StateRegistry、InitialLoginSessionHandler、AuthSessionHandler),两模块编译基准不一致时反射目标容易错位,是 #1 那类启动崩溃的温床。引入 JUnit 5,74 个用例覆盖
SemVersion、AssignInGameFlows.incrementString、MapperConfig、ValueUtil。选这四处的原因是它们都是纯函数,能脱离服务器环境验证,且本次修复集中在此。上述缺陷 1、2 均有对应的回归测试。CI 加入 test 步骤,位于构建之前。
文档:
CONTRIBUTING.md原内容是占位符Enter file contents here;SECURITY.md是 GitHub 默认模板,且支持版本表格把所有版本都标为不支持。两份均已重写。README 修正了安装步骤的编号错乱与重复项,补充测试命令,中英文同步。验证
已知限制
未在真实 Velocity 上验证登录流程,只做了编译与单元测试验证。缺陷 3、5 涉及运行期行为,建议合并前在测试服过一遍登录、名称冲突改名、低版本客户端进出这三个场景。
Velocity API 统一到
3.5.0-SNAPSHOT后,如果需要继续兼容 3.4.x,这个改动需要另做讨论。.digests未纳入版本控制(.gitignore中已忽略),本次没有改动libraries,因此依赖摘要无需重新生成。