Skip to content

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_desc array describing each replicated leaf field. The core does the bit-packing. (serialize/deserialize NULL.)
  • Manual path (INetworkSerializable) — supply serialize/deserialize function pointers and write the bits yourself with the lattice_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

typedef struct { float min; float max; float precision; } 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

LATTICE_API lattice_result lattice_despawn(lattice_runner* r, lattice_netid id);

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

LATTICE_API uint32_t lattice_type_content_hash(lattice_runner* r, lattice_type_id type);

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.