State Management and Data Persistence

Summary

State management emerged as one of the most critical and complex challenges in building LLM-powered game engines. This thread documents the evolution from simple “what is state?” questions to sophisticated systems handling world persistence, save systems, scene transitions, and multi-session data synchronization. The community grappled with fundamental questions: What should the LLM handle? What should be programmatic? How do you serialize an entire game world without breaking narrative coherence?

Key Concepts

Game State

The complete set of data representing the current world condition: player stats, NPC states, location details, inventory, quest progress, relationships, time, and environmental conditions.

Persistence

Saving game state in a way that survives application restarts, allows loading, and maintains consistency across sessions.

State vs Story

A crucial distinction: In traditional games, state is separate from narrative. In LLM games, state is story - they’re interleaved.

Scene-Based State Management

Segmenting the world into “cells” or “scenes” with localized state that persists when revisited.

Evolution of Ideas

Phase 1: Foundational Questions (January 2024)

vali98 [06:50]: “So we know we have:

  • A game state
  • We need to send data to the LLM
  • We need to extract data from the LLM
  • We need to update the game state”

The Core Loop

This simple four-step loop became the foundation for all state management discussions: read state → send to LLM → extract updates → write state.

50h100a [07:27]: “in my case, i use them to guide it, as a persistent form of state”

Early realization: Stats and state should guide LLM output, not be generated by it. The program maintains authoritative state.

Phase 2: Scene-Based Persistence (Early 2024)

monkeyrithms [16:22]: “I can ‘reset’ the scene, and all measures of ‘progress’ will reset with it, so I can reset and start over from the beginning (‘A busy tavern in Stonevale.’) but it also resets my status (hunger, thirst etc) and any changes made to any NPC’s character sheets in the course of the scene”

monkeyrithms [16:23]: “or, I can change the scene, so I can walk out the door and it links to the next scene (which is the street of the city). It saves summaries of the chat log in the tavern for all the NPCs involved, and ‘locks in’ any game-state changes accrued during that scene”

Scene as State Boundary

Scenes became natural boundaries for state persistence. Entering/leaving a scene triggers save operations. This mirrors how RPG “cells” work in games like Skyrim.

monkeyrithms [23:07]: “If you’re considering locations to be folders somewhere, and each folder has a .txt file or .json in it explaining what the location details are, and your program creates these as it goes, then you can procedurally generate while storing the information and growing a persistent game map”

monkeyrithms [23:14]: “maybe there’s events that are associated with locations and that event persists (if you revisit the location)”

monkeyrithms [23:16]: “thats essentially how im doing it but with folders instead of an array, and how Fallout or Skyrim manage their big amount of content, a lot of the material is segmented into what belongs in what ‘cell’ or ‘scene’ or location”

Phase 3: Save Systems and State Transitions (Early 2024)

monkeyrithms [01:32]: “so relevant to the short-chats-are-better-than-long-ones thing… I can’t decide if the player going to sleep should permanently advance the time (save game & summarize) or temporarily advance the time (/reset would still take you back to when you first entered that ‘cell’, and roll back all the hours)”

50h100a [01:34]: “Sleep-as-savegame is traditional. and understood, generally”

Save UX Pattern

Using in-game actions (sleeping, resting) as save triggers creates natural checkpoints and is familiar to players.

50h100a [22:03]: “proper rerolling would require preserving copies of the previous game state, or preserving a list of events and rollback steps”

underscore_x [22:07]: “having every game state stored in a structured way would be vital as you flesh things out further. it’s just text”

Technical Debt Warning

Early decisions about state structure compound over time. Structured state from the beginning prevents painful refactors.

Phase 4: veritasr’s Rejection of Programmatic State (February 2024)

A major philosophical shift occurred when veritasr reconsidered the role of programmatic state:

veritasr [23:35]: “The TLDR for people who want to ignore the next handful of posts is essentially that after discussions with a handful of people I’m readjusting some of my expectations on what I’m building. Originally planned on implementing a thing that tracked state and various variables programmatically, but it sounds like the general consensus is that the LLM should be responsible for doing the heavy lifting, state management, and decision making.

Essentially that means I’m punting on:

  • enhanced detail extraction from user input / previous chat context (programmatically anyways, this can still be done through workflows).
  • state management and state transitions within the world
  • logic for managing decisons (since there won’t be any variables or state in program to accomodate this anyways. Can still be handled through workflows).”

Philosophical Pivot

This represents a major fork in approaches: LLM-first (let AI handle state) vs program-first (engine handles state, LLM narrates). veritasr ultimately reversed this decision later.

Phase 5: Data Persistence Implementation (February 2024)

veritasr [01:32]: “Whelp. It’s out there. A bunch of stuff is in a partial state and I haven’t completed data persistence or the API endpoints, but you can see it in it’s current state. https://github.com/Nexus333/ReallmCraft

veritasr [01:53]: “Alright gonna try and start working through data persistence and pulling some of this more together.”

monkeyrithms [02:07]: Review feedback included:

  • “Data Persistence: The write_template function returns a JSON string but does not actually write it to a file. If the intention is to persist the updated template, you would need to open a file in write mode and save the contents.”

veritasr [07:00]: “oh yeah, and actual data persistence now. Guessing prompts is up next, and then workflows then I’ll start thinking about how to cobble together the settings and overarching structure stuff. then Test full scale world generation.”

Phase 6: Universal Data Structures (February 2024)

veritasr [19:52]: “ok.. So starting point / update. I’ve decided to handle this through the concept of templates. Users can define templates of a given type, which are basically customized entities based off of the main data structure.

For each of the fields in the data structure, you can choose how the template generates content in 1 of 6 ways:

  • value variable : hardcodes whatever value is present here
  • range(#, #) number : returns a random number between # and #
  • prompt(prompt_name) string : runs a prompt and dumps the output into a specified field

Template-Based Persistence

Universal data structure + template system = consistent persistence. Every entity type (character, location, item) follows the same storage pattern.

veritasr [20:56]: “yeah. There’s a good benefit here, which is why i stuck with a universal data structure, in the sense that you can be sure that it will be handled the same way every time. Consistency aids in predictability.”

Phase 7: Keywords and Dynamic State (February 2024)

monkeyrithms [23:18]: “just added keywords section to location sheets.. if any are mentioned (in addition to the name of the setting), it triggers the setting/narrator. This really helped with the flow of the scene a lot and made it more flexible. Now it responds to mention of ‘office’ and ‘tower’ and ‘street’ and some other city-stuff, and I generally get the information I’m looking for when I need it. feels way better

and now I can ask the LLM as the scene develops what keywords to add. ‘grocery store’ or maybe ‘gas station’ will add itself to the keywords as new features of settings are introduced. These keywords will persist just like a character’s prior memories of that location do, so when I come back to the old road my companion remembers there was a gas station there. Now it will respond to the mention of gas station.”

Dynamic State Expansion

Allow state to grow organically during gameplay. Keywords added during play persist and become part of the location’s permanent state.

Phase 8: State Types and Segmentation (February 2024)

monkeyrithms [00:58]: “the challenge right now is that there isn’t a lot of gameplay in the first place. State is being kept on certain narrative threads, like environmental (time of day, persistence of inventory so locations ‘remember’ what items you’ve dropped and you can pick them back up, etc), and narrative (quest stages, NPC relationships)”

50h100a [01:01]: “honestly, im not here for a game per se just a world-persistence-engine”

50h100a [01:03]: “but a general world-persistence solution can’t rely on having predefined actions or categories or really anything, and needs to expand on the fly… there’s just no winning”

veritasr [04:33]: “ok. I’m starting to get into the core ‘engine’ territory now that data is solved(ish).

So I’m thinking I need to introduce the concept of location tracking, which is straight forward (it’s just a variable tracked in memory - Then persisted on change).

I’m also thinking of implementing state tracking so that the ‘engine’ is aware of what the current game state is…

theoretically this should help with it trying to determine which pre-processing scripts to load.”

State Categories

Different types of state require different handling:

  • Environmental: Time, weather, location state
  • Narrative: Quest progress, story flags
  • Entity: Character stats, inventory, relationships
  • Meta: Current location, active scene, game mode

Phase 9: Equipment and Visual State Persistence (February 2024)

monkeyrithms [context]: Equipment system with layered clothing: “if there are no objects in the Outer layer, inner layer will be visible. the layers and clothing types can be improved/expanded upon but if the coat is removed, the ballistic vest will become visible.

If you give the coat to another NPC and they put it on, they will now write that they are wearing the coat and this new appearance will persist across scenes”

monkeyrithms [07:05]: “Chest and Legs come with 2 layers, I havent decided about feet — Im not sure how much use-case ‘socks’ is really gonna have in something like this lol. But boots that impart a speed enhancement or jumping enhancement that can be transferred between characters and persists across scenes — that I can see”

monkeyrithms [07:21]: “thats basicaly what I store, also include a flag for whether or not ‘items persist’, because if you drop your money in a tavern that’s gonna be gone the next day. But if it’s your home, or a chest, you want it persistent. Flag for public locations might be important as it determines wehether certain actions will change your reputation or trigger the guards”

Conditional Persistence

Not all state changes are permanent. Public vs. private locations, temporary vs. permanent item drops - persistence rules vary by context.

Phase 10: Multiplayer and Concurrent Access (February 2024)

50h100a [01:28]: “serialize access to the world state”

veritasr [01:28]: “Ah.. that’s a good call.. I’d need to make an instance of the world dbs for each session, so that people don’t bork each other’s data.”

Concurrency Challenge

Multiplayer requires careful state isolation. Each session needs its own state instance or sophisticated locking mechanisms.

Phase 11: Flask API and Persistent Background Processes (February 2024)

veritasr [01:40]: “so… The question is do I leave this as a persistent process running in the background and trigger it through events.. Or do I call the stages as independent functionality run through the loop and only use it when it triggers?”

veritasr [02:21]: “I think I’m just going to leave the functions as classes that get created on the fly. I’ll come back to the actual game loop idea if I see a reason later on to leave something persistent in the background. I don’t think it needs to always be listening. Flask is already doing that.”

50h100a [02:21]: “i dont see a reason for persistence in the background”

Architecture Decision

RESTful API architecture (Flask) eliminates need for persistent background processes. Each request is stateless; persistence happens on-demand.

Phase 12: Deterministic Identifiers and Deduplication (February 2024)

veritasr [17:05]: “hmmmm. So right now for entities I’m using a random guid, but I feel like for storing messages, it’s probably better to use a hash, since it’s deterministic, and I can ensure that I haven’t already stored it. Should prevent adding of duplicates.”

veritasr [17:30]: “Also need to consider how locations are going to work. Initially I imagined that they were going to force the player into a persistent location, but it seems like I sorta have to mix and match.”

Phase 13: Game State as Story State (February 2024)

monkeyrithms [04:30]: “i see how combat might need its own game state, like ‘this is a combat scene’, because the complexity and number of actors can get really high but i dont see why romance needs a story state, that’s more like an npc state I think”

veritasr [09:04]: “That’s sort of the crux of the problem. Game state is story state with llms. They’re sort of interleaved. I feel like you hit the nail on the head though when you referred to it as a scene. Each scene generally has some sort of theme and the transition to another theme is basically a different type of scenario”

Fundamental Insight

Game state = Story state in LLM engines. Unlike traditional games where state (HP, position) is separate from story, in LLM games the state is the narrative context.

underscore_x [02:08]: “This is, to borrow hhaa’s bit, dwarf fortress levels of state management. You don’t need this level of control”

Phase 14: Data Structure Choice - JSON vs Database (February 2024)

monkeyrithms [05:17]: “but the save file, which stores states about the game that ‘normal people’ shouldnt mess with, like your gameHour — that’s in Json”

veritasr [22:09]: “basically persistent objects”

veritasr [18:34]: “Template data now persisting to dbs.”

veritasr [19:52]: “Changed persistence to use DB instead of flat files:”

veritasr [20:29]: “DB is basically json / nosql using TinyDB, but could have just as easily been sqlite or mongo”

Storage Evolution

Progression: Flat files → JSON files → TinyDB → SQLite. As complexity grew, structured databases became necessary.

Phase 15: Configuration and Runtime State (February 2024)

veritasr [19:19]: “Persisting config as world state and loading at startup… Means you can now modify the default values to whatever you want and load state at runtime. I’ll create some endpoints for modifying this.”

veritasr [21:54]: “So… question: Currently having API keys managed as flat files on the backend, would people prefer to have them persisted in config on the frontend instead of creating a flat file?”

Phase 16: World-Level Persistence (February 2024)

veritasr [22:07]: “Theme of the chat is sorta around building persistent worlds in an alternate way to lorebooks.”

veritasr [19:49]: “yeah, I feel ya. The concept of my engine is all focused on wordlbuilding. The characters, locations, quests, lore, etc. is persistent per world. The chats themselves are unique though. That means I could spin up another chat and interact with the character I’m playing now in the place that they’re currently at. Since the world itself is persistent.”

veritasr [19:51]: “spin up a new world, i.e. a scifi game, and all the world details are gone (that way you can create it around new world x).”

Separation of Concerns

World state (locations, NPCs, lore) is persistent across all chats in that world. Chat state (specific conversations, player actions in this playthrough) is per-session. This enables multiple concurrent stories in the same world.

Phase 17: Advanced Persistence Systems (Mid-Late 2024)

veritasr [01:12]: “so the ooc chat thing is actually useful enough for me to make it a real boy and persist the stuff in a db. Working through that and the ability to split generation with another api.”

veritasr [04:49]: “Completions and persistence all hooked up.”

veritasr [03:57]: “Starting to sorta feel like I need a quest tracker.. There’s so many things going on that it’s hard to keep track of everything”

veritasr [04:30]: “Persistent drawer to the left, that way you can look at it while chatting.”

Phase 18: SQLite and Final Architecture (Late 2024 - 2025)

yukidaore [02:22]: “There might be an argument for a real DB in multiplayer, but SQLite is plenty for solo play”

yukidaore [02:24]: “My understanding is that concurrency is a pain in SQLite, but I have no personal experience with it. Only ever used it for solo applications”

appl2613 [02:52]: “not using nested folders and .json files for everything anymore either. no more mess on the computer. Everything is neatly tucked away in SQLite files. An entire ‘game’ comes in a .world file, and saves for current playthroughs of a game are .save files.”

appl2613 [02:53]: “but game has an ‘API’ bridge so that LLMs (including its own built-in agents) can use .json format to manipulate and add new data to the db”

Final Pattern

ChatBot RPG’s solution: .world files (SQLite) contain all game data. .save files contain playthrough state. JSON API layer allows LLM agents to interact with the database without direct SQL access.

Technical Patterns

1. State Flow Architecture

User Input
  ↓
Pre-Processing (extract intent/entities)
  ↓
Load Current State (location, inventory, NPCs, etc.)
  ↓
Send to LLM (state + input → narrative)
  ↓
Post-Processing (extract state changes from narrative)
  ↓
Update State (apply changes)
  ↓
Persist State (save to disk/db)
  ↓
Return Output to User

2. Scene-Based State Management

class Scene:
    - scene_id
    - location_data
    - active_npcs
    - current_state (local)
    - keywords (dynamically added)
    - connections (to other scenes)
    - persistence_rules (what survives leaving)
 
on_exit_scene():
    - Save NPC interaction summaries
    - Persist inventory changes (if applicable)
    - Lock in quest progress
    - Clear temporary state

3. Layered Persistence Model

Level 1: World State (always persists)

  • Locations and geography
  • NPCs and their base properties
  • Items and their definitions
  • Lore and background

Level 2: Playthrough State (persists per save)

  • Player location
  • Inventory
  • Quest progress
  • NPC relationships with player
  • World changes caused by player

Level 3: Session State (volatile)

  • Current conversation
  • Temporary effects
  • Uncommitted changes
  • Draft responses

Level 4: Scene State (conditionally persists)

  • Items dropped in location
  • NPC positions
  • Environmental changes
  • Active events

4. Data Storage Evolution

Phase 1: Flat text files

/locations/tavern.txt
/characters/npc_mara.txt

Phase 2: JSON files

{
  "type": "location",
  "name": "Tavern",
  "description": "...",
  "keywords": ["bar", "drinks"],
  "npcs_present": ["mara_uuid"]
}

Phase 3: Embedded databases (TinyDB, SQLite)

CREATE TABLE entities (
    id TEXT PRIMARY KEY,
    type TEXT,
    data JSON
);

Phase 4: Structured relational + JSON hybrid

CREATE TABLE locations (id, name, description);
CREATE TABLE location_state (location_id, state_json);

Design Principles

  1. State is Truth: The programmatic state is the source of truth, not the LLM output
  2. Scene as Boundary: Scene transitions trigger persistence operations
  3. Conditional Persistence: Not all changes are permanent (public vs private, temporary vs permanent)
  4. Separation of Concerns: World state vs playthrough state vs session state
  5. Deduplication: Use deterministic identifiers (hashes) to prevent duplicate entries
  6. Structured Consistency: Universal data structures ensure predictable persistence
  7. API Abstraction: LLMs interact with state through JSON APIs, not direct database access
  8. Lazy Persistence: Only save when necessary (scene changes, player actions, timeouts)

Implementation Considerations

Performance

  • Write optimization: Batch updates, only persist diffs
  • Read optimization: Cache frequently accessed state in memory
  • Indexing: Proper database indexes for common queries (location lookups, NPC searches)

Data Integrity

  • Validation: Ensure state changes are valid before persisting
  • Rollback capability: Save previous states for undo/reroll functionality
  • Schema versioning: Handle evolving data structures across versions

Concurrency

  • Single player: Simple file locking or exclusive access patterns
  • Multiplayer: Session isolation, per-player state instances, or proper DB transactions

Storage Format Trade-offs

FormatProsConsBest For
Text filesSimple, human-readable, easy debuggingNo structure, slow searches, no atomicityPrototypes, simple games
JSON filesStructured, still readable, portableNo queries, file-level locking, slow for large dataSmall to medium games
TinyDBNo SQL knowledge needed, JSON-like, embeddedLimited query capability, slower than SQLPython-based simple games
SQLiteFull SQL, fast, atomic, embedded, single fileConcurrency limitations, requires SQLMost solo game engines
PostgreSQL/MySQLFull features, concurrent, scalableSetup overhead, not embeddedMultiplayer games

Common Pitfalls

State Management Antipatterns

  1. Trusting LLM-generated state: LLMs hallucinate. Always validate state updates.
  2. No state history: Without rollback capability, player mistakes are permanent.
  3. Conflating session and world state: Mixing temporary and permanent state causes bugs.
  4. Over-persistence: Saving every message is wasteful; batch and deduplicate.
  5. Under-persistence: Losing hours of progress due to rare save points frustrates players.
  6. Schema rigidity: Hard-coded structures break when adding new features.
  7. No validation: Garbage in, garbage persisted, garbage out.

Tools and Technologies

Python Stack

  • TinyDB: Lightweight JSON database
  • SQLite3: Embedded SQL database
  • Flask: REST API for state access
  • json/pickle: Serialization

Storage Patterns

  • Flat files: Simple, debugging-friendly
  • JSON documents: Structured but flexible
  • Relational DB: Structured queries
  • Hybrid: Relational schema + JSON blobs for flexibility

State Management Libraries

  • UUID/ULID: Unique identifiers for entities
  • Hash functions: Deterministic IDs for deduplication
  • File locking: Prevent concurrent write conflicts

Key Insights

  1. Game state = Story state in LLM engines - they’re inseparable
  2. Scene boundaries are natural save points - leverage them
  3. Worlds persist, playthroughs persist, sessions are temporary - three-tier model
  4. Universal data structures enable consistent persistence logic
  5. Sleep as save is intuitive and familiar to players
  6. Conditional persistence (public/private, temporary/permanent) is necessary
  7. SQLite + JSON API emerged as the winning architecture for solo games
  8. State must be validated - never trust LLM-generated state changes directly

Open Questions

Unresolved Issues

  1. How to handle state conflicts when multiple NPCs modify the same location?
  2. What’s the optimal granularity for state updates? (Per message? Per scene? On demand?)
  3. How to implement efficient state diffing for large worlds?
  4. Best practices for schema migration as game features evolve?
  5. How to balance state compression vs. query performance?

Timeline

  • January 2024: Basic state loop defined, scene-based persistence emerges
  • Early 2024: Save systems, sleep-as-checkpoint pattern
  • February 2024: veritasr’s initial “LLM handles state” experiment, later reversed
  • February 2024: Data persistence implementation, JSON → DB migration
  • February 2024: Universal data structures and template systems
  • Mid 2024: Dynamic state expansion (keywords), equipment/visual state
  • Late 2024: Multiplayer considerations, concurrency discussions
  • 2024-2025: SQLite + API pattern emerges as standard
  • July 2025: ChatBot RPG’s .world/.save file system released

Pattern Library

Schema Reference


Core Achievement

The community successfully identified that state management in LLM games is fundamentally different from traditional game engines. Rather than tracking positions and stats, they’re persisting narrative context that must remain coherent across sessions. The winning pattern: program maintains authoritative state, LLM narrates state, three-tiered persistence (world/playthrough/session), and scene-based save boundaries.