Some checks failed
CultMesh QUIC native / Build win32-x64 (push) Has been skipped
CultMesh QUIC native / Build linux-x64 (push) Has been skipped
CultMesh QUIC native / Release native binaries (push) Has been skipped
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (push) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (push) Has been cancelled
119 lines
6.9 KiB
Markdown
119 lines
6.9 KiB
Markdown
# GameCult.Caching.MessagePack
|
|
|
|
`GameCult.Caching.MessagePack` provides the canonical CultCache persistence
|
|
format for the attribute-first `GameCult.Caching` stack.
|
|
|
|
## Included Types
|
|
|
|
- `SingleFileMessagePackBackingStore`
|
|
- `CultDocumentMessagePackSerialization`
|
|
- `CultDocumentResolver`
|
|
- `CultRecordRefFormatter<T>`
|
|
- `CultMathResolver`: the canonical component-array encoding of every public CultMath value type, part of the default resolver chain
|
|
- `CultMathExprResolver`: the canonical encoding of `CultMath.Expr.ExprGraph`, decoded through its validator, part of the default resolver chain
|
|
|
|
Cult document payloads serialize through `MessagePackSerializer` with the
|
|
options of the document's assembly (`CultDocumentMessagePackSerialization.OptionsFor`),
|
|
after the reflective `CultDocumentRegistry` has accepted the document shape. The
|
|
store snapshot format is written by hand against
|
|
`MessagePackWriter`/`MessagePackReader`.
|
|
|
|
## What It Does
|
|
|
|
- stores whole CultCache snapshots in MessagePack
|
|
- serializes attributed document payloads as MessagePack `[Key(n)]` arrays
|
|
- keeps explicit `CultRecordRef<T>` values compact on disk/wire
|
|
- preserves the cache/store split where metadata lives outside the domain model
|
|
|
|
## Example Model
|
|
|
|
```csharp
|
|
using GameCult.Caching;
|
|
using MessagePack;
|
|
|
|
[CultDocument("gamecult.item_data", "gamecult.item_data.v1")]
|
|
public sealed class ItemData
|
|
{
|
|
[Key(0)] [CultName] public string Name = string.Empty;
|
|
[Key(1)] public int Value;
|
|
}
|
|
```
|
|
|
|
## Typical Usage
|
|
|
|
```csharp
|
|
using GameCult.Caching;
|
|
using GameCult.Caching.MessagePack;
|
|
|
|
var cache = new CultCache();
|
|
var store = new SingleFileMessagePackBackingStore("Data.msgpack");
|
|
|
|
cache.AddBackingStore(store);
|
|
await cache.PullAllBackingStoresAsync();
|
|
```
|
|
|
|
## Notes
|
|
|
|
- Payload bytes are MessagePack. Store metadata and schema catalogs are also
|
|
persisted through hand-written MessagePack array layouts in this package.
|
|
- The raw CultNet document lane should treat these payload bytes as already
|
|
blessed, not decode and re-encode them for sport.
|
|
|
|
## Store format and rollback
|
|
|
|
A store whose elements hold an id is marked `cultcache.store.v3` (a directory store `v5`); the single-file and directory stores here write the mark from content. See the Store format and rollback section of the GameCult.Caching README. Refusal by name depends on the store kind and the pin. A **directory** store (`v5`) is refused by every Unity pin from `cultlib-unity-v1.0.57` and `caching-unity-v1.2.0` on (the earliest tags of each line whose `DirectoryMessagePackBackingStore` reads only the `v4` stamp: "only ... is readable"). A **single-file** store (`v3`) is refused only from `cultlib-unity-v2.0.0` and `caching-unity-v2.0.0`: `cultlib-unity-v1.0.61` and `caching-unity-v1.4.0` read the header string and never compare it. The NuGet line (`cultlib-v1.0.32`, the last `cultlib-v` tag) refuses neither: its directory store writes `cultcache.store.v1.directory` and never checks the stamp on read. Rolling back a single-file store to any of those pins reads it silently, drops the ids, and can rewrite the elements without them. That is the dangerous case. The only carry-back is narrow and lossy (a whole-store write that leaves no id in the store drops the mark and the ids); otherwise restore from a copy taken before upgrading, and upgrade every reader before any writer.
|
|
|
|
## MessagePack source generator
|
|
|
|
NuGet consumers get `[assembly: MessagePackAssumedFormattable(typeof(CultRecordRef<>))]` from the package, so a
|
|
`CultRecordRef<T>` member or dictionary key compiles with MessagePack's source generator on and is formatted by
|
|
CultLib's resolver; no per-member `[MessagePackFormatter]` is needed. Unity and `ProjectReference` consumers that turn the generator
|
|
on write that line themselves (or import `buildTransitive/GameCult.Caching.MessagePack.targets`).
|
|
|
|
## CultMath Value Shapes
|
|
|
|
Every public CultMath value type has one MessagePack shape (`CultMathResolver`,
|
|
in the default chain) and one JSON shape (`CultMathJson`, added to a consumer's
|
|
options with `new JsonSerializerOptions().AddCultMathConverters()`; CultLib owns
|
|
no JSON options of its own). MessagePack decoding skips extra elements, leaves
|
|
missing ones at zero (`Color32` alpha at 255) and refuses nil. JSON reading
|
|
skips unknown properties (`UnmappedMemberHandling.Disallow` refuses them), leaves
|
|
missing ones at zero, honours `PropertyNameCaseInsensitive`, lets a repeated
|
|
property replace the whole earlier value (refused when `AllowDuplicateProperties`
|
|
is false), and throws `JsonException` for a wrong token or an out-of-range number.
|
|
Numbers follow `JsonSerializerOptions.NumberHandling` as System.Text.Json's own
|
|
float does: `"NaN"`, `"Infinity"` and `"-Infinity"` are written under
|
|
`AllowNamedFloatingPointLiterals` or `WriteAsString` and read under
|
|
`AllowNamedFloatingPointLiterals` or `AllowReadingFromString`; quoted numbers read
|
|
under `AllowReadingFromString` with no surrounding whitespace.
|
|
Every type is also a dictionary key: its flattened components joined by commas,
|
|
invariant culture (`"1,2"`, `"true,false"`, matrices row by row, `rect` as
|
|
`"minX,minY,maxX,maxY"`), and only that exact spelling reads back. Two intended
|
|
divergences from System.Text.Json: a bare number beyond a component's range
|
|
(`1e40` as a float) is refused where System.Text.Json reads infinity, and a
|
|
refusal on write is a `JsonException` where System.Text.Json throws
|
|
`ArgumentException`. A consumer resolver or converter
|
|
registered ahead of these wins.
|
|
|
|
`CultMath.Expr.ExprGraph` is a class, not a value type, and has its own shape
|
|
(`CultMathExprResolver`, in the default chain): `[version, inputs, params, outputs, nodes]`,
|
|
with a param `[name, default float bits]`, an output `[name, node]` and a node
|
|
`[op name, operands, payload]`. Decoding runs the graph's one validator and refuses a
|
|
broken graph whole (an `ExprGraphException` naming the rule), so a consumer
|
|
`[CultDocument]` record with an `ExprGraph` field needs no registration. A nil graph is nil, as in every reference slot; decoding refuses any encoding but the canonical one.
|
|
|
|
| Type | MessagePack | JSON |
|
|
| --- | --- | --- |
|
|
| `float2`, `double2`, `int2`, `bool2` | `[x, y]` | `{"x":..,"y":..}` |
|
|
| `float3`, `double3`, `int3`, `bool3` | `[x, y, z]` | `{"x":..,"y":..,"z":..}` |
|
|
| `float4`, `int4`, `bool4`, `quaternion` | `[x, y, z, w]` | `{"x":..,"y":..,"z":..,"w":..}` |
|
|
| `Color32` | `[r, g, b, a]` as uint8 | `{"r":..,"g":..,"b":..,"a":..}` |
|
|
| `Random` | `[state]` | `{"state":..}` |
|
|
| `float2x2`, `float3x3` | array of row vectors (row-major) | array of row objects |
|
|
| `rect` | `[min, max]` of `float2` | `{"min":{..},"max":{..}}` |
|
|
| `CultCellular` | `[nearest, edge, id]` | `{"nearest":{..},"edge":{..},"id":..}` |
|
|
| `CultPhasor` | `[cosine, sine]` of `float4` | `{"cosine":{..},"sine":{..}}` (reads legacy `cos`/`sin`) |
|
|
| `CultSiteCells` | `[nearest, second, nearestSite, secondSite]` | `{"nearest":{..},"second":{..},"nearestSite":..,"secondSite":..}` |
|
|
|
|
Floats are MessagePack float32 (`0xCA`), doubles float64 (`0xCB`), integers the
|
|
shortest MessagePack integer.
|