Class GameStateSnapshot
- Namespace
- Core
- Assembly
- Core.dll
ENGINE - Double-buffer pattern for zero-blocking UI reads
Problem: UI needs province data without blocking simulation Victoria 3 uses locks → "waiting" bars in profiler
Solution: Double-buffer (two NativeArrays)
- Simulation writes to buffer A
- UI reads from buffer B
- After tick: swap pointers (O(1), not memcpy!)
Memory: 2x hot data (~160KB for 10k provinces at 8 bytes) Performance: O(1) pointer swap (no copying!) Staleness: Zero (UI reads completed tick)
Pattern from Paradox analysis:
- Victoria 3 locks = bad (waiting bars)
- Snapshots = good (zero blocking)
- Double-buffer = optimal (no memcpy overhead)
public class GameStateSnapshot
- Inheritance
-
objectGameStateSnapshot
Properties
Capacity
Get buffer capacity
public int Capacity { get; }
Property Value
- int
CurrentWriteBufferIndex
Get current write buffer index (0 or 1) - for debugging
public int CurrentWriteBufferIndex { get; }
Property Value
- int
IsInitialized
Check if initialized
public bool IsInitialized { get; }
Property Value
- bool
Methods
Dispose()
Dispose both buffers
public void Dispose()
GetProvinceReadBuffer()
Get read buffer (UI reads here) Thread-safe: UI can read while simulation writes to other buffer
public NativeArray<ProvinceState> GetProvinceReadBuffer()
Returns
GetProvinceWriteBuffer()
Get write buffer (simulation writes here)
public NativeArray<ProvinceState> GetProvinceWriteBuffer()
Returns
Initialize(int)
Initialize double buffers
public void Initialize(int provinceCapacity)
Parameters
provinceCapacityint
SwapBuffers()
Swap buffers after simulation tick (O(1) pointer flip)
Call from TimeManager after tick completes:
- Simulation just finished writing to write buffer
- Flip pointers so UI sees new data
- Simulation starts writing to old read buffer next tick
Performance: O(1) pointer flip only
NOTE: This just flips the buffer pointers. The caller (ProvinceSystem.SwapBuffers) is responsible for copying dirty entries to preserve persistent state. This separation allows ProvinceSystem to use dirty tracking for O(dirty) copies instead of O(all) full buffer copies.
public void SwapBuffers()
SyncBuffersAfterLoad()
Synchronize read buffer with write buffer (copy write → read)
Used after scenario loading to ensure both buffers have the same initial data. This prevents the first tick from reading from an empty buffer after the first swap.
Performance: O(n) memcpy - only call during initialization, not during gameplay
public void SyncBuffersAfterLoad()