Skip to content

ElectricalProgressive Core: DamageManager KeyNotFoundException during physics / tickables #42

Description

@Baldrvolsk

English (short)

When an entity collides with an ElectricalProgressive block, DamageManager.DamageEntity throws KeyNotFoundException: The given key '0' was not present in the dictionary. Vintage Story surfaces this as Error while enumerating tickables from the physics thread. Short burst (8 errors in ≈4 seconds) with ≈2k tickables online — no server crash, but entity damage/collision handling fails for that interaction.

English — full issue body (copy to GitHub)

Title: DamageManager.DamageEntity: KeyNotFoundException for key 0 on entity collide (tickables / physics errors)

Labels (suggested): bug, core, basics, crash-adjacent, physics

Summary

BlockEBase.OnEntityCollide calls DamageManager.DamageEntity. Inside DamageManager (≈ line 220) code indexes a dictionary with key 0 (dict[0] / get_Item(0)) without a presence check. When that entry is missing, the exception escapes into the engine physics path and is reported as:

Error while enumerating tickables. Tickables total count is N

followed by the KeyNotFoundException.

Environment

Component Version / note
Vintage Story 1.22.3 (Stable), network 1.22.6, API 1.22.0
electricalprogressivecore 3.3.0
electricalprogressivebasics 3.3.1
electricalprogressiveqol 3.3.1 (loaded; not in this stack)
electricalprogressiveindustry 0.7.0
electricalprogressivetransport 2.0.0
OS / runtime Linux server (Ubuntu), .NET Server GC

Steps to reproduce

Not fully isolated to a single block type in our logs, but the call path is deterministic:

  1. Have ElectricalProgressive blocks in the world that implement damage-on-collide via BlockEBase.OnEntityCollide.
  2. Move a player/entity into collision with such a block while electrical parameters / EParams state is incomplete or missing index 0.
  3. Observe server-main.log for Error while enumerating tickables + KeyNotFoundException with the stack below.

On our server the burst coincided with a player (Useewa) reconnecting / moving near underground coordinates (HUD ≈ 689 … −1377, world ≈ 512682 5–8 510652), with corpse cleanup spam immediately before the errors. Exact block code at the collide site was not printed by the engine.

Expected

  • Missing electrical parameter slot / dictionary key is treated as “no damage” or default params.
  • Physics tick continues without an unhandled exception.
  • At most one diagnostic warning if state is corrupt.

Actual

Exception on the physics worker; VS logs tickable enumeration errors. On 2026-08-08 we recorded 8 such errors between 18:35:57 and 18:36:01, with tickable counts rising ≈ 2084 → 2142. Server did not crash; activity resumed normally afterward.

Stack trace (representative)

[Error] Error while enumerating tickables. Tickables total count is 2084
[Error] Exception: The given key '0' was not present in the dictionary.
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at ElectricalProgressive.Utils.DamageManager.DamageEntity(
        IWorldAccessor world, Entity entity, BlockPos pos, BlockFacing facing,
        EParams[] AllEparams, Block block, Single specifiedDamage)
     in .../ElectricalProgressive-Core/Utils/DamageManager.cs:line 220
   at ElectricalProgressive.Content.Block.BlockEBase.OnEntityCollide(
        IWorldAccessor world, Entity entity, BlockPos pos, BlockFacing facing,
        Vec3d collideSpeed, Boolean isImpact)
     in .../ElectricalProgressive-Basics/Content/Block/BlockEBase.cs:line 65
   at Vintagestory.API.MathTools.CollisionTester.ApplyTerrainCollision_Patch1(...)
   at Vintagestory.GameContent.EntityBehaviorControlledPhysics.ApplyTests(...)
   at Vintagestory.GameContent.EntityBehaviorControlledPhysics.OnPhysicsTick(Single dt)
   at Vintagestory.Server.PhysicsManager.DoWork(Int32 threadNumber)

Root cause (mod-side)

In DamageManager.cs ≈ line 220, dictionary access assumes key 0 always exists (likely voltage tier, facing bucket, or EParams channel). For some placed blocks / unloaded BE state / partial AllEparams, that key is absent.

BlockEBase.OnEntityCollide (Basics, ≈ line 65) invokes damage without guarding against incomplete params.

Suggested fix

  1. Use TryGetValue(0, out var …) (or ContainsKey) and fall back to a safe default / skip damage.
  2. Validate AllEparams / block entity electrical state before indexing.
  3. Catch and log once per block position (rate-limited) instead of letting the exception reach PhysicsManager.
  4. Add a regression test: entity collide with EP block that has empty/partial param dictionary.

Impact

  • Severity: Medium (no hard crash observed; physics/damage path broken for the colliding entity; noisy errors; potential unfair damage skip or repeated fails while standing in the block).
  • Can appear as random “tickables” errors when players walk into machines/cables.

Related context from our logs (same minute)

  • playercorpseforked destroyed/recreated corpses for the same player just before the burst.
  • Player death a few minutes later was from a mob (bell-headed shivers), not necessarily from this exception.
  • Not related to the separate EOven × XSkills AmbiguousMatchException spam earlier the same day.

Русский (кратко)

При коллизии сущности с блоком ElectricalProgressive DamageManager.DamageEntity падает с KeyNotFoundException (ключ 0). Движок пишет Error while enumerating tickables. У нас — 8 ошибок за ≈4 секунды (18:35:57–18:36:01). Сервер жив, но обработка урона/физики в этом кадре ломается.

Русский — подробности

Заголовок: DamageManager.DamageEntity: нет ключа 0 в словаре при OnEntityCollide (ошибки tickables)

Цепочка

PhysicsManager → controlled physics → BlockEBase.OnEntityCollide (Basics ≈65) → DamageManager.DamageEntity (Core ≈220) → Dictionary[0] без проверки.

Окружение

  • VS 1.22.3
  • EP Core 3.3.0, Basics 3.3.1

Наблюдение (наш сервер, 2026-08-08)

  • Время: 18:35:57–18:36:01
  • 8 пар Error + Exception
  • Tickables total count ≈ 2084…2142 (это размер списка tickable’ов, не число багов)

Перед этим игрок Useewa перезаходил; около HUD ≈ 689 / −1377 шли сообщения playercorpseforked про труп. Точный код электроблока в лог не попал.

Ожидание

Нет ключа → нет урона / дефолтные параметры, без exception в physics thread.

Предлагаемый фикс

TryGetValue, валидация EParams, не пробрасывать исключение в PhysicsManager; один rate-limited warning при битом состоянии блока.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions