I want to delta compress snapshots before sending them over the wire unreliably (UDP) for a client/server game. The goal is to be able to serialize and compress an array of components into a snapshot struct, generate a delta between two snapshot, apply that delta on the other end and finally, interpolate between (any given) two snapshots on the client. This probably sounds familiar to most of you.
tl;dr: there are a couple of questions embedded in this post: 1. Should snapshots be stored in their snapshot buffer in binary or deserialized form? and 2. What libraries or mechanisms are recommended for actually generating a delta and applying interpolation generically, or at all?
A few naive approaches to this, and some concerns:
- If snapshots are stored in the snapshot buffer in a serialized state, I.e. byte array, I can use FossilDelta or a similar lib to generate and apply the deltas directly in binary, and a fast and simple to use lib such as MemoryPack to serialize. However, it means deserializing snapshot binary data when ready to interpolate on the client — that or try to build something that can interpolate without deserializing via reflection which sounds like overkill. this Won’t work completely as I would need some data from the snapshot on reception, such as server tick. This still seems like the best solution if it can work.
- If the snapshot buffer maintains an array of deserialized snapshots, then interpolation is no longer a concern, but now I have to serialize snapshots before generating or applying deltas. Which, is obviously wasteful, as would be storing both binary and object versions of a snapshot.
- I could instead of using MemoryPack and FosilDelta, use something like NetCode lib which is a BitWriter that allows you to serialize (and deserialize) directly against a baseline object. I no longer need FossilDelta lib since NetCode’s BitWriter supports delta compression during serialization. This would give me the best of both worlds but I now have to write all the serialization code by hand using BitWriter for all classes and structs I want serialized.
- Another approach, may be to use FlatBuffers via FlatSharp and the corresponding delta lib for C# FlatSharpDeltawhich solves for #2 since it provides accessors I could use to iterate a snapshots components and data for client snapshot interpolation without deserializing. This however means I now have to manage a separate FlatBuffers schema and effectively duplicate component structs just for the sake of serialization, accessors are cool, but I don’t need a schema versioning anyway and so it feels like overkill.
I also need to send reliable data in the form of larger nested object arrays, and so ideally, I can stick to a single serialization library.