CultLib/src/GameCult.Mesh
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-05-21 00:00:15 +01:00
..
docs Add durable CultMesh quickstart witness 2026-05-21 00:00:15 +01:00
CultMesh.cs Add CultMesh game session facade 2026-05-20 07:45:10 +01:00
CultMeshAuthority.cs Add CultMesh authority lease checks 2026-05-20 01:49:18 +01:00
CultMeshGameSession.cs Add CultMesh game session facade 2026-05-20 07:45:10 +01:00
CultMeshPeerExchange.cs Add CultMesh peer exchange client 2026-05-20 01:41:12 +01:00
CultMeshSimulationFacts.cs Commit simulation facts from witness quorum 2026-05-20 01:58:36 +01:00
CultMeshVerse.cs Apply Verse discovery responses to catalogs 2026-05-19 21:29:18 +01:00
CultMeshVerseDiscovery.cs Add CultMesh peer exchange catalogs 2026-05-20 01:35:39 +01:00
GameCult.Mesh.csproj Add GameCult Mesh package home 2026-05-19 19:08:48 +01:00
README.md Add durable CultMesh quickstart witness 2026-05-21 00:00:15 +01:00

CultMesh

CultMesh is the distributed realtime database and simulation-consensus layer for GameCult projects. The NuGet package and assembly are named GameCult.Mesh; the public surface is branded as CultMesh.

CultMesh sits over:

  • GameCult.Caching for typed domain documents, local indexes, persistence, and file-compatible cache storage.
  • GameCult.Networking for LiteNetLib transport, schema-v0 wire messages, and secure client/server sessions.

The intent is direct: a game can treat a mesh of clients and servers as one reactive database for persistent state, prediction-friendly input state, and simulation observations.

First Node

using GameCult.Mesh;

using var node = await CultMesh.StartNodeAsync("world.ccmp");

var player = await node.Database.GetAsync<PlayerData>(playerKey);
await node.Database.PutAsync(playerKey, player);

If you want one inspectable path that starts with a durable local cache file and ends with a typed read/write through the public CultMesh surface, see docs/durable-node-quickstart.md. If you want the lower-level typed-to-raw handoff that CultNet moves across the wire, see docs/typed-document-path.md.

Enable durable authoritative shard logs when a node should serve replica catch-up after restart:

using var node = await CultMesh.StartNodeAsync("world.ccmp", new CultMeshNodeOptions
{
    EnableDurableShardLogs = true
});

If ShardLogPath is omitted, CultMesh stores logs beside the cache under world.cultmesh/shard-logs.

Verses

A Verse is a rule-bearing consensus graph. Aetheria Main can be a cloud-authoritative Verse backed by GameCult regional simulators. A modded community branch can be transport-compatible with Aetheria Main while declaring different rules, required plugins, and authority policy. A peer-hosted game can choose a peer-to-peer authority model.

var rulesHash = CultMeshVerseDescriptor.ComputeRulesHash(
    "aetheria",
    "rules:v1",
    "plugins:vanilla");

var aetheria = new CultMeshVerseDescriptor(
    "aetheria-main",
    "Aetheria",
    CultMeshVerseAuthorityModel.OperatorCluster,
    new CultMeshVerseCompatibility("cultmesh.v0", rulesHash),
    discoveryEndpoints: ["cultmesh://us-east.aetheria.gamecult.net:3075"]);

using var catalog = CultMesh.CreateVerseCatalog();
catalog.Upsert(aetheria);

using var discovery = CultMesh.ServeVerseCatalog(node, catalog);

Peers can request Verse catalogs over schema-v0 with cultmesh.verse_catalog_request.v0 and receive transport-filtered cultmesh.verse_catalog_response.v0 descriptors.

Peer Exchange

Verse discovery tells a node which consensus graph exists. Peer exchange tells it who else is already in that graph.

using var peers = CultMesh.CreatePeerCatalog();
peers.Upsert(new CultMeshPeerCard(
    "gc-us-east-1",
    "aetheria-main",
    ["cultnet://us-east.aetheria.gamecult.net:3075"],
    roles: [CultMeshPeerRoles.Discovery, CultMeshPeerRoles.ShardPrimary],
    shardIds: ["players-us-east"],
    authorityLeaseId: "lease:players-us-east:42"));

using var exchange = CultMesh.ServePeerExchange(node, peers);

Peer cards are contact candidates, not authority by themselves. Public Verses should validate authority leases or signatures before trusting a peer for committed state.

var leases = CultMesh.CreateAuthorityLeaseCatalog();
leases.Upsert(new CultMeshAuthorityLease(
    "lease:players-us-east:42",
    "aetheria-main",
    "gc-us-east-1",
    [CultMeshPeerRoles.ShardPrimary],
    ["players-us-east"],
    "gamecult-operator",
    DateTimeOffset.UtcNow.AddMinutes(-1),
    DateTimeOffset.UtcNow.AddMinutes(10),
    signature: "operator-signature"));

Clients can fetch more peers from known peers:

var exchangeClient = CultMesh.CreatePeerExchangeClient();
await exchangeClient.DiscoverAsync(
    peers,
    aetheria.DiscoveryEndpoints,
    "aetheria-main",
    roles: [CultMeshPeerRoles.ReadReplica]);

Client-Side Prediction

CultMesh lets a runtime declare which input documents it may author optimistically. The local cache updates immediately, game simulation can proceed against that predicted state, and the authoritative shard log later reconciles the record.

var db = node.Database;

await db.PutPredictedAsync(inputKey, inputState);

using var sub = db.WatchRecord<PlayerInput>(inputKey)
    .Subscribe(change =>
    {
        ReconcilePrediction(change);
    });

Game Session

CultMeshGameSession wires the common gameplay loop: local catalogs, observation hub, observation server, peer exchange, Verse discovery, prediction, and quorum fact commits.

using var session = CultMesh.CreateGameSession(node, new CultMeshGameSessionOptions
{
    ConsensusOptions = new CultNetSimulationConsensusOptions
    {
        MinimumWitnesses = 2,
        QuorumRatio = 0.66d
    }
});

await session.PredictAsync(inputKey, inputState);

var committed = await session.SubmitAndCommitAsync(new CultNetSimulationObservation
{
    WitnessRuntimeId = "watcher-1",
    ShardId = "arena",
    ShardEpoch = 4,
    Frame = 100,
    SubjectId = "bob",
    ClaimKind = "hit",
    ClaimHash = CultNetSimulationObservation.ComputeClaimHash("hit", "alice", "bob", "frame:100")
});

Witness Consensus

CultMesh nodes can publish observations about simulation facts: shard epoch, frame, subject, claim kind, and claim hash. CultNetSimulationConsensus aggregates those observations deterministically into candidate facts with support weight, total observed weight, confidence, and quorum status.

Those candidates are not committed world state by themselves. They are the fast mesh opinion layer that can feed an authoritative shard commit path. Once a candidate has quorum, a shard-authoritative node can commit it as a CultMeshSimulationFact.

var committer = CultMesh.CreateSimulationFactCommitter(node.Database);
await committer.CommitAsync(candidate);

The central server does not need to decide what every witness saw. It only needs to accept the quorum result through the shard log.

Durable Catch-Up

CultMesh can persist authoritative shard logs through ICultNetShardMutationLogStore. The included file store writes one MessagePack log per shard, so a restarted primary can still serve replica catch-up without depending on warm process memory. When retained log history is compacted, stale replicas get an explicit resync requirement instead of a partial log pretending to be enough. The replicator can answer that by fetching a shard-bounded snapshot, replacing the local shard view, and advancing its cursor to the snapshot's represented log sequence.

Documentation