587 lines
28 KiB
Markdown
587 lines
28 KiB
Markdown
# CultMesh Public API
|
|
|
|
Package: `GameCult.Mesh`
|
|
|
|
Brand: CultMesh
|
|
|
|
Namespace: `GameCult.Mesh`
|
|
|
|
## Entry Points
|
|
|
|
- `CultMesh.CreateNodeAsync(...)`
|
|
- `CultMesh.StartNodeAsync(...)`
|
|
- `CultMesh.CreateVerseCatalog()`
|
|
- `CultMesh.CreatePeerCatalog()`
|
|
- `CultMesh.CreateVerseDiscoveryClient(...)`
|
|
- `CultMesh.CreatePeerExchangeClient(...)`
|
|
- `CultMesh.CreateAuthorityLeaseCatalog()`
|
|
- `CultMesh.ParseRudpEndpoint(...)`
|
|
- `CultMesh.CreateRudpServer(...)`
|
|
- `CultMesh.CreateRudpClient(...)`
|
|
- `CultMesh.CreateRudpClientForPeer(...)`
|
|
- `CultMesh.CreateRudpClientForAuthorizedPeer(...)`
|
|
- `CultMesh.ConnectRudpClient(...)`
|
|
- `CultMesh.ConnectRudpClientForPeer(...)`
|
|
- `CultMesh.ConnectRudpClientForAuthorizedPeer(...)`
|
|
- `CultMesh.CreateSimulationFactCommitter(...)`
|
|
- `CultMesh.CreateGameSession(...)`
|
|
- `CultMesh.CreateClient(...)`
|
|
- `CultMesh.ConnectClient(...)`
|
|
|
|
`CultMeshNode` wraps the current local runtime pieces:
|
|
|
|
- `Cache`: the underlying `CultCache`
|
|
- `Server`: the underlying CultNet server
|
|
- `Database`: the distributed realtime database facade
|
|
- `DatabaseServer`: the schema-v0 database bridge
|
|
|
|
`CultMeshNodeOptions.EnableDurableShardLogs` attaches a file-backed
|
|
authoritative shard-log store when `DatabaseOptions.MutationLogStore` is not
|
|
already supplied. `ShardLogPath` can override the location; otherwise CultMesh
|
|
uses a `.cultmesh/shard-logs` directory beside the cache file.
|
|
|
|
The package home and primary entrypoint are `GameCult.Mesh` / `CultMesh`.
|
|
Some lower-level database and wire-contract types still retain `CultNet` names
|
|
because they are shared with the transport package. Treat those as plumbing,
|
|
not the brand surface.
|
|
|
|
## Core Behaviors
|
|
|
|
### Verses
|
|
|
|
`CultMeshVerseDescriptor` describes a rule-bearing consensus graph. It includes
|
|
transport compatibility, rules hash, authority model, exact authority-route
|
|
bindings, optional parent Verse id, and plugin requirements. Each route binds
|
|
an authority runtime to one endpoint, protocol set, priority, and generation.
|
|
The old endpoint/runtime arrays are derived projections; a legacy descriptor
|
|
with several authority runtimes is rejected because it cannot identify which
|
|
runtime owns which route.
|
|
|
|
`CultMeshVerseCatalog` is the local reactive catalog for discovered Verses.
|
|
It can publish discovery updates and find compatible transfer targets.
|
|
|
|
`CultMesh.ServeVerseCatalog(node, catalog)` attaches schema-v0 Verse discovery
|
|
responses to a node. The wire contracts are
|
|
`cultmesh.verse_catalog_request.v0` and `cultmesh.verse_catalog_response.v0`.
|
|
Consumers can apply a response directly with
|
|
`CultMeshVerseCatalog.Upsert(CultMeshVerseCatalogResponseMessage)`.
|
|
|
|
### Peer Exchange
|
|
|
|
`CultMeshPeerCatalog` is the local reactive catalog for peer cards.
|
|
`CultMeshPeerCard` describes a candidate peer endpoint for one Verse with
|
|
roles, shard hints, optional region, optional authority lease id, expiry, and
|
|
signature.
|
|
|
|
`CultMesh.ServePeerExchange(node, catalog)` attaches schema-v0 peer exchange
|
|
responses to a node. The wire contracts are
|
|
`cultmesh.peer_exchange_request.v0` and `cultmesh.peer_exchange_response.v0`.
|
|
`CultMeshPeerExchangeClient` can fetch peer cards from known endpoints and
|
|
upsert them into a local catalog.
|
|
Peer cards are discovery hints; authority still requires a valid lease or
|
|
signature for the target Verse. `CultMeshPeerCatalog.FindAuthorized(...)` and
|
|
`FirstAuthorized(...)` compose peer lookup with
|
|
`CultMeshAuthorityLeaseCatalog.IsAuthorized(...)`.
|
|
|
|
### Authority Leases
|
|
|
|
`CultMeshAuthorityLease` grants one peer specific roles for a Verse, optionally
|
|
scoped to shard ids and bounded by time. `CultMeshAuthorityLeaseCatalog` checks
|
|
whether a peer card is authorized for a requested role and shard. This keeps
|
|
peer exchange separate from authority instead of letting contact gossip mutate
|
|
the world.
|
|
|
|
### Cross-Runtime Primitives
|
|
|
|
These are the first shared primitives for the cozy typed Verse surface that
|
|
projects like Aetheria should build on instead of inventing daemon-specific
|
|
adapters:
|
|
|
|
In CultMesh, a primitive can be more than a data shape. Useful state-access
|
|
patterns should graduate into shared managed affordances: typed handles,
|
|
generated binding metadata, route selection, authority context, watches,
|
|
diagnostics, and native-view descriptors that let each runtime use the same
|
|
semantic API. The domain package names the thing; CultMesh supplies the fast,
|
|
portable machinery that makes it feel native.
|
|
|
|
- `CultMeshStatePointer<T>` is a typed pointer to Verse state that UI surfaces,
|
|
tools, and clients can resolve or watch without string state-ref plumbing.
|
|
`CultMesh.StatePointer(...)` creates simple or context-aware pointers.
|
|
Pointers carry source descriptors and route hints, and
|
|
`CultMesh.DescribeStatePointer(...)` exposes that metadata for catalogs and
|
|
inspectors. Context-aware pointers can bind with `Bind(...)`,
|
|
`CultMesh.BindStatePointer(...)`, or `CultMeshVerse.BindStatePointer(...)`,
|
|
producing a Verse-bound handle that resolves and watches through the shared
|
|
Verse query context instead of forcing UI/tool code to rebuild route plumbing.
|
|
- `CultMeshMutableStatePointer<T>` extends the same state pointer shape with a
|
|
typed `ReplaceAsync(...)` operation for authoritative document handles,
|
|
editor tools, and UI surfaces that intentionally write state. It keeps the
|
|
read/watch/replace trio in CultMesh instead of letting each domain package
|
|
invent private document wrappers. `CultMesh.MutableStatePointer(...)`,
|
|
`CultMesh.BindMutableStatePointer(...)`, and
|
|
`CultMeshVerse.BindMutableStatePointer(...)` preserve the same Verse context,
|
|
route hints, source diagnostics, and state-binding metadata as read-only
|
|
pointers.
|
|
- `CultMeshStateRefResolver` is the shared bridge for UI/tool surfaces that
|
|
still receive state references from authored Eve/CultUI documents. Resolvers
|
|
are named, composable, carry source descriptors and route hints, and expose
|
|
diagnostics through `CultMesh.DescribeStateRefResolver(...)`. Legacy
|
|
`Func<string, string>` lowerers can use `AsFunc()` while generated surfaces
|
|
migrate to typed state pointers and binding descriptors.
|
|
- `CultMeshStateBindingDescriptor` and `CultMeshStateBindingRecord` are the
|
|
UI/tool state edge. Live surfaces bind a component prop to a typed state
|
|
pointer; persistence and transport boundaries use
|
|
`CultMesh.StateBindingRecord(...)` instead of local pointer-field copies or
|
|
route parsers.
|
|
- `CultMeshOperationHandle<TRequest, TResponse>` is a method-shaped operation
|
|
handle. `CultMeshOperationContext`, `CultMeshAuthorityClaim`, and
|
|
`CultMeshOperationReceipt` carry runtime identity, authority metadata,
|
|
route hints, idempotency, acceptance, and diagnostics.
|
|
`CultMesh.DescribeOperationHandle(...)` exposes the operation id for surface
|
|
catalogs and generated tooling. `Bind(...)`, `CultMesh.BindOperation(...)`,
|
|
and `CultMeshVerse.BindOperation(...)` produce Verse-bound handles so
|
|
generated domain facades can invoke without re-threading context plumbing at
|
|
every call site.
|
|
- `CultMeshOperationBindingDescriptor`, `CultMeshOperationBindingRecord`,
|
|
`CultMeshOperationInvocationDescriptor`, `CultMeshOperationInvocationRecord`,
|
|
and `CultMeshOperationPayload` are the UI/tool operation edge. Live surfaces
|
|
carry typed operation identity and scalar payload reads; persistence and
|
|
transport boundaries use `CultMesh.OperationBindingRecord(...)`,
|
|
`CultMesh.OperationInvocationRecord(...)`, and
|
|
`CultMeshOperationPayload.ToDictionary()` instead of local route parsers or
|
|
ad-hoc payload maps.
|
|
- `CultMeshQuerySurface<TParameters, TResult>` is a typed derived-state query
|
|
surface. Query execution can be local, remote, shared-memory, IPC, or
|
|
WASM-routed while preserving the same semantic API. Query surfaces can also
|
|
carry source descriptors, route hints, and reactive watches so tools and UI
|
|
runtimes can inspect what state a generated query depends on, which route it
|
|
prefers, and subscribe without inventing polling glue. Query surfaces can be
|
|
bound with `Bind(...)`, `CultMesh.BindQuery(...)`, or
|
|
`CultMeshVerse.BindQuery(...)`.
|
|
- `CultMeshLiveFeed<TParameters, TResult>` is a typed live view surface for
|
|
composed client snapshots such as an RTS viewport plus selection, health, and
|
|
authority panels. It gives clients one `snapshot(...)` / `watch(...)` handle
|
|
while preserving source descriptors and route defaults. Live feeds can be
|
|
bound with `Bind(...)`, `CultMesh.BindLiveFeed(...)`, or
|
|
`CultMeshVerse.BindLiveFeed(...)`.
|
|
- `CultMeshDocumentHandle<TDocument>` is the typed reactive document edge for
|
|
CultCache/CultNet state. It exposes document metadata, one coherent
|
|
`LatestAsync()` read, a `Watch()` stream, and `ReplaceAsync(...)` when the
|
|
handle is backed by mutable state. Distributed `CultNetDatabase` handles also
|
|
expose `SubmitPredictionAsync(...)`, which applies a local prediction through
|
|
the database's configured client-authority scope and later reconciles through
|
|
the normal shard log. Callers can bind a projected live feed, a local
|
|
`CultCache` record, a distributed `CultNetDatabase` record, or a
|
|
`CultMeshNode` record through the same surface instead of walking transport
|
|
and projection layers themselves. Same-schema CLR aliases can be requested
|
|
directly from node/database/snapshot surfaces; CultMesh resolves the canonical
|
|
registered document by shared `[CultDocument(schemaName, schemaVersion)]`
|
|
identity and uses the shared CultCache MessagePack codec for
|
|
read/watch/replace/prediction conversion. `AsSchemaAlias<TAlias>()` remains
|
|
available when a caller already has a handle and wants to project it.
|
|
- `CultMeshDocumentCatalog` is the schema-aware lookup edge for a set of
|
|
document handles. It indexes handles by CLR document type, schema name, and
|
|
schema version, and resolves same-schema CLR aliases by delegating to
|
|
`CultMeshDocumentHandle<TDocument>.AsSchemaAlias<TAlias>()`. Domain facades
|
|
can expose `Document<T>()`, `DocumentBySchema(...)`, `LatestAsync<T>()`, and
|
|
`Watch<T>()` without owning schema dictionaries or alias serializers. Mutable
|
|
facades can also call `ReplaceAsync<T>(...)` or
|
|
`SubmitPredictionAsync<T>(...)` directly on the catalog when they only need
|
|
the schema-aliased operation and not the intermediate handle.
|
|
- `CultMeshCollectionHandle<TDocument>` is the typed reactive edge for
|
|
multi-record state. It exposes one `LatestAsync()` collection snapshot and a
|
|
`WatchChanges()` stream while hiding whether the backing source is an
|
|
in-process `CultCache`, a distributed `CultNetDatabase`, or a `CultMeshNode`.
|
|
Collection handles can represent all records of a document type, a named
|
|
document view, or an indexed value view, and they support the same
|
|
same-schema CLR alias conversion as document handles.
|
|
- `CultMeshCollectionCatalog` is the collection sibling of
|
|
`CultMeshDocumentCatalog`. It indexes typed collection handles by CLR
|
|
document type, schema id, schema name, and schema version, then exposes
|
|
`Collection<T>()`, `LatestAsync<T>()`, and `WatchChanges<T>()` so domain
|
|
facades can publish typed multi-record state without maintaining parallel
|
|
schema lookup tables.
|
|
- `CultMeshProjectionRecipe<TParameters, TResult>` names a reusable projection
|
|
from typed source state into derived state. It records source handles,
|
|
route hints, and projection execution, and can be exposed as a typed query
|
|
surface. Reactive projection recipes preserve their watcher when exposed as
|
|
query surfaces. When a caller supplies an automatic query context, the recipe
|
|
route becomes the default; explicit caller routes still win.
|
|
- `CultMeshNativeSliceViewDescriptor` and `CultMeshNativeSliceColumn` describe
|
|
typed native views for co-located runtimes such as Unity Burst jobs, Rust
|
|
slices, or browser typed arrays. `CultMesh.DescribeNativeSliceView(...)`
|
|
exposes the full view id, schema id, row count, columns, route, native handle,
|
|
and dense stride for tools and adapters.
|
|
- `CultMeshFrameBodyPublisher` and `CultMeshMappedFrameBodyCursor` carry
|
|
fixed-layout hot bodies through a triple-buffered memory mapping. Producers
|
|
reserve a writable slot and commit in place; consumers retain one bootstrap
|
|
capability and acquire only newer read leases. Windows uses a named memory
|
|
map; Unix uses a private file-backed map because .NET does not support named
|
|
maps there. The capability and cursor API is identical, and publisher
|
|
disposal revokes new opens and removes the Unix backing file. `Stats()` reports committed
|
|
frames, blocked writes, and unavoidable copies. Direct slot writes report
|
|
zero copies; the convenience `TryPublish(ReadOnlySpan<byte>)` path reports
|
|
one explicit fallback copy instead of laundering it as zero-copy transport.
|
|
- `CultMeshRouteHint`, `CultMeshRouteRecord`, and `CultMeshLocalityKind` name
|
|
locality choices without forcing application code to choose transport-specific
|
|
APIs. `CultMesh.RouteRecord(...)` is the shared way to flatten and rehydrate
|
|
route kind/description fields at persisted document, UI, tool, and transport
|
|
boundaries.
|
|
|
|
These types are intentionally small handles and descriptors. Generated bindings
|
|
and runtime adapters should compose them into domain sugar such as
|
|
`verse.aetheria().entity(id).pilot().move(...)`,
|
|
`zone.objects().visibleTo(units).within(rect)`, and
|
|
`renderView.AsNativeArrays()`.
|
|
|
|
The C# helpers already support the first layer of that shape:
|
|
|
|
```csharp
|
|
var context = CultMeshOperationContext
|
|
.ForRuntime("unity-raven")
|
|
.WithClaim(new CultMeshAuthorityClaim("pilot-control", shardId: "zone:local-rts"))
|
|
.WithRoute(new CultMeshRouteHint(CultMeshLocalityKind.InProcess));
|
|
|
|
await moveOperation.InvokeAsync(moveRequest, context);
|
|
```
|
|
|
|
Generated facades should bind surfaces once to the correct Verse view:
|
|
|
|
```csharp
|
|
var queryVerse = verse.WithRoute(new CultMeshRouteHint(CultMeshLocalityKind.SharedMemory));
|
|
var commandVerse = verse.WithRoute(new CultMeshRouteHint(CultMeshLocalityKind.Network));
|
|
|
|
var visibleObjects = queryVerse.BindQuery(objectsViewport);
|
|
var movePilot = commandVerse.BindOperation(setMoveVector);
|
|
|
|
var objects = await visibleObjects.ExecuteAsync(viewportRequest);
|
|
await movePilot.InvokeAsync(moveRequest, idempotencyKey);
|
|
```
|
|
|
|
A single typed document can be surfaced the same way. The caller asks for the
|
|
typed view it wants; if that type is a same-schema alias of the registered
|
|
canonical document, CultMesh still opens the correct backing record:
|
|
|
|
```csharp
|
|
var stockForUi = CultMesh.Document<StationStockUiDocument>(
|
|
node,
|
|
new CultRecordKey("station:starbridge:stock"),
|
|
verse);
|
|
|
|
var stock = await stockForUi.LatestAsync();
|
|
using var watch = stockForUi.Watch(RenderStock);
|
|
await stockForUi.SetAsync(updatedStockFromUi);
|
|
```
|
|
|
|
Projected or derived documents can use the same handle shape with explicit
|
|
snapshot/watch delegates:
|
|
|
|
```csharp
|
|
var cockpit = CultMesh.Document(
|
|
"aetheria.ship.cockpit.current",
|
|
verse,
|
|
context => cockpitStore.LatestAsync(context),
|
|
context => cockpitStore.Watch(context),
|
|
sources: new[]
|
|
{
|
|
CultMesh.ProjectionSource(
|
|
"pilot:cockpit",
|
|
schemaId: "gamecult.aetheria.cockpit_state.v1")
|
|
});
|
|
|
|
var current = await cockpit.LatestAsync();
|
|
using var subscription = cockpit.Watch(next => RenderCockpit(next));
|
|
|
|
var daemonAlias = cockpit.AsSchemaAlias<DaemonCockpitState>();
|
|
```
|
|
|
|
Remote CultNet snapshots use the same document handle surface. The caller names
|
|
the endpoint, record key, and typed document; CultMesh owns the snapshot request
|
|
id, response filtering, raw MessagePack payload decode, route metadata, and
|
|
polling watch fallback:
|
|
|
|
```csharp
|
|
var remoteHealth = CultMesh.DocumentFromPeerSnapshot<DaemonHealthUiDocument>(
|
|
"cultnet://daemon.local:3075",
|
|
"daemon:aetheria.health.v1",
|
|
verse);
|
|
|
|
var uiHealth = await remoteHealth.LatestAsync();
|
|
```
|
|
|
|
Snapshot requests are record-key-first so same logical documents can still be
|
|
read when another runtime generated a different schema id for a compatible
|
|
schema alias. The decode path prefers exact schema id matches when present and
|
|
falls back to the requested record key. Ask for the runtime-facing alias type
|
|
directly; `AsSchemaAlias<TAlias>()` is for adapting handles that were already
|
|
constructed elsewhere.
|
|
|
|
A domain facade can still collect existing handles into one schema-aware
|
|
catalog when it is composing pre-built surfaces:
|
|
|
|
```csharp
|
|
var documents = CultMesh.Documents(
|
|
currentDocking,
|
|
stationRefit,
|
|
zoneContacts);
|
|
|
|
var refit = await documents.LatestAsync<StationRefitDocument>();
|
|
var uiDocking = documents.Document<CurrentDockingUiDocument>();
|
|
var bySchema = documents.DocumentBySchema("gamecult.aetheria.station_refit.v1");
|
|
```
|
|
|
|
Typed collections use the same one-call style:
|
|
|
|
```csharp
|
|
var allies = CultMesh.CollectionByIndex<PlayerShipDocument>(
|
|
node,
|
|
"Faction",
|
|
"au");
|
|
|
|
var currentAllies = await allies.LatestAsync();
|
|
using var changes = allies.WatchChanges(change => UpdateRoster(change));
|
|
|
|
var uiAllies = allies.AsSchemaAlias<PlayerShipUiDocument>();
|
|
|
|
var collections = CultMesh.Collections(allies);
|
|
var uiRoster = await collections.LatestAsync<PlayerShipUiDocument>();
|
|
using var uiRosterChanges = collections.WatchChanges<PlayerShipUiDocument>(RenderRoster);
|
|
```
|
|
|
|
```ts
|
|
const queryVerse = verse.withRoute("shared-memory", statePath);
|
|
const commandVerse = verse
|
|
.withRoute("network", endpoint)
|
|
.withClaim("commander-control", { shardId: "aetheria.local" });
|
|
|
|
const visibleObjects = CultMesh.bindQuery(queryVerse, queries.objectsViewport);
|
|
const movePilot = CultMesh.bindOperation(commandVerse, operations.setMoveVector);
|
|
|
|
const objects = await visibleObjects.execute(viewportRequest);
|
|
await movePilot.invoke({ actorEntityKey, directionX: 1, directionY: 0 });
|
|
```
|
|
|
|
TypeScript runtimes expose the same document and collection handle shape:
|
|
|
|
```ts
|
|
const station = await CultMesh.startNode(statePath, {
|
|
documents: [stationStockDocument, currentDockingDocument],
|
|
});
|
|
|
|
const stock = station.document(stationStockUiDocument, "station:starbridge:stock");
|
|
const docking = station.document(currentDockingDocument, "player:raven:docking");
|
|
const catalog = CultMesh.documents(stock, docking);
|
|
|
|
const currentStock = await stock.latest();
|
|
const unsubscribe = stock.watch(next => renderStock(next));
|
|
await stock.set(updatedStock);
|
|
unsubscribe();
|
|
|
|
const pilots = station.collection(playerShipUiDocument);
|
|
const currentPilots = await pilots.latest();
|
|
const stopRoster = pilots.watchChanges(change => refreshRoster(change));
|
|
|
|
const remoteHealth = CultMesh.documentFromPeerSnapshot(
|
|
() => daemonPeer,
|
|
daemonHealthUiDocument,
|
|
"daemon:aetheria.health.v1",
|
|
);
|
|
const health = await remoteHealth.latest();
|
|
```
|
|
|
|
Projected, remote, shared-memory, IPC, WASM, and local-cache documents all use
|
|
the same C# and TS handle contract. The caller names the typed document or
|
|
collection it wants; the handle owns route defaults, snapshot reads, reactive
|
|
watches, schema alias validation, prediction submission, and replacement when
|
|
the backing source supports those mutations. Generated Aetheria and Ymir
|
|
facades should bind those handles to a Verse once and keep transport, quorum,
|
|
and cache mechanics behind CultMesh.
|
|
|
|
Native view descriptors can name unmanaged columns without hand-entered byte
|
|
sizes:
|
|
|
|
```csharp
|
|
var view = new CultMeshNativeSliceViewDescriptor(
|
|
"aetheria.zone.render",
|
|
"gamecult.aetheria.render_body.v1",
|
|
rowCount,
|
|
new[]
|
|
{
|
|
CultMeshNativeSliceColumn.For<CultVec2>("position"),
|
|
CultMeshNativeSliceColumn.For<CultVec2>("velocity")
|
|
},
|
|
new CultMeshRouteHint(CultMeshLocalityKind.SharedMemory));
|
|
```
|
|
|
|
Projection recipes give local projection code a shared shape before it becomes
|
|
runtime-specific glue:
|
|
|
|
```csharp
|
|
var objectsViewport = CultMesh.ProjectionRecipe<ViewportRequest, ObjectsViewport>(
|
|
"aetheria.zone.objects.visible",
|
|
new[]
|
|
{
|
|
CultMesh.ProjectionSource("daemon:aetheria.frame.latest.v1", "gamecult.aetheria.daemon_frame.v1"),
|
|
CultMesh.ProjectionSource("daemon:aetheria.authority.policy.v1")
|
|
},
|
|
(request, context) => ProjectObjectsAsync(request, context),
|
|
new CultMeshRouteHint(CultMeshLocalityKind.SharedMemory));
|
|
|
|
var query = objectsViewport.AsQuerySurface();
|
|
```
|
|
|
|
Calling `query.ExecuteAsync(request, "unity-raven")` inherits the recipe's
|
|
shared-memory route. Calling it with a `CultMeshQueryContext` that already names
|
|
`Network`, `Ipc`, `Wasm`, or another route uses the caller's route instead.
|
|
`query.Sources` and `query.RouteHint` preserve the recipe metadata for
|
|
diagnostics, tooling, and UI/MCP surfaces. If the projection supports reactive
|
|
execution, `query.Watch(...)` / `query.watch(...)` keeps the same projection
|
|
route resolution, so a browser map tab, Unity renderer, Eve surface, or MCP
|
|
tool can subscribe through the same typed query affordance it uses for one-shot
|
|
reads.
|
|
|
|
`CultMesh.DescribeQuerySurface(...)` and
|
|
`CultMesh.DescribeProjectionRecipe(...)` expose the same metadata in a stable
|
|
diagnostic shape for generated clients, Eve/CultUI inspectors, MCP tools, and
|
|
surface catalogs. Tooling should ask CultMesh for these descriptors instead of
|
|
copying source lists and route hints by convention.
|
|
`CultMesh.DescribeSurface(...)` and `CultMesh.DescribeSurfaceCatalog(...)`
|
|
collect query surfaces, projection recipes, live feeds, operations, document
|
|
handles, collection handles, state pointers, and native views into one
|
|
inspectable catalog for a runtime or generated binding package. Documents are
|
|
advertised by document id, collections by collection id, state pointers by
|
|
pointer id, and native slice views by view id and route while the full column
|
|
layout stays in `CultMeshNativeSliceViewDescriptor`.
|
|
Catalog diagnostics support exact id lookup and kind filtering so tools can
|
|
discover, for example, every operation or native view without local scan
|
|
conventions. `CultMesh.DescribeSurfaceCatalogIndex(...)` / TS
|
|
`CultMesh.surfaceCatalogIndex(...)` promotes that grouping into a shared
|
|
diagnostic shape with `queries`, `projectionRecipes`, `liveFeeds`,
|
|
`operations`, `documents`, `collections`, `statePointers`, and
|
|
`nativeSliceViews`, so generated bindings and tools can ask for the semantic
|
|
bucket they need without copying catalog scan logic.
|
|
|
|
Live feeds compose several query surfaces into one client-facing snapshot
|
|
without making the UI own refresh loops:
|
|
|
|
```csharp
|
|
var viewportFeed = CultMesh.LiveFeed<ViewportRequest, RtsViewportSnapshot>(
|
|
"aetheria.rts.viewport.feed",
|
|
(request, context) => SnapshotViewportAsync(request, context),
|
|
(request, context) => WatchViewport(request, context),
|
|
new[]
|
|
{
|
|
CultMesh.ProjectionSource("daemon:aetheria.frame.latest.v1"),
|
|
CultMesh.ProjectionSource("daemon:aetheria.health.latest.v1")
|
|
},
|
|
new CultMeshRouteHint(CultMeshLocalityKind.SharedMemory));
|
|
|
|
var feedDiagnostics = CultMesh.DescribeLiveFeed(viewportFeed);
|
|
```
|
|
|
|
When a runtime has a snapshot path before it has native reactive transport,
|
|
`CultMesh.PollingQueryWatcher(...)` provides a disposable watch adapter:
|
|
|
|
```csharp
|
|
var feed = CultMesh.LiveFeed<ViewportRequest, RtsViewportSnapshot>(
|
|
"aetheria.rts.viewport.feed",
|
|
SnapshotViewportAsync,
|
|
CultMesh.PollingQueryWatcher<ViewportRequest, RtsViewportSnapshot>(
|
|
SnapshotViewportAsync,
|
|
new CultMeshPollingWatchOptions<RtsViewportSnapshot>(
|
|
TimeSpan.FromMilliseconds(50))));
|
|
```
|
|
|
|
The polling adapter is intentionally a bridge primitive: application UI code
|
|
still receives a typed `Watch(...)` handle, and the sampling loop can later be
|
|
replaced by shared-memory, IPC, network, or WASM-native reactivity without
|
|
changing renderer call sites.
|
|
|
|
The staged roadmap for growing these handles into the cross-runtime developer
|
|
experience lives in `docs/cross-runtime-primitives-roadmap.md`. When a project
|
|
discovers a reusable state-ref, operation, query, projection, authority, watch,
|
|
surface-binding, schema-generation, routing, diagnostic, or native-slab
|
|
abstraction, it should graduate into that roadmap and then into the shared
|
|
CultMesh/CultLib layer instead of remaining a private bridge.
|
|
|
|
### RUDP Helpers
|
|
|
|
`CultMesh.ParseRudpEndpoint(...)`, `CreateRudpServer(...)`,
|
|
`CreateRudpClient(...)`, `CreateRudpClientForPeer(...)`, and
|
|
`CreateRudpClientForAuthorizedPeer(...)` provide the branded CultMesh path to
|
|
the native CultNet RUDP socket transport. `ConnectRudpClient(...)`,
|
|
`ConnectRudpClientForPeer(...)`, and `ConnectRudpClientForAuthorizedPeer(...)`
|
|
also perform the client handshake before returning the same transport. These
|
|
helpers parse endpoint contact hints and choose authorized peers;
|
|
`GameCult.Networking` still owns the RUDP packet codec, session state, resend,
|
|
fragmentation, and channel semantics.
|
|
|
|
TypeScript runtimes can also wrap a CultNet peer snapshot as a normal document
|
|
handle with `CultMesh.documentFromPeerSnapshot(...)`. The caller supplies the
|
|
peer or peer factory, document definition or schema id, and record key; CultMesh
|
|
sends the snapshot request, waits for the matching response, decodes the raw
|
|
MessagePack document, and returns the same `latest()`/`watch()` document surface
|
|
used by local cache and single-file documents. Exact schema ids are preferred,
|
|
but record-key publications remain readable when two runtimes alias the same
|
|
logical document through different schema names.
|
|
|
|
### Shard Authority
|
|
|
|
Each shard has one primary writer for now. Non-primary writes are rejected or
|
|
explicitly forwarded. Stale epochs fail loudly.
|
|
|
|
### Reactive Documents
|
|
|
|
Consumers subscribe to typed document changes through `CultNetDatabase` watch
|
|
methods. Subscriptions receive domain changes rather than storage envelopes.
|
|
Exact remote values use `SubscribeLiveValueAsync<T>(...)`; they remain ephemeral
|
|
and own their unsubscribe lifetime. Hot body state uses
|
|
`CultMesh.SubscribeLiveBodyAsync(...)`: the reactive value is only the current
|
|
body descriptor, while `OpenCurrentReadOnly()` negotiates shared memory,
|
|
shared-file mapping, or network from observed locality and available adapters.
|
|
Same-machine bodies are opened from the mapped producer slab. Remote live bodies
|
|
are capability-bound and read through one reusable `cultmesh.bodies.v1` session;
|
|
they are not packed into CultCache or CDN records. `CultMeshClient.BodyProvider(...)`
|
|
creates that verified network adapter without exposing session machinery to the
|
|
consumer. Body bytes never become subscription snapshot payload. Either subscription may
|
|
legitimately start with `HasValue == false`; it remains active until the provider
|
|
publishes the exact record, avoiding a startup retry loop or broad snapshot.
|
|
`CultNetDatabaseSubscriptionServer.DemandChanged` exposes the exact requested
|
|
record and schema set as well as optional body negotiation. A provider can
|
|
therefore materialize a computed record only while it has consumers. The watch
|
|
is installed before demand is announced, so the first publication travels as a
|
|
normal live change and does not require a bootstrap snapshot.
|
|
|
|
### Client Prediction
|
|
|
|
`CultNetClientAuthorityScope` declares input documents a runtime may predict.
|
|
CultMesh document handles opened over a `CultNetDatabase` expose
|
|
`SubmitPredictionAsync(...)`, which writes local state and emits `Predicted`
|
|
through the configured database authority policy. When the authoritative log
|
|
arrives, the database emits `Reconciled`.
|
|
|
|
### Replica Catch-Up
|
|
|
|
Shard logs can be requested over schema-v0 and applied by replicas. Replica
|
|
cursors can be stored with `ICultNetShardReplicaCursorStore`. Authoritative
|
|
shard logs can be persisted with `ICultNetShardMutationLogStore`; the file
|
|
implementation stores one MessagePack log per shard so catch-up survives a
|
|
primary restart. Compacted history returns an explicit resync requirement so a
|
|
replica can fall back to a shard-bounded snapshot before applying newer log
|
|
entries. Applying that snapshot replaces the local shard view and advances the
|
|
replica cursor to the snapshot's represented log sequence.
|
|
|
|
### Simulation Witness Consensus
|
|
|
|
`CultNetSimulationObservation` records what a node saw for a simulation fact.
|
|
`CultNetSimulationObservationHub` collects those observations and emits
|
|
`CultNetSimulationConsensusCandidate` updates.
|
|
|
|
Candidates are opinions. `CultMeshSimulationFactCommitter` commits quorum
|
|
candidates as `CultMeshSimulationFact` documents through `CultNetDatabase`, so
|
|
final world state still belongs in the authoritative shard log.
|
|
|
|
`CultMeshGameSession` is the gameplay-facing composition layer. It owns the
|
|
common loop for prediction, observations, candidate watches, simulation fact
|
|
watches, and committing new quorum facts without forcing game code to manually
|
|
wire every bridge.
|