08 — Server / Client API Reference (lattice.h)¶
Status: Reference (authoritative for the implemented public C ABI). Source of truth:
reference/include/lattice/lattice.h— the real, compilable C ABI exported byliblattice.so. Every signature below is copied verbatim from that header. Read alongside: README · 02 Netcode LLD (wire/semantics) · 05 Engine Integration (ergonomic per-engine API) · 07 Reference Implementation & Conformance (what the skeleton implements + the test harness).
0. How to read this document¶
This is the function-level reference for everything an engine binding (lattice-unity, lattice-unreal, lattice-godot, lattice-web) or a native integrator compiles against. It is organized by role and topic:
- Runner lifecycle & the tick pump
- Server / host functions
- Client functions
- Serialization API —
BitWriter/BitReader - Callback / event model, threading & enums
- C ABI → engine high-level mapping
For each public function you get: the real signature, parameters, return value + error codes, the role (server / client / both) it is valid on, and a short usage snippet.
0.1 Scope: public ABI vs. test-only hooks¶
Two families of symbols live in lattice.h:
| Family | Prefix | Status |
|---|---|---|
| Public ABI | lattice_* (everything in §§1–4 here) |
The supported engine-binding surface. Stable; this is what bindings call. |
| Test-only hooks | lattice_test_* |
NOT part of the public API. Conformance/unit-test scaffolding only. |
The
lattice_test_*symbols are TEST-ONLY hooks and are deliberately excluded from this reference. They exist solely so the conformance harness can drive the internal deterministic network-condition simulator (NetSim), the loopback "wire", the reliability layer, and the ACK-bitfield logic without reaching into an internal header — see 07 Reference Implementation & Conformance. They are namespacedlattice_test_*precisely so they are obviously distinct from production calls, and a shipping build may strip them entirely. Do not bind against them, do not document them to game developers, and do not rely on them at runtime. (For the curious, they cover:lattice_test_netsim_*,lattice_test_wire_*,lattice_test_reliable_*, andlattice_test_ack_bitfield— all test scaffolding for 02 §5 reliability and 02 §3.4 ACK-bitfield conformance.)
0.2 Provenance & currency note¶
This reference documents the implemented reference ABI as it exists now (lattice.h + liblattice.so). The reference core is a faithful, compilable model of the documented wire encodings, plus an in-memory loopback transport, simple delta replication, and reliable RPC; it deliberately omits crypto, NAT/relay, prediction/rollback, and interest management (those are documented as design in 02 and called out as extension points in 07). This document is current as of the present core and is kept updated as later features add calls — when the production core lands prediction, AoI, authority transfer, etc., their ABI entry points are added here in the same per-function format.
Note on naming: 05 §2.2 shows a representative ABI sketch using
lt_*names andlt_prefab_id/lt_field_idhandles. That sketch is illustrative of the binding strategy; the implemented ABI uses thelattice_*names and the handle/typedef set documented here. Where they differ, this document andlattice.hare authoritative.
0.3 Universal ABI rules (02 §19, lattice.h header comment)¶
These hold for every function below:
- Opaque handles.
lattice_runner*,lattice_bitwriter*,lattice_bitreader*are forward-declared incomplete types. Never dereference them. - POD only across the line. No C++ types, STL containers, or exceptions ever cross the boundary. Structs are fixed-layout POD, passed by value or pointer.
- Caller-owned buffers. All buffers you pass in are caller-owned; the core copies anything it retains (type descriptors, field arrays, key strings, initial state, RPC payloads).
- Little-endian wire. Byte-aligned scalars are little-endian; bit-packed fields are MSB-first within each byte, consistently on both ends (02 §20.3).
- Synchronous callbacks. All callbacks fire synchronously inside
lattice_runner_tick()on the calling thread. Do no blocking work in them, and never let an exception propagate out of one into the core. - Status codes, not exceptions. Functions that can fail return
lattice_result; outputs are written through pointers. Uselattice_result_strto render a code.
1. Runner lifecycle & the tick pump¶
A runner (lattice_runner) is one networked simulation instance — the implemented analogue of 05's NetworkRunner / ULatticeRunnerSubsystem / LatticeRunner node. The lifecycle is the same for every role; the role is chosen by lattice_runner_start.
1.1 The standard sequence¶
create → set_callbacks → start(MODE) → listen(port) [SERVER/HOST]
\ connect(addr,port,token) [CLIENT]
→ register_type (×N, identically on every peer)
→ loop: tick(dt) // recv → tick → send; callbacks fire here
→ destroy
1.2 lattice_runner_create¶
Create a runner. Role: both.
| Param | Meaning |
|---|---|
cfg |
Optional lattice_runner_config. May be NULL for all defaults. Deep-copied; need not outlive the call. |
Returns: an owning lattice_runner*, or NULL on allocation failure. Destroy with lattice_runner_destroy.
1.2.1 lattice_runner_config¶
typedef struct {
uint32_t tick_rate_hz; /* fixed simulation rate; default 60 if 0 */
uint32_t max_objects; /* soft hint; 0 => unbounded in this skeleton */
uint32_t reserved;
} lattice_runner_config;
tick_rate_hz sets the fixed simulation rate (default 60 Hz, 02 §10.1); max_objects is a soft capacity hint (0 ⇒ unbounded in the reference). Leave reserved zeroed.
lattice_runner_config cfg = { .tick_rate_hz = 60, .max_objects = 4096, .reserved = 0 };
lattice_runner* r = lattice_runner_create(&cfg);
if (!r) { /* OOM */ }
1.3 lattice_runner_set_callbacks¶
LATTICE_API lattice_result lattice_runner_set_callbacks(lattice_runner* r, const lattice_callbacks* cb);
Install the event callback table. Role: both. Set this before start/listen/connect so no event is missed. The lattice_callbacks struct (including its user_data) is copied; the function pointers must remain valid for the runner's lifetime.
Returns: LATTICE_OK, or LATTICE_ERR_INVALID_ARG if r is NULL.
lattice_callbacks cb = {0};
cb.user_data = my_ctx;
cb.on_spawned = on_spawned;
cb.on_state_updated= on_state_updated;
cb.on_despawned = on_despawned;
cb.on_rpc = on_rpc;
lattice_runner_set_callbacks(r, &cb);
1.4 lattice_runner_start¶
Put the runner into a role. Role: both (the mode decides which). Call once, after create/set_callbacks and before listen/connect.
mode (lattice_game_mode) |
Role | Authority | Local participant |
|---|---|---|---|
LATTICE_MODE_SERVER |
Dedicated server | yes | no (headless) |
LATTICE_MODE_HOST |
Listen-server / P2P host | yes | yes |
LATTICE_MODE_CLIENT |
Client | no | yes |
LATTICE_MODE_SHARED_HOST |
Shared/distributed authority | yes (per-object owners) | yes — behaves like HOST in the reference |
Returns: LATTICE_OK, or LATTICE_ERR_INVALID_ARG.
1.5 lattice_runner_listen¶
Begin accepting connections on port. Role: server / host only (valid after start with SERVER, HOST, or SHARED_HOST). In the reference's loopback transport port is a logical rendezvous id rather than a real socket bind.
Returns: LATTICE_OK; LATTICE_ERR_INVALID_ARG (bad r); LATTICE_ERR_NO_SESSION if start has not been called or the mode is CLIENT.
1.6 lattice_runner_connect¶
LATTICE_API lattice_result lattice_runner_connect(lattice_runner* r, const char* addr, uint16_t port,
const uint8_t* token, uint32_t token_len);
Initiate a client connection. Role: client only (valid after start with CLIENT). See 3.1.
1.7 lattice_runner_tick — the recv → tick → send pump¶
/* Pump one or more fixed steps. Order per step is recv -> tick -> send; callbacks
* fire synchronously on the calling thread (docs/02 §19 ABI rules). */
LATTICE_API lattice_result lattice_runner_tick(lattice_runner* r, double dt);
Advance the runner. Role: both. This is the single pump the host loop drives every frame, and it is the only place callbacks fire.
Per fixed step the runner performs, in order:
- recv — drain inbound datagrams; decode snapshots/RPCs/connect events into an internal queue.
- tick — advance the simulation one fixed step; apply replicated deltas; drain the event queue, firing callbacks synchronously on this thread (
on_spawned,on_state_updated,on_rpc, etc.). - send — build and pace outbound snapshots and ACKs (server/host), or input/RPC traffic (client).
dt is real wall-clock seconds since the last call; the runner internally accumulates and may run zero or more fixed steps of 1 / tick_rate_hz to consume it (02 §10.1 accumulator loop). Drive it from the engine's fixed step (Unity FixedUpdate, Unreal game thread tick, Godot _physics_process) on the main/game thread, because callbacks land on the calling thread (§5.5).
Returns: LATTICE_OK; LATTICE_ERR_INVALID_ARG (bad r); LATTICE_ERR_NOT_CONNECTED / LATTICE_ERR_NO_SESSION if pumped before a session exists.
double last = now_seconds();
for (;;) {
double t = now_seconds(), dt = t - last; last = t;
lattice_runner_tick(r, dt); // recv -> tick -> send; callbacks fire inside here
}
1.8 lattice_runner_state¶
Current connection state. Role: both. Returns a lattice_connection_state (DISCONNECTED / CONNECTING / CONNECTED / DISCONNECTING); a NULL runner reports LATTICE_CONN_DISCONNECTED. Clients poll this after connect to know when the handshake completed (the on_connected callback is the event-driven equivalent).
1.9 lattice_runner_destroy¶
Tear down the runner and free all core-owned storage (objects, snapshot buffers, type registry, connection state). Role: both. Idempotent-safe on NULL. After this the handle is invalid; do not call any other function with it. The core never frees buffers you own.
2. Server / host functions¶
The functions in this section require authority — i.e. the runner was started with LATTICE_MODE_SERVER, LATTICE_MODE_HOST, or LATTICE_MODE_SHARED_HOST. Spawning/despawning and authoritative state writes from a plain CLIENT are rejected (see each function's error codes). Type registration and the serialization helpers are role-agnostic and must run identically on every peer so schemas match.
2.1 lattice_register_type¶
LATTICE_API lattice_type_id lattice_register_type(lattice_runner* r, const lattice_type_desc* desc); /* 0 on error */
Register a replicable type (a NetworkObject/struct schema). Role: both — and it must be called in the same order with the same descriptors on the server and every client, so the derived type_ids and the content hash agree (05 §7, 02 §20.2). The core deep-copies the field array and key string, so your buffers need not outlive the call.
| Param | Meaning |
|---|---|
r |
The runner. |
desc |
A lattice_type_desc describing the type's state block + serialization. |
Returns: a non-zero lattice_type_id on success; 0 on error (NULL args, zero state_size, malformed field array, or a key collision).
2.1.1 lattice_type_desc¶
typedef struct {
const char* key; /* stable type key; hashed + matched across peers */
uint32_t state_size; /* bytes of the local state block for one object */
const lattice_field_desc* fields; /* may be NULL when the manual path is used */
uint32_t field_count;
lattice_serialize_fn serialize; /* optional manual path; NULL => declarative */
lattice_deserialize_fn deserialize; /* optional manual path */
void* user; /* passed back to manual fns */
} lattice_type_desc;
Two registration paths, chosen by whether serialize/deserialize are set:
- Declarative path (
serialize == NULL): the core walks thefieldsarray (onelattice_field_descper replicated leaf field) and packs/unpacks each field from the state block automatically. This is the analogue of the codegen output in 02 §20 — the[Networked]/UPROPERTYdeclarative path of 05 §12.2. - Manual path (
serialize/deserializenon-NULL): the whole state block is one unit; your function drives aBitWriter/BitReader. This is theINetworkSerializablepath of 05 §12.3 / 02 §21.useris handed back to both functions.
typedef void (*lattice_serialize_fn)(lattice_bitwriter* w, const void* state, void* user);
typedef void (*lattice_deserialize_fn)(lattice_bitreader* r, void* state, void* user);
The key is hashed into the schema/version negotiation; mismatched catalogs are rejected at connect (see lattice_type_content_hash).
2.1.2 lattice_field_desc¶
typedef struct {
const char* name; /* field name (part of the content hash) */
lattice_field_kind kind;
uint32_t offset; /* byte offset of this field in the state block */
lattice_float_quant quant; /* for COMPRESSED_FLOAT / VECTOR* */
int32_t ranged_min; /* for RANGED_INT */
int32_t ranged_max; /* for RANGED_INT */
uint32_t quat_bits; /* for QUATERNION smallest-three (default 10)*/
uint32_t byte_capacity;/* for STRING/BYTES: fixed capacity in block*/
lattice_type_id struct_type; /* for STRUCT: a nested registered type */
} lattice_field_desc;
One entry per replicated leaf field in the declarative path. kind is a lattice_field_kind; offset is the field's byte offset within the state block; the remaining fields are encoding hints used only by the relevant kind (quantization for COMPRESSED_FLOAT/VECTOR*, ranged_min/max for RANGED_INT, quat_bits for QUATERNION, byte_capacity for STRING/BYTES, struct_type for a nested STRUCT). name participates in the content hash. See 02 §14.6 for the wire encodings.
Declarative registration example:
typedef struct { float health; lattice_vec3 pos; int32_t score; } PlayerBlock;
lattice_field_desc fields[] = {
{ .name="health", .kind=LATTICE_FIELD_COMPRESSED_FLOAT, .offset=offsetof(PlayerBlock,health),
.quant={ .min=0.f, .max=100.f, .precision=0.1f } },
{ .name="pos", .kind=LATTICE_FIELD_VECTOR3, .offset=offsetof(PlayerBlock,pos),
.quant={ .min=-4096.f, .max=4096.f, .precision=0.001f } }, /* 1 mm */
{ .name="score", .kind=LATTICE_FIELD_RANGED_INT, .offset=offsetof(PlayerBlock,score),
.ranged_min=0, .ranged_max=1000000 },
};
lattice_type_desc desc = {
.key="Player", .state_size=sizeof(PlayerBlock),
.fields=fields, .field_count=3,
};
lattice_type_id PLAYER = lattice_register_type(r, &desc); // 0 == failure
Manual registration example:
static void player_ser(lattice_bitwriter* w, const void* s, void* u) {
const PlayerBlock* p = s;
lattice_bw_write_compressed_float(w, p->health, (lattice_float_quant){0,100,0.1f});
lattice_bw_write_vector3(w, p->pos, (lattice_float_quant){-4096,4096,0.001f});
lattice_bw_write_ranged_int(w, p->score, 0, 1000000);
}
static void player_deser(lattice_bitreader* r, void* s, void* u) {
PlayerBlock* p = s;
p->health = lattice_br_read_compressed_float(r, (lattice_float_quant){0,100,0.1f});
p->pos = lattice_br_read_vector3(r, (lattice_float_quant){-4096,4096,0.001f});
p->score = lattice_br_read_ranged_int(r, 0, 1000000);
}
lattice_type_desc desc = { .key="Player", .state_size=sizeof(PlayerBlock),
.serialize=player_ser, .deserialize=player_deser };
lattice_type_id PLAYER = lattice_register_type(r, &desc);
2.2 lattice_spawn¶
LATTICE_API lattice_netid lattice_spawn(lattice_runner* r, lattice_type_id type,
const void* initial_state, uint64_t owner); /* authority only; 0 on error */
Create a replicated object. Role: authority only (server / host / shared-host). The core allocates a stable lattice_netid, copies initial_state (a state_size-byte block of type) into its arena, and queues a spawn to peers — firing on_spawned on the authority and on every receiving client during their next tick.
| Param | Meaning |
|---|---|
type |
A lattice_type_id from lattice_register_type. |
initial_state |
Pointer to the initial state block (copied). May be NULL ⇒ zero-initialized. |
owner |
The owning peer id (0 for none / server-owned). Used by RPC OWNER targeting and authority semantics. |
Returns: a non-zero lattice_netid; 0 on error — type unknown, runner not authority (client called it), or no session.
PlayerBlock init = { .health = 100.f, .score = 0 };
lattice_netid id = lattice_spawn(r, PLAYER, &init, owner_peer); // 0 == failed
2.3 lattice_despawn¶
Destroy a replicated object. Role: authority only. Queues a despawn to peers and fires on_despawned on every peer (and locally) on the next tick; the core frees the object's state block.
Returns: LATTICE_OK; LATTICE_ERR_UNKNOWN_OBJECT (no such id); LATTICE_ERR_NOT_AUTHORITY (caller is a client); LATTICE_ERR_INVALID_ARG.
2.4 lattice_object_state¶
LATTICE_API void* lattice_object_state(lattice_runner* r, lattice_netid id); /* mutable; NULL if unknown */
Get a pointer to the object's mutable host-memory state block. Role: both — but the meaning differs:
- On the authority this is the writable source of truth. Mutate fields in place, then call
lattice_object_mark_dirtyso the change is included in the next snapshot. - On a client this is the locally-replicated copy maintained by the core. Treat it as read-only: read it (typically inside
on_state_updated) to drive your view. Authoritative writes from a client are not replicated.
Returns: a pointer to the state_size-byte block, or NULL if id is unknown. The pointer is valid until the object is despawned or the runner is destroyed; do not free it.
PlayerBlock* p = lattice_object_state(r, id); // NULL if unknown
if (p) { p->health -= 10.f; lattice_object_mark_dirty(r, id, /*field*/0); }
2.5 lattice_object_mark_dirty¶
LATTICE_API lattice_result lattice_object_mark_dirty(lattice_runner* r, lattice_netid id, uint32_t field_index);
Mark a field of an object dirty so it is delta-encoded into the next snapshot. Role: authority (writes are authority-gated). field_index is the index into the type's fields array (declarative path); for the manual path, marking any field re-serializes the whole block. The dirty bit drives delta compression (02 §14.2–14.3).
Returns: LATTICE_OK; LATTICE_ERR_UNKNOWN_OBJECT; LATTICE_ERR_NOT_AUTHORITY (client caller); LATTICE_ERR_INVALID_ARG (e.g. field_index out of range).
PlayerBlock* p = lattice_object_state(r, id);
p->score += 50;
lattice_object_mark_dirty(r, id, 2); // field index 2 == "score"
2.6 lattice_object_count / lattice_object_at¶
LATTICE_API uint32_t lattice_object_count(lattice_runner* r);
LATTICE_API lattice_netid lattice_object_at(lattice_runner* r, uint32_t index);
Enumerate the runner's known objects. Role: both. lattice_object_count returns how many objects the runner currently tracks (those it has authority over, or those replicated to it as a client). lattice_object_at maps a dense index in [0, count) to its lattice_netid, or returns 0 if out of range. Iteration order is deterministic (ascending network_id, 02 §18.2); a despawn invalidates indices, so re-read count per pass.
for (uint32_t i = 0, n = lattice_object_count(r); i < n; ++i) {
lattice_netid id = lattice_object_at(r, i);
/* ... */
}
2.7 Authority & ownership¶
Authority is set at runner level by the lattice_game_mode (SERVER/HOST/SHARED_HOST = authority; CLIENT = not) and at object level by the owner argument to lattice_spawn. The reference enforces this through the LATTICE_ERR_NOT_AUTHORITY gate on spawn/despawn/mark_dirty and through RPC target routing (§2.8). Per-object authority transfer for shared/distributed mode (the RequestAuthority/GrantAuthority protocol and on_authority_changed callback of 02 §17.2 / 05 §2.2) is a design feature not yet exposed in the reference ABI; its entry points will be added here when the production core lands them.
2.8 RPC (server / host side targets)¶
LATTICE_API lattice_result lattice_rpc(lattice_runner* r, lattice_netid id, uint16_t rpc_id,
lattice_rpc_target target, const uint8_t* payload,
uint32_t len, int reliable);
Send an event RPC associated with object id. Role: both (see targeting rules). The same function is used by server and client; the lattice_rpc_target selects recipients and the reliable flag selects the channel.
| Param | Meaning |
|---|---|
id |
The object the RPC is scoped to (for routing — e.g. OWNER/ALL_BUT_OWNER resolve against this object's owner). |
rpc_id |
Caller-defined 16-bit method id; surfaced verbatim to on_rpc. |
target |
lattice_rpc_target — SERVER / OWNER / ALL / ALL_BUT_OWNER. |
payload / len |
Opaque argument bytes (copied). Typically built with a BitWriter; read back with a BitReader. May be NULL/0. |
reliable |
Non-zero ⇒ a reliable channel (Reliable-Ordered/Unordered, 02 §5); zero ⇒ unreliable. |
Server-side targets. When the authority invokes lattice_rpc:
target |
Delivered to |
|---|---|
LATTICE_RPC_SERVER |
The authority itself (local dispatch). |
LATTICE_RPC_OWNER |
The peer that owns id (owner from spawn). |
LATTICE_RPC_ALL |
Every peer that has the object (and the authority). |
LATTICE_RPC_ALL_BUT_OWNER |
Every peer except the owner. |
Which callback fires: every recipient's on_rpc(user_data, id, rpc_id, payload, len) fires inside their next tick.
Returns: LATTICE_OK; LATTICE_ERR_UNKNOWN_OBJECT; LATTICE_ERR_NOT_CONNECTED / LATTICE_ERR_NO_SESSION; LATTICE_ERR_INVALID_ARG.
/* Authority broadcasts "door opened" reliably to everyone: */
lattice_bitwriter* w = lattice_bw_create();
lattice_bw_write_ranged_int(w, door_index, 0, 255);
lattice_rpc(r, door_id, RPC_DOOR_OPENED, LATTICE_RPC_ALL,
lattice_bw_data(w), lattice_bw_byte_count(w), /*reliable=*/1);
lattice_bw_destroy(w);
2.9 Which callbacks fire on the server/host¶
| Callback | Fires on server/host when |
|---|---|
on_connected |
A client completes its handshake (per-runner connect signal). |
on_disconnected |
A peer drops / times out (reason code). |
on_spawned |
Locally, when this authority spawns an object (mirrors what clients receive). |
on_despawned |
Locally, on despawn. |
on_state_updated |
Rare on a pure authority — it owns the truth; mainly relevant for SHARED_HOST accepting a remote owner's state. |
on_rpc |
A client sent a LATTICE_RPC_SERVER RPC, or a local SERVER-targeted RPC dispatched. |
on_log |
Any time the core emits a diagnostic. |
3. Client functions¶
A client runner (LATTICE_MODE_CLIENT) connects to an authority, reads replicated state, and sends client→server RPCs. It cannot spawn/despawn or write authoritative state (those return LATTICE_ERR_NOT_AUTHORITY / 0).
3.1 lattice_runner_connect (client)¶
LATTICE_API lattice_result lattice_runner_connect(lattice_runner* r, const char* addr, uint16_t port,
const uint8_t* token, uint32_t token_len);
Connect to a server/host. Role: client only (after start(LATTICE_MODE_CLIENT)).
| Param | Meaning |
|---|---|
addr |
Server address (UTF-8 const char*). In the reference loopback transport, a logical rendezvous id. |
port |
Server port (logical id in the reference). |
token / token_len |
Optional connect token bytes (copied). In production this is the Ed25519-signed connect token from 03 Auth Service, validated during the handshake (02 §4.2); the reference accepts but does not cryptographically verify it. May be NULL/0. |
Returns: LATTICE_OK (connect initiated — the runner moves to LATTICE_CONN_CONNECTING); LATTICE_ERR_NO_SESSION (not started, or started as a server); LATTICE_ERR_INVALID_ARG. Connection completion is observed via on_connected or by polling lattice_runner_state for LATTICE_CONN_CONNECTED.
lattice_runner_start(r, LATTICE_MODE_CLIENT);
lattice_runner_connect(r, "host.example", 7777, token, token_len);
while (lattice_runner_state(r) != LATTICE_CONN_CONNECTED)
lattice_runner_tick(r, dt); // pump until connected (on_connected also fires here)
3.2 Reading replicated state¶
A client never writes authoritative state; it observes it through three callbacks plus the read-only state pointer:
on_spawned(user, id, type, owner)— a new object entered the client's world. Look uptype, instantiate your view, and mapid ↔ your object.on_state_updated(user, id)—id's replicated state changed; read it withlattice_object_stateand apply to your view.on_despawned(user, id)—idleft; destroy your view and unmap.
static void on_spawned(void* u, lattice_netid id, lattice_type_id type, uint64_t owner) {
spawn_view(u, id, type, owner);
}
static void on_state_updated(void* u, lattice_netid id) {
const PlayerBlock* p = lattice_object_state(((App*)u)->runner, id); /* read-only on client */
if (p) update_view(u, id, p);
}
static void on_despawned(void* u, lattice_netid id) { destroy_view(u, id); }
You can also poll the world with lattice_object_count/lattice_object_at (e.g. a late initialization sweep), reading each via lattice_object_state.
3.3 Client → server RPC¶
A client uses the same lattice_rpc function, but clients may only originate LATTICE_RPC_SERVER RPCs — a request to the authority, which validates and may re-broadcast (02 §16.2, 05 §4.3 RpcTargets.StateAuthority). Attempting ALL/OWNER/ALL_BUT_OWNER from a client is rejected (it cannot forge a broadcast).
target from a client |
Result |
|---|---|
LATTICE_RPC_SERVER |
Sent to the authority; its on_rpc fires. The supported client→server path. |
LATTICE_RPC_OWNER / ALL / ALL_BUT_OWNER |
Rejected — authority-only targets (LATTICE_ERR_NOT_AUTHORITY). |
/* Client asks the server to fire a weapon: */
lattice_bitwriter* w = lattice_bw_create();
lattice_bw_write_vector3(w, aim_dir, (lattice_float_quant){-1.f, 1.f, 0.001f});
lattice_rpc(r, my_pawn_id, RPC_FIRE, LATTICE_RPC_SERVER,
lattice_bw_data(w), lattice_bw_byte_count(w), /*reliable=*/1);
lattice_bw_destroy(w);
3.4 Which callbacks fire on the client¶
| Callback | Fires on the client when |
|---|---|
on_connected |
The handshake to the server/host completes. |
on_disconnected |
The connection drops / times out (reason). |
on_spawned |
An object enters the client's world (full baseline). |
on_state_updated |
A replicated object's state delta is applied. |
on_despawned |
An object leaves / is destroyed. |
on_rpc |
The server sent this client an RPC (OWNER/ALL/ALL_BUT_OWNER that includes it). |
on_log |
The core emits a diagnostic. |
4. Serialization API — BitWriter / BitReader¶
These are the typed bit-packing primitives ("functions for custom variables"). They back the manual serialization path (lattice_type_desc.serialize/deserialize) — i.e. the C-ABI realization of the INetworkSerializable / BitBuffer interface in 05 §12.3 and 02 §21. The same handles are passed into your serialize/deserialize callbacks, and the create/destroy forms are available standalone (RPC payload construction, round-trip testing).
The Write* / Read* methods are 1:1 mirrors: read in the exact order, with the exact descriptors, you wrote. All packing is little-endian, MSB-first within each byte (02 §20.3).
4.1 lattice_float_quant — the quantization descriptor¶
Passed to every compressed-float / compressed-vector method; defines the quantization range and step (02 §14.6 / §21.4). It is the C-ABI form of 05's FloatQuant { min, max, precision } / [Quantize(min,max,precision)].
4.2 BitWriter lifecycle & buffer access¶
LATTICE_API lattice_bitwriter* lattice_bw_create(void);
LATTICE_API void lattice_bw_destroy(lattice_bitwriter* w);
LATTICE_API void lattice_bw_reset(lattice_bitwriter* w);
LATTICE_API uint32_t lattice_bw_bit_count(const lattice_bitwriter* w);
LATTICE_API uint32_t lattice_bw_byte_count(const lattice_bitwriter* w);
LATTICE_API const uint8_t* lattice_bw_data(const lattice_bitwriter* w); /* flushed byte buffer */
| Function | Role | Notes |
|---|---|---|
lattice_bw_create |
both | Allocate a standalone writer. (Inside a serialize callback the core hands you the writer — do not create/destroy that one.) |
lattice_bw_destroy |
both | Free a writer you created. |
lattice_bw_reset |
both | Clear contents for reuse without reallocating. |
lattice_bw_bit_count |
both | Number of bits written so far. |
lattice_bw_byte_count |
both | Byte length of the flushed buffer (ceil(bits/8)); the len to pass to lattice_rpc. |
lattice_bw_data |
both | Pointer to the flushed byte buffer (valid until the next write/reset/destroy). |
4.3 BitWriter typed Write* methods¶
LATTICE_API void lattice_bw_write_bool(lattice_bitwriter* w, int v);
LATTICE_API void lattice_bw_write_enum(lattice_bitwriter* w, uint32_t value, uint32_t count);
LATTICE_API void lattice_bw_write_byte(lattice_bitwriter* w, uint8_t v);
LATTICE_API void lattice_bw_write_int(lattice_bitwriter* w, int32_t v);
LATTICE_API void lattice_bw_write_ranged_int(lattice_bitwriter* w, int32_t value, int32_t min, int32_t max);
LATTICE_API void lattice_bw_write_long(lattice_bitwriter* w, int64_t v);
LATTICE_API void lattice_bw_write_ranged_long(lattice_bitwriter* w, int64_t value, int64_t min, int64_t max);
LATTICE_API void lattice_bw_write_float(lattice_bitwriter* w, float v);
LATTICE_API void lattice_bw_write_compressed_float(lattice_bitwriter* w, float value, lattice_float_quant q);
LATTICE_API void lattice_bw_write_double(lattice_bitwriter* w, double v);
LATTICE_API void lattice_bw_write_vector2(lattice_bitwriter* w, lattice_vec2 v, lattice_float_quant q);
LATTICE_API void lattice_bw_write_vector3(lattice_bitwriter* w, lattice_vec3 v, lattice_float_quant q);
LATTICE_API void lattice_bw_write_vector4(lattice_bitwriter* w, lattice_vec4 v, lattice_float_quant q);
LATTICE_API void lattice_bw_write_quaternion(lattice_bitwriter* w, lattice_quat v, uint32_t bits_per_comp);
LATTICE_API void lattice_bw_write_string(lattice_bitwriter* w, const char* s);
LATTICE_API void lattice_bw_write_bytes(lattice_bitwriter* w, const uint8_t* p, uint32_t n);
LATTICE_API void lattice_bw_write_bits(lattice_bitwriter* w, uint32_t value, uint32_t bitcount);
| Method | Encoding | Maps to 05 §12 |
|---|---|---|
write_bool(w, v) |
1 bit (v non-zero ⇒ 1) |
WriteBool |
write_enum(w, value, count) |
ceil(log2(count)) bits — a ranged int over [0,count) |
WriteEnum |
write_byte(w, v) |
8 bits | WriteByte |
write_int(w, v) |
32 bits | WriteInt |
write_ranged_int(w, value, min, max) |
ceil(log2(max-min+1)) bits |
WriteRangedInt |
write_long(w, v) |
64 bits | WriteLong |
write_ranged_long(w, value, min, max) |
bounded 64-bit | WriteRangedLong |
write_float(w, v) |
32 bits (IEEE-754) | WriteFloat |
write_compressed_float(w, value, q) |
quantized per q |
WriteCompressedFloat |
write_double(w, v) |
64 bits | WriteDouble |
write_vector2/3/4(w, v, q) |
2/3/4 components, each compressed per q |
WriteVector2/3/4 |
write_quaternion(w, v, bits_per_comp) |
smallest-three (2-bit index + 3×bits_per_comp; default 10 ⇒ ~32 bits) |
WriteQuaternion |
write_string(w, s) |
length-prefixed UTF-8 | WriteString |
write_bytes(w, p, n) |
length-prefixed blob | WriteBytes |
write_bits(w, value, bitcount) |
raw bitcount low bits of value (escape hatch) |
WriteBits |
4.4 BitReader lifecycle & typed Read* methods¶
LATTICE_API lattice_bitreader* lattice_br_create(const uint8_t* data, uint32_t byte_len);
LATTICE_API void lattice_br_destroy(lattice_bitreader* r);
LATTICE_API int lattice_br_read_bool(lattice_bitreader* r);
LATTICE_API uint32_t lattice_br_read_enum(lattice_bitreader* r, uint32_t count);
LATTICE_API uint8_t lattice_br_read_byte(lattice_bitreader* r);
LATTICE_API int32_t lattice_br_read_int(lattice_bitreader* r);
LATTICE_API int32_t lattice_br_read_ranged_int(lattice_bitreader* r, int32_t min, int32_t max);
LATTICE_API int64_t lattice_br_read_long(lattice_bitreader* r);
LATTICE_API int64_t lattice_br_read_ranged_long(lattice_bitreader* r, int64_t min, int64_t max);
LATTICE_API float lattice_br_read_float(lattice_bitreader* r);
LATTICE_API float lattice_br_read_compressed_float(lattice_bitreader* r, lattice_float_quant q);
LATTICE_API double lattice_br_read_double(lattice_bitreader* r);
LATTICE_API lattice_vec2 lattice_br_read_vector2(lattice_bitreader* r, lattice_float_quant q);
LATTICE_API lattice_vec3 lattice_br_read_vector3(lattice_bitreader* r, lattice_float_quant q);
LATTICE_API lattice_vec4 lattice_br_read_vector4(lattice_bitreader* r, lattice_float_quant q);
LATTICE_API lattice_quat lattice_br_read_quaternion(lattice_bitreader* r, uint32_t bits_per_comp);
LATTICE_API uint32_t lattice_br_read_string(lattice_bitreader* r, char* out, uint32_t cap); /* returns length, NUL-terminates */
LATTICE_API uint32_t lattice_br_read_bytes(lattice_bitreader* r, uint8_t* out, uint32_t cap);/* returns length */
LATTICE_API uint32_t lattice_br_read_bits(lattice_bitreader* r, uint32_t bitcount);
lattice_br_create(data, byte_len)— wrap a caller-owned byte buffer (e.g. an RPCpayload). Role: both. Destroy withlattice_br_destroy. (Inside adeserializecallback the core hands you the reader.)- Each
read_*is the inverse of the matchingwrite_*and must be called in the same order with the same descriptors.read_boolreturns0/1; the vector/quaternion readers return the POD value types (§5.7). read_string/read_byteswrite into your caller-ownedoutbuffer of capacitycapand return the actual length;read_stringNUL-terminates. Ifcapis too small the value is truncated tocap(the documented full-error path in production maps toLATTICE_ERR_BUFFER_TOO_SMALL); sizeoutfrom the field'sbyte_capacity.
4.5 Round-trip example¶
/* WRITE */
lattice_bitwriter* w = lattice_bw_create();
lattice_bw_write_bool(w, alive);
lattice_bw_write_ranged_int(w, score, 0, 1000000);
lattice_bw_write_compressed_float(w, stamina, (lattice_float_quant){0,100,0.1f});
lattice_bw_write_vector3(w, pos, (lattice_float_quant){-4096,4096,0.001f});
lattice_bw_write_quaternion(w, rot, 10);
uint32_t n = lattice_bw_byte_count(w);
const uint8_t* bytes = lattice_bw_data(w);
/* READ (same order, same descriptors) */
lattice_bitreader* rd = lattice_br_create(bytes, n);
int alive2 = lattice_br_read_bool(rd);
int32_t score2 = lattice_br_read_ranged_int(rd, 0, 1000000);
float stamina2 = lattice_br_read_compressed_float(rd, (lattice_float_quant){0,100,0.1f});
lattice_vec3 pos2 = lattice_br_read_vector3(rd, (lattice_float_quant){-4096,4096,0.001f});
lattice_quat rot2 = lattice_br_read_quaternion(rd, 10);
lattice_br_destroy(rd);
lattice_bw_destroy(w);
5. Callback / event model, threading & enums¶
5.1 lattice_callbacks¶
typedef struct {
void* user_data;
void (*on_connected) (void* user_data);
void (*on_disconnected) (void* user_data, int reason);
void (*on_spawned) (void* user_data, lattice_netid id, lattice_type_id type, uint64_t owner);
void (*on_despawned) (void* user_data, lattice_netid id);
void (*on_state_updated)(void* user_data, lattice_netid id);
void (*on_rpc) (void* user_data, lattice_netid id, uint16_t rpc_id,
const uint8_t* payload, uint32_t len);
void (*on_log) (void* user_data, lattice_log_level level, const char* msg);
} lattice_callbacks;
Installed via lattice_runner_set_callbacks. user_data is passed back to every callback (your wrapper/runner context). Any pointer may be NULL to ignore that event.
| Callback | Args | Meaning |
|---|---|---|
on_connected |
— | Session established (client: handshake done; server: a peer connected). |
on_disconnected |
reason |
Session/peer lost; reason is an implementation code (e.g. timeout vs. explicit). |
on_spawned |
id, type, owner |
A replicated object appeared. Use type to instantiate; record owner. |
on_despawned |
id |
A replicated object was removed. |
on_state_updated |
id |
id's replicated state changed; read via lattice_object_state. |
on_rpc |
id, rpc_id, payload, len |
An RPC arrived; payload/len are the (copied) args — wrap in a BitReader. The buffer is valid for the duration of the callback only. |
on_log |
level, msg |
Diagnostic at lattice_log_level; msg is valid for the callback only. |
The implemented callback set is leaner than the design sketch in 05 §2.2 (which also lists
on_input_poll,on_sim_tick,on_authority_changed). Those belong to the prediction/input and authority-transfer features not yet in the reference; they will be added to this table when those ABI entry points land. Today, snapshot application surfaces ason_state_updated, and the simulation step is driven directly bylattice_runner_tick.
5.2 lattice_game_mode¶
typedef enum {
LATTICE_MODE_SERVER = 0,
LATTICE_MODE_HOST = 1, /* server that is also a local participant */
LATTICE_MODE_CLIENT = 2,
LATTICE_MODE_SHARED_HOST = 3 /* shared/distributed authority (skeleton: like HOST) */
} lattice_game_mode;
5.3 lattice_connection_state¶
typedef enum {
LATTICE_CONN_DISCONNECTED = 0,
LATTICE_CONN_CONNECTING,
LATTICE_CONN_CONNECTED,
LATTICE_CONN_DISCONNECTING
} lattice_connection_state;
Returned by lattice_runner_state; mirrors the connection FSM of 02 §4.1.
5.4 lattice_result (error codes)¶
typedef enum {
LATTICE_OK = 0,
LATTICE_ERR_INVALID_ARG,
LATTICE_ERR_NOT_AUTHORITY,
LATTICE_ERR_NO_SESSION,
LATTICE_ERR_UNKNOWN_TYPE,
LATTICE_ERR_UNKNOWN_OBJECT,
LATTICE_ERR_SCHEMA_MISMATCH,
LATTICE_ERR_BUFFER_TOO_SMALL,
LATTICE_ERR_NOT_CONNECTED,
LATTICE_ERR_INTERNAL
} lattice_result;
| Code | Meaning / typical cause |
|---|---|
LATTICE_OK |
Success. |
LATTICE_ERR_INVALID_ARG |
NULL/invalid handle, out-of-range index, malformed descriptor, stale netid. |
LATTICE_ERR_NOT_AUTHORITY |
An authority-only call (spawn/despawn/mark_dirty, or a non-SERVER RPC target) made by a client. |
LATTICE_ERR_NO_SESSION |
Called before start, or in the wrong role (e.g. connect on a server). |
LATTICE_ERR_UNKNOWN_TYPE |
type_id not registered. |
LATTICE_ERR_UNKNOWN_OBJECT |
No object with that netid. |
LATTICE_ERR_SCHEMA_MISMATCH |
Peer catalogs/content hashes disagree (02 §20.2). |
LATTICE_ERR_BUFFER_TOO_SMALL |
A read/get target buffer was too small for the value. |
LATTICE_ERR_NOT_CONNECTED |
Operation needs an established session. |
LATTICE_ERR_INTERNAL |
Unexpected internal failure. |
Render any code with lattice_result_str. Note the non-lattice_result failure conventions: lattice_register_type and lattice_spawn signal failure by returning 0 (an invalid id), and lattice_object_state by returning NULL.
5.5 lattice_rpc_target¶
typedef enum {
LATTICE_RPC_SERVER = 0,
LATTICE_RPC_OWNER,
LATTICE_RPC_ALL,
LATTICE_RPC_ALL_BUT_OWNER
} lattice_rpc_target;
See §2.8 (authority targeting) and §3.3 (clients may originate only LATTICE_RPC_SERVER). Maps to 02 §16.2's ToServerOrHost/ToOwner/ToAll and 05 §4.3's RpcTargets.
5.6 lattice_field_kind¶
typedef enum {
LATTICE_FIELD_BOOL = 0,
LATTICE_FIELD_INT32,
LATTICE_FIELD_INT64,
LATTICE_FIELD_FLOAT,
LATTICE_FIELD_DOUBLE,
LATTICE_FIELD_RANGED_INT,
LATTICE_FIELD_COMPRESSED_FLOAT,
LATTICE_FIELD_VECTOR2,
LATTICE_FIELD_VECTOR3,
LATTICE_FIELD_VECTOR4,
LATTICE_FIELD_QUATERNION,
LATTICE_FIELD_STRING,
LATTICE_FIELD_BYTES,
LATTICE_FIELD_STRUCT
} lattice_field_kind;
The kind in a declarative lattice_field_desc; each maps to the matching BitWriter/BitReader encoding and to the per-engine primitive table in 05 §12.1.
5.7 POD value types¶
typedef struct { float x, y; } lattice_vec2;
typedef struct { float x, y, z; } lattice_vec3;
typedef struct { float x, y, z, w; } lattice_vec4;
typedef struct { float x, y, z, w; } lattice_quat;
Fixed-layout POD; mirror with [StructLayout(LayoutKind.Sequential)] (C#), a matching struct (Unreal/Godot), or a typed-array view (WASM) (05 §2.3 / §12.6).
5.8 lattice_log_level¶
typedef enum {
LATTICE_LOG_TRACE = 0,
LATTICE_LOG_DEBUG,
LATTICE_LOG_INFO,
LATTICE_LOG_WARN,
LATTICE_LOG_ERROR
} lattice_log_level;
Severity passed to the on_log callback.
Handles & scalar typedefs¶
typedef struct lattice_runner_t lattice_runner; /* opaque: one networked simulation */
typedef struct lattice_bitwriter_t lattice_bitwriter; /* opaque: typed bit-packer */
typedef struct lattice_bitreader_t lattice_bitreader; /* opaque: typed bit-unpacker */
typedef uint64_t lattice_netid; /* NetworkObject id; 0 == invalid */
typedef uint32_t lattice_type_id; /* registered type id; 0 == invalid */
lattice_netid (uint64_t) and lattice_type_id (uint32_t) are values, not pointers; 0 means invalid in both. Peer ids (the owner argument / field) are uint64_t.
5.5b Threading rules¶
- The core's internal I/O may run on a worker thread, but all callbacks are deferred and fire synchronously inside
lattice_runner_tickon the thread that called it (02 §19;lattice.hheader comment). - Therefore call
tickon your engine's main/game thread (UnityFixedUpdate, Unreal game thread, Godot_physics_process) so callbacks land where it is safe to touch engine objects (05 §10). - Do no blocking work in a callback, and never let an exception/
longjmppropagate out of one into the core. - Treat a runner handle as single-threaded: do not call its functions concurrently from multiple threads.
6. C ABI → engine high-level mapping¶
How each implemented lattice_* call surfaces in the idiomatic per-engine wrappers of 05 Engine Integration. (Unity uses a Roslyn source generator over [Networked]/[Rpc]; Unreal uses LATTICE_* macros; Godot uses the LatticeRunner/LatticeSync GDExtension nodes.)
6.1 Lifecycle & pump¶
| C ABI | Unity (lattice-unity) |
Unreal (lattice-unreal) |
Godot (lattice-godot) |
|---|---|---|---|
lattice_runner_create + lattice_runner_start |
NetworkRunner.StartGame(GameMode, session) |
ULatticeRunnerSubsystem::StartGame(ELatticeMode, Session) |
LatticeRunner.start_game(mode, session) |
lattice_runner_set_callbacks |
internal (wrapper installs its dispatch table) | internal (subsystem) | internal (node emits signals) |
lattice_runner_listen |
host/server path of StartGame(Host/Server) |
StartGame(Host/Server) |
start_game(MODE_HOST/SERVER) |
lattice_runner_connect |
StartGame(Client, …) (token from 03) |
StartGame(Client, …) |
start_game(MODE_CLIENT, …) |
lattice_runner_tick |
driven from FixedUpdate (accumulator → 60 Hz) |
game-thread tick (subsystem) | _physics_process |
lattice_runner_state |
NetworkRunner.State / IsConnected |
GetConnectionState() |
runner.connection_state() |
lattice_runner_destroy |
SafeHandle finalize / Shutdown() |
subsystem Deinitialize |
node _exit_tree |
6.2 Types, objects & state¶
| C ABI | Unity | Unreal | Godot |
|---|---|---|---|
lattice_register_type |
source-gen lt_object_schema baked into the prefab catalog (§7) |
LATTICE_NETWORKED* macros → LATTICE_NETSTRUCT registration |
LatticeSync.replicate(...) / define_struct(...) |
lattice_spawn |
Runner.Spawn(prefab, owner) |
Runner->Spawn(Class, xform, owner) |
runner.spawn(scene, owner) |
lattice_despawn |
Runner.Despawn(Object) |
Runner->Despawn(Actor) |
runner.despawn(node) |
lattice_object_state + lattice_object_mark_dirty |
[Networked] property setter (source-gen marks dirty) |
LATTICE_NETWORKED property setter |
exported property write via LatticeSync |
read of lattice_object_state (client) |
[Networked] getter / [OnChanged] |
property getter / OnRep |
property getter / *_changed signal |
lattice_object_count / lattice_object_at |
Runner object enumeration |
runner object iteration | runner object iteration |
6.3 RPC & serialization¶
| C ABI | Unity | Unreal | Godot |
|---|---|---|---|
lattice_rpc (target SERVER) |
[Rpc(RpcSources.InputAuthority, RpcTargets.StateAuthority)] |
LATTICE_RPC(InputAuthority, StateAuthority) |
@lattice_rpc(INPUT_AUTHORITY, STATE_AUTHORITY) |
lattice_rpc (target ALL/OWNER/ALL_BUT_OWNER) |
[Rpc(StateAuthority, All/…)] |
LATTICE_RPC(StateAuthority, All/…) |
@lattice_rpc(STATE_AUTHORITY, …) |
on_rpc callback |
generated [Rpc] dispatch stub |
*_Implementation |
annotated method body |
lattice_bw_write_* / lattice_br_read_* |
BitBuffer / INetworkSerializable.Serialize |
lattice::BitWriter/BitReader Write*/Read* |
BitWriter/BitReader write_*/read_* |
lattice_float_quant |
FloatQuant / [Quantize(min,max,prec)] |
LATTICE_RANGE/LATTICE_BOUNDS |
{"range"/"bounds", "precision"} dict |
6.4 Callbacks¶
| C ABI callback | Unity | Unreal | Godot |
|---|---|---|---|
on_connected / on_disconnected |
OnConnectedToServer / OnDisconnected |
subsystem delegates | connected / disconnected signals |
on_spawned / on_despawned |
OnSpawned (instantiate from catalog) / OnDespawned |
spawn/despawn delegates | spawned / despawned signals |
on_state_updated |
[OnChanged(nameof(X))] |
OnRep_* |
*_changed signal |
on_rpc |
generated [Rpc] handler |
*_Implementation |
annotated method |
on_log |
Unity log bridge | UE_LOG bridge |
Godot logger |
6.5 Enums¶
| C ABI enum | Unity | Unreal | Godot |
|---|---|---|---|
lattice_game_mode |
GameMode.{Server,Host,Client,SharedHost} |
ELatticeMode::{...} |
LatticeRunner.MODE_{...} |
lattice_rpc_target |
RpcTargets.{StateAuthority,…,All} |
ELatticeRpcTarget |
LatticeSync.{SERVER,OWNER,ALL,…} |
lattice_field_kind |
[Networked] primitive type table (05 §12.1) |
LATTICE_NETWORKED member type |
LatticeSync.T_* |
lattice_result |
translated to exception/warning | translated to log/error | translated to error |
6.6 Not yet in the reference ABI (design-only today)¶
These appear in 02/05 but have no implemented lattice_* entry point yet; the wrappers stub or defer them, and they will be added to this reference when the production core exposes them:
| Feature | Where designed | Future surface |
|---|---|---|
| Input poll / prediction tick | 02 §10–11, 05 §4.3 | on_input_poll / on_sim_tick callbacks + input window APIs |
| Authority request/transfer | 02 §17.2 | lattice_request_authority + on_authority_changed |
| Interest management (AoI) | 02 §15 | per-object relevancy / spatial config |
| NAT/relay, crypto handshake | 02 §8–9 | transport-config entry points |
6.7 Misc helpers¶
LATTICE_API const char* lattice_result_str(lattice_result r);
LATTICE_API uint32_t lattice_type_content_hash(lattice_runner* r, lattice_type_id type);
lattice_result_str(r)— Role: both. Returns a static, human-readable string for alattice_result(for logging). Never returnsNULL.lattice_type_content_hash(r, type)— Role: both. Returns the content hash the core derived for a registeredtype(the per-type contribution to the catalog/version gate of 02 §20.2 / 05 §7);0iftypeis unknown. Compare across peers / log it to diagnoseLATTICE_ERR_SCHEMA_MISMATCH.
lattice_result rc = lattice_despawn(r, id);
if (rc != LATTICE_OK) log("despawn failed: %s", lattice_result_str(rc));
7. Cross-document references¶
- Wire format, reliability channels, delta/quantization, prediction, lag comp, authority models — 02 Netcode LLD. Every encoding hint here (
compressed_float, smallest-three quaternion, ranged int, delta dirty bits) is specified there. - Ergonomic per-engine API the bindings present over this ABI (
[Networked],[Rpc],NetworkRunner/LatticeRunner/ULatticeRunnerSubsystem, custom types, prefab catalog) — 05 Engine Integration. - Reference implementation scope + the
lattice_test_*conformance harness (NetSim, loopback wire, reliability layer, ACK-bitfield checks — excluded from this reference by §0.1) — 07 Reference Implementation & Conformance. - Connect tokens consumed by
lattice_runner_connect— 03 Auth Service.
The reference ABI (
lattice.h) is authoritative wherever it differs from the illustrativelt_*sketches in earlier design docs. Product/component names are working codenames and may be renamed before release without changing any signature here.