Sync Module
Added in Version 1.7.0 - Currently in beta
This guide documents the Sync module in Crystal Save. The Sync module is transport-agnostic scaffolding for sending save snapshots and optional byte diffs across a network transport. It is intended for real-time or near-real-time replication of save data between peers or between client and server.
Overview
The Sync module builds SyncEnvelope messages that contain:
Message type and channel
Slot identifier and sequence number
Timestamp and payload hash
Base64-encoded payload
SyncManager is the orchestration layer. It caches the last snapshot per slot, builds diffs when enabled, and broadcasts messages through registered transports.
Build Flags
All Sync scripts compile only when the following scripting define symbols are enabled:
MEMORYPACKARAWN_REMEMBERME
The Netcode for GameObjects transport also requires:
REMEMBERME_NGO_PRESENT
Add these via Project Settings > Player > Scripting Define Symbols for the relevant build target.
Integration With SaveManager
Crystal Save integrates the Sync module at save time:
SaveSettingsexposesenableSyncandsyncSettingsto opt in to Sync.SaveManagercreates aSyncManagerinstance when Sync is enabled.SaveOperationServicesends a snapshot or diff afterSaveDatais serialized.
Important details:
Sync uses the plain serialized
SaveDatabytes before compression or encryption.The slot number is used as the
slotIdwhen sending messages.
Related files:
Assets/Plugins/CrystalSave/Runtime/ScriptableObjects/SaveSettings.csAssets/Plugins/CrystalSave/Runtime/Managers/SaveManager.csAssets/Plugins/CrystalSave/Runtime/Managers/Services/SaveOperationService.cs
Data Flow
A save operation serializes
SaveDatainto bytes.SyncManagerbuilds aSyncEnvelopewith metadata, hash, and Base64 payload.If diffs are enabled,
SyncManagertries to compute aSyncDeltaagainst the last snapshot.The envelope is broadcast to all registered transports.
On the receiving side,
SyncReconcilerapplies snapshots or diffs per slot.
Core Types
Core
Assets/Plugins/CrystalSave/Runtime/Sync/SyncManager.cs
Orchestrates sync, caches snapshots, builds envelopes, and broadcasts messages.
Assets/Plugins/CrystalSave/Runtime/Sync/SyncSettings.cs
ScriptableObject config for enabling sync, intervals, and transport list.
Assets/Plugins/CrystalSave/Runtime/Sync/SyncJsonSerializer.cs
Default serializer using JsonUtility and UTF-8 bytes.
Models
Assets/Plugins/CrystalSave/Runtime/Sync/Models/SyncEnvelope.cs
Wire payload containing message metadata and Base64 payload.
Assets/Plugins/CrystalSave/Runtime/Sync/Models/SyncMessageType.cs
Enumerates message types (Snapshot, Diff, Ack, Ping, etc).
Assets/Plugins/CrystalSave/Runtime/Sync/Models/SyncChannel.cs
Channel hint for transport behavior.
Assets/Plugins/CrystalSave/Runtime/Sync/Models/SyncTransportConfig.cs
Bootstrap config passed to transports.
Assets/Plugins/CrystalSave/Runtime/Sync/Models/SyncResult.cs
Simple success or failure result container.
Interfaces
Assets/Plugins/CrystalSave/Runtime/Sync/Interfaces/ISyncTransport.cs
Contract for transports (init, connect, send, receive).
Assets/Plugins/CrystalSave/Runtime/Sync/Interfaces/ISyncSerializer.cs
Contract for payload serialization.
Assets/Plugins/CrystalSave/Runtime/Sync/Interfaces/ISyncClock.cs
Minimal clock abstraction (ticks).
Diff System
Assets/Plugins/CrystalSave/Runtime/Sync/Diff/SyncDeltaCodec.cs
Builds and applies byte-level deltas and hashes.
Assets/Plugins/CrystalSave/Runtime/Sync/Diff/SyncDelta.cs
Delta container (base hash, lengths, chunks).
Assets/Plugins/CrystalSave/Runtime/Sync/Diff/SyncDeltaChunk.cs
A changed byte range (offset, data).
Reconciliation
Assets/Plugins/CrystalSave/Runtime/Sync/Reconciliation/SyncReconciler.cs
Applies snapshots or deltas per slot and requests resync on failure.
Transports
Assets/Plugins/CrystalSave/Runtime/Sync/Transports/SyncTransportAsset.cs
ScriptableObject base class implementing ISyncTransport.
Assets/Plugins/CrystalSave/Runtime/Sync/Transports/NGOSyncTransport.cs
Netcode for GameObjects transport via CustomMessagingManager.
Diff System
SyncDeltaCodec performs a linear byte comparison and stores only changed segments. It returns false if:
No bytes changed.
The delta would be too large (90 percent or more of the new payload).
Each delta includes a SHA-256 hash of the base snapshot. ApplyDelta only succeeds when the base hash matches the receiver's last snapshot.
Reconciliation
SyncReconciler keeps state per slot:
Last snapshot bytes
Last snapshot hash
Last sequence number
Behavior summary:
Snapshot messages replace the stored snapshot.
Diff messages require a valid base snapshot and a matching base hash.
If the delta cannot be applied,
OnRequireSnapshotis raised.Messages with a sequence number less than or equal to the last seen sequence are ignored.
Transports
A transport is any class that implements ISyncTransport or derives from SyncTransportAsset.
NGOSyncTransport is provided for Netcode for GameObjects:
Registers a named message with
CustomMessagingManager.Serializes
SyncEnvelopewith the configured serializer.Sends to all clients when running as server, or to the server when running as client.
Create transport assets via the CreateAssetMenu entry:
Crystal Save/Sync/NGO Sync Transport
Example Usage
Notes:
InitializeAsyncconfigures transports and auto-connects them when enabled.SendSnapshotAsyncandSendDiffFromSnapshotAsyncaccept raw serialized bytes.
Extending The System
To add a new transport:
Implement
ISyncTransportor derive fromSyncTransportAsset.Add an instance to
SyncSettings.transports.Call
SyncManager.RegisterTransportfor runtime registration.
To change serialization:
Implement
ISyncSerializerand pass it throughSyncTransportConfigduring transport initialization.
Limitations And Notes
SyncSettings.snapshotIntervalSecondsanddiffIntervalSecondsare configuration values only.SyncManagerdoes not schedule timed sends by itself.SyncMessageTypeincludes Ack, Nack, Ping, and Pong, but the current reconciler only handles Snapshot and Diff.SyncEnvelope.payloadHashis populated but not validated bySyncReconciler.The default serializer is
JsonUtility, which does not support polymorphic serialization.Diffs are byte-level and may be less effective if the payload changes substantially between frames.
Last updated