gale.net.interpolation module
This file contains the implementation of the class SnapshotInterpolator, a small recipe for smoothing remote state (an opponent’s car, a ball) received over an unreliable, latency-bearing connection: buffer a few recent, timestamped snapshots and interpolate between the two that bracket the moment you want to render, instead of snapping straight to whatever the latest UDP packet said (which looks jittery whenever packets arrive unevenly).
It also contains lag_compensated_position, a thin helper built on top of SnapshotInterpolator for the other classic use of buffered snapshot history: lag compensation. When a laggy client reports “I shot the target”, the server should check that hit against where the target was in that client’s view of the world (some time in the past), not where it is right now.
Both are engine-agnostic, generic over plain, JSON-serializable state dicts, and optional: gale.net only gives you the primitives (timestamps, per-peer RTT via Client.get_rtt()/Server.get_rtt()) and leaves the right amount of buffering/delay and the right shape to interpolate to the game, the same way examples/rally’s snapshot_buffer.py (which this module generalizes) always has. Choosing how much to correct visible discrepancies (snapping vs. smoothing) is likewise left to the game.
Author: Alejandro Mujica (aledrums@gmail.com)
- class gale.net.interpolation.SnapshotInterpolator[source]
Bases:
objectBuffers timestamped snapshots of a single piece of remote state and reconstructs it at an arbitrary past moment by linearly interpolating between the two snapshots that bracket it.
Usage example:
history = SnapshotInterpolator()
- client.on_message(
“snapshot”, lambda payload: history.add(payload)
)
# Every render frame, rendering slightly in the past so there # is usually a snapshot on either side to interpolate between: render_time = time.monotonic() - interpolation_delay state = history.sample(render_time)
- if state is not None:
draw_car(state[“x”], state[“y”], state[“heading”])
One SnapshotInterpolator holds the history for one remote entity; a game with several remote entities (other cars, other players) keeps one instance per entity.
- add(data)[source]
- Parameters:
data (Dict[str, Any]) – A JSON-serializable dict of numeric (possibly nested) fields, stamped with the local arrival time.
- Return type:
None
- sample(render_time)[source]
- Parameters:
render_time (float) – The moment (in time.monotonic() terms) to reconstruct the state for.
- Returns:
The interpolated data, the oldest snapshot if render_time is at or before it, the newest snapshot if render_time is at or after it, or None if nothing has been added yet.
- Return type:
Dict[str, Any] | None
- gale.net.interpolation.lag_compensated_position(history, rewind_time, field_path=None, now=None)[source]
Rewind a target’s snapshot history back to how a (laggy) shooter perceived it, for server-side hit detection: instead of testing a shot against the target’s current, authoritative position, test it against where the target was rewind_time seconds ago, which is approximately where the shooter actually saw it when they fired.
rewind_time is typically the shooter’s RTT / 2 (one-way trip from server to shooter) plus whatever interpolation delay the shooter’s own SnapshotInterpolator/rendering uses.
- Parameters:
history (SnapshotInterpolator) – The target’s snapshot history.
rewind_time (float) – How far back in time, in seconds, to rewind.
field_path (Sequence[str] | None) – Optional sequence of keys to drill into the sampled snapshot (e.g. (“position”,) to get just the nested position dict). The default value is None, meaning the whole sampled snapshot is returned.
now (float | None) – The current time in time.monotonic() terms. The default value is None, meaning time.monotonic() is used.
- Returns:
The target’s interpolated state at the rewound moment (or the value at field_path within it), or None if history has no snapshots yet.
- Return type:
Any | None