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

LateJoinSimple

中途加入:让朋友在卡车 / 商店 / 关卡进行中随时加入你的房间。基于Zichen的 LateJoinNow,在其授权下接手修复。

By cn_xc
Date uploaded 6 days ago
Version 0.3.6
Download link cn_xc-LateJoinSimple-0.3.6.zip
Downloads 367
Dependency string cn_xc-LateJoinSimple-0.3.6

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 群:573485890


版权声明

  • 原作:中途立即加入 / LateJoinNow,作者 (Zichen)。

  • 本模组:LateJoinSimple 在获得原作者许可的前提下,基于 LateJoinNow 1.1.0 接手修复与继续维护的版本,由 cn_xc 维护,非原作官方发行。

  • 原作声明照录:

    本模组为独家原创作品,禁止未经授权进行代码复制、二次打包移植或转载,违者将依法进行维权投诉。

  • 未经原作者与维护者许可,请勿复制、二次打包、移植或转载本模组代码。


⚠️ 测试版说明(v0.3.6)

这是一个公开测试版。双端联机测试目前主要依靠群里的测试员协助(见文末致谢),也欢迎大家继续帮忙验证:

  • 中途加入主流程已实测可用(排队 → 场景 / 物品 / 宝物 / 状态同步 → 落地动画 → 移动检测通过)。
  • 联机细节仍可能存在未知问题,欢迎反馈。
  • 反馈请附日志:BepInEx/LogOutput.log(或 BepInEx 控制台),并说明是房主端还是加入者端。
  • 本版相关修复:不再销毁带铰链的门(原版会销毁,可能导致加入者看到「只有门框、门没了」)。
  • 本版新增:房间可见性同步(需双端都装)——房主把本局已探索的房间发给中途加入者,修的是游戏内地图(DirtFinder)上的房间揭示;并新增光照守卫,拦截光照系统异常以免整片房间不发亮。
  • 「虚空门 / 缺墙 / 门框里没有门」已修复:0.3.4 起双端实测与日志中未再出现(Level Point: Not connected、Ev Destroy Failed 均已归零)。

✨ 功能

  • 随时中途加入:朋友可以在卡车 / 商店 / 关卡进行中加入你的房间,三个场景可分别用配置控制。
  • 房主权威:场景同步全部由房主执行,加入者只接收,避免状态污染。
  • 同步内容:地图模块(墙体 / 内饰连接)、贵重物品价格与发现状态、物品属性,以及 22 项场景状态(撤离点、门禁、升级台、各类陷阱与宝物等)。
  • 房间信息 HUD(仅房主可见):左下角显示房间类型(含「可加入 / 已锁定」)、房间名称、当前人数;字号与透明度可调。
  • 中途加入进度显示:等待连接 / 排队中 / 加载中 / 已中途加入。
  • 公开房名自动前缀:创建公开房时自动加 [中途加入-小菜] 前缀。
  • 切关冷却保护:切换场景时自动临时锁房,防止过渡阶段有人撞入。
  • 双端协同优化:房主与加入者都装本模组时自动启用(加载更快、外观与语音自动刷新),HUD 中该玩家显示「(优化)」。
  • 虚空兜底:地图分帧重发 + 20 秒移动检测,尽量避免加入者卡在虚空。
  • 房间可见性同步(需双端都装):房主把本局已探索的房间发给中途加入者,让你的游戏内地图(DirtFinder)与团队一致(与「缺墙」不是同一条线)。
  • 光照守卫:拦截光照系统的异常,避免中途加入时房间整片发黑。
  • 中途加入稳健性:模块连接对每个加入者只发一次(避免抬高客户端的 ModulesSpawned)、后台只补发「没收到过」的模块、快捷栏槽位自动补注册、以及若干游戏侧 NRE 守卫。本模组不会改动任何墙 / 门 / 通路的开关。
  • 冲突自检:检测到其它「中途加入」类模组时弹窗警告。

📦 安装

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

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


⚙️ 配置

配置文件位置:BepInEx/config/cn_xc.LateJoinSimple.cfg

配置项 默认值 说明
ModEnabled true 关闭后整个模组全部功能失效
PublicRoomNamePrefix true 创建公开房时自动加 [中途加入-小菜] 前缀
TeleportToHostOnJoin false 加入后传送到房主位置;房主死亡则传送到最近的存活玩家
CoopMode true 双端都装同版本时启用协同优化
AllowJoinInTruck true 卡车阶段允许中途加入
AllowJoinInShop true 商店阶段允许中途加入
AllowJoinInLevel true 关卡进行中允许中途加入
HudEnabled true 左下角显示房间信息框(仅房主)
ShowJoinStatus true 显示中途加入玩家的进度
ShowPlayerCount true HUD 显示当前 / 最大人数
ShowRoomType true HUD 显示房间类型(公开 / 私人 / 单人)
ShowRoomName true HUD 显示房间名称
HudFontSize 16 HUD 字号(10–28)
HudOpacity 80 HUD 文字透明度 %(20–100)
VerboseLog false 输出详细调试日志(排查问题时可开)
ShowConflictWarning true 检测到同类模组时弹窗警告
FixInventorySlots true 中途加入后把场景里的 InventorySpot 补注册进 Inventory(修复快捷栏)
ModuleResendSeconds 60 后台只补发「尚未发过连接数据」的模块(每个模块只发一次;0 = 关闭)
模组版本号 当前版本 只读,勿改。0.3.5 起版本更新不会清空你的设置;若要还原默认,删除 cfg 后重启

⚠️ 已知问题

已修复(0.3.x)

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

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

仅「单端模式」可能仍存在(加入者是原版,本模组无法保护)

  1. 部分建筑不可见:早期版本偶有反馈;0.3.4 之后的双端日志中未再出现,但未专门验证该限制是否已完全消除。
  2. 虚空门 / 快捷栏限制:原版客户端不受本模组逻辑保护,可能仍会遇到。

规避办法:让加入者也一起安装本模组(协同模式)。

中途加入者的生命 / 属性同步(已知,尚未处理)

触发条件:玩家在关卡进行中加入(协同模式下也会出现)。

  • 新加入的玩家有时血量 / 生命上限等属性显示不同步(队友视角可能看到数值不对)。
  • 当前实现为了避免每帧刷 NRE,会在「生命组件的头像引用为空」时跳过该组件的更新(日志里会出现:PlayerHealth.Update 的头像引用为空,已跳过该类更新以避免每帧 NRE。),因此这类实例上的血量刷新会被跳过。
  • 该问题尚未修复,留待后续版本处理。

其它

  • 过渡期禁入:最后一个提取点完成 → 下一关加载完成前,模组会临时拒绝新玩家加入,这是设计行为。
  • 网络要求:房主未开加速器时,加入者可能连接超时或卡在加载界面。
  • 人数上限:建议与 MorePlayersSimple 搭配;房间人数上限的修改只在创建房间前生效。

❓ 常见问题

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.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

对照参考实现后的补充

参考 RepoLateJoin / LateJoinPlus 的同名实现(SendPlayers):他们的「玩家属性」同步就是 死亡状态 + 血量/上限,并且会 跳过加入者自己的 avatar;SendCosmeticHealth 同步的是地图上的化妆品物体血量,不是玩家。他们的分类表里也没有玩家颜色 / 装扮 / 升级属性的同步。

本版据此补上两处:

  • 跳过加入者自己的 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.* vs LateJoinSimple.*),协议不通,等效单端。只读检测 + 日志,不改变任何行为。

文档

  • 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(GUID cn_xc.LateJoinSimple),房间属性键改为 LateJoinSimple.*;与原版 LateJoinNow 不兼容,不可同时启用。
  • 房间名前缀改为 [中途加入-小菜]。
  • 目标框架 .NET Framework 4.7.2,BepInEx 5.4。