Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
| Date uploaded | 5 hours ago |
| Version | 0.5.18 |
| Download link | cn_xc-LateJoinSimple-0.5.18.zip |
| Downloads | 268 |
| Dependency string | cn_xc-LateJoinSimple-0.5.18 |
This mod requires the following mods to function
BepInEx-BepInExPack
BepInEx pack for Mono Unity games. Preconfigured and ready to use.
Preferred version: 5.4.2100README
中途加入 Simple / LateJoinSimple
REPO 游戏交流 QQ 群:573485890
感谢各位玩家长期以来的使用与支持。本模组的所有优化与修复,均基于本人随机加入房间、逐局查阅运行日志的实测排查;由于联机环境复杂,仍可能存在未覆盖的情况,欢迎反馈以便持续改进。
📜 版权声明与使用条款
应原作者要求,现补充说明如下。
Supplemented at the original author's request.
一、作品来源
本项目为衍生作品(derivative work):
- 上游原作:中途立即加入 / LateJoinNow,作者 Zichen(王子晨),版本 1.1.0。
- 本作品:LateJoinSimple,由 cn_xc 在上游基础上接手修复与继续维护, 已取得原作者本人许可后发布,并非原作官方发行。
- 本声明仅覆盖 cn_xc 新增与修改的部分,不改变上游原作的著作权归属。
原作者声明(照录,未做修改):
本模组为独家原创作品,禁止未经授权进行代码复制、二次打包移植, 反编译,或转载,违者将依法进行维权投诉。
二、授权范围
仅授权在 R.E.P.O.(REPO)游戏内以未经修改的形式运行本作品。 Licence granted: a limited, non-exclusive, non-transferable licence to run this work, unmodified, inside the game "R.E.P.O." only.
三、未经书面许可明确禁止
- 反编译、反汇编、逆向工程、脱壳,或提取本作品新增/修改部分的实现逻辑;
- 复制、改编、翻译、演绎,或基于本作品创作衍生作品;
- 二次打包、重新分发、上架第三方平台,或用于商业用途;
- 移除或篡改版权标识、原作者声明与本条款。
四、法律依据与后果
本作品作为计算机程序(按文字作品保护)受以下法律与国际条约保护: 《中华人民共和国著作权法》《计算机软件保护条例》, 以及《伯尔尼保护文学和艺术作品公约》、TRIPS 协定第 10 条。
对上述禁止行为的实施者,cn_xc 与上游权利人有权依据 《中华人民共和国著作权法》第五十二条、第五十三条及《计算机软件保护条例》 要求停止侵害、消除影响、赔偿损失;情节严重、以营利为目的的, 可能依据《中华人民共和国刑法》第二百一十七条(侵犯著作权罪)承担刑事责任。
说明:部分司法辖区(如欧盟 2009/24/EC 指令第 6 条)允许"为互操作性"的 有限反编译。本条款旨在明确作者的授权边界,并不排除法律强制赋予的权利。
五、致谢
- 感谢 Zichen(王子晨) 创作 LateJoinNow 并授权本项目继续维护。
- 感谢所有在 QQ 群与反馈中帮助测试的玩家。
English Version
1. Origin of this work
This project is a derivative work:
- Upstream original: LateJoinNow 1.1.0 by Zichen (王子晨).
- This work: LateJoinSimple, maintained by cn_xc, derived from the upstream original and published with the original author's permission. It is not an official release of the upstream project.
- The terms below cover only the portions added or modified by cn_xc; they do not alter the copyright ownership of the upstream original.
Original author's notice, quoted unchanged:
本模组为独家原创作品,禁止未经授权进行代码复制、二次打包移植,反编译, 或转载,违者将依法进行维权投诉。
(This mod is an original work. Unauthorised copying of code, repacking or porting, decompilation, or redistribution is prohibited; infringements will be pursued in accordance with the law.)
2. Licence granted
A limited, non-exclusive, non-transferable licence to run this work, unmodified, inside the game R.E.P.O. only.
3. Prohibited without prior written permission
- Decompiling, disassembling, reverse engineering, unpacking, or extracting the implementation of the portions added or modified here;
- Copying, adapting, translating or creating derivative works;
- Repacking, redistributing, publishing on third-party platforms, or any commercial use;
- Removing or altering this notice, the original author's notice, or any copyright identifier.
4. Legal basis and consequences
This work is a computer program protected as a literary work under the Berne Convention and TRIPS Agreement, Article 10, as well as the applicable domestic law, including the Copyright Law of the People's Republic of China and the Regulations on Computer Software Protection. The rights holders (cn_xc and the upstream rights holder) may seek injunctive relief, removal of infringing copies and damages; wilful infringement for commercial advantage may also carry criminal liability under Article 217 of the Criminal Law of the PRC.
Note: some jurisdictions (e.g. EU Directive 2009/24/EC, Article 6) permit limited decompilation for interoperability. This notice states the author's licensing boundary and does not exclude rights granted by mandatory law.
5. Credits
- Thanks to Zichen (王子晨) for creating LateJoinNow and for allowing this project to continue its maintenance.
- Thanks to all players who helped test and report issues.
Contact: QQ group 573485890, or leave a message on the mod page.
⚠️ 测试版说明(v0.5.18)
这是一个公开测试版。欢迎大家继续帮忙验证:
- 中途加入主流程已实测可用(排队 → 场景 / 物品 / 宝物 / 状态同步 → 落地动画 → 移动检测通过)。
- 联机细节仍可能存在未知问题,欢迎反馈。
- 反馈请附日志:
BepInEx/LogOutput.log(或 BepInEx 控制台),并说明是房主端还是加入者端。 - 本版相关修复:不再销毁带铰链的门(原版会销毁,可能导致加入者看到「只有门框、门没了」)。
- 本版新增:房间可见性同步(需双端都装)——房主把本局已探索的房间发给中途加入者,修的是游戏内地图(DirtFinder)上的房间揭示;并新增光照守卫,拦截光照系统异常以免整片房间不发亮。
- 「虚空门 / 缺墙 / 门框里没有门」已修复:0.3.4 起双端实测与日志中未再出现(
Level Point: Not connected、Ev Destroy Failed均已归零)。
✨ 功能
- 随时中途加入:朋友可以在卡车 / 商店 / 关卡进行中加入你的房间,三个场景可分别用配置控制。
- 房主权威:场景同步全部由房主执行,加入者只接收,避免状态污染。
- 同步内容:地图模块(墙体 / 内饰连接)、贵重物品价格与发现状态、物品属性,以及 22 项场景状态(撤离点、门禁、升级台、各类陷阱与宝物等)。
- 房间信息 HUD(房主与加入者都可见):左下角显示房间类型(含「可加入 / 已锁定」)、房间名称、当前人数、全部玩家名单(房主第一 → 其余按编号;每行 3~6 人可配)。
- 与房主的计数对比:HUD 首行显示你本机地图的「房间 / 墙 / 门」数量,并与房主上报的数量对比 —— 一致打
✓,不一致显示(房主N),可据此判断两边世界是否一致(虚空排查)。 - 中途加入进度显示:等待连接 / 排队中 / 加载中 / 已中途加入。
- 公开房名自动前缀:创建公开房时自动加
[中途加入-小菜]前缀。 - 切关冷却保护:切换场景时自动临时锁房,防止过渡阶段有人撞入。
- 双端协同优化:房主与加入者都装本模组时自动启用(加载更快、外观与语音自动刷新),HUD 中该玩家显示「(优化)」。
- 虚空兜底:地图分帧重发 + 20 秒移动检测,尽量避免加入者卡在虚空。
- 房间可见性同步(需双端都装):房主把本局已探索的房间发给中途加入者,让你的游戏内地图(DirtFinder)与团队一致(与「缺墙」不是同一条线)。
- 光照守卫:拦截光照系统的异常,避免中途加入时房间整片发黑。
- 中途加入稳健性:模块连接对每个加入者只发一次(避免抬高客户端的
ModulesSpawned)、后台只补发「没收到过」的模块、快捷栏槽位自动补注册、以及若干游戏侧 NRE 守卫。本模组不会改动任何墙 / 门 / 通路的开关。 - 冲突自检:检测到其它「中途加入」类模组时弹窗警告。
- 升级属性重放(0.5.0):中途加入者会错过历史上所有「购买升级」的网络 RPC,导致跳跃/体力/冲刺/力量/抓取范围/投掷/攀爬/翅膀/血上限等属性停留在默认值。现在加入后按
StatsManager存档调用游戏自带的PunManager.Update*RightAway重放一次。 OwnerOnlyRPC异常兜底(0.4.1):对象没有 owner 时,游戏自己的SemiFunc.OwnerOnlyRPC会在内部抛 NRE(导致抓取链接 / 地图工具等 RPC 失败)。现在捕获后放行并只记一次日志。[玩家视图]诊断(0.4.1,只读):加入后比对「房间人数」与「本机可见 avatar 数」,若本机 avatar 少于房间人数会明确提示 —— 用于定位「属性同步不了」是否其实是玩家视图缺失。[属性快照]/ 目标信息诊断(0.4.3,只读):房主推送时打印目标与数量;加入者落地 12 秒后打印本机看到的每个玩家血量/上限实际值 —— 两端对照即可判定属性是否真的收到。
📦 安装
- 确保已安装 BepInExPack。
- 将
LateJoinSimple.dll放入BepInEx/plugins。 - 启动游戏,模组会自动生成配置文件。
强烈建议:房主与加入者都装本模组(协同模式),可避免单端模式的已知问题(见下)。
⚙️ 配置
配置文件位置:BepInEx/config/cn_xc.LateJoinSimple.cfg
配置热重载:0.5.11 起,修改 cfg 后无需重启游戏,约 2 秒自动生效(日志会打印一次「检测到配置文件变更,已热重载」)。
| 配置项 | 默认值 | 说明 |
|---|---|---|
TeleportToHostOnJoin |
false |
加入后传送到房主位置;房主死亡则传送到最近的存活玩家 |
AllowJoinInTruck |
true |
卡车阶段允许中途加入 |
AllowJoinInShop |
true |
商店阶段允许中途加入 |
AllowJoinInLevel |
true |
关卡进行中允许中途加入 |
ShowRoomType |
true |
HUD 显示房间类型(公开 / 私人 / 单人) |
ShowRoomName |
true |
HUD 显示房间名称 |
HudNamesPerLine |
5 |
玩家名单每行显示几个(3–6) |
HudFontSize |
16 |
HUD 字号(10–28) |
HudOpacity |
80 |
HUD 文字透明度 %(20–100) |
VerboseLog |
false |
输出详细调试日志(排查问题时可开) |
ShowConflictWarning |
true |
检测到同类模组时弹窗警告 |
FixInventorySlots |
true |
中途加入后把场景里的 InventorySpot 补注册进 Inventory(修复快捷栏) |
ModuleResendSeconds |
60 |
后台只补发「尚未发过连接数据」的模块(每个模块只发一次;0 = 关闭) |
PlayerHealthSync |
true |
房主在加入者落地后推送每个玩家的血量 / 生命上限(修复「看不到别人的血」) |
PlayerCosmeticsSync |
true |
房主在加入者落地后推送每个玩家的外观颜色(修复「看不到别人的颜色」) |
ApplyUpgradesOnJoin |
true |
加入后按存档重放升级属性(跳跃/体力/冲刺/力量/抓取/投掷/攀爬/翅膀/血上限…) |
模组版本号 |
(已移除) | 0.5.12 起不再出现在配置界面:版本标记改为与 cfg 同目录的 cn_xc.LateJoinSimple.cfg.version 文件。0.3.5 起版本更新不会清空你的设置;若要还原默认,删除 cfg 后重启 |
⚠️ 已知问题
已修复(0.3.x)
- ✅ 虚空门 / 缺墙 / 门框里没有门 —— 已修复。0.3.4 之后的双端实测与日志中未再出现(
Level Point: Not connected从 31 归到 1 且仅剩入口点、Ev Destroy Failed从 122 归零)。 - ✅ 快捷栏限制(加入者无法把武器放进快捷栏)—— 已修复(
FixInventorySlots:关卡生成后把场景里的InventorySpot补注册进Inventory)。
以上修复都需要加入者也安装本模组(协同模式)。加入者是原版客户端时,本模组无法修正其表现。
混合模式 / 单端模式仍可能存在的问题(本模组无法修正)
以下两种情况本模组都无法修正 —— 「客户端协同补丁」需要房主也运行本模组才会启用:
- 房主没装任何中途加入模组,只有加入者装了本模组。
- 房主装的是 LateJoinNow(Zichen 原版),加入者装的是本模组 —— 两边房间属性键不同(
LateJoinNow.*vsLateJoinSimple.*),协议不通,等效单端。本模组检测到这种房主时会打一行明确提示。
表现(属预期,非 bug):日志可能出现 Received RPC "ModuleConnectionSetRPC" for viewID … but this …、Received RPC "DollarValueSetRPC" …、KeyNotFoundException: The given key '6024' —— 房主在同步「加入者本地并不存在」的对象;部分门 / 建筑物件与宝物数据可能缺失。
另有 部分建筑不可见:早期版本偶有反馈;0.3.4 之后的双端日志中未再出现,但未专门验证是否已完全消除。
规避办法:房主与加入者使用同一个中途加入模组(推荐都用本模组 → 协同模式)。
中途加入者的「玩家属性」同步
已覆盖的(房主在加入者落地后推送,并 4 秒后补一次):
- ✅ 血量 / 血上限(0.3.9)——
PlayerHealth.UpdateHealthRPC。开关PlayerHealthSync。 - ✅ 外观颜色(0.4.0)——
PlayerCosmetics.SetupColorsRPC(该 RPC 仅房主可发)。开关PlayerCosmeticsSync。 - 两个开关互相独立,只关一个不影响另一个。
游戏本身没有同步通道的属性(不存在"没同步"这回事):
- 升级属性:速度 / 力量 / 体力上限 / 攀爬 / 翅膀 / 电池 / 地图人数 / 蹲伏恢复 —— 这些在游戏里没有任何 PunRPC,是各客户端按本地存档计算的;其他玩家本来就不会看到你的升级数值(只能看到动作表现)。
尚未覆盖:
- 装扮(帽子等):其 RPC(
SetupCosmeticsRPC)需要int[],而本地字段是List,需要做一次转换,留待后续版本。
说明:PlayerHealth.Update 在「头像引用为空」时仍会跳过该组件的更新,以避免每帧刷 NRE(日志里的 PlayerHealth.Update 的头像引用为空… 即此),属于防崩措施,与本修复不冲突。
其它
- 过渡期禁入:最后一个提取点完成 → 下一关加载完成前,模组会临时拒绝新玩家加入,这是设计行为。
- 网络要求:房主未开加速器时,加入者可能连接超时或卡在加载界面。
- 人数上限:建议与 MorePlayersSimple 搭配;房间人数上限的修改只在创建房间前生效。
- 日志里的 Level Point: Not connected 可忽略:这是游戏自身提示,与外观无关。协同模式实测:该提示出现 1 / 15 / 31 条时,均未观察到虚空墙、门框没门或通路堵塞。
❓ 常见问题
Q:这个能和 Zichen 的 LateJoinNow 一起装吗? A:不能。两者 GUID 与房间属性键都不同,同时启用会出现重复 RPC 与缓存清理,导致卡加载 / 地图异常。请只保留一个。
Q:加入者的门框里没有门怎么办?
A:该问题已修复(0.3.4 起,协同模式下双端实测未再出现)。若仍遇到,请先确认加入者也安装了本模组,然后把双端 LogOutput.log 发我。
Q:怎么判断是不是协同模式? A:HUD 里玩家名后显示「(优化)」即表示协同模式生效。
Q:改了配置没生效?
A:配置在启动时读取,改完请重启游戏。版本更新不会清空你的设置(0.3.5 起);若要还原默认,删除 BepInEx/config/cn_xc.LateJoinSimple.cfg 后重启即可。
🙏 致谢
- ( Zichen) —— 原作「中途立即加入 / LateJoinNow」的作者。本模组的全部核心逻辑、场景同步思路与踩坑经验均来自其 1.1.0 版本,并在其授权下继续维护。
- 本 README 中的功能说明与「已知局限」记录同样来自原作文档。
- 测试协助:感谢 排排坐 食果果(绊倒铁盒) 与 Sunshine° 帮忙做双端联机测试与日志反馈。
QQ群:573485890
CHANGELOG
[0.5.18] - 2026-10-24
文档
- 应原作者要求,README 增补完整的「版权声明与使用条款」(中文 + English Version),并新增
LICENSE文件。 - 源码顶部加入版权声明头,新增
LicenseNotice常量(编译进 DLL,便于查看)。 - 本次无任何功能或逻辑改动。
[0.5.17] - 2026-10-24
修复
- 恢复
CoopMode配置项(0.5.16 中误将其固定为开启):当房主或队友仍是旧版本 / 没装本模组时,需要关闭协同模式按单端模式运行,否则会导致本机玩家对象异常(表现为中途加入后无法操控、ESC 无响应)。 与版本不一致的房主联机时请把CoopMode设为false。
[0.5.16] - 2026-10-24
修复
- HUD 文本宽度写死 480px:名字行较长时会被裁掉(表现为"玩家没显示全"、部分文字看不到)。改为按屏幕动态计算宽度(
min(屏幕宽-24, 1400))。
[0.5.15] - 2026-10-24
修复
- 去掉玩家名单的 12 人上限(20 人房之前只显示 12 个,其余被省略):现在按实际人数全部显示(31 人为防御性上限)。
[0.5.14] - 2026-10-24
修复 / 调整
- 玩家名单不再受「显示人数」开关影响:此前关闭
ShowPlayerCount会连玩家名字一起消失,现在名单始终显示。 - 玩家排序:房主第一 → 其余按 ActorNumber 排序。
[0.5.13] - 2026-10-24
- HUD 计数:扫描失败导致「房间/墙/门」全为 0 时,不再把
0,0,0写入房间属性,避免客户端误显示「房主 0」。 - 版本标记写入失败改为提示一次,不再静默吞异常。
- 版本标记写入统一
Trim(),避免与读取端比较不一致。 - HUD 关闭再开启后立即重建内容(此前最多空白 1 秒)。
- 配置热重载轮询异常时只提示一次,不再静默。
- 玩家名截断提示改用实际显示数量判断(有 null 玩家时不再误显示「…」)。
[0.5.12] - 2026-10-24
修改
- 移除「模组信息」配置组:模组版本号改为独立标记文件(
cn_xc.LateJoinSimple.cfg.version),不再显示在配置界面。
性能优化
- HUD 状态行改为先判断刷新间隔再统计人数,减少每帧开销。
GUIStyle以GUI.skin.label为基类,字体/对齐更稳定。- 房主身份变化时 HUD 角色标签立即刷新(原为最多 5 秒延迟)。
[0.5.11] - 2026-10-24
新增
- 配置热重载:每 2 秒检测
BepInEx\config\下本模组.cfg的修改时间,变化即Config.Reload()并刷新 HUD —— 修改HudNamesPerLine等配置无需重启游戏(约 2 秒内生效)。 日志会打印一次:检测到配置文件变更,已热重载(无需重启)。
[0.5.10] - 2026-10-24
调整
HudNamesPerLine可选范围从 1~12 收紧为 3~6(默认仍 5)。
[0.5.9] - 2026-10-24
新增(HUD 可配置)
- 新增配置项
HudNamesPerLine(分组「房间信息显示」,默认 5,范围 1~12):玩家名字每行显示几个。 配置文件:BepInEx\config\下本模组的.cfg,改完重启游戏生效。
[0.5.8] - 2026-10-24
调整(HUD)
- 玩家名字改为每行 3 个(最多显示 12 人,超出显示
…),房主仍带(房主)。
[0.5.7] - 2026-10-24
调整(HUD)
- 玩家名字不再挤在一行:改为
玩家(N):标题 + 每人一行(最多显示 8 人,超出显示…),房主名字后仍带(房主)。
[0.5.6] - 2026-10-24
新增(HUD)
- 玩家名字行:左下角 HUD 新增
玩家:A、B(房主)、C,客户端也能看到所有人。 - 与房主对比:房主把「房间 / 墙 / 门」计数写入 Photon 房间属性(
LJS.MapCounts),客户端读取后逐项对比:- 一致显示
12✓ - 不一致显示
12(房主14)用于快速判断"我这边的世界是不是和房主不一样"(虚空排查)。
- 一致显示
[0.5.5] - 2026-10-24
修复
- 房间可见性补发:第一次(3s)若尚未收集到已探索房间,改为跳过本轮而不是
yield break终止协程 —— 之前会连带取消 9s 的那次补发。 - 零房间语义:收集结果为空时返回空数组而不是
null,与"异常"区分开,客机可确认"同步完成且为 0 间"。 - 补发等待:
WaitForSeconds(5f)改为WaitForSecondsRealtime(5f),避免暂停/加载时 timeScale=0 导致等待不走、截止时间照走。 - 弹窗守卫:
TryShowMenuPopup增加mm == null判断,不再依赖 catch 兜底。 - 加入者刷新:
RefreshOldPlayersForNewJoiner增加newPlayer == null守卫。 - HUD:
showStatus也要求inGame && CurrentRoom != null,避免大厅/菜单误显示。 - 状态同步表:
new StateSyncEntry[22]改为new[],长度由编译器推断。
[0.5.4] - 2026-10-23
新增
- 加入者(客户端)也能看到左下角 HUD:
OnGUI去掉了「仅房主」限制,中途加入的玩家同样能看到房间信息框。 - HUD 第一行显示地图计数:
【房主】/【加入者】 房间 N · 墙 N · 门 N- 房间:
RoomVolume数量 - 墙:
ModuleBlockObject(通道封堵)+BreakableWall - 门:
TruckDoor/ShopKeycardDoor/DoorTrap/TutorialDoor - 用途:房主与加入者的数字一对比,就能判断地图是否重建一致(虚空排查用)。
- 房间:
优化
- HUD 文本缓存(不再每帧拼字符串,减少 GC)。
- 地图计数改为「换图立即刷新 + 平时 5 秒间隔」,不再每 2 秒全场景
FindObjectsOfType。
更新日志
[0.5.3] - 2026-10-18
修复:加入者「抓不动东西 / 交互不了」
- 修复
PhysGrabber的引用:在PlayerAvatar.Awake后置钩子里,若PhysGrabber.photonView或PhysGrabber.playerAvatar为空则补上(参考实现里的GrabberViewPatch)。中途加入的玩家这两个引用为空时会表现为抓取 / 交互无反应。 - 若
physGrabPoint为空,会打一行 Verbose 提示([抓取器] … physGrabPoint 为空),用于确认是否属于组件级损坏。
说明
- 仅补引用,不改任何交互数值;
spawned强制逻辑此前已存在(客机侧 +PhysGrabObject.Awake)。
[0.5.2] - 2026-10-18
修复 + 诊断
- 修正升级属性的读取位置:0.5.1 读的是
StatsManager.playerUpgrade*字段(实测为空,导致[自身属性]全是 0、0.5.0 的重放也找不到数据)。现在改为优先读游戏真正的存储StatsManager.dictionaryOfDictionaries[升级名][steamID],字段仅作后退。 - 新增
[升级字典]dump:[升级字典] 加入后 3 秒:共 N 个字典;本地(<steamID>) 非零项 M 个;样例: playerUpgradeHealth(2) …—— 可直接确认升级数据是否存在、键是什么。
已确认正常(0.5.1 日志)
- 血量推送:房主推
1300/1300,加入者 12 秒即显示1300/1300(set)✓ - 外观颜色:双方
色[]数组均为真实值且不同(说明颜色同步成功)✓ - 加入者自己的
100/100是真值(其血上限就是 100)✓
[0.5.1] - 2026-10-18
新增诊断(只读)
[自身属性]对照日志:加入后 3 秒(重放前后各一次)与 12 秒,同时打印存档里的升级等级与本机实际生效的字段:[自身属性] 重放前 3 秒 steamID=… | 存档: 跳3 体50 速2 血5 力1 | 生效: JumpExtra=0 EnergyStart=100 SprintSpeed=5 maxHealth=100 grabStrength=1- 用途:区分两种根因 —— 存档有值但没生效(0.5.0 的重放应能修,看重放后那条是否变化)/存档本身没到本机(属存档同步问题,需另查)。
说明
- 0.5.0 的升级重放保留(正常情况无副作用)。
[0.5.0] - 2026-10-18
修复:中途加入者的升级属性一直没被应用(这次找到真正原因)
- 游戏里 跳跃 / 体力 / 冲刺速度 / 抓取力量 / 抓取范围 / 投掷强度 / 攀爬 / 翅膀 / 血量上限 / 蹲伏恢复 / 电池 / 地图人数 这些属性,只在玩家「购买升级」的网络 RPC 里应用:
PunManager.UpgradePlayerX(...)→PunManager.UpdateXRightAway(steamID, value)→ 写入PlayerController.JumpExtra/EnergyStart/SprintSpeed/PhysGrabber.grabStrength/PlayerHealth.maxHealth等。 - 中途加入者错过了历史上所有这类 RPC → 本机这些属性一直是默认值(表现为跳得少、体力短、跑得慢、血上限低)。
- 现在:加入后 3 秒 / 12 秒各重放一次 —— 读出
StatsManager.dictionaryOfDictionaries中每个玩家的每个playerUpgrade*数值,调用游戏自带的PunManager.Update*RightAway(steamID, value)让游戏按原生逻辑应用(这些方法内部会自行判断是否适用于本地玩家,对所有人都安全)。
开关:[玩家属性] ApplyUpgradesOnJoin(默认开)。预期日志:[属性应用] 加入后 3 秒:已按存档重放 N 项升级属性。
说明
- 只调用游戏自带方法,不自己写属性值;不涉及关卡 / 墙 / 门 / 通路。
[0.4.9] - 2026-10-18
对照参考实现后的补充
本版据此补上两处:
- 跳过加入者自己的 avatar(不再把本机已有的值推回去,避免污染)。
- 同步死亡状态:对
deadSet == true的玩家发送PlayerAvatar.PlayerDeathRPC(-1)(与参考实现一致);死亡玩家不再推血量。日志形如属性同步:血量 N 个,死亡 M 个,外观颜色 K 个 → 目标 #…。
说明
- 保留 0.4.8 的
healthSet门槛:只在房主侧血量已初始化后才推,避免推默认 100。
[0.4.8] - 2026-10-18
修复(关键)
- 不再推送未就绪的血量(垃圾值):0.4.7 的日志证据 —— 房主自身的真实血上限是 1300,但它推给加入者的却是 100/100(连自己都是 100)。原因是推送时机早于游戏从存档初始化血量(
PlayerHealth.healthSet == false时值仍是默认 100)。现在只在healthSet == true时才推,否则跳过等待后续推送(0~30 秒共 11 次)。加入者因此不会先看到错误的 100,而是直接等到真值。 [属性快照]增强:现在输出<名字> 血量/上限(set|unset) 色[颜色索引…],可同时观察血量是否已初始化、以及外观颜色是否到达。
说明
- 加入者自己的血量(日志中的
100/100)本来就是真值,不是同步失败。
[0.4.7] - 2026-10-18
修复 / 诊断
- 客机落地后自报血量:
BroadcastOwnHealth(加入后 12 秒 / 25 秒)把自己的真实health/maxHealth广播给其他玩家(UpdateHealthRPC允许 owner 发送)。这样房主那边的副本就是正确的,房主再推给新加入者时不会推错值 —— 针对「某一位做房主时同步不到」的方向性问题。 - 房主推送日志带上具体数值:
属性同步:… → 目标 #N '名字';推送值 PlayerAvatar=100/100; PlayerAvatar=180/180;—— 可直接看出房主手里的副本是否已经正确。
已验证
- 0.4.6 的「推送提前 + 加密」生效:客户机日志
[属性快照] 加入后 12 秒 → 绊倒铁盒 180/180(0.4.4 时 12 秒还是 100/100,25 秒才对)。
[0.4.6] - 2026-10-18
修复
- 属性推送改为「一加入就开始」+ 更密(0/2/4/6/8/10/13/16/20/25/30 秒):此前推送协程挂在加入管线末尾,前面还有「补建尸体对象 / 刷新老玩家外观语音」等步骤,可能拖十几秒才开始推 —— 这就是 0.4.4 快照里「12 秒仍是 100/100、25 秒才对」的原因。现在在管线第一行就启动推送,并把窗口从 5 次加密到 11 次,让属性尽早落地。
说明
- 只改推送时机与频率,不新增任何行为;仍只推房主的真实数据、只发给该加入者。
[0.4.5] - 2026-10-18
修复
- 属性推送改为 30 秒内多次(0 / 4 / 10 / 20 / 30 秒):实测(0.4.4 的
[属性快照])显示,加入者落地时PlayerHealth/PlayerCosmetics可能尚未就绪,过早发出的 RPC 会丢失 —— 快照里 12 秒时仍是100/100,25 秒才变成真实的180/180。现在把推送摊开,尽早落地。只在前两次和最后一次打 Info,其余走 Verbose。
已确认的事实(0.4.4 诊断)
[玩家视图] 房间 2 人,本机 avatar 2 个,一致→ 玩家视图完整,不是 avatar 缺失。[属性快照]12 秒100/100→ 25 秒180/180→ 属性最终确实同步到位,此前"同步不了"很可能是看得太早(15~20 秒内才会到位)。
[0.4.4] - 2026-10-18
修复
[属性快照]/[玩家视图]诊断改为必然触发:原先从ClientLateJoinFixesCoroutine启动,但该流程在实际会话中会被跳过(对方 0.4.3 的日志里快捷栏修复有、两条诊断没有,证实未启动)。现在改为从LevelGenerator.Start后置钩子(每关必跑,客机专属)启动,并在 12 秒 / 25 秒各打一次快照,短会话也能拿到。
已确认的事实(本轮日志)
- 房主端推送目标正确:
属性同步:血量 2 个,外观颜色 2 个 → 目标 #2 '绊倒铁盒'。—— 不是"发给不存在的人"。 - 因此方向性同步失败的原因只剩两种:接收端(组件就绪/RPC 被拒)或数值其实已一致。
[属性快照]会给出答案。
[0.4.3] - 2026-10-18
新增(只读诊断,无行为改动)
- 房主端推送目标:
属性同步:血量 N 个,外观颜色 M 个 → 目标 #<actor> '<名字>'。—— 用于确认推送对象是否正确。 - 加入者端属性快照:落地 12 秒后打印
[属性快照] 本机可见 N 人 → 名字 血量/上限;…—— 打印本机实际看到的数值。
为什么加这两条
- 实测存在方向性差异:某位玩家做房主时同步正常,另一位做房主时同步不到。
- 两端对照即可定位:房主显示已推送、加入者数值不对 → 问题在传输/接收;数值一致 → 同步其实正常(属于观感或其它模组干扰)。
[0.4.2] - 2026-10-18
修复
[玩家视图]诊断移到独立协程:原先挂在ClientLateJoinFixesCoroutine里,而该协程前面有「等协同就绪 1 秒」的窗口,可能提前yield break导致诊断不输出。现在改为加入后 12 秒必跑(WaitForSecondsRealtime),不再依赖协同窗口。
说明
- 纯诊断位置调整,无行为改动(0.4.1 的
OwnerOnlyRPC兜底已实测生效:日志出现已拦截 SemiFunc.OwnerOnlyRPC 的异常并放行)。
[0.4.1] - 2026-10-18
修复 / 诊断
SemiFunc.OwnerOnlyRPC的 NRE 兜底:当对象没有 owner 时,游戏自己的OwnerOnlyRPC会在内部抛NullReferenceException(日志中表现为DMD<SemiFunc::OwnerOnlyRPC>←PhysGrabObject.GrabLinkRPC/MapToolController.SetActiveRPC),导致该次抓取链接或地图工具动作失败。现在加了 Finalizer:捕获后放行并只记一次日志。- 新增只读诊断
[玩家视图]:加入修复完成后,比对「房间人数」与「本机可见的PlayerAvatar数量」;若本机 avatar 少于房间人数,会打印一行提示 —— 用于确认「属性同步不了」是否其实是玩家视图缺失(此前日志中的Received RPC "HealOtherRPC" … but this PhotonView does not exist!即属此类)。
说明
- 两项均不改变关卡 / 墙 / 门 / 通路状态;
OwnerOnlyRPC兜底是唯一的行为改动(放行原本会被拒的 RPC)。
[0.4.0] - 2026-10-18
新增
- 外观颜色同步:房主在加入者落地后(并 4 秒后补一次),把每个玩家的
colorsEquipped通过游戏自带的SetupColorsRPC(该 RPC 仅房主可发)推给该加入者 —— 修复加入者看不到别人颜色(日志中PlayerCosmetics: No colors equipped after 10 seconds的表现)。开关PlayerCosmeticsSync(默认开)。 - 血量与颜色两个开关互相独立(只关一个不影响另一个)。
说明
- 游戏内可同步的玩家属性已全部覆盖:血量 / 血上限、外观颜色。
- 升级属性(速度 / 力量 / 体力 / 攀爬 / 翅膀 / 电池 / 地图人数 / 蹲伏恢复)在游戏里没有任何 PunRPC,是各客户端按本地存档计算的,其他玩家本来就看不到 → 不存在「未同步」问题。
- 装扮(帽子等) 暂未推送:其 RPC 需要
int[],本地字段是List,需转换后另版处理。
[0.3.9] - 2026-10-18
修复
- 新加入玩家的血量 / 生命上限不同步:中途加入者看不到其他玩家的血量与上限。原因是
PlayerHealth.playerAvatar只在Awake中赋值,血量同步依赖UpdateHealthRPC广播,而加入者进来时没有拿到当前值。现在房主在加入者落地后(并 4 秒后补一次)把场上每个玩家的health/maxHealth通过游戏自带的UpdateHealthRPC推给该加入者。开关PlayerHealthSync(默认开)。
说明
- 只推送房主的真实数据,不修改任何本地状态;不涉及关卡 / 墙 / 门 / 通路。
[0.3.8] - 2026-10-18
新增
- 检测房主是否为 LateJoinNow(Zichen 原版):当房主未运行本模组、但其玩家属性里带有
LateJoinNow.Version时,打一行明确提示 —— 两套模组房间属性键不同(LateJoinNow.*vsLateJoinSimple.*),协议不通,等效单端。只读检测 + 日志,不改变任何行为。
文档
- README 的「已知问题」补充混合模式:房主用 LateJoinNow、加入者用本模组时,日志会出现
Received RPC … but this …、KeyNotFoundException等,属预期;建议双方统一使用同一个模组。
[0.3.7] - 2026-10-18
修复 / 说明
- 确认
Level Point: Not connected属游戏自身提示,与可见外观无关:协同模式实测中,该提示出现 1 / 15 / 31 条时均未观察到虚空墙、门框没门或通路堵塞,已在 README 的「其它」中注明可忽略。 [光照守卫]日志修正:异常消息为空时改为打印异常类型名(此前会输出一个空冒号)。
说明
- 无行为改动:不涉及关卡 / 墙 / 门 / 通路状态。
[0.3.6] - 2026-10-18
变更(实现清理,无功能新增)
- 移除四段实验性「虚空修复」代码(
LevelPointRelink、RoomConnectionRebuild、WallDoorRefresh、NavMeshRebuildOnJoin)及其配套诊断与 Hook: 这些项在实测配置中一直是关闭状态,而关闭状态下虚空问题未再出现,因此删除以简化实现。旧 cfg 里残留的这四个键会被忽略。 - 快捷栏修复改为独立实现(行为不变):先用「槽位自己上报的索引」建立已登记集合,再把场景里未登记的槽位按索引升序补注册。
- 保留并沿用:模块连接「只发一次」+ 只补发未发送模块、游戏侧 NRE 守卫、配置版本检测非破坏性。
说明
- 移除的均为默认关闭的实验项;
FixInventorySlots、ModuleResendSeconds保持默认生效。
[0.3.5] - 2026-10-18
修复
[墙体诊断]口径改正:原先「有连接标志但未连通」把「模块任一方向有连接」和「该开关是否连通」混在一起比,会产生大量假阳性(连接在 Top、而这个开关负责 Right 时也会被算进去)。现在按每个开关自己那一侧与对应连接标志比对,只统计确实不一致的个数。- 配置版本检测改为非破坏性:旧实现会
config.Clear()+ 删除 cfg +Reload(),而Reload在文件已被删除时会抛Could not find file ...cfg—— 结果既清空了用户设置,又打出「重置配置失败」的误报。现在只提示版本变化并保留设置,随后把新版本号写回配置项。
说明
- 状态更新(据 0.3.4 / 0.3.5 双端实测与日志):
- ✅ 「虚空门 / 缺墙 / 门框里没有门」已不再出现 ——
Level Point: Not connected由 31 降到 1(仅剩入口点Level Path Point)、Ev Destroy Failed由 122 归零、RaiseEvent(202) failed由 4 归零;墙/门开关与本侧连接标志不符为 0。 - ✅ 快捷栏限制已修复(
FixInventorySlots补注册InventorySpot)。
- ✅ 「虚空门 / 缺墙 / 门框里没有门」已不再出现 ——
- 已知未处理:新加入者的生命 / 属性同步(血量 / 生命上限显示不同步)—— 详见 README 的「已知问题」。
- 本版不涉及任何关卡 / 墙 / 门 / 通路状态改动;四个虚空实验项仍为默认关。
- README 更新:补充「生命 / 属性同步」已知问题、测试致谢(排排坐 食果果、Sunshine°),并更正「版本更新会重置配置」的旧说法(0.3.5 起改为保留设置)。
[0.3.4] - 2026-10-17
修复
- 同一模块的连接同步不再重复发送:
Module.ModuleConnectionSetRPC会自增LevelGenerator.ModulesSpawned(已用 IL 核实),而该计数被关卡生成协程读取;原先的后台兜底会对同一加入者把整个模块表重发。现在按加入者记录「已发过的模块」,每个模块只发一次(日志会显示「已发过 N」)。 - 后台只补发「尚未发过」的模块(最多 60 秒、每 5 秒检查一次;开关
ModuleResendSeconds,0 = 关闭):解决「模块在首次同步之后才变为 SetupDone,于是永远没被发出去」的情况 —— 走房主的真实数据,不做任何客户端推算。
新增(只读诊断,不修改任何状态)
[墙体诊断] 模块 N 个:未收到连接数据 X 个;墙/门开关 K 个:未绑定模块 Y 个、有连接标志但未连通 Z 个。每关一次,用于判定「缺墙 / 门框没门」到底是没拿到连接数据还是拿到了但开关状态没更新。
说明
- 四个虚空实验项(
LevelPointRelink、WallDoorRefresh、RoomConnectionRebuild、NavMeshRebuildOnJoin)仍为默认关;FixInventorySlots(快捷栏修复)保持默认开。
[0.3.3] - 2026-10-17
修复
- 快捷栏限制(加入者无法把道具放进快捷栏):中途加入时
Inventory的槽位列表可能留下空位(场景里的InventorySpot没被注册进Inventory),导致GetSpotByIndex返回 null。现在在关卡生成完成后(并重试 3 次)把所有场景里的InventorySpot补注册回去。开关FixInventorySlots(默认开)。
说明
- 「缺墙 / 门框里没有门」仍未修复:相关实验项(
LevelPointRelink、WallDoorRefresh、RoomConnectionRebuild、NavMeshRebuildOnJoin)继续保持默认关。
[0.3.2] - 2026-10-16
修复
- 纠正上一版会把「本来能通行的门 / 通路封死」的问题:墙 / 门道具重算(
WallDoorRefresh)原先无条件执行,会把已经正确的开关翻到另一侧;现在只在房主下发的连接确实发生变化时才重算,连接未变则一个开关都不碰。 - 实验开关默认值统一为关:
WallDoorRefresh、LevelPointRelink、RoomConnectionRebuild、NavMeshRebuildOnJoin全部默认关 —— 默认状态下本模组不会改动任何墙 / 门 / 通路状态。 - 内嵌版本号与 manifest 对齐:此前 DLL 内仍报 0.3.0,与 manifest 不一致。
说明
- 0.3.0 条目里写的「默认开」以本版为准,现在都是默认关。
- 想试验修复的,请一次只开一个开关(
WallDoorRefresh或LevelPointRelink),逐关观察;发现异常立刻改回false。
[0.3.1] - 2026-10-16
- 仅修正
manifest.json的版本号;本版未同步更新 README / CHANGELOG,包内 DLL 内嵌版本仍为 0.3.0(0.3.2 已统一)。
[0.3.0] - 2026-10-16
新增
- 虚空修复(客机侧,需双端都装)
NavMeshRebuildOnJoin(默认开):中途加入者落地后调用游戏公开的LevelGenerator.NavMeshSetup()本地重建导航网格,改善寻路与卡虚空。RoomConnectionRebuild(默认关,实验):对房主从未下发连接数据的房间(SetupDone == false),按关卡网格布局推算Top/Bottom/Right/Left/First,再调用游戏自身的ModuleConnectionSetRPC把墙 / 门建出来;房主数据到达后以房主为准。 实现说明:该 RPC 带 master-only 校验,这里构造一条「来自房主」的本地PhotonMessageInfo只为让校验通过 —— 纯本地调用,不发网络包,也不改房主的权威数据。若出现墙体异常请关闭此项。
修复(内部审计发现)
- 协同模式下的地图兜底重发:原先该分支只打印「协同模式未移植」,实际是无条件重发;现改为按对端上报的 Module 数判断 —— 已齐则跳过,不足才补发一次。
SlowProjectile销毁:补上「先转让所有权再PhotonNetwork.Destroy」(原先注释称委托未移植,实际该委托已移植却未使用)。- 171 事件处理器加固:房主为 null 时不再抛异常,也不会误报为「撤离点锁定同步失败」。
- 语音链接自动重试:原版在本地找不到对应语音视图时会直接 NRE,因此只能拦截并等待对方重发;现改为自己起协程重试(最多 2 秒),视图一出现就用游戏自身的
UpdateMyPlayerVoiceChat补调一次,不再依赖重发。 - 【关键】墙体 / 门道具刷新 —— 「缺墙 / 门框里没有门」的根本修复:
ModulePropSwitch.Setup()依据Module.ConnectingTop/Right/Bottom/Left决定墙与门的两种形态(用SetActive切换两组父子物体,并非销毁)。中途加入者这边,这些道具在房主连接数据到达之前就已按默认(错误)连接处理过,所以必须在数据到达后重算。现在对每个道具还原方向 → 复位Connected→ 重跑Setup()。开关WallDoorRefresh(默认关,实验):只在连接确实发生变化时才重算;连接未变时绝不动任何开关 —— 无条件重算会把本来正确的墙/门翻到另一侧,可能封死本该通行的路。 - 【关键】关卡连接点重链接 —— 「虚空墙 / 虚空房间」的根本修复:
LevelPoint之间的链接(LevelPoint.Start→ModuleConnectSetup协程)只在关卡加载时跑一次、之后永不重跑,而中途加入者的关卡生成早于房主的连接数据 → 模块交界处缺件。现在在LevelPoint.Start之后按几何补链接(距离足够近 + 双方朝向相对 + 对方正对我),判据不依赖房主数据,客机可自愈。开关LevelPointRelink(默认关,实验):按几何补链接可能给本该不通行的位置补上连接,请逐关观察后再决定是否常开。 - 修正「虚空修复」自身的两个问题(据实测日志,重要):
- 之前无条件在每关开头调用
LevelGenerator.NavMeshSetup();实测其内部是PhotonView.RPC,会向整个房间广播,强制所有人重建导航网格并触发导航网格校验(即打印Module Name / Local Position那组报错的东西)。现改为:只在本地、且仅在本机确实补建过连接之后,通过私有的NavMeshSetupRPC重建一次,绝不广播;开关默认改为关。 - 之前把「房主引用为空」也算作缺少反射目标,而关卡刚生成时
PhotonNetwork.MasterClient尚未就绪 → 直接跳过并把该关标记为已处理 → 导致补建连接一次都没执行。现改为:先等到「关卡生成 + 已在房间内 + 房主引用就绪」再执行;条件不满足则不标记,下次触发重试。
- 之前无条件在每关开头调用
- 新增虚空诊断日志:房主连接数据到达时对比本地原值,不一致会打印
虚空诊断:本地连接与房主不一致,已按房主修正:<模块名>—— 用于确认「虚空」是否属于本地连接错误。 - 中途加入常见的游戏侧 NRE 守卫(据实测日志新增 6 处):
PlayerHealth.Update:头像引用为空时跳过该帧(菜单头像则补上本地头像引用)—— 实测日志中这一条每帧刷了 3255 次 NRE。EnvironmentDirector.AmbientLightLogic/FogLogic:本地头像尚未就绪时跳过(原来只守了前者,FogLogic仍刷了 9 条)。PlayerDeathHead.UpdateParticleColors:吞掉「头部刚生成、材质还没到」的异常,保持默认粒子色。EnemyOnScreen.GetOnScreen:执行原逻辑前先登记该玩家的 ViewID。LightManager.HandleLightActivation/HandleEmissionActivation:吞掉异常,避免该区域剩余灯光不亮。
- 协同模式兜底重发误判修复:原先用「进入关卡时抓到的模块数」判断对端是否已齐,关卡还在生成时会误判为已齐而跳过重发(缺的墙就补不上了)。现改为用当前模块数判断,且重发时用最新的模块列表。
- 房间可见性日志细化:原先只报「补写 N 个,跳过 M 个」,现在区分「已是探索态 / 位置未匹配 / 无地图模块」,便于判断是命中还是匹配失败。同时说明:该功能修的是游戏内地图的房间揭示,与「缺墙 / 虚空墙」不是同一条线。
- README 配置表补上「模组版本号」只读项。
[0.2.0] - 2026-10-16
新增
- 房间 / 地图可见性同步(需房主与加入者都装):房主把本局
RoomVolume中已探索的房间位置发给中途加入者(加入后 3 秒 / 9 秒各一次),加入者按位置就近匹配后调用游戏自带的RoomVolume.SetExplored()补写,避免加入者看到空白 / 全黑房间。 实现为自研:复用撤离点锁的 171 事件码(用载荷标签LJS.Rooms区分),不新增事件码,不反射私有字段写入(只反射读取Explored判断哪些房间已探索)。 - 光照守卫:给
LightManager.Update/LogicUpdate/Setup与EnvironmentDirector.AmbientLightLogic加 Harmony Finalizer,吞掉异常让同一帧剩余的光照逻辑继续执行(首次记录 Info,之后记 Verbose)。
说明
- 房间可见性同步与外观 / 语音刷新一样,只在双端都装本模组(协同模式)时生效;单端模式(加入者为原版)仍受原版底层限制。
[0.1.1] - 2026-10-16
修复
- 修复冲突检测漏检(双装同类模组时不弹冲突警告):原先只在自己的
Awake里扫描一次Chainloader.PluginInfos,而加载顺序排在后面的同类模组此时还没登记进去 —— 实测表现为「Zichen 的 LateJoinNow 能检测到本模组,本模组却检测不到它」,所以不弹窗。现在改为启动时 → 延迟 5 秒 → 进入房间时各检测一次,并输出「扫描 N 个插件、命中 M 个」日志便于确认;冲突弹窗等待菜单的时间由 15 秒放宽到 60 秒,超时会留下日志。
[0.1.0] - 2026-10-16
首个公开测试版:基于Zichen的 LateJoinNow 1.1.0,在其授权下接手修复与重新发布。
新增
- 中途加入完整流程:场景判定 → 排队调度 → 地图模块 / 宝物 / 物品属性 / 22 项场景状态同步 → 落地动画补发 → 虚空兜底重发 → 移动检测。
- 房间信息 HUD(房间类型 / 名称 / 人数,字号与透明度可调)与中途加入进度显示。
- 公开房名自动前缀、切关冷却锁房、双端协同优化、冲突检测与弹窗、运行时兼容性自检。
修复
- 修复原版
PhysGrabber.Update空值守卫 Transpiler 完全失效:原版用AccessTools.PropertyGetter(..., "instance")去查一个字段(必然返回 null),且匹配OpCodes.Call而实际 IL 是ldsfld,该补丁从未生效。现改为AccessTools.Field+ldsfld匹配,对 4 处调用点生效。 - 不再销毁带铰链的门:原版在「强制 spawned」流程中对所有带
PhysGrabHinge的对象执行Destroy,会导致加入者看到「只有门框、门没了」。现改为仅记录、不销毁。 - 冲突检测改为 AND 匹配:原版只要名称含
late或join任一即报警,误报率高;现要求同时包含,并修正了自身识别。 - 协同模式下等待加入者就绪的超时由 8 秒放宽到 15 秒。
变更
- 移除双语配置面板与 I18N 机制。
- 插件更名为
LateJoinSimple(GUIDcn_xc.LateJoinSimple),房间属性键改为LateJoinSimple.*;与原版 LateJoinNow 不兼容,不可同时启用。 - 房间名前缀改为
[中途加入-小菜]。 - 目标框架 .NET Framework 4.7.2,BepInEx 5.4。