Table of Contents
include/nilorea/n_dead_reckoning.h
Dead Reckoning API for latency hiding in networked games. More...
Structures
| Name | |
|---|---|
| struct | DR_VEC3 3D vector used for position, velocity, and acceleration |
| struct | DR_STATE A snapshot of entity kinematic state at a point in time. |
| struct | DR_ENTITY Dead reckoned entity with extrapolation and convergence state. |
Detailed Description
Dead Reckoning API for latency hiding in networked games.
Author: Castagnier Mickael
Version: 1.0
Date: 01/03/2026
Implements dead reckoning (DR) as described in:
- "Dead Reckoning: Latency Hiding for Networked Games" (Game Developer)
- "Believable Dead Reckoning for Networked Games" by Curtiss Murphy (Game Engine Gems 2, 2011)
Dead reckoning is a technique where every participant in a networked game simulates all entities using agreed-upon extrapolation algorithms. When the owner's true state diverges from the extrapolated state beyond a configurable threshold, a new state update is sent. On receipt, the remote side uses a convergence algorithm to smoothly blend from the predicted position to the newly received authoritative state.
Can be used together with n_trajectory for cubic Hermite spline interpolation between known states, while dead reckoning provides the network-level threshold checking and convergence blending.
Supports three extrapolation models:
- DR_ALGO_STATIC: entity stays at last known position
- DR_ALGO_VEL: P(t) = P0 + V0 * t
- DR_ALGO_VEL_ACC: P(t) = P0 + V0 * t + 0.5 * A0 * t^2
Supports three convergence/blending modes:
- DR_BLEND_SNAP: immediately snap to new state (no smoothing)
- DR_BLEND_PVB: Projective Velocity Blending (recommended)
- DR_BLEND_CUBIC: Cubic Bezier spline convergence
Usage:
// Create entity with default settings (VEL_ACC extrapolation, PVB blending)
DR_ENTITY *ent = dr_entity_create(DR_ALGO_VEL_ACC, DR_BLEND_PVB, 0.2, 5.0);
// When an authoritative state update arrives from the network:
DR_VEC3 pos = {10.0, 5.0, 0.0};
DR_VEC3 vel = {2.0, 1.0, 0.0};
DR_VEC3 acc = {0.0, 0.0, 0.0};
dr_entity_receive_state(ent, &pos, &vel, &acc, current_time);
// Each frame, compute the blended display position:
DR_VEC3 display_pos;
dr_entity_compute(ent, current_time, &display_pos);
// For the owning player: check if a network update should be sent:
DR_VEC3 true_pos = { ... };
DR_VEC3 true_vel = { ... };
DR_VEC3 true_acc = { ... };
if (dr_entity_check_threshold(ent, &true_pos, &true_vel, &true_acc, current_time)) {
// send state update to other players
dr_entity_receive_state(ent, &true_pos, &true_vel, &true_acc, current_time);
}
dr_entity_destroy(&ent);
Source code
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef N_DEAD_RECKONING_H
#define N_DEAD_RECKONING_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <math.h>
/* 3D Vector type for dead reckoning */
typedef struct DR_VEC3 {
double x;
double y;
double z;
} DR_VEC3;
/* Enumerations */
typedef enum DR_ALGO {
DR_ALGO_STATIC = 0,
DR_ALGO_VEL = 1,
DR_ALGO_VEL_ACC = 2
} DR_ALGO;
typedef enum DR_BLEND {
DR_BLEND_SNAP = 0,
DR_BLEND_PVB = 1,
DR_BLEND_CUBIC = 2
} DR_BLEND;
/* Kinematic state snapshot */
typedef struct DR_STATE {
DR_VEC3 pos;
DR_VEC3 vel;
DR_VEC3 acc;
double time;
} DR_STATE;
/* Dead reckoning entity */
typedef struct DR_ENTITY {
/* Configuration */
DR_ALGO algo;
DR_BLEND blend_mode;
double pos_threshold;
double blend_time;
/* Last known authoritative state (received from network) */
DR_STATE last_known;
/* State at the moment we received the last update */
DR_STATE prev_predicted;
/* Convergence tracking */
double blend_start;
bool blending;
/* Cubic Bezier control points (computed on update) */
DR_VEC3 bezier_p0;
DR_VEC3 bezier_p1;
DR_VEC3 bezier_p2;
DR_VEC3 bezier_p3;
/* Output */
DR_VEC3 display_pos;
DR_VEC3 display_vel;
/* Statistics */
int update_count;
} DR_ENTITY;
/* Vector operations (inline helpers) */
static inline DR_VEC3 dr_vec3(double x, double y, double z) {
DR_VEC3 v = {x, y, z};
return v;
}
static inline DR_VEC3 dr_vec3_zero(void) {
DR_VEC3 v = {0.0, 0.0, 0.0};
return v;
}
static inline DR_VEC3 dr_vec3_add(DR_VEC3 a, DR_VEC3 b) {
DR_VEC3 v = {a.x + b.x, a.y + b.y, a.z + b.z};
return v;
}
static inline DR_VEC3 dr_vec3_sub(DR_VEC3 a, DR_VEC3 b) {
DR_VEC3 v = {a.x - b.x, a.y - b.y, a.z - b.z};
return v;
}
static inline DR_VEC3 dr_vec3_scale(DR_VEC3 v, double s) {
DR_VEC3 r = {v.x * s, v.y * s, v.z * s};
return r;
}
static inline DR_VEC3 dr_vec3_lerp(DR_VEC3 a, DR_VEC3 b, double t) {
DR_VEC3 r = {
a.x + (b.x - a.x) * t,
a.y + (b.y - a.y) * t,
a.z + (b.z - a.z) * t};
return r;
}
static inline double dr_vec3_length_sq(DR_VEC3 v) {
return v.x * v.x + v.y * v.y + v.z * v.z;
}
static inline double dr_vec3_length(DR_VEC3 v) {
return sqrt(dr_vec3_length_sq(v));
}
static inline double dr_vec3_distance(DR_VEC3 a, DR_VEC3 b) {
return dr_vec3_length(dr_vec3_sub(a, b));
}
/* API Functions */
DR_ENTITY* dr_entity_create(DR_ALGO algo, DR_BLEND blend_mode, double pos_threshold, double blend_time);
void dr_entity_destroy(DR_ENTITY** entity_ptr);
void dr_entity_receive_state(DR_ENTITY* entity,
const DR_VEC3* pos,
const DR_VEC3* vel,
const DR_VEC3* acc,
double time);
void dr_entity_compute(DR_ENTITY* entity, double time, DR_VEC3* out_pos);
DR_VEC3 dr_entity_extrapolate(const DR_ENTITY* entity, const DR_STATE* state, double dt);
bool dr_entity_check_threshold(const DR_ENTITY* entity,
const DR_VEC3* true_pos,
const DR_VEC3* true_vel,
const DR_VEC3* true_acc,
double time);
void dr_entity_set_algo(DR_ENTITY* entity, DR_ALGO algo);
void dr_entity_set_blend_mode(DR_ENTITY* entity, DR_BLEND blend_mode);
void dr_entity_set_threshold(DR_ENTITY* entity, double threshold);
void dr_entity_set_blend_time(DR_ENTITY* entity, double blend_time);
void dr_entity_set_position(DR_ENTITY* entity,
const DR_VEC3* pos,
const DR_VEC3* vel,
const DR_VEC3* acc,
double time);
#ifdef __cplusplus
}
#endif
#endif /* N_DEAD_RECKONING_H */
Modules
- AABB3D
- ACCEPT_POOL
- ALLEGRO4
- ALLEGRO5
- ANIM_LIB
- ASTAR
- ATLAS_LIB
- AVRO
- CLOCK_SYNC
- COMMONS
- CONFIGFILE
- CYPHER_BASE64
- CYPHER_VIGENERE
- DEAD_RECKONING
- ENUMS
- EXCEPTIONS
- GAME_ENV
- GIT
- GUI
- HASH_TABLE
- ISOMETRIC_ENGINE
- LIST
- LOG
- LOGNODUP
- LZ4
- NETWORKING
- NETWORK_MSG
- N_COOKIES
- N_DIFF
- N_DIGEST
- N_DISK_CACHE
- N_DNS
- N_ECC
- N_ENTROPY
- N_FILES
- N_FLUIDS
- N_GUI_COLOR
- N_GUI_COLORWHEEL
- N_GUI_CURVE
- N_GUI_GRID
- N_GUI_POPUP
- N_HEX
- N_HTTP3
- N_HTTP_COOKIE
- N_HTTP_FORM
- N_HTTP_MIME
- N_HTTP_REQ
- N_HTTP_RESP
- N_HTTP_ROUTER
- N_HTTP_SERVER
- N_ICC
- N_JSON
- N_KAFKA
- N_LINALG
- N_MODULE
- N_OPENSSL_COMPAT
- N_PRETTY
- N_PTH
- N_QUERY
- N_RANDOM
- N_SHA256
- N_STR
- N_TIFF
- N_TIME
- N_USER
- N_X509
- N_ZIP
- PARTICLES
- PCRE
- PHYSICS
- SIGNALS
- STACK
- THREADS
- TRAJECTORY
- TREE
- ZLIB