Grandpa Minion
Hunter
- Version
- 3.0.1
- Platform
- Windows
TFP BUG REPORT —
Summary: InvalidOperationException in TriggerManager.CheckPowerState — coroutine yields while enumerating a mutable trigger list (plus a second, unfixed NRE in TriggerManager.TriggerBlocks)Game Version: V 3.0.1 (b4) — latest_experimental
OS/Platform: Linux dedicated server (LinuxServer 64 Bit), Windows clients
Game mode: Dedicated server, MP client
Did you wipe old saves? Yes — fresh world/seed created on V3.0.x
Did you start a new game? Yes
Did you validate your files? Yes
Are you using any mods? Yes — see the "About the mods" note below. Neither the mods nor any Harmony patch touches TriggerManager; the exception is entirely within vanilla code.
EAC on/off: ON
Status: Sometimes — intermittent. In one 3-hour session with 13–28 players: 3 occurrences across 87 player connects (~3%). It is a race, so it will not reproduce on every join.
Bug Description
Two separate defects in TriggerManager. Both are uncaught exceptions that escape to the Unity frame boundary during normal player activity. Neither is fatal (the server continues), but both occur in ordinary gameplay: player connects, teleports, and block triggers.BUG 1 — InvalidOperationException in TriggerManager.CheckPowerState (new)
InvalidOperationException: Collection was modified; enumeration operation may not execute.at System.Collections.Generic.List`1+Enumerator[T].MoveNextRare () [0x00013]
at System.Collections.Generic.List`1+Enumerator[T].MoveNext () [0x0004a]
at TriggerManager+<CheckPowerState>d__21.MoveNext () [0x00215]
at UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) [0x00026]
Log context (immediately preceding, every time):
21:31:52 INF RequestToSpawnPlayer: 189, Grandpa Minion, 7
21:31:52 INF Created player with id=189
21:31:52 INF Player connected, entityid=189, name=Grandpa Minion, ...
21:31:52 INF teleport success!
InvalidOperationException: Collection was modified; ...
Cause (from the compiled 3.0.1 assembly): CheckPowerState is a coroutine that yields in the middle of a foreach. It enumerates BlockTrigger.TriggeredByIndices (List<byte>) and, nested inside that, PrefabTriggerData.Triggers (List<BlockTrigger>). Inside the inner loop it yields (waiting on the chunk at rootPos), so the enumeration is suspended across frames. If anything registers or unregisters a trigger during that yield window, the enumerator is invalidated and MoveNext() throws when the coroutine resumes. A player connecting/teleporting into a prefab does exactly that. The stack offset [0x00215] lands after the post-yield resume point, which is consistent with this.
Suggested fix: don't enumerate a live collection across a yield. Snapshot it (foreach (var t in pfd.Triggers.ToList())) or iterate by index with a bounds re-check on resume.
BUG 2 — ArgumentNullException in TriggerManager.TriggerBlocks (previously reported, still unfixed)
AugumentNullException: Value cannot be null. Parameter name: keyat System.Collections.Generic.Dictionary`2.FindEntry (TKey key)
at System.Collections.Generic.Dictionary`2.ContainsKey (TKey key)
at TriggerManager.TriggerBlocks (EntityPlayer player, PrefabInstance instance, BlockTrigger trigger) [0x00009]
at Block.HandleTrigger (EntityPlayer, World, Vector3i, BlockValue) [0x00069]
at NetPackageBlockTrigger.ProcessPackage (World, GameManager) [0x00033]
at ConnectionManager.ProcessPackages (...)
Cause: TriggerBlocks calls PrefabDataDict.ContainsKey(instance) with no null check. If a triggered block does not resolve to a prefab instance, instance arrives null and the dictionary lookup throws. The method body is byte-identical in 3.0.0 b259 and 3.0.1 b4, so this is pre-existing, not a 3.0.1 regression. It is the same defect family as the NetPackageLockRequest NRE that was fixed in 3.0.1.
Suggested fix: if (instance == null) return; before the dictionary access (or use TryGetValue).
Steps to reproduce
Bug 1 (race — intermittent):- Dedicated server, V3.0.1 b4, world with POIs containing powered/trigger blocks.
- Have players active in/near POIs so trigger registration is churning (chunk loads, POI resets, sleeper volumes spawning).
- Have a player connect and spawn/teleport into a POI area.
- Repeat. The exception appears in the server log shortly after teleport success! on a small fraction of connects (~3% observed).
Dedicated server, V3.0.1 b4.
- A client activates a trigger-capable block whose position does not resolve to a prefab instance server-side.
- NetPackageBlockTrigger.ProcessPackage → Block.HandleTrigger → TriggerManager.TriggerBlocks throws.
Actual result
Uncaught exceptions escape to the Unity boundary. Server survives, but the affected trigger/power evaluation is abandoned and remaining package handling for that connection that frame may be skipped.Expected result
No exception. Trigger/power state evaluation completes; a null prefab instance is handled gracefully.About the mods (stated up front)
The server runs a large mod set, so I want to be straightforward rather than have this dismissed on that basis:- No mod patches, references, or otherwise touches TriggerManager. Verified against the compiled mod assemblies.
- Both defects are visible statically in your own compiled 3.0.1 code, independent of any repro: CheckPowerState yields inside a foreach over a mutable list, and TriggerBlocks calls ContainsKey on an unchecked parameter. Both can be confirmed by reading the source — no modded server required.
- teleport success! in the log context is a vanilla log line (spawn teleport), not a mod's.
- Reproduction Steps
Steps to reproduce
Bug 1 (race — intermittent):
- Dedicated server, V3.0.1 b4, world with POIs containing powered/trigger blocks.
- Have players active in/near POIs so trigger registration is churning (chunk loads, POI resets, sleeper volumes spawning).
- Have a player connect and spawn/teleport into a POI area.
- Repeat. The exception appears in the server log shortly after teleport success! on a small fraction of connects (~3% observed).
Dedicated server, V3.0.1 b4.
- A client activates a trigger-capable block whose position does not resolve to a prefab instance server-side.
- NetPackageBlockTrigger.ProcessPackage → Block.HandleTrigger → TriggerManager.TriggerBlocks throws.
- Link to Logs
- https://discord.gg/ZfcsJU9TQC
- Link to Screenshot/Video
- https://discord.gg/ZfcsJU9TQC