Anti-cheat / server-authoritative validation new¶
An opt-in, server-side validation layer (design 02 §18) that runs at the authority's apply step and rejects impossible / illegitimate client submissions before they touch authoritative state: out-of-bounds or out-of-window inputs, input/RPC floods, speed/teleport hacks, writes/RPCs to non-owned objects, lag-comp rewind abuse, rapid-fire / aim-snap, and (for shared authority) a tampered per-tick state hash.
Default-off and additive
The layer is default-off: a runner with no anti-cheat config behaves byte-for-byte as
before. It adds the config + the on_violation callback + the lattice_ac_* functions and
changes nothing else. It is never on the wire codec / serialization hot path — it only
validates already-decoded submissions, and every check is integer/bounded, so a verdict is
bit-identical on Linux and Windows/wine.
lattice_ac_configure¶
Install (or replace) the runner's anti-cheat configuration. Role: has effect only on an
authority (SERVER/HOST/SHARED_HOST); a client stores the config but does not gate (the
authority does). With cfg->enabled == 0 (or cfg == NULL) the layer is off (the default).
With cfg->enabled != 0, the authority validates each client-submitted input / RPC / event /
move / fire at its apply step, fires on_violation on a rejection, and
kicks at kick_threshold. Reconfigurable at any time.
Returns: LATTICE_OK, or LATTICE_ERR_INVALID_ARG for a NULL runner.
lattice_ac_config¶
The all-zero value DISABLES the whole layer. enabled 0 makes every gate a no-op; an individual
bound left 0 / a *_on flag left 0 disables just that gate. Movement bounds are in the game's
integer world units per tick.
typedef struct {
int enabled; /* 0 => the whole anti-cheat layer is off (default) */
/* 1. Input validation. */
int32_t input_min; /* min legal per-axis input intent (when input_bounds_on) */
int32_t input_max; /* max legal per-axis input intent */
int input_bounds_on; /* enable the per-input value-bounds check */
uint32_t max_input_future; /* reject an input tick > now + this (0 => off) */
uint32_t max_input_age; /* reject an input tick < now - this (0 => off) */
uint32_t max_inputs_per_tick; /* per-client input-rate cap per authority tick (0 => off) */
/* 2. Movement / physics validation (per owned-object position delta, per tick). */
int64_t max_speed_per_tick; /* max |delta| world-units/tick (Chebyshev); 0 => off */
int64_t max_accel_per_tick; /* max change in per-tick delta vs last tick; 0 => off */
int64_t max_teleport_dist; /* hard single-tick jump cap (distance); 0 => off */
/* 3. RPC / event firewall: token-bucket rate limit + argument bounds. */
uint32_t rpc_bucket_capacity; /* max burst tokens a client may hold (0 => rate off) */
uint32_t rpc_refill_per_tick; /* tokens refilled each authority tick */
int32_t rpc_arg_min; /* min legal RPC/event integer argument (when rpc_arg_bounds_on)*/
int32_t rpc_arg_max;
int rpc_arg_bounds_on;
/* 4. Ownership enforcement: reject an RPC/write/event to a non-owned object. */
int enforce_ownership;
/* 5. Lag-comp view-tick window (anti rewind abuse). */
uint32_t max_rewind_ticks; /* furthest into the past a client may claim to have seen */
uint32_t future_view_slack; /* tolerance for a view-tick slightly ahead of now (skew) */
/* 7. Heuristic detectors. */
uint32_t min_fire_interval_ticks; /* min ticks between shots from one client (0 => off) */
int64_t max_aim_delta_per_tick; /* max plausible aim change per tick (0 => off) */
/* 8. Violation scoring + enforcement thresholds (accumulated severity per connection). */
uint32_t warn_threshold; /* 0 => never warn */
uint32_t kick_threshold; /* 0 => never kick */
} lattice_ac_config;
Example — enable a sensible baseline¶
lattice_ac_config ac = {0};
ac.enabled = 1;
ac.input_bounds_on = 1; ac.input_min = -1; ac.input_max = 1;
ac.max_inputs_per_tick = 4;
ac.max_speed_per_tick = 600; /* world units/tick */
ac.max_teleport_dist = 2000;
ac.rpc_bucket_capacity = 20; ac.rpc_refill_per_tick = 5;
ac.enforce_ownership = 1;
ac.min_fire_interval_ticks = 6;
ac.warn_threshold = 50;
ac.kick_threshold = 200;
lattice_ac_configure(r, &ac); /* authority only */
lattice_ac_score¶
The accumulated anti-cheat violation score for conn. Role: authority. Returns: 0
if anti-cheat is off or conn is unknown. Useful for surfacing "flagged players" to a console /
analytics dashboard.
Reacting — on_violation¶
void (*on_violation)(void* user_data, uint64_t conn, lattice_ac_violation violation,
uint32_t severity, int64_t detail);
Fires synchronously inside lattice_runner_tick() on the authority the tick an offending
submission is rejected (or the tick the accumulated score crosses a threshold). conn is the
offending peer; violation is a lattice_ac_violation; severity is
the score increment this violation added; detail is a small integer context value (e.g. the
offending delta or count). Only ever fires when anti-cheat is enabled. A NULL pointer (the
default) means "no anti-cheat callback".
lattice_ac_violation¶
typedef enum {
LATTICE_AC_NONE = 0,
LATTICE_AC_INPUT_BOUNDS = 1, /* an input value was outside its configured range */
LATTICE_AC_INPUT_TICK = 2, /* an input tick was in the future / older than the window */
LATTICE_AC_INPUT_RATE = 3, /* a client exceeded its per-tick input-rate cap (flood) */
LATTICE_AC_MOVE_SPEED = 4, /* a move exceeded max speed / accel (speedhack) */
LATTICE_AC_MOVE_TELEPORT = 5, /* a move exceeded the teleport-distance cap */
LATTICE_AC_RPC_RATE = 6, /* an RPC/event exceeded the token-bucket rate (spam/flood) */
LATTICE_AC_RPC_ARG_BOUNDS = 7, /* an RPC/event argument was outside its configured bound */
LATTICE_AC_OWNERSHIP = 8, /* a client targeted an object it does not own / control */
LATTICE_AC_VIEW_TICK = 9, /* a claimed view-tick fell outside the RTT/interp window */
LATTICE_AC_FIRE_RATE = 10, /* shots fired faster than the configured minimum interval */
LATTICE_AC_AIM_SNAP = 11, /* an aim delta exceeded the max plausible per-tick turn */
LATTICE_AC_STATE_HASH_DESYNC = 12 /* a per-tick state hash disagreed across peers (P2P tamper) */
} lattice_ac_violation;
static void on_violation(void* u, uint64_t conn, lattice_ac_violation v,
uint32_t severity, int64_t detail) {
fprintf(stderr, "[AC] conn=%llu violation=%d sev=%u detail=%lld score=%u\n",
(unsigned long long)conn, (int)v, severity, (long long)detail,
lattice_ac_score(runner, conn));
/* the core auto-kicks at kick_threshold; you can also report to analytics here */
}