Skip to content

fix: preserve agent inventories when serializing GameState - #374

Open
tsubasakong wants to merge 1 commit into
JackHopkins:mainfrom
tsubasakong:fix/inventory-serialization-pydantic-extra
Open

fix: preserve agent inventories when serializing GameState#374
tsubasakong wants to merge 1 commit into
JackHopkins:mainfrom
tsubasakong:fix/inventory-serialization-pydantic-extra

Conversation

@tsubasakong

Copy link
Copy Markdown

Problem

Inventory (fle/env/entities.py) is a pydantic model with extra="allow", so item counts are stored in __pydantic_extra__ and inventory.__dict__ is always {}. Two call sites serialize inventories via __dict__:

  • GameState.to_raw() (fle/commons/models/game_state.py) — every saved game state gets "inventories": [{}]
  • the reset tool client (fle/env/tools/admin/reset/client.py) — FactorioInstance.reset(game_state) passes inventories through this path, so every GameState restore silently resets all agents to empty inventories

This appears to be a regression: Inventory previously populated self.__dict__ directly in a custom __init__ (still visible, commented out, in fle/env/entities.py), which is why __dict__-based serialization used to work — and why it broke silently when that constructor was removed.

Fix

Serialize via model_dump() (which includes pydantic extras), with items() / __dict__ fallbacks for non-pydantic inputs. Plain-dict inventories are unaffected.

Tests

Adds server-free regression tests under tests/unit/ (with a local conftest that shadows the repo-level autouse fixture requiring a live Factorio server):

  • to_raw preserves pydantic-extra inventories
  • plain-dict inventories still pass through
  • multi-agent to_rawparse_raw round-trip
  • the reset client sends actual item counts in its JSON payload

Negative control: on the unfixed code these tests fail 3/4 (the only pass is the plain-dict case, which was never broken); with the fix, 4/4 pass.

🤖 Generated with Claude Code

Inventory (fle/env/entities.py) is a pydantic model with extra="allow",
so item counts are stored in __pydantic_extra__ and inventory.__dict__
is always {}. GameState.to_raw() and the reset tool client serialized
inventories via __dict__, which:

- wrote "inventories": [{}] into every saved game state, and
- reset every agent to an EMPTY inventory whenever a GameState was
  restored (FactorioInstance.reset(game_state) passes inventories
  through the reset tool).

This worked historically because Inventory populated self.__dict__ in a
custom __init__ (still visible, commented out, in entities.py) and broke
silently when that constructor was removed.

Serialize via model_dump() instead, with items()/__dict__ fallbacks for
non-pydantic inputs. Adds server-free regression tests under tests/unit/
including a to_raw -> parse_raw round-trip; on the previous code these
fail 3/4, with the fix 4/4 pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant