CultLib/docs/start
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Libby 533b492af2
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh portability and performance / ubuntu-latest one-command multiplayer onboarding (pull_request) Has been cancelled
CultMesh portability and performance / windows-latest one-command multiplayer onboarding (pull_request) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
CultMesh portability and performance / windows-latest reactive semantics and leases (pull_request) Has been cancelled
CultNet cross-runtime interop / C# / TypeScript / Rust / Python / Kotlin (pull_request) Has been cancelled
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Has been cancelled
docs/start: tested Rust and Python CultCache first calls
Each test file carries the snippet between markers; the docs show it verbatim and a second test in the file fails if the README block drifts. Matrix cells for Rust and Python CultCache now link them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 20:20:33 +00:00
..
README.md docs/start: tested Rust and Python CultCache first calls 2026-11-04 20:20:33 +00:00

Start Here

CultLib is one stack in several languages: CultCache (typed .cc persistence), CultNet (schema-v0 wire messages and transports) on top of it, and CultMesh (Verse discovery, replication, authority) on top of that. CultMath and Geometry sit beside the stack as portable numerics and engine-neutral geometry. The C# runtime in src/ is the reference; the other runtimes keep wire parity with it.

If you want typed state on disk, start with CultCache. Everything else assumes it.

Language x package

A cell links a snippet that a test in this repo runs, or says not yet. "Package" links the package's own README or source, which is real but carries no tested first-call snippet from this page.

CultCache CultMath Geometry CultNet CultMesh
C# first call, tested package; snippet not yet package; snippet not yet package; snippet not yet quickstart; run by scripts/verify-eve-getting-started.ps1
TypeScript first call, tested not yet not yet package; snippet not yet package; snippet not yet
Rust first call, tested native core; snippet not yet not yet package, examples; snippet not yet package; snippet not yet
Python first call, tested not yet not yet package; snippet not yet package; snippet not yet

For the whole picture of what each piece is for, see the package chooser in the top-level README.

C# first call

Define a typed document, put it in a cache backed by a .cc file, read it back through a second cache. The source is tests/GameCult.Caching.Tests/FirstCallTests.cs, which is what dotnet test tests/GameCult.Caching.Tests --filter FirstCallTests runs, so the snippet cannot drift without the test failing.

using GameCult.Caching;
using GameCult.Caching.MessagePack;
using MessagePack;

[CultDocument("gamecult.first_call_item", "gamecult.first_call_item.v1")]
[MessagePackObject]
public sealed class Item
{
    [Key(0)] [CultName] public string Name = string.Empty;
    [Key(1)] [CultIndex] public string Category = string.Empty;
    [Key(2)] public int Value;
}

var store = new SingleFileMessagePackBackingStore("first-call.cc");
var cache = new CultCache();
cache.AddBackingStore(store);
var handle = await cache.AddAsync(new Item { Name = "Potion", Category = "Consumable", Value = 50 });
store.PushAll();                                   // write the .cc file

var reopened = new CultCache();
reopened.AddBackingStore(new SingleFileMessagePackBackingStore("first-call.cc"));
await reopened.PullAllBackingStoresAsync();        // read it back
var potion = reopened.Get<Item>(handle.Key);       // by key
var byName = reopened.GetByName<Item>("Potion");   // by [CultName]

The test's Item is nested and internal, so it carries [MessagePackObject(AllowPrivate = true)]; the snippet above is the public-class form and is not itself compiled. More: src/GameCult.Caching/README.md.

TypeScript first call

Same shape in TypeScript: the document is a name, a schema id and a parse-able schema (zod here), and the .cc file is wire-compatible with the C# one. The source is packages/cultcache-ts/test/first-call.test.ts, run by npm test in packages/cultcache-ts (or node --test dist-test/test/first-call.test.js after tsc).

import { z } from "zod";
import {
  CultCache,
  SingleFileMessagePackBackingStore,
  defineDocumentRegistry,
  defineDocumentType,
} from "@gamecult/cultcache-ts";

const itemDocument = defineDocumentType({
  type: "item",
  schemaId: "gamecult.first_call_item.v1",
  schemaName: "gamecult.first_call_item",
  schemaVersion: "gamecult.first_call_item.v1",
  schema: z.object({ name: z.string(), category: z.string(), value: z.number().int() }),
  name: "name",
  indexes: { category: "category" },
});
const registry = defineDocumentRegistry(itemDocument);

const cache = CultCache.builder()
  .withRegistry(registry)
  .withGenericStore(new SingleFileMessagePackBackingStore("first-call.cc"))
  .build();
await cache.pullAllBackingStores();
await cache.put(itemDocument, "item:potion", { name: "Potion", category: "Consumable", value: 50 });

const reopened = CultCache.builder()
  .withRegistry(registry)
  .withGenericStore(new SingleFileMessagePackBackingStore("first-call.cc"))
  .build();
await reopened.pullAllBackingStores();
const potion = reopened.get(itemDocument, "item:potion");
const byName = reopened.getByName(itemDocument, "Potion");

The test imports from ../src/index; an application imports @gamecult/cultcache-ts. More: packages/cultcache-ts/README.md.

Rust first call

Same shape with derive(DatabaseEntry): slots are #[cultcache(key = N)], so the .cc payload is the slot-indexed array the C# runtime writes. The source is packages/cultcache-rs/tests/first_call.rs, run by cargo test --test first_call in packages/cultcache-rs; a second test in that file fails if the block below stops matching the file. The Rust runtime has no name lookup, so the read-back is by key. More: packages/cultcache-rs/README.md.

#[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)]
#[cultcache(type = "item")]
struct Item {
    #[cultcache(key = 0)]
    name: String,
    #[cultcache(key = 1)]
    category: String,
    #[cultcache(key = 2, default)]
    value: u32,
}

fn first_call() -> anyhow::Result<()> {
    let dir = tempfile::tempdir()?;
    let path = dir.path().join("first-call.cc");

    let mut cache = CultCache::new();
    cache.register_entry_type::<Item>()?;
    cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(&path));
    cache.pull_all_backing_stores()?;
    cache.put("item:potion", &Item {
        name: "Potion".to_string(),
        category: "Consumable".to_string(),
        value: 50,
    })?; // written to the .cc file before this returns

    let mut reopened = CultCache::new();
    reopened.register_entry_type::<Item>()?;
    reopened.add_generic_backing_store(SingleFileMessagePackBackingStore::new(&path));
    reopened.pull_all_backing_stores()?; // read it back
    let potion = reopened.get_required::<Item>("item:potion")?;
    assert_eq!(potion.name, "Potion");
    assert_eq!(potion.value, 50);
    Ok(())
}

Python first call

A document is a definition with an encoder and decoder; name= and indexes= register the name lookup and indexes. The source is packages/cultcache-py/tests/test_first_call.py, run by python -m unittest tests.test_first_call in packages/cultcache-py with src on PYTHONPATH and msgpack installed (the store needs it). A second test fails if the block below stops matching the file. More: packages/cultcache-py/README.md.

import tempfile
from dataclasses import asdict, dataclass
from pathlib import Path

from cultcache_py import CultCache, SingleFileMessagePackBackingStore, define_document_type


@dataclass
class Item:
    name: str
    category: str
    value: int


item_doc = define_document_type(
    "item",
    encode=lambda item: asdict(item),
    decode=lambda raw: Item(**raw),
    name="name",
    indexes={"category": "category"},
)


def first_call() -> Item:
    path = Path(tempfile.mkdtemp()) / "first-call.cc"

    cache = (
        CultCache.builder()
        .register_document_type(item_doc)
        .add_generic_store(SingleFileMessagePackBackingStore(path))
        .build()
    )
    cache.pull_all_backing_stores()
    cache.put(item_doc, "item:potion", Item("Potion", "Consumable", 50))  # written to the .cc file

    reopened = (
        CultCache.builder()
        .register_document_type(item_doc)
        .add_generic_store(SingleFileMessagePackBackingStore(path))
        .build()
    )
    reopened.pull_all_backing_stores()  # read it back
    assert reopened.get_by_name(item_doc, "Potion") == reopened.get_required(item_doc, "item:potion")
    return reopened.get_required(item_doc, "item:potion")

Where next

  • Smallest durable mesh path: src/GameCult.Mesh/docs/durable-node-quickstart.md.
  • Application path, four chapters: src/GameCult.Mesh/docs/getting-started/README.md.