Type registration & objects¶
Register your networked types in the same order on every peer so type ids and content hashes line up. The core hashes the ordered field list into a content hash and rejects spawns on a mismatch — this is what keeps server, host, and client schema-compatible.
lattice_register_type¶
LATTICE_API lattice_type_id lattice_register_type(lattice_runner* r,
const lattice_type_desc* desc); /* 0 on error */
Register one networked type. Role: both (do it identically on every peer). The core deep-copies the field array and key string, so your buffers need not outlive the call.
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;
There are two serialization paths:
- Declarative path — supply a
lattice_field_descarray describing each replicated leaf field. The core does the bit-packing. (serialize/deserializeNULL.) - Manual path (
INetworkSerializable) — supplyserialize/deserializefunction pointers and write the bits yourself with thelattice_bw_*/lattice_br_*helpers.
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;
kind is a lattice_field_kind. quant is a
lattice_float_quant {min, max, precision} used by
COMPRESSED_FLOAT and the VECTOR* kinds.
Manual serializer function pointers¶
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);
state points at the host-memory state block for one object/struct; user is the cookie from
lattice_type_desc.user.
lattice_float_quant¶
Returns: the new lattice_type_id (non-zero), or 0 on error (bad arg, duplicate key, etc.).
Example — register a manual struct, then a declarative type nesting it¶
/* Loadout — manual / INetworkSerializable path. */
lattice_type_desc loadout = {0};
loadout.key = "Loadout";
loadout.state_size = sizeof(Loadout);
loadout.serialize = loadout_serialize;
loadout.deserialize = loadout_deserialize;
lattice_type_id loadout_id = lattice_register_type(r, &loadout); /* 0 == error */
/* Player — declarative path, nesting Loadout as a STRUCT field. */
lattice_type_desc player = {0};
player.key = "Player";
player.state_size = sizeof(PlayerState);
player.fields = player_fields; /* lattice_field_desc[] */
player.field_count = field_count;
lattice_type_id player_id = lattice_register_type(r, &player);
lattice_spawn¶
LATTICE_API lattice_netid lattice_spawn(lattice_runner* r, lattice_type_id type,
const void* initial_state, uint64_t owner); /* 0 on error */
Spawn an authoritative object. Role: authority only (SERVER / HOST / SHARED_HOST); on a
CLIENT it returns 0. initial_state is copied. owner is the peer id that owns the object
(used by RPC OWNER/ALL_BUT_OWNER routing and ownership enforcement).
Returns: a new lattice_netid (non-zero), or 0 on error. Remote peers observe the spawn via
the on_spawned callback.
lattice_despawn¶
Despawn an object. Role: authority only. Returns: LATTICE_OK,
LATTICE_ERR_NOT_AUTHORITY, or LATTICE_ERR_UNKNOWN_OBJECT. Remote peers observe it via
on_despawned.
lattice_object_state¶
LATTICE_API void* lattice_object_state(lattice_runner* r, lattice_netid id); /* mutable; NULL if unknown */
Get a mutable pointer to an object's host-memory state block. Role: both. On an authority you
mutate it and call lattice_object_mark_dirty; on a client you read
the converged value. Returns: the block pointer, or NULL if id is unknown.
lattice_object_mark_dirty¶
LATTICE_API lattice_result lattice_object_mark_dirty(lattice_runner* r, lattice_netid id,
uint32_t field_index);
Mark a declarative field dirty so the core delta-replicates it on the next send. Role:
authority (for that object). field_index is the field's index in the registered
lattice_field_desc array. Returns: LATTICE_OK or an error.
PlayerState* s = (PlayerState*)lattice_object_state(r, nid);
if (s) {
s->score += 1; lattice_object_mark_dirty(r, nid, F_SCORE);
s->position = (lattice_vec3){5,6,7}; lattice_object_mark_dirty(r, nid, F_POSITION);
}
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 objects this runner currently tracks. Role: both. lattice_object_at returns
0 for an out-of-range index. Order is unspecified but stable within a tick.
for (uint32_t i = 0, n = lattice_object_count(r); i < n; ++i) {
lattice_netid id = lattice_object_at(r, i);
/* ... */
}
lattice_type_content_hash¶
The content hash the core computed for a registered type (its ordered field list + key). Role:
both. Useful for diagnostics and for confirming two peers registered byte-identical schemas; a
mismatch is what the core rejects spawns on. Returns 0 for an unknown type.