rotate-exclamationSync 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:

  • MEMORYPACK

  • ARAWN_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:

  • SaveSettings exposes enableSync and syncSettings to opt in to Sync.

  • SaveManager creates a SyncManager instance when Sync is enabled.

  • SaveOperationService sends a snapshot or diff after SaveData is serialized.

Important details:

  • Sync uses the plain serialized SaveData bytes before compression or encryption.

  • The slot number is used as the slotId when sending messages.

Related files:

  • Assets/Plugins/CrystalSave/Runtime/ScriptableObjects/SaveSettings.cs

  • Assets/Plugins/CrystalSave/Runtime/Managers/SaveManager.cs

  • Assets/Plugins/CrystalSave/Runtime/Managers/Services/SaveOperationService.cs


Data Flow

  1. A save operation serializes SaveData into bytes.

  2. SyncManager builds a SyncEnvelope with metadata, hash, and Base64 payload.

  3. If diffs are enabled, SyncManager tries to compute a SyncDelta against the last snapshot.

  4. The envelope is broadcast to all registered transports.

  5. On the receiving side, SyncReconciler applies snapshots or diffs per slot.


Core Types

Core

File
Purpose

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

File
Purpose

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

File
Purpose

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

File
Purpose

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

File
Purpose

Assets/Plugins/CrystalSave/Runtime/Sync/Reconciliation/SyncReconciler.cs

Applies snapshots or deltas per slot and requests resync on failure.

Transports

File
Purpose

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, OnRequireSnapshot is 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 SyncEnvelope with 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:

  • InitializeAsync configures transports and auto-connects them when enabled.

  • SendSnapshotAsync and SendDiffFromSnapshotAsync accept raw serialized bytes.


Extending The System

To add a new transport:

  1. Implement ISyncTransport or derive from SyncTransportAsset.

  2. Add an instance to SyncSettings.transports.

  3. Call SyncManager.RegisterTransport for runtime registration.

To change serialization:

  • Implement ISyncSerializer and pass it through SyncTransportConfig during transport initialization.


Limitations And Notes

  • SyncSettings.snapshotIntervalSeconds and diffIntervalSeconds are configuration values only. SyncManager does not schedule timed sends by itself.

  • SyncMessageType includes Ack, Nack, Ping, and Pong, but the current reconciler only handles Snapshot and Diff.

  • SyncEnvelope.payloadHash is populated but not validated by SyncReconciler.

  • 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