Data Format
This page documents the actual binary persistence format currently implemented in Moongate v2.
Serialization Technology
- Serializer:
MemoryPackover runtime entity contracts - Snapshot container:
WorldSnapshot - Journal payload item:
JournalEntry - Snapshot shape:
EntitySnapshotBucket[] - Journal routing:
TypeId + Operation + Payload
Snapshot File
Path:
save/world.snapshot.bin
Write behavior:
- Truncate snapshot stream (
SetLength(0)) - Serialize
WorldSnapshotdirectly intoworld.snapshot.bin - Flush stream
Read behavior:
- If snapshot stream is empty: returns
null - Otherwise: deserializes
WorldSnapshotwith MemoryPack
There is no trailing checksum block in the snapshot file.
Journal File
Path:
save/world.journal.bin
Each record layout in the file:
[int32 payloadLength LE]
[byte[payloadLength] payload]
[uint32 checksum LE]
Where:
payloadisMemoryPackSerializer.Serialize(entry)checksumis computed frompayload
Journal Validation Rules
During replay (ReadAllAsync):
- Record length must be present and valid
- Length must be in allowed bounds
- Payload must be fully readable
- Checksum must match
- Payload must deserialize to
JournalEntry
Replay stops at first invalid/truncated record.
Operational Lifecycle
- Repositories append operations to journal
- Unit of work builds fresh
WorldSnapshotfrom state store - Snapshot save completes
- Journal entries included in the captured snapshot are trimmed through the captured sequence id
Persistence Options
PersistenceOptions currently includes:
SnapshotFilePathJournalFilePathEnableFileLock(default:true)
No extra sidecar/checksum/history paths are currently configured.
Current Entity Snapshot Shape
WorldSnapshot stores an array of EntitySnapshotBucket values. Each bucket contains:
TypeIdTypeNameSchemaVersionPayload
Payload is a MemoryPack array of runtime entities for one registered entity kind. Core descriptors currently cover:
- accounts
- mobiles
- items
- bulletin board messages
- help tickets
Concrete entity contracts live directly in Moongate.UO.Data.Persistence.Entities and are serialized without a separate snapshot DTO layer.
Within those buckets, UOMobileEntity currently persists:
BaseStatsBaseResistancesResourcesEquipmentModifiersRuntimeModifiersModifierCapsSkills- status-oriented scalars such as
StatCap,Followers,FollowersMax,Weight,MaxWeight,MinWeaponDamage,MaxWeaponDamage, andTithing
Skills are stored as explicit entries containing:
SkillIdValueBaseCapLock
UOItemEntity currently persists:
CombatStatsModifiers
Each bulletin-board message snapshot stores:
MessageIdBoardIdParentIdOwnerCharacterIdAuthorSubjectPostedAtUtcBodyLines
This means snapshot payloads now preserve:
- item combat requirements and AoS-style item modifiers
- mobile aggregated equipment/runtime modifiers
- mobile skill tables used by
0x3A - mobile modern status data used by
0x11 - classic bulletin board message trees used by
0x71
Current Journal Entry Shape
JournalEntry currently stores:
SequenceIdTimestampUnixMillisecondsTypeIdOperationPayload
Operation is a generic enum:
UpsertRemove
The payload format is provided by the registered entity descriptor for the matching TypeId:
Upsertpayloads serialize one entityRemovepayloads serialize one entity key
Previous: Persistence Overview | Next: Persistence Repositories