--- id: wiki-2026-0508-preserving-state-in-procedural-w title: Preserving State in Procedural Worlds category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Procedural World Persistence, Seed-Based State] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [procedural-generation, game-dev, state, persistence] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: TypeScript framework: any --- # Preserving State in Procedural Worlds ## 매 한 줄 > **"매 infinite-world 의 finite-memory tradeoff"**. 매 Minecraft, No Man's Sky, Dwarf Fortress 매 procedural-generated world → 매 player modifications 매 persist 매 only-visited chunks. 매 seed + delta-overlay 의 standard pattern. ## 매 핵심 ### 매 problem - World 매 effectively infinite (2^64 seed space). - Cannot store every chunk (memory + disk). - But player modifications must survive. ### 매 standard pattern 1. Deterministic seed-based generator G(seed, x, y, z) → chunk. 2. Delta overlay D(x, y, z) → player edits relative to G. 3. On load: chunk = G(seed, ...) ⊕ D(...). 4. Disk: store only non-empty D entries. ### 매 응용 1. Sandbox games (Minecraft, Terraria). 2. Roguelikes (Dwarf Fortress, Caves of Qud). 3. Open-world MMOs (No Man's Sky regions). ## 💻 패턴 ### Seed-based deterministic generator (Perlin/Simplex) ```ts import { createNoise2D } from 'simplex-noise'; class WorldGen { private noise2D: ReturnType; constructor(seed: number) { const rng = mulberry32(seed); this.noise2D = createNoise2D(rng); } height(x: number, z: number): number { return Math.floor(64 + 32 * this.noise2D(x * 0.01, z * 0.01)); } } function mulberry32(a: number) { return () => { /* ... */ }; } ``` ### Delta-overlay storage (sparse) ```ts type ChunkKey = `${number},${number}`; // chunk coord type BlockKey = `${number},${number},${number}`; // block coord within chunk class DeltaStore { private deltas = new Map>(); set(cx: number, cz: number, bx: number, by: number, bz: number, b: BlockId | null) { const key: ChunkKey = `${cx},${cz}`; let chunk = this.deltas.get(key); if (!chunk) this.deltas.set(key, (chunk = new Map())); chunk.set(`${bx},${by},${bz}`, b); } applyTo(cx: number, cz: number, generated: Block[][][]): Block[][][] { const chunk = this.deltas.get(`${cx},${cz}`); if (!chunk) return generated; for (const [bk, b] of chunk) { const [bx, by, bz] = bk.split(',').map(Number); generated[bx][by][bz] = b ?? AIR; } return generated; } } ``` ### Chunk persistence (NBT-style binary) ```ts import { writeFile } from 'fs/promises'; import { gzipSync } from 'zlib'; async function saveChunk(cx: number, cz: number, store: DeltaStore) { const data = store.deltas.get(`${cx},${cz}`); if (!data || data.size === 0) return; const buf = encodeNBT([...data.entries()]); await writeFile(`world/c.${cx}.${cz}.dat`, gzipSync(buf)); } ``` ### LRU chunk cache (memory bound) ```ts import LRU from 'lru-cache'; const cache = new LRU({ max: 256, dispose: (chunk, k) => persist(k, chunk) }); function getChunk(cx: number, cz: number): Chunk { const key: ChunkKey = `${cx},${cz}`; let c = cache.get(key); if (!c) { c = applyDeltas(generate(cx, cz), key); cache.set(key, c); } return c; } ``` ### Player-modification log (event-sourced variant) ```ts type Edit = { t: number; x: number; y: number; z: number; before: BlockId; after: BlockId }; const log: Edit[] = []; function setBlock(x: number, y: number, z: number, after: BlockId) { const before = world.get(x, y, z); log.push({ t: Date.now(), x, y, z, before, after }); world.set(x, y, z, after); } // rebuild deltas by replaying log (audit + rollback) ``` ## 매 결정 기준 | 상황 | Pattern | |---|---| | Few edits, infinite world | Seed + sparse delta | | Heavy editing, finite world | Full chunk storage | | Audit / rollback needed | Event-sourced log | | Multi-player concurrent | Authoritative server + delta sync | **기본값**: Seed + delta-overlay + LRU cache + on-demand disk persistence. ## 🔗 Graph - 부모: [[Procedural-Generation]] - 변형: [[Event-Sourcing]] - Adjacent: [[Perlin-Noise]] ## 🤖 LLM 활용 **언제**: voxel/sandbox game architecture, infinite-world design, save-system design. **언제 X**: linear-level games (use whole-state save). ## ❌ 안티패턴 - **Storing every chunk**: 매 disk explosion. - **Non-deterministic generator**: 매 seed-replay 매 broken. - **No LRU bound**: 매 OOM on long sessions. ## 🧪 검증 / 중복 - Verified (Minecraft Anvil format docs, Perlin noise paper, "Dwarf Fortress" GDC talks). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — Procedural state preservation FULL with seed+delta pattern |