You are viewing a potentially older version of this package. View all versions.
cn_xc-LateJoinSimple-0.7.10 icon

LateJoinSimple

中途加入:让任何人在卡车 / 商店 / 关卡进行中随时加入你的房间。基于 Zichen 的 LateJoinNow,在其授权下接手修复「本版移除内置共享升级等新增功能,回到 0.6.1 稳定基线」、电池电量同步、撤离点锁定、死亡玩家尸体位置、虚空/模块同步修复、LateJoinNow 兼容。模组问题交流QQ群573485890。

By cn_xc
Date uploaded 2 weeks ago
Version 0.7.10
Download link cn_xc-LateJoinSimple-0.7.10.zip
Downloads 516
Dependency string cn_xc-LateJoinSimple-0.7.10

This mod requires the following mods to function

BepInEx-BepInExPack-5.4.2100 icon
BepInEx-BepInExPack

BepInEx pack for Mono Unity games. Preconfigured and ready to use.

Preferred version: 5.4.2100

README

中途加入 Simple / LateJoinSimple

REPO 游戏交流 QQ 2群:573485890

感谢各位玩家长期以来的使用与支持。本模组的所有优化与修复,均基于本人随机加入房间、逐局查阅运行日志的实测排查;由于联机环境复杂,仍可能存在未覆盖的情况,欢迎反馈以便持续改进。


🆕 最新更新(v0.7.10)

本版新增修复

  • 大房间「有路却是墙 / 门下方过不去」:游戏对大房间(Mega 等)用的是另一套连接 RPC (9 个参数,比普通模块多 8 个「对角连接」)。此前只发了 5 参版本, 导致这些房间的对角连接从未同步 → 门口/通道被判为封闭。 现在按模块自动选择正确的 RPC(Mega 或任一对角连接为 true → 发 9 参), 并去掉了补发重试里对 SetupDone 的过滤、把等待上限放宽到 15 秒。
  • ⚠️ 修复在房主侧:房主与加入者都需 0.7.10。

⚠️ 已知未解决:死亡玩家的尸体位置 / 头部交互 / 死亡语音; 新大房间里的门(PhysGrabHinge)状态尚未完全同步(计划 0.8.0 用自定义事件解决)。 ⚠️ 已知冲突:经群友实测,与 https://thunderstore.io/c/repo/p/RicketyWrecked/REPO_Fixes_Antispam_Latency_Fix/
https://thunderstore.io/c/repo/p/RicketyWrecked/LateJoinPlus/
https://thunderstore.io/c/repo/p/sunwu/LateJoin/ 同时使用会(有墙 / 门全开/虚空房间等等诸多异常),请在使用本模组不要启用。

📜 版权声明与使用条款

应原作者要求,现补充说明如下。

Supplemented at the original author's request.

一、作品来源

本项目为衍生作品(derivative work):

  • 上游原作:中途立即加入 / LateJoinNow,作者 Zicheh,版本 1.1.1。
  • 本作品: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.

三、未经书面许可明确禁止

  1. 反编译、反汇编、逆向工程、脱壳,或提取本作品新增/修改部分的实现逻辑;
  2. 复制、改编、翻译、演绎,或基于本作品创作衍生作品;
  3. 二次打包、重新分发、上架第三方平台,或用于商业用途;
  4. 移除或篡改版权标识、原作者声明与本条款。

四、法律依据与后果

本作品作为计算机程序受以下法律与国际条约保护:

  • 国内法:《中华人民共和国著作权法》《计算机软件保护条例》。
  • 国际条约:本作品在下列条约的缔约方内均受保护(中国、美国、欧盟均为缔约方):
    • 《伯尔尼保护文学和艺术作品公约》:国民待遇、复制权、发行权、公开传播权; 需说明的是,伯尔尼 1971 年巴黎文本未直接列出计算机程序 —— “计算机程序按文字作品保护”系由 WCT 第 4 条(伯尔尼公约的特别协议) 与 TRIPS 第 10 条第 1 款予以明确。
    • 《与贸易有关的知识产权协定》(TRIPS):确认计算机程序按文字作品保护, 并确立“保护表达、不保护思想、程序、操作方法或数学概念”(第 9 条第 2 款)。
    • 《世界知识产权组织版权条约》(WCT):明确计算机程序(含目标代码形态) 作为文字作品受保护,并赋予发行权与向公众传播权。
  • 责任与救济:对上述禁止行为,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.1 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

  1. Decompiling, disassembling, reverse engineering, unpacking, or extracting the implementation of the portions added or modified here;
  2. Copying, adapting, translating or creating derivative works;
  3. Repacking, redistributing, publishing on third-party platforms, or any commercial use;
  4. 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 by the Copyright Law of the People's Republic of China and the Regulations on Computer Software Protection, and — in the parties to each treaty — by the following international treaties:

  • Berne Convention: national treatment, reproduction, distribution and communication to the public. Note: the 1971 Paris Act does not itself mention computer programs; the rule that programs are protected as literary works is set out in WCT Article 4 and TRIPS Article 10(1).
  • TRIPS: confirms protection of programs as literary works and that copyright protects expression, not ideas, procedures, methods or mathematical concepts (Article 9(2)).
  • WIPO Copyright Treaty (WCT): programs (including object-code form) are protected as literary works, with a right of distribution and a right of communication to the public.

The rights holders (cn_xc and the upstream rights holder) may seek injunctive relief and removal of infringing copies under Article 52 of the PRC Copyright Law and damages under Article 54 (Article 53 concerns administrative handling and referral for prosecution). Under Article 217 of the Criminal Law of the PRC, infringement is criminal where the illegal gains are relatively large or other serious circumstances exist (intent to profit is no longer required). These treaties are implemented through domestic law; enforcement is governed by the law and courts of the relevant jurisdiction.

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.


✨ 功能

  • 随时中途加入:朋友可以在卡车 / 商店 / 关卡进行中加入你的房间,三个场景可分别用配置控制。
  • 房主权威:场景同步全部由房主执行,加入者只接收,避免状态污染。
  • 同步内容:地图模块(墙体 / 内饰连接)、贵重物品价格与发现状态、物品属性,以及 22 项场景状态(撤离点、门禁、升级台、各类陷阱与宝物等)。
  • 房间信息 HUD(房主与加入者都可见):左下角显示房间类型(含「可加入 / 已锁定」)、房间名称、当前人数、全部玩家名单 (房主第一 → 作者 → 其余按编号;每行 3~6 人可配)。可在配置里整体关闭(显示HUD,默认开启)。
  • 身份标记:作者显示黄色(模组作者)、房主显示红色(房主); 模组测试员(指定 SteamID)显示为紫色并追加(模组测试员) —— 仅改颜色与标记,不改变名单顺序。 但本模组修掉的那些问题(升级属性叠加、尸体位置、撤离点锁定、模块补发、电池电量等)只在房主也使用本模组时才能完整生效 —— LateJoinNow 1.1.1 只做了 Unity 6 适配,未包含这些修复。
    • 中途加入时按房主的升级等级应用一次(会持续等待房主数据就绪,最多 60 秒)。
    • 实时共享只在「升级等级发生变化」时生效(有人买升级 → 全队立刻同步,走游戏原生 RPC,客户端无需安装本模组)。
    • ⚠️ 如果某次广播丢了,不会自动补发(游戏的升级接口是累加式的,重发会翻倍)→ 建议退出重进让"加入时同步"兜底。
    • ⚠️ 可能还有别的问题:本功能仍在观察期,如遇异常请反馈日志。
    • ⚠️ 不要与 SharedUpgradesPlus / BetterTeamUpgrades 或者其他模组的共享功能同时开启,否则会双重应用。
  • 兼容模式(对接 LateJoinNow):房主仍在用 LateJoinNow 时,本模组按它的协议对接,让对端把本机视为可协同客户端;HUD 上方会显示 兼容模式。
  • HUD 工作模式指示:玩家名单上方显示 协同模式 / 兼容模式 / 单端模式:房主 vX / 本机 vY,一眼看出当前状态。
  • 与房主的计数对比: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 秒后打印本机看到的每个玩家血量/上限实际值 —— 两端对照即可判定属性是否真的收到。

📦 安装

  1. 确保已安装 BepInExPack。
  2. 将 LateJoinSimple.dll 放入 BepInEx/plugins。
  3. 启动游戏,模组会自动生成配置文件。

强烈建议:房主与加入者都装本模组(协同模式),可避免单端模式的已知问题(见下)。


⚙️ 配置

配置文件:BepInEx/config/cn_xc.LateJoinSimple.cfg(改完重启游戏生效)。本版配置项已全部中文化。

节 配置项 默认 说明
全局设置 传送到房主 false 新加入者直接传送到房主位置(房主死亡则传送到最近的活人)
全局设置 协同模式 true 双端「版本号 + 协议号」一致才启用协同补丁,否则休眠为单端;房主是 LateJoinNow 时走「兼容模式」
场景白名单 卡车阶段允许加入 true 卡车阶段是否允许中途加入
场景白名单 商店阶段允许加入 true 商店阶段是否允许中途加入
场景白名单 关卡中允许加入 true 关卡进行中是否允许中途加入
房间信息显示 显示房间类型 true HUD 显示公开 / 私人 / 单人
房间信息显示 显示房间名称 true HUD 显示房间名
房间信息显示 显示HUD true 左下角信息框总开关(关闭后完全不显示)
房间信息显示 HUD字号 16 10~28
房间信息显示 HUD透明度 80 20~100
房间信息显示 每行显示人数 5 3~6
虚空修复 快捷栏修复 true 补注册 InventorySpot 槽位,修复加入者无法把道具放进快捷栏
虚空修复 模块补发秒数 60 后台只补发该加入者还没收到过的模块,最多持续这么多秒(0 = 关闭)
虚空修复 血量同步 true 房主把每个玩家的血量 / 上限推给加入者
虚空修复 外观颜色同步 true 房主把每个玩家的外观颜色推给加入者
玩家属性 加入时重放升级 true 按存档重放全部升级属性(跳跃 / 体力 / 冲刺 / 力量 / 血上限等)
调试 详细调试日志 false 输出 Verbose 级日志(排查问题用)
调试 冲突检测弹窗 true 检测到同类中途加入模组时弹警告

⚠️ 键名从英文改成中文后,旧 cfg 里的英文键会被忽略(设置回到默认值)。如需保留,请手动对应改名。

如遇异常,请附日志反馈。

⚠️ 已知问题

🔧 已知冲突

  • repo-fixes-antispam-latency-fix:实测同时开启时会导致房间/通道碰撞异常(「有路却是墙」或「门全开」)。 关闭该模组后恢复正常,请二选一。

🔧 已知未解决(正在排查)

  • 中途加入时,若已有玩家死亡:

    1. 死亡玩家的尸体/头可能出现在卡车门口(而不是死亡地点);
    2. 加入者无法抓取/交互那个头;
    3. 加入者可能听到只有死亡玩家才该听到的死亡语音。

    说明:这是已知问题,正在排查(与死亡状态同步相关)。目前不影响中途加入的其他功能 (物品 / 属性 / 撤离点 / 电池同步均正常)。遇到时请附双方日志反馈,便于定位。

已修复(0.3.x)

  • ✅ 虚空门 / 缺墙 / 门框里没有门 —— 已修复。0.3.4 之后的双端实测与日志中未再出现(Level Point: Not connected 从 31 归到 1 且仅剩入口点、Ev Destroy Failed 从 122 归零)。
  • ✅ 快捷栏限制(加入者无法把武器放进快捷栏)—— 已修复(FixInventorySlots:关卡生成后把场景里的 InventorySpot 补注册进 Inventory)。

以上修复都需要加入者也安装本模组(协同模式)。加入者是原版客户端时,本模组无法修正其表现。

混合模式 / 单端模式仍可能存在的问题(本模组无法修正)

以下两种情况本模组都无法修正 —— 「客户端协同补丁」需要房主也运行本模组才会启用:

  1. 房主没装任何中途加入模组,只有加入者装了本模组。
  2. 房主装的是 LateJoinNow(Zichen 原版),加入者装的是本模组 —— 两边房间属性键不同(LateJoinNow.* vs LateJoinSimple.*),协议不通,等效单端。本模组检测到这种房主时会打一行明确提示。

表现(属预期,非 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:可以。房主用 LateJoinNow 时,本模组会以「兼容模式」对接;但要注意两点:① 两边房间属性键不同,不会互相污染;② 本模组自己的修复(属性叠加、撤离点、电池等)在兼容模式下不生效,要完整体验请让房主也改用本模组。

Q:怎么判断当前是不是双端协同? A:看 HUD 玩家名单上方:显示 协同模式 = 双端同版生效;显示 单端模式:房主 vX / 本机 vY = 两端不一致(功能性修复不生效)。

Q:改了配置没生效? A:配置在启动时读取,改完请重启游戏。版本更新不会清空你的设置(0.3.5 起);若要还原默认,删除 BepInEx/config/cn_xc.LateJoinSimple.cfg 后重启即可。


🙏 致谢

  • ( Zichen) —— 原作「中途立即加入 / LateJoinNow」的作者。本模组的全部核心逻辑、场景同步思路与踩坑经验均来自其 1.1.0 版本,并在其授权下继续维护。
  • 本 README 中的功能说明与「已知局限」记录同样来自原作文档。
  • 测试协助:感谢所有帮忙做双端联机测试与日志反馈的玩家。

QQ群:573485890

CHANGELOG

[0.9.1]

变更

  • 优化日志输出逻辑

[0.9.0]

变更

  • 「商店外区域」拆分为独立模组 「允许走出商店 / 商店墙上开口 / 给商店外补地面碰撞」全部移出本模组, 改由新模组 商店外区域(cn_xc.ShopOutsideSimple 0.1.0) 提供。

    • 只想要稳定中途加入 → 只装本模组,商店完全不受影响;
    • 想要走出商店 → 额外装「商店外区域」;
    • HUD 里的「商店外补碰撞:N」也一并移出。
  • 新增 [同步精简] 最小可用模式(默认关闭) 开启后只补「模块连接」,跳过宝物 / 物品 / 状态 / 血量 / 撤离点 / 电池 / 外观同步。 适合「客机大多是原版客户端」的场合,最稳定。

  • 新增 [同步精简] 补发撤离点(默认关闭) 开启后房主会向中途加入者补发撤离点激活状态;撤离点锁定同步(ExtractionPoint.UpdateLock)也受此项控制。 默认关闭的原因:实测不补也正常(在撤离点交易一下就会刷新),且补发在部分场景可能影响加入流程。

  • 商店阶段中途加入(含原版客户端)可用 商店没有 LevelGenerator,游戏自身不会给客机发 AllPlayerSpawnedRPC;改由房主主动补发该 RPC (守卫为 MasterOnlyRPC,房主发送能通过),客机收到后把 LevelGenerator.AllPlayersReady 置为 true 即可结束加载。 2026-11-07 多人实测:原版客户端也能在商店阶段加入。 [场景白名单] 商店阶段允许加入 默认 true。

  • 中途加入不再白等一个超时周期 实测确认:客机真正结束加载靠的是房主补发的 LoadingLevelAnimationCompletedRPC,而不是就绪 ack。 原实现把这条 RPC 放在「等 ack 超时之后」才发,于是每次加入都要白等一个超时周期 (8 秒 / 15 秒 / 30 秒三次实测,客机都是在超时之后才进来)。 现改为状态同步完成后立即补发,等待 ack 缩到 5 秒且只用于日志观测。加入耗时从数十秒降到数秒。

  • 「模块已齐」的判据改用游戏自己的连接计数 原先比较「场景里模块对象的数量」,而该值只在客机加入瞬间采一次快照、加载途中会失真, 可能让房主误判为「已齐」而跳过墙体兜底重发。 现在由客机上报 ModulesSpawned / ModulesSpawnedTarget,房主以「对端自己认为连接已收齐」为准; 对端是旧版本时自动退回原判据。

  • 自定义网络事件码迁移:171 → 174 / 175 171 与 LateJoinNow 等模组撞码。现拆成两个专用码: 174 = 房间可见性同步,175 = 撤离点锁定同步。 CoopProtocolNumber 同步升级为 2,不再兼容 0.8.1 及更早版本的协同协议 (旧版会被判为不兼容并自动降级为单端模式)。

默认值调整(代码保留,可自行开启)

  • [玩家属性] 加入时重放升级:默认 true → false 实测重放会让属性叠加(房主 200 HP 重放后变 600),且游戏原版本身会应用存档。
  • [虚空修复] 外观颜色同步:默认 true → false 该同步依赖 SetupCosmeticsModdedRPC,对方是原版客户端时该 RPC 不存在,日志会大量报错。

修复

  • ExtractionPoint.StateComplete 的异常吞噬补丁注册失败 上一版把 Finalizer 写在错误的类里,Harmony 找不到方法(日志「补丁注册完成(失败 1 项)」)。 已移到正确的类,补丁集 58 项全部注册成功。
  • FieldAccessException: PlayerAvatar:rb is inaccessible 每 2 秒刷屏 直接访问私有字段导致,已改为反射读取。
  • 「虚有的人」校正会误伤正常游玩 位置阈值 5m → 10m、翻滚判定 2m → 8m,只在加入后 120 秒内检查, 每次校正后 10 秒冷却;正常翻滚 / 被拎起不会再被强行拉回。
  • 草地/装饰仍会被补碰撞(已随商店外功能移出本模组)
  • 配置项可以填负数 HUD字号 / HUD透明度 / 每行显示人数 / 模块补发秒数 四项改用 AcceptableValueRange 限定范围, 读取处再钳制一次兜底;旧配置文件里的越界值会被自动修正。 配置项名称里的 (10~28) 之类范围后缀一并去掉(范围由滑条 / 输入限制体现)。
  • 有人中途加入时,全场玩家被传送回出生点(卡车一带) 根因:PlayerAvatar.Start() 在「房主 + 关卡已生成」时会调 LevelGenerator.PlayerSpawn(), 而原版 PlayerSpawn() 会遍历 GameDirector.PlayerList 给每一个玩家调 PlayerAvatar.Spawn(出生点)。 现在只在中途加入触发的那一帧内拦掉这次「挪位置」,原版其余部分 (创建 PlayerDeathHead / PlayerTumble、绑定名字与 steamID)照常执行。
  • 客机 avatar 有时不会被创建 LevelGenerator.PlayerSpawn 的前缀原先在多个分支返回 false(跳过原版方法), 当该玩家尚未进入中途加入表时,原版与模组都没有人执行出生,客机 avatar 就不存在。 现改为始终放行原版 PlayerSpawn。
  • 撤离点锁定事件可被任意玩家伪造 接收端增加「必须来自房主」校验,并对载荷做类型校验。
  • 客机侧的协同修复在正常中途加入时从未执行 PlayerAvatar.Start 的补丁要求 GameDirector.currentState == Main,而客机中途加入时该状态还是 Load / Start;且 PlayerAvatar.Start 一生只触发一次 —— 于是整套客机修复 (强制标记已生成、LoadingLevelAnimationCompleted、上报协同就绪)全部空转, 这也是之前「房主永远等不到就绪 ack」的根因。 现在增加「进入 Main 后补触发一次」的兜底,并用一次性标志避免重复触发。

兼容性

  • 已用游戏 v0.5.1 的 Assembly-CSharp.dll 重新编译验证:依赖 API 全部存在, 中途加入相关的 13 个关键方法中 12 个的 IL 与 v0.5.0 完全一致 (仅 LevelGenerator.PlayerSpawn 因官方修复房间问题有变化,我们的 Prefix 参数未受影响)。

补丁集

  • 共 59 项(0.6.1 基线 57 + 1 个 Finalizer + 1 个 PlayerAvatar.Spawn 位置拦截)。

[0.8.1]

修复

  • 修复 0.8.0 在商店场景无法中途加入的问题(加载流程恢复为与 0.7.10 一致)。

  • README 增加「关于更新节奏」说明,并记录群友反馈的待修问题清单(下个版本统一处理)。

[0.8.0]

新增:商店外区域(仅商店场景生效)

  • 允许走出商店(配置项 [商店外区域] 允许走出商店,默认开启),开启后:
    1. 在商店东侧墙上开口:把该区域的碰撞体关闭、可见面隐藏,形成一个能走出去的口子;
    2. 移除只挡玩家的隐形墙(PlayerOnlyCollision 层);
    3. 给商店外部补碰撞:商店外围的步道/地面模型本身没有碰撞。 4. 兜底地板:脚下 3 米内没有任何碰撞时,在脚底铺一块 20m×20m 临时地板,防止掉出世界。
  • 全部为本地效果(谁装谁生效,不影响别人);不在商店场景时完全不执行。
  • 商店场景是随机的,因此每 10 秒会重新扫描补做一次。

说明

  • 补丁集仍为 0.6.1 的 57 项,零增减。

  • 0.9.0 计划:门的同步(PhysGrabHinge)改用自定义 Photon 事件实现。

  • README 顶部新增 3 段演示动图(走出商店 / 中途加入 / 同步),放在公开媒体仓库 cn_xc-media。

[0.7.10]

修复

  • 大房间「有路却是墙 / 门下方过不去」: Module 有两套连接 RPC—— 普通模块用 ModuleConnectionSetRPC(5 个参数),大房间(Mega 等)用 ModuleConnectionSetMegaRPC(9 个参数,多 8 个「对角连接」)。 此前只发 5 参版本 → 对角连接恒为 false → 门口/通道被判为封闭。 现在按模块选择:Module.type == Mega 或「任意对角连接为 true」 → 先发 5 参、再发 9 参; 其余模块仍只发 5 参。日志:地图同步:N 个(其中 Mega M 个;…)。
  • 模块补发的重试条件:去掉 SetupDone == true 过滤(未 setup 的模块此前会被算作已处理、 永久不补发);「等待地图 setup」上限从 5 秒放宽到 15 秒。

说明

  • 补丁集仍为 0.6.1 的 57 项,零增减。
  • 修复在房主侧:房主需升级到 0.7.10。

[0.7.9]

修复

  • 双端都装同一版本,HUD 却显示「单端模式」:此前为“暂时关闭与 LateJoinNow 的协议对接”, 把开关误套在「协同模式」总开关上,导致 CoopModeEnabled 恒为 false、 IsClientCoopActive() 恒为 false,客户端协同补丁全部不启用。 现已拆开:LjnCompatEnabled(仅 LJN 对接)与 CoopModeEnabled(本模组↔本模组协同)。
  • 房主同时装了本模组 + LateJoinNow 时被误判为「兼容模式」:判定改为先看房主是否带 LateJoinSimple.Version,有则视为「本模组房主」,由版本号决定 协同 / 单端。
  • Steam 好友 / 邀请无法加入房间:锁定房间不再设置 PhotonNetwork.CurrentRoom.IsOpen = false、 不再调用 Steam LockLobby()(两者都会让所有人无法加入),改为只把房间设为列表不可见, 邀请与好友仍可正常进入;切关后冷却从 600 秒缩短到 30 秒。
  • 中途加入时尸体出现在卡车门口、且无法抓取: 补建尸体时增加位置有效性校验(y > 1000 视为隐藏位,回退到死亡地点; 仍无效则不生成),并在补建后调用 PlayerDeathHead.Setup() 与 SetColliders(true); 存活玩家补建出的死亡头改为隐藏(避免隐藏位上出现空洞可交互的头)。
  • 死亡状态同步:撤销「有死亡头即视为死亡」的判据(复活后死亡头仍存在,会把活人误判为死亡), 改为 deadSet == true 或 血量<=0。

新增诊断日志(便于反馈排查)

  • [加入诊断](3 / 12 / 25 秒):本机 actor、是否房主、房间人数与上限、协同/LJN 开关、 房主协同兼容与是否 LJN、本机角色(名字 / steamID 是否已设置 / 死亡状态 / 血量是否初始化)、 玩家视图(房间人数 vs 本机 avatar 数)、房主房间属性键值、本地尸体明细(deadSet / avatar 状态 / 死亡头是否激活与位置)。
  • [死亡诊断](房主侧,随属性同步输出):每个玩家的 deadSet / 是否有死亡头 / 头位置。
  • [地图接收]、[状态重置]、[房间可见性] 等辅助日志。

调整

  • 恢复与 LateJoinNow(原版)的协议对接 / 兼容模式(LjnCompatEnabled = true)。 房主使用 LateJoinNow 时 HUD 显示「兼容模式」;本模组自身的修复仍只在房主也用本模组时生效。

隐私

  • DLL 中不再保存明文 SteamID:作者与「模组测试员」身份改为 SHA-256 前 16 位哈希比对, 反编译 DLL 也读不到原 ID。
  • 日志中的 SteamID 全部脱敏(只输出「已设置 / 为空」)。

说明

  • 补丁集仍为 0.6.1 的 57 项,零增减。
  • 已知未解决:中途加入时若已有玩家死亡,尸体位置 / 头部交互 / 死亡语音仍可能异常(正在排查)。

[0.7.8]

修复

  • 概率性中途加入失败(卡在加载界面):游戏的 AddToStatsManagerRPC 是 OwnerOnly 且只发一次, 早于对端角色创建时会被丢弃 → 本机 steamID 为空 → PlayerAvatar.LateStart 里 TryGetValue(null) 抛异常 → 角色初始化中断。 现在加入者侧自愈:加入后 3 秒 / 12 秒检查,若本机角色缺 steamID, 用 Photon.UserId(为空则用 Steam 本机 ID)调用 AddToStatsManagerForced 补齐。 日志:角色自愈:本地角色缺少 steamID,已用 Photon/Steam 身份补齐。
  • 本机角色血量未初始化导致没有 HUD、不能交互:加入后 3/12/25 秒检查, 若 healthSet == false 则用 maxHealth 填一次并置为 true。 日志:加入自愈:本机角色血量未初始化,已填为 N/N。
  • 大房间出现「原本有路却是墙」:LevelGenerator 没有 seed(关卡由房主实例化模块、 逐模块发 ModuleConnectionSetRPC 的 5 个连接标志)。此前对 SetupDone == false 的模块 直接跳过且不再补发 → 该模块在加入者本地连接标志全为「未连接」→ 外观与碰撞不一致。 现在不再跳过,直接按当前标志发送;接收端已有幂等去重,重复发送安全。 并新增进度日志:地图同步:N 个(未发 M,已发过 K)| 房主 ModulesSpawned=X/目标 Y。

说明

  • 补丁集仍为 0.6.1 的 57 项,零增减(以上改动都是管线内的“补数据/多发几次”,不新增 Harmony 补丁)。
  • 房主与加入者建议同装 0.7.8:加入者装 → 治“进不去”;房主装 → 治“路变墙”。

修复(反复加入后越来越进不去 / 虚空房间)

  • 每次进入房间时重置本模组的本地状态:模块发送去重表、模块接收去重表、待处理加入队列、 冲突检测标记。此前反复「进房→断连→再进」时,上一轮的残留会让后续几次的补发与初始化判断出错。
  • 断连时同样重置(OnDisconnected),避免残留跨会话累积。

新增诊断日志

  • 加入者侧:[地图接收] 累计收到 ModuleConnectionSetRPC N 次 | 本地 Module 数 M | ModulesSpawned=X/目标 Y (每 5 秒汇总一条)—— 用于直接判断是「没收到」还是「收到了但没生效」。
  • [状态重置] 已重置本模组加入状态(on-joined-room / on-disconnected)。
  • 房主侧模块补发日志已含 | 房主 ModulesSpawned=X/目标 Y。

新增

  • HUD 可在配置里关闭:[房间信息显示] 显示HUD,默认开启。

  • HUD 玩家名单新增「模组测试员」标记, 在名单中显示为紫色并追加(模组测试员); 名单排序规则不变**(房主 → 作者 → 其余按 ActorNumber),不做前置。

  • 补丁集仍为 0.6.1 的 57 项,零增减(只在已有钩子与管线里增加逻辑与日志)。

调整

  • 暂时移除与 LateJoinNow(原版)的协议对接 / 兼容模式(代码保留,开关 LjnCompatEnabled = false)。 原因:优先保证当前版本稳定性;后续版本会重新加回。 影响:房主若使用 LateJoinNow 原版,本模组将以单端模式运行(HUD 显示单端), 中途加入相关修复不会生效。

  • 文档同步:README 中与 LateJoinNow 相关的「兼容模式」描述已改为单端模式说法, 并删除重复的旧「兼容边界」段落与失效的 FAQ 表述。

[0.7.7]

调整

  • 配置项名称全部改为中文(节名与键名都是中文,配置界面不再出现英文键名): 传送到房主 / 卡车阶段允许加入 / 商店阶段允许加入 / 关卡中允许加入 / 显示房间类型 / 显示房间名称 / HUD字号 / HUD透明度 / 每行显示人数 / 协同模式 / 详细调试日志 / 冲突检测弹窗 / 快捷栏修复 / 模块补发秒数 / 血量同步 / 外观颜色同步 / 加入时重放升级。
  • 功能与源码没有任何改动:本版源码仍是 0.6.1 基线,只改了配置项名称与版本号。

⚠️ 键名从英文改成中文后,旧的 cn_xc.LateJoinSimple.cfg 里的英文键会被忽略(设置回到默认值)。 如需保留原设置,可删除该 cfg 让游戏重新生成,或手动把旧键名对应成中文键名。

[0.7.6]

本版做了什么

源码基线回到 0.6.1,只把版本号改成 0.7.6,功能代码一行未改。

  • 源码来源:本仓库 git 历史中最后一个 PluginVersion = "0.6.1" 的提交(efccc45)。
  • 编译:改用 Unity 6(6000.3.17f1)程序集 + publicized Assembly-CSharp (publicized 只在编译期放宽私有成员访问,不进入发布包,运行时行为不变)。
  • 逐项校验(编译产物 vs 线上 0.6.1 的 DLL):
    • 补丁注册集 57 / 57,完全一致(缺 0 项、多 0 项)
    • Plugin 方法数 204 / 204,完全一致

为什么是 0.6.1,而不是 0.6.0 或 0.7.x

实测结论(同一台机器、同一房间、同样流程):

版本 结果
0.6.0 ❌ 中途加入不可用
0.6.1 ✅ 可用(兼容模式、协同模式都正常)
0.7.2 ~ 0.7.5 ❌ 全部不可用

根本原因:0.6.1 是第一个做了 Unity 6 适配的版本。 它比 0.6.0 多了 8 个方法,其中两个是关键:

GetSteamLobbyManagerInstance   /  InvokeSteamLobby

新版游戏把 Steam 大厅逻辑从 SteamManager 迁到了新的 SteamLobbyManager, 0.6.1 通过反射访问新类,因此本身就能在新版游戏上编译通过(0 错误); 0.6.0 仍然是直接调用 SteamManager,在新版游戏上必然失效。

0.7.2 ~ 0.7.5 为什么全部不能用(逐条说明)

  1. 基线选错了:0.7.2 ~ 0.7.5 都是"0.6.0 的补丁集 + 后续新增功能", 而不是"0.6.1(真正可用的那一版)"。缺了上面那两个 Unity 6 适配方法,方向从一开始就偏了。
  2. 凭日志猜着删补丁:在缺少可用版本对照的情况下,把上游长期验证过的两个补丁 (LevelGen.PlayerSpawn Pre、LoadingUI.AnimStart Pre)误判为"有害"并删掉了, 直接导致卡在加载界面、角色以死亡/旁观状态落地(地上有一个自己的死亡头、没有 UI、不能交互)。
  3. 加的"兜底"反而破坏游戏状态机:给 ExtractionPoint.DestroyAllPhysObjectsInHaulList 加异常兜底后,结算时的销毁流程被中断 → 状态残留 → ExtractionPoint.StateComplete 每帧抛 NRE(单局实测 860 条),把帧率拖死。
  4. 在关键初始化路径上加干预:曾在 PlayerAvatar.LateStart 之前调用 AddToStatsManagerForced 做自愈,打断了游戏自己的初始化顺序。
  5. 切关清理过于激进:清空 Photon 实例化缓存后,后来加入的人拿不到其他玩家的对象注册信息, 表现为对端收到海量 Received RPC ... for viewID X but this PhotonView does not exist。
  6. 多轮改动叠加、无法二分:功能(内置共享升级)、修复、兜底、自愈同时存在, 出问题后无法快速定位是哪一条引入的。

本版起的工作方式(不再重复上面的错误)

  • 以 0.6.1 原样源码为唯一基线;
  • 后续每次只加一项功能,单独编译、单独出包、单独验证;
  • 任何改动都必须能通过「补丁注册集与基线比对」这道检查。

[0.7.5]

基线回退(关键)

本版把补丁集完整回退到 0.6.0,并移除 0.6.0 之后新增的所有功能与兜底, 只保留 Unity 6 引擎换代所必需的编译期适配。目的是先让「中途加入」在新版游戏上重新可用。

  • 补丁注册集与 0.6.0 完全一致(57/57,零差异) —— 已用程序集逐项比对确认: 缺 0 项、多 0 项。
  • 移除 0.6.0 之后新增的全部功能: 内置共享升级、电池电量同步、撤离点激活补发、收取列表清理、 以及各种 NRE 兜底(碰撞回调 / 抓取 RPC / 翻滚 RPC / 商店代币机 / DebugTester / 撤离点结算)。
  • 保留:GetSteamLobbyManagerInstance / InvokeSteamLobby 两个反射辅助 (新版游戏把 Steam 大厅逻辑迁到了 SteamLobbyManager,不改编译不过)。

说明

  • 之前两版出现的 ExtractionPoint.StateComplete 每帧 NRE(单局 860 条), 经时间线比对确认是我加在 DestroyAllPhysObjectsInHaulList 上的兜底导致的: 吞掉异常 → 销毁流程没跑完 → 状态残留。本版已移除。
  • 本版不含任何附加功能:没有共享升级、没有电池同步、没有撤离点补发。 后续按「一次只加一项 + 单独验证」的方式逐步加回。

更新日志 / Changelog

(仅本地) = 只在本地测试过,没有上传到 Thunderstore。

[0.7.4]

⚠️ 本版(含属性共享升级等新增功能)没有经过完全测试,可能仍有未发现的问题。 如遇异常请附日志反馈。

修复

  • 卡车阶段共享的升级,进入关卡后消失:共享升级此前只应用到运行时字段 (PlayerController.JumpExtra / PlayerHealth.maxHealth 等),换关时这些对象会重建,值随之丢失。 现在每次关卡开始后(约 5 秒)把房主的等级写进本机存档字典(DictionaryUpdateValue,取最大值、幂等), 由游戏自己在关卡开始时按字典恢复属性。 ⚠️ 这里刻意不再调用累加式的 Update*RightAway —— 否则会与游戏恢复的值叠加导致翻倍。 日志:关卡开始共享:已把房主的 N 项升级等级写入本机存档。

  • 队友购买现在也能共享(上一版遗留):

    1. 购买瞬间 StatsManager 里的等级还没写入,读到 0 会导致漏广播 → 改为延迟两帧再读;
    2. 房主收到队友的请求(事件 173)后,除转发外自己也会应用一次。 日志:[属性共享] 队友购买 … 已请求房主 → [属性共享] 收到队友请求… → [属性共享] 房主本机应用…。
  • 力量(抓取强度)没能共享:游戏 UpdateGrabStrengthRightAway 依赖 PhysGrabber, 而加入初期该组件往往还没创建 → 调用抛异常被跳过(血量和体力挂在 PlayerHealth / PlayerController 上,所以正常)。 现在:① 加入后 30 秒再补一次重放(此时抓取器已就绪);② 力量 / 抓取范围应用失败时不记入已应用列表,留待下次重试。

  • 升级项按「依赖组件是否就绪」决定何时应用(修正力量/范围/投掷/翻滚发射): 查游戏 IL 后确认 13 项升级依赖如下 —— 血量→PlayerHealth、体力/跳跃/冲刺→PlayerController、蹲伏恢复/电池/地图人数/攀爬/翅膀→PlayerAvatar 字段 (这些在加入初期就存在,能正常共享); 而 力量 / 抓取范围 / 投掷强度→PlayerAvatar.physGrabber、 翻滚发射→PlayerTumble 属于晚创建组件,早先的重放会因组件为空被跳过。 现在:① 应用前先检查依赖组件是否就绪,未就绪则跳过且不记入已应用; ② 加入后从 30 秒起每 10 秒重试一次、最多 90 秒,直到依赖就绪。

一次性收敛(移除所有会干预游戏流程的操作)

起因:中途加入时卡在加载界面进不去。对照日志发现「不装本模组时错误 18 条,装了之后 613 条」, 且出现海量 Received RPC ... for viewID X but this PhotonView does not exist(其他玩家的对象全部没注册上)。 因此把模组里所有会改变游戏流程的操作一次性移除,只保留纯附加能力。

移除清单:

  1. 不再拦截 LevelGenerator.PlayerSpawn —— 原版负责创建并绑定 PlayerDeathHead / PlayerTumble、 设置玩家名字与 steamID;跳过它会让加入者角色不完整。
  2. 不再拦截 LoadingUI.LevelAnimationStart —— 强制把加载 UI 标记为"已完成"会导致卡在加载界面。
  3. 切关时不再清空 Photon 实例化缓存(ClearAllScenePhotonCacheWithKeepPublic)—— 缓存被清后, 后加入者拿不到其他玩家的对象注册信息,直接导致卡加载。
  4. 切关时不再清 RunManagerPUN 的 buffered RPC、不再清物品实例化缓存。
  5. 切关时不再隐藏房间(避免玩家看到"房间消失 / 进不去")。
  6. 之前已移除:DestroyAllSceneValuablesAndItemsWithKeepPublic(销毁场景物品)、结算前清理收取列表。

保留的能力(都是只读或只附加,不改游戏流程): 加入时的模块/物品/宝物/状态同步、血量与外观推送、撤离点锁定同步、电池电量同步、 快捷栏补槽、房间可见性补写、属性重放与共享升级、以及若干"吞异常不刷屏"的兜底。

[0.7.3]

新增

  • 内置共享升级(可开关,默认开启):配置项 [玩家属性] 属性共享升级。
    • 加入时:中途加入者按房主的升级等级应用一次(会持续等待房主数据就绪,每 2 秒检查、最多 60 秒)。
    • 实时:房主买到升级,其他人立刻同步。走游戏原生 TesterUpgradeCommandRPC,
    • 队友购买没有发原生 RPC 的权限,因此通过自定义事件(173)通知房主,由房主代为广播。
    • 同一项同值只应用一次(游戏接口是累加式的,重复应用会翻倍)。
  • 电池电量同步:房主把场上所有电池电量广播给加入者,客机调用游戏自身 ItemBattery.SetBatteryLife。

修复

  • 加入卡住 / 角色没初始化(本版最重要的修复): LevelGenerator.PlayerSpawn 的前缀会 return false 跳过原版方法,而原版才是给每个 avatar 创建并绑定 PlayerDeathHead / PlayerTumble 的地方。跳过之后若不再补建,加入者就完全没有这两样, 表现为:对端收到 PhotonView does not exist、PlayerAvatar.LateStart 抛异常、 名字显示 avatar-xxxxx、血量 100/100(unset)、角色卡住不能动。 现已恢复补建逻辑(并使用原版死亡点 deathSpot 定位)。
  • 撤离点结算不了:游戏 ExtractionPoint.DestroyAllPhysObjectsInHaulList 会遍历所有玩家并调用 playerDeathHead.Revive(),只要有一个玩家的头是 null 就抛 NRE,结算状态机卡死。 根因就是上面那条(加入者没有头),已随之修复;另加兜底:万一仍有异常,吞掉并继续结算。
  • 房主等待加入者就绪超时(15s):前缀曾吞掉 PlayerAvatar.LoadingLevelAnimationCompletedRPC 的 原版逻辑,现已改为先记录状态再放行原方法。
  • 加载界面卡住:仅在本地 avatar 确实就绪时才跳过加载动画,否则交还原版流程。
  • 升级属性重复叠加:游戏的 Update*RightAway 是累加语义,现已保证同一项同值只应用一次。
  • 撤离点显示 READY 而非 LOCKED:等对端关卡生成完成后再补发激活事件。
  • 模块补发提前退出:只要还有模块未 SetupDone 就继续等待,避免某些房间的墙门永远收不到连接数据。
  • ItemBattery.batteryLife 类型错误:该字段是 Single(float),此前按 Int32 读写;已改用游戏自身方法。
  • 移除两处无效的事件码 202 调用(PUN 内部码,RaiseEvent 只允许 0~199,等于死代码)。

移除

  • 删除「切关销毁场景物品」逻辑(DestroyAllSceneValuablesAndItemsWithKeepPublic 不再被调用), 相关配置项一并删除。理由:收益很小(Photon 场景切换时本来就会重建对象),风险很高 —— 曾销毁撤离点收取列表里的物品,导致结算 NRE。切关现在只做非破坏性的 Photon 缓存清理。

调整

  • 配置项全部中文化:节名与键名均为中文(如 [玩家属性] 属性共享升级、[虚空修复] 血量同步)。 ⚠️ 键名变更后旧 cfg 里的英文键会被忽略(恢复默认值)。
  • 公开房名前缀的版本号改为点换横线:[中途加入-小菜 v0-7-3] 房名 (游戏房间列表会把数字打码成 0.*.*,横线写法可读)。
  • 适配 Unity 6 引擎换代(2022.3 → 6000.3.17f1):全部补丁目标与反射字段重新校验、重新编译, FindObjectsOfType 等过时 API 全部替换,编译 0 错误 0 警告。
  • 兼容 LateJoinNow 1.1.1:改为回显房主上报的版本号,1.1.0 / 1.1.1 都能对上。
  • 日志降频:重复性日志下沉为 Verbose,详细调试日志 默认关闭。

[0.7.2] (线上已发布)

  • 该版本为 2026-10-30 上传到 Thunderstore 的构建,内容同 0.7.3 的早期阶段: 内置共享升级、电池电量同步、撤离点锁定、Unity 6 适配、LateJoinNow 1.1.1 兼容。
  • 已知问题(0.7.3 已修复):中途加入可能卡住 / 角色显示 avatar-xxxxx、血量 100/100(unset) —— 原因是 LevelGenerator.PlayerSpawn 前缀跳过了原版,导致加入者缺少 PlayerDeathHead, 进而使撤离点结算时 playerDeathHead.Revive() 抛 NRE。

[0.7.1]

修复

  • 属性共享升级不生效:补丁表按 typeof(Hooks) 查找钩子方法,但实现被放进了 Plugin 类 → 启动日志出现 Could not find method for type LateJoinSimple.Hooks and name PunManagerUpdateStatRPCPostfix、 补丁安装失败:PunManager.UpdateStatRPC Post,导致实时共享完全没挂上。 现在在 Hooks 类里加了转发方法,实时共享可以正常安装。
  • 移除两处无效的事件码 202 调用:202 是 PUN 的内部实例化码(RaiseEvent 只允许 0~199), 调用必然被拒绝(日志:RaiseEvent(202) failed ... eventCode must be less than 200),等于死代码。 这两处分别用于「语音视图补发」与「实例化缓存清理」,前者已由 UpdateMyPlayerVoiceChat RPC + ownership 更新覆盖, 后者已由 ClearAllScenePhotonCacheWithKeepPublic / RemoveBufferedRPCs 覆盖,直接删除。
  • 模块补发不再提前退出:原先只要「已 SetupDone 且未发送」的模块数为 0 就结束补发协程, 但有些模块在同步窗口内还没 SetupDone(实测:Start Room - Shop Harbor(SetupDone=False))→ 它们的连接数据永远不会发出 → 表现为某个房间/商店区域的墙门不同步。现在只要还有模块未 SetupDone 就继续等待(直到补发窗口结束), 日志会写 模块补发:还有 N 个模块未 SetupDone,继续等待。

[0.7.0]

新增

  • 属性共享升级(内置,可开关):新配置项 [玩家属性] SharedUpgrades(默认 开启)。
    • 加入时:中途加入者按房主的升级等级应用属性(全队共用房主那一套),而不是只按自己的存档。
    • 实时:挂 PunManager.UpdateStatRPC 的后置钩子 —— 队里任何人买到升级,全场其他人都会立刻在本机应用一次 (该 RPC 本身只写 StatsManager 字典、不应用到玩家身上,应用只在购买者本机发生)。 同值去重,防止多人买同一等级时重复累加(游戏接口是累加语义,重复应用会翻倍)。
    • 日志:[属性共享] 队友升级 xxx = N,已在本机应用。
    • 关闭时各用各的存档(默认行为)。不要与 SharedUpgradesPlus 同时开启共享,否则会双重应用。

[0.6.1]

新增

  • 电池电量同步:房主把场上所有 ItemBattery 的电量通过自定义事件广播给加入者,客机写入本地电池组件(batteryLife / batteryLifeInt)。修复中途加入后部分武器电量不显示 / 显示错误。

修复

  • 升级属性被重复叠加(生命上限翻倍):PunManager.Update*RightAway 是累加语义(maxHealth += 20 × 等级),此前同一项会被重放多次。现在同一项、同一个值只应用一次,换房 / 重连时清空记录。
  • 死亡玩家的尸体出现在卡车出口:补建 PlayerDeathHead / PlayerTumble 时固定生成在 (0, 3000, 0),掉落后落在世界原点。现改为优先取 PlayerAvatar.deathSpot(原版死亡流程的生成点),其次取玩家当前位置,都取不到则不生成。
  • 其余撤离点显示「准备就绪」而非「已锁定」:原版激活事件非 buffered,加入者收不到历史事件。现改为等对端关卡生成完成后补发激活事件(对端是本模组时按其上报的模块数判定,否则退化为固定等待)。
  • 血量 / 外观同步窗口:首次推送 0 → 2 秒(避免对端组件未就绪导致 RPC 被丢弃),总窗口 31 → 约 60 秒。
  • 健壮性修正:模块数阈值整数截断、PhotonNetwork.Instantiate 返回 null、死亡点有效性误判、协议模块数上报过早。

适配游戏大更新(牛仔地图版本)

  • 游戏把 Steam 大厅相关成员从 SteamManager 迁移到新类 SteamLobbyManager,且 SteamManager 变成静态类。 本模组原先通过 SteamManager.instance 读写 currentLobby / privateLobby 并调用 LockLobby / UnlockLobby, 在新版会全部失效(房间锁定 / 公开房前缀功能失效)。现已改为通过 SteamLobbyManager 反射读写与调用。

  • 用新版 Assembly-CSharp.dll 重新校验全部反射目标:50 个补丁方法全部存在、69 个反射字段/属性全部存在。

  • HUD 中文字形被裁切:行高由 字号 × 1.35 放宽到 字号 × 1.6,并给文本样式加内边距 —— CJK 字形上下延伸比拉丁字母高,原行高会把字的顶部和底部切掉。

[0.6.0]

新增功能

  • LateJoinNow 协议级兼容:检测到房主运行 LateJoinNow 1.1.0 时,本模组按它的协议上报自身状态(LateJoinNow.Version / Protocol / Ready / ModuleCount),使原版房主把本机视为可协同客户端,从而下发模块连接与属性同步。
  • HUD 显示工作模式:玩家名单上方显示 兼容模式:对接 LateJoinNow 协议 / 协同模式 / 单端模式:房主 vX / 本机 vY。
  • HUD 首行「房间」后面追加 (已同步 N 个房间)(仅加入者显示)。

调整

  • 公开房名前缀带上版本号:[中途加入-小菜 vX] 房名。
  • HUD 首行只保留「房间」计数(墙/门不再显示),兼容模式下该行末尾另有蓝色 (仅供参考)。
  • CoopMode 说明更新为「自动」:双端版本 + 协议一致才启用协同。

修复(重要)

  • 修复升级属性重放把所有人的属性累加:Update*RightAway(steamId, 值) 作用对象是「本机玩家」,此前把所有人的行都重放了一遍(HP 200 被重放成 600)。现在只重放本机自己的那一行。
  • 修复「进自己人的房间也显示兼容模式」:换房 / 重连 / 主机切换时清除上一局判定。
  • 新增接收端防重复:Module.ModuleConnectionSetRPC 同一模块 + 相同连接标志只放行一次。

[0.5.22] (仅本地)

  • LateJoinNow 协议级兼容(按 1.1.0 协议上报)+ HUD 模式显示。
  • 恢复 CoopMode 自动说明与接收端防重复。过渡版本,未上传。

[0.5.21] (仅本地)

  • 修复 GetActiveSceneModules() 的无限自递归(会栈溢出直接崩溃游戏)。
  • 新增接收端防重复:模块连接数据重复到达时只生效一次。
  • 修复兼容模式判定残留;公开房名前缀加版本号;房主名字与「房主」标记整体显示为红色。

[0.5.20]

调整

  • HUD 首行只保留「房间」计数(墙 / 门不再显示),便于与房主端对比。
  • 兼容模式下该行末尾追加蓝色 (仅供参考)。
  • HUD 玩家名单:房主的名字与 (房主) 标记整体显示为红色(#FF6B6B)。

修复

  • 修复后台补发在去重表被清空后重复发送模块连接数据、导致加入者多生成一块模块的问题。

[0.5.19]

  • 模块同步只取当前关卡场景:此前 FindObjectsOfType<Module>() 会把其它已加载场景与 DontDestroyOnLoad 中的模块一并同步,可能造成加入者多出不属于本关卡的模块。
  • 修复偶发的「加入者多出一块关卡模块」根因:LevelGeneratorStartPostfix 会清空「已发送模块」去重表,若清空发生在后台补发窗口内,整批连接 RPC 会被重复发送;该 RPC 每被接收一次会让对端 ModulesSpawned +1。现在补发协程维护「本次已尝试发送的 ViewID」,同一模块最多发一次。
  • 修复 GetActiveSceneModules() 的无限自递归(会触发 StackOverflowException 崩溃游戏进程)。

[0.5.18]

文档

  • 应原作者要求,README 增补完整的「版权声明与使用条款」(中文 + English Version),并新增 LICENSE;源码顶部加入版权声明头,新增 LicenseNotice 常量。本次无功能改动。

[0.5.17]

  • 恢复 CoopMode 配置项(0.5.16 中误将其固定为开启):当房主或队友仍是旧版本 / 没装本模组时,需要关闭协同模式按单端模式运行,否则会导致本机玩家对象异常(表现为中途加入后无法操控、ESC 无响应)。

[0.5.16]

  • HUD 文本宽度写死 480px:名字行较长时会被裁掉。改为按屏幕动态计算宽度(min(屏幕宽-24, 1400))。
  • 作者识别增加昵称兜底。

[0.5.15] (仅本地)

  • 去掉玩家名单的 12 人上限(20 人房之前只显示 12 个),现在按实际人数全部显示。

[0.5.14] (仅本地)

  • 玩家名单不再受「显示人数」开关影响:关闭 ShowPlayerCount 时名单仍显示。
  • 玩家排序:房主第一 → 其余按 ActorNumber 排序。

[0.5.13]

  • HUD 计数:扫描失败导致全 0 时,不再把 0,0,0 写入房间属性。
  • 版本标记写入失败改为提示一次,不再静默吞异常;写入统一 Trim()。
  • HUD 关闭再开启后立即重建内容(此前最多空白 1 秒)。
  • 配置热重载轮询异常时只提示一次;玩家名截断提示改用实际显示数量判断。

[0.5.12] (仅本地)

  • 移除「模组信息」配置组:模组版本号改为独立标记文件(cn_xc.LateJoinSimple.cfg.version),不再显示在配置界面。
  • HUD 状态行改为先判断刷新间隔再统计人数;GUIStyle 以 GUI.skin.label 为基类。
  • 房主身份变化时 HUD 角色标签立即刷新(原为最多 5 秒延迟)。

[0.5.11]

新增

  • 配置热重载:每 2 秒检测本模组 .cfg 的修改时间,变化即 Config.Reload() 并刷新 HUD —— 修改配置无需重启游戏(约 2 秒内生效)。

[0.5.10] (仅本地)

  • HudNamesPerLine 可选范围从 1~12 收紧为 3~6(默认仍 5)。

[0.5.9] (仅本地)

  • 新增配置项 HudNamesPerLine(分组「房间信息显示」,默认 5,范围 1~12):玩家名字每行显示几个。

[0.5.8] (仅本地)

  • 玩家名字改为每行 3 个(最多显示 12 人,超出显示 …),房主仍带 (房主)。

[0.5.7] (仅本地)

  • 玩家名字不再挤在一行:改为 玩家(N): 标题 + 每人一行(最多显示 8 人)。

[0.5.6] (仅本地)

新增(HUD)

  • 玩家名字行:左下角 HUD 新增玩家列表,客户端也能看到所有人。
  • 与房主对比:房主把「房间 / 墙 / 门」计数写入 Photon 房间属性(LJS.MapCounts),客户端读取后逐项对比:一致显示 12✓,不一致显示 12(房主14)。

[0.5.5] (仅本地)

  • 房间可见性补发:第一次(3s)若尚未收集到已探索房间,改为跳过本轮而不是 yield break —— 之前会连带取消 9s 的那次补发。
  • 零房间语义:收集结果为空时返回空数组而不是 null。
  • 补发等待:WaitForSeconds(5f) 改为 WaitForSecondsRealtime(5f)。
  • 弹窗守卫:TryShowMenuPopup 增加 mm == null 判断。
  • 加入者刷新:RefreshOldPlayersForNewJoiner 增加 newPlayer == null 守卫。
  • HUD:showStatus 也要求 inGame && CurrentRoom != null。

[0.5.4]

新增

  • 加入者(客户端)也能看到左下角 HUD:OnGUI 去掉「仅房主」限制。
  • HUD 第一行显示地图计数:【房主】/【加入者】 房间 N · 墙 N · 门 N(房间 = RoomVolume;墙 = ModuleBlockObject + BreakableWall;门 = TruckDoor / ShopKeycardDoor / DoorTrap / TutorialDoor),用于判断地图是否重建一致(虚空排查)。

优化

  • HUD 文本缓存(不再每帧拼字符串);地图计数改为「换图立即刷新 + 平时 5 秒间隔」。

[0.5.3]

修复:加入者「抓不动东西 / 交互不了」

  • 修复 PhysGrabber 的引用:在 PlayerAvatar.Awake 后置钩子里,若 PhysGrabber.photonView 或 PhysGrabber.playerAvatar 为空则补上。中途加入的玩家这两个引用为空时表现为抓取 / 交互无反应。
  • physGrabPoint 为空时打一行 Verbose 提示。

[0.5.2]

修复

  • 修正升级属性的读取位置:原先读的是 StatsManager.playerUpgrade* 字段(实测为空),现改为优先读真正的存储 StatsManager.dictionaryOfDictionaries[升级名][steamID]。
  • 新增 [升级字典] dump:可直接确认升级数据是否存在、键是什么。

[0.5.1]

新增诊断(只读)

  • [自身属性] 对照日志:加入后 3 秒(重放前后各一次)与 12 秒,同时打印存档里的升级等级与本机实际生效的字段,用于区分「存档有值但没生效」与「存档本身没到本机」。

[0.5.0]

修复:中途加入者的升级属性一直没被应用

  • 游戏里跳跃 / 体力 / 冲刺 / 抓取力量 / 抓取范围 / 投掷 / 攀爬 / 翅膀 / 血量上限 / 蹲伏恢复 / 电池 / 地图人数这些属性,只在玩家购买升级的网络 RPC 里应用,中途加入者错过了历史上所有这类 RPC。
  • 现在加入后 3 秒 / 12 秒各重放一次:读出存档里每个玩家的每个升级数值,调用游戏自带的 Update*RightAway(steamID, value) 让游戏按原生逻辑应用。开关 ApplyUpgradesOnJoin(默认开)。

[0.4.9]

  • 跳过加入者自己的 avatar(不再把本机已有的值推回去)。
  • 同步死亡状态:对 deadSet == true 的玩家发送 PlayerDeathRPC(-1);死亡玩家不再推血量。

[0.4.8]

修复(关键)

  • 不再推送未就绪的血量(垃圾值):此前推送时机早于游戏从存档初始化血量(healthSet == false 时值仍是默认 100),导致房主真实血上限 1300 却推 100/100。现在只在 healthSet == true 时才推。
  • [属性快照] 增强:输出 名字 血量/上限(set|unset) 色[颜色索引…]。

[0.4.7]

  • 客机落地后自报血量:加入后 12 / 25 秒把自己的真实血量广播给其他玩家,保证房主那边的副本正确。
  • 房主推送日志带上具体数值,可直接看出房主手里副本是否已正确。

[0.4.6]

  • 属性推送改为「一加入就开始」+ 更密(0/2/4/6/8/10/13/16/20/25/30 秒):此前推送协程挂在加入管线末尾,可能拖十几秒才开始推。

[0.4.5]

  • 属性推送改为 30 秒内多次(0 / 4 / 10 / 20 / 30 秒):加入者落地时 PlayerHealth / PlayerCosmetics 可能尚未就绪,过早发出的 RPC 会丢失。

[0.4.4]

  • [属性快照] / [玩家视图] 诊断改为必然触发:改为从 LevelGenerator.Start 后置钩子启动,12 秒 / 25 秒各打一次,短会话也能拿到。

[0.4.3]

新增(只读诊断)

  • 房主端推送目标日志与加入者端属性快照,用于定位方向性同步差异(某位玩家做房主时同步正常、另一位不正常)。

[0.4.2]

  • [玩家视图] 诊断移到独立协程:原先挂在协同等待窗口里,可能提前 yield break 导致不输出。

[0.4.1]

  • SemiFunc.OwnerOnlyRPC 的 NRE 兜底:对象没有 owner 时游戏内部会抛 NRE,导致抓取链接或地图工具动作失败。现在捕获后放行并只记一次日志。
  • 新增只读诊断 [玩家视图]:比对房间人数与本机可见 avatar 数量。

[0.4.0]

新增

  • 外观颜色同步:房主在加入者落地后(并 4 秒后补一次)把每个玩家的颜色通过 SetupColorsRPC 推给该加入者。开关 PlayerCosmeticsSync(默认开)。
  • 血量与颜色开关互相独立。

[0.3.9]

  • 新加入玩家的血量 / 生命上限不同步:房主在加入者落地后(并 4 秒后补一次)把场上每个玩家的血量 / 上限通过 UpdateHealthRPC 推给该加入者。开关 PlayerHealthSync(默认开)。

[0.3.8]

  • 检测房主是否为 LateJoinNow:当房主未运行本模组但属性里带 LateJoinNow.Version 时打一行明确提示(只读检测 + 日志,不改行为)。

[0.3.7]

  • 确认 Level Point: Not connected 属游戏自身提示,与可见外观无关;实测出现时未观察到虚空墙 / 门框没门。
  • [光照守卫] 日志修正:异常消息为空时改为打印异常类型名。

[0.3.6]

  • 移除四段实验性「虚空修复」代码(LevelPointRelink、RoomConnectionRebuild、WallDoorRefresh、NavMeshRebuildOnJoin)及其 Hook:实测关闭状态下虚空问题未再出现。
  • 快捷栏修复改为独立实现(行为不变)。

[0.3.5]

  • [墙体诊断] 口径改正:按每个开关自己那一侧与对应连接标志比对,只统计确实不一致的个数(此前有大量假阳性)。
  • 配置版本检测改为非破坏性:旧实现会清空用户设置且误报「重置失败」,现在只提示版本变化并保留设置。

[0.3.4]

  • 同一模块的连接同步不再重复发送:ModuleConnectionSetRPC 会自增 ModulesSpawned,原先后台兜底会把整个模块表重发。现在按加入者记录「已发过的模块」,每个模块只发一次。
  • 后台只补发「尚未发过」的模块(最多 60 秒、每 5 秒检查;开关 ModuleResendSeconds)。
  • 新增只读诊断 [墙体诊断]。

[0.3.3]

  • 快捷栏限制(加入者无法把道具放进快捷栏):中途加入时 Inventory 的槽位列表可能留下空位,现在在关卡生成完成后(并重试 3 次)把所有场景里的 InventorySpot 补注册回去。开关 FixInventorySlots(默认开)。

[0.3.2]

  • 纠正上一版会把「本来能通行的门 / 通路封死」的问题:墙 / 门道具重算原先无条件执行,现在只在房主下发的连接确实发生变化时才重算。
  • 实验开关默认值统一为关;内嵌版本号与 manifest 对齐。

[0.3.1]

  • 仅修正 manifest.json 的版本号;包内 DLL 内嵌版本仍为 0.3.0(0.3.2 已统一)。

[0.3.0]

新增

  • 虚空修复(客机侧,需双端都装):NavMeshRebuildOnJoin(默认开)本地重建导航网格;RoomConnectionRebuild(默认关,实验)按几何推算缺失连接。

修复

  • 协同模式下的地图兜底重发改为按对端上报的模块数判断;SlowProjectile 销毁前先转让所有权;171 事件处理器加固;语音链接自动重试。
  • 【关键】墙体 / 门道具刷新(WallDoorRefresh,默认关):在房主连接数据到达后重算 ModulePropSwitch。
  • 【关键】关卡连接点重链接(LevelPointRelink,默认关):按几何补 LevelPoint 链接。
  • 修正「虚空修复」自身两个问题:不再向全房广播 NavMeshSetup;修复「房主引用未就绪 → 一次都没执行」。
  • 中途加入常见的游戏侧 NRE 守卫(6 处):PlayerHealth.Update / FogLogic / PlayerDeathHead.UpdateParticleColors / EnemyOnScreen.GetOnScreen / LightManager.Handle*。

[0.2.0]

新增

  • 房间 / 地图可见性同步(需双端都装):房主把本局已探索的房间位置发给加入者(3 秒 / 9 秒各一次),加入者按位置就近匹配后调用 RoomVolume.SetExplored() 补写。
  • 光照守卫:给 LightManager 与 EnvironmentDirector.AmbientLightLogic 加 Harmony Finalizer。

[0.1.1]

  • 修复冲突检测漏检:原先只在自己 Awake 里扫描一次,加载顺序排在后面的同类模组还没登记。现在改为启动时 → 延迟 5 秒 → 进入房间时各检测一次。

[0.1.0]

首个公开测试版:基于 Zichen 的 LateJoinNow 1.1.0,在其授权下接手修复与重新发布。

新增

  • 中途加入完整流程:场景判定 → 排队调度 → 地图模块 / 宝物 / 物品属性 / 22 项场景状态同步 → 落地动画补发 → 虚空兜底重发 → 移动检测。
  • 房间信息 HUD(房间类型 / 名称 / 人数)与中途加入进度显示。
  • 公开房名自动前缀、切关冷却锁房、双端协同优化、冲突检测与弹窗。

调整

  • 修复原版 PhysGrabber.Update 空值守卫 Transpiler 完全失效;不再销毁带铰链的门;冲突检测改为 AND 匹配;协同模式等待超时 8 → 15 秒。
  • 插件更名为 LateJoinSimple(GUID cn_xc.LateJoinSimple),房间属性键改为 LateJoinSimple.*;与原版 LateJoinNow 不兼容,不可同时启用。
  • 房间名前缀改为 [中途加入-小菜];目标框架 .NET Framework 4.7.2,BepInEx 5.4。