Skip to content

Game.Serialization.WriteSystem

Assembly: Assembly-CSharp
Namespace: Game.Serialization

Type: class

Base: GameSystemBase, IWriteBufferProvider

Summary:
WriteSystem is a managed game system used by the save pipeline to collect WriteBuffer instances, schedule jobs to write their contents to the active save stream, and handle optional compression. It coordinates with SaveGameSystem (providing the stream) and SerializerSystem (tracking totalSize), uses Unity.Jobs IJob for background work, and uses GCHandle to pass a StreamBinaryWriter reference into jobs. It supports writing raw buffers and compressed buffers (via CompressionUtils) and ensures proper disposal of native containers and the writer handle.


Fields

  • private SaveGameSystem m_SerializationSystem
    Reference to the SaveGameSystem used to obtain the output stream for writing.

  • private SerializerSystem m_SerializerSystem
    Reference to the SerializerSystem used to accumulate total serialized size and potentially other serialization metadata.

  • private List<(WriteBuffer, BufferFormat)> m_Buffers
    A list of pending buffers (paired with their BufferFormat) that have been added via AddBuffer and await processing.

  • private JobHandle m_WriteDependency
    Current dependency handle for scheduled write/compression jobs. New jobs are scheduled dependent on this; it is also completed before certain operations and on destroy.

  • private GCHandle m_WriterHandle
    A GCHandle pointing to a StreamBinaryWriter instance that wraps the SaveGameSystem stream. Allocated when first needed and freed/disposed in a DisposeWriterJob.

  • private struct WriteRawBufferJob : IJob
    Job that writes an uncompressed buffer. Fields:

  • [ReadOnly] public NativeList<byte> m_Buffer — buffer bytes to write.
  • public GCHandle m_WriterHandle — writer handle to the StreamBinaryWriter.
    Execute: writes the buffer length (int) and then the raw bytes to the writer.

  • private struct WriteCompressedBufferJob : IJob
    Job that writes compressed data. Fields:

  • [ReadOnly] public CompressedBytesStorage m_CompressedData — compressed bytes storage.
  • public int m_UncompressedSize — original uncompressed size.
  • public GCHandle m_WriterHandle — writer handle to the StreamBinaryWriter.
    Execute: builds a BufferHeader (size / compressedSize), writes the header, then writes compressed bytes.

  • private struct DisposeWriterJob : IJob
    Job that disposes the StreamBinaryWriter and frees the GCHandle. Fields:

  • public GCHandle m_WriterHandle
    Execute: calls Dispose on the writer and frees the handle.

  • private struct BufferHeader
    Small struct containing:

  • public int size — uncompressed size.
  • public int compressedSize — compressed size.

Properties

  • public Unity.Jobs.JobHandle writeDependency { get; }
    Returns the current JobHandle representing outstanding write/compression work scheduled by the WriteSystem (m_WriteDependency). Useful for other systems to chain work or ensure completion.

Constructors

  • public WriteSystem()
    Default constructor (preserved). Initialization of managed fields happens in OnCreate.

Methods

  • protected override void OnCreate()
    Initializes the system: gets or creates SaveGameSystem and SerializerSystem, and initializes the m_Buffers list. Called when the system is created.

  • protected override void OnDestroy()
    Completes outstanding write dependencies, disposes any WriteBuffer instances still held in m_Buffers, and calls base.OnDestroy. Ensures no native memory leaks remain.

  • public WriteBuffer AddBuffer(BufferFormat format)
    Adds a new WriteBuffer for the specified BufferFormat. Before adding, it checks the front of the m_Buffers list for completed buffers and processes them (WriteBuffer) to free space. Returns the newly created WriteBuffer for callers to populate.

  • protected override void OnUpdate()
    Called each tick/update: iterates all m_Buffers and calls WriteBuffer on each, then clears m_Buffers. If a StreamBinaryWriter GCHandle has been allocated, schedules a DisposeWriterJob to dispose it once outstanding work completes.

  • private void WriteBuffer(WriteBuffer buffer, BufferFormat format)
    Core logic for turning a WriteBuffer into scheduled write jobs:

  • Allocates and stores a StreamBinaryWriter in m_WriterHandle if not already allocated.
  • Calls buffer.CompleteDependencies() so the buffer is ready to be read.
  • If format == BufferFormat.Raw: increments serializer total size, schedules WriteRawBufferJob and disposes the buffer with the returned dependency.
  • If format.IsCompressed(): converts BufferFormat to CompressionFormat, allocates a CompressedBytesStorage, calls CompressionUtils.Compress (scheduling the compression job), schedules WriteCompressedBufferJob dependent on compression job and current m_WriteDependency, disposes compressed storage when write is complete.
  • Logs a warning if BufferFormat is unsupported.

  • private static void WriteData<T>(StreamBinaryWriter writer, T data) where T : unmanaged
    Unsafe helper: writes any unmanaged struct by getting its address and calling writer.WriteBytes with sizeof(T).

  • private static void WriteData(StreamBinaryWriter writer, NativeArray<byte> data)
    Unsafe helper: writes the bytes from a NativeArray to the writer using GetUnsafeReadOnlyPtr.

  • private static void WriteData(StreamBinaryWriter writer, NativeSlice<byte> data)
    Unsafe helper: writes the bytes from a NativeSlice to the writer using GetUnsafeReadOnlyPtr.

Notes on behavior and safety: - The system uses unsafe code and GCHandle to pass managed StreamBinaryWriter instances into jobs. The writer itself is not directly a job-native object; GCHandle ensures the managed reference is available inside the job Execute method. - Native containers (buffer.buffer, compressed storage) are disposed with job dependencies so disposal happens after jobs finish. - Compression uses a hardcoded compression level of 3 when allocating CompressedBytesStorage and calling CompressionUtils.Compress. - m_SerializerSystem.totalSize is updated to account for added bytes (raw: 4 + rawLength, compressed: 8 + rawLength) — these prefixes account for header sizes (int length / BufferHeader).

Usage Example

// Example usage inside another game system / mod component:
[Preserve]
protected override void OnCreate()
{
    base.OnCreate();
    var writeSystem = World.GetOrCreateSystemManaged<Game.Serialization.WriteSystem>();
    // Request a raw buffer to write custom data into the save.
    WriteBuffer buffer = writeSystem.AddBuffer(BufferFormat.Raw);

    // Fill buffer.buffer (NativeList<byte>) with data and ensure any dependencies are completed
    // (API to add bytes depends on WriteBuffer definition — e.g. buffer.buffer.AddRange(...)).
    // Once the buffer is populated and its dependencies are satisfied, WriteSystem will schedule
    // jobs to write (and optionally compress) it to the save stream on the next update.
}