Repository navigation
Village netcode
The village library holds no networking. It gives hooks, and a game's own networking layer decides how to carry what they give. tests/_village_netcode.mojo is a sample layer built on the hooks alone, and tests/test_village_netcode.mojo tests it.
The module is extensions/village/fighter_state.mojo.
| Hook | What it gives |
|---|---|
state_of |
A fighter's FighterState: its place, heading, health, stamina, mana, special gauge, move, clock, stance, style and combo, as plain values |
apply_state |
Sets a character and its fighter from a FighterState
|
MoveInput |
A player's input for a frame, as plain values. Battle.tick and VillageGame.tick take one each frame |
Character.step |
The same move for the same input, frame and ground |
Boss.think |
The warden's choice from a BossSight alone, with dice from its seed |
VillageGame.progress |
What a player keeps from one game to the next, which pack_progress packs |
The library has no tick rate, no wire format, no schedule and no sockets. A networking layer, a replay or a test can use the same hooks.
The rest of this page is about the sample layer in tests/_village_netcode.mojo. It is test code: a game can use it as a start, or write its own.
The server runs the fight at TICK_RATE, 30 ticks a second. Each tick, it runs Battle.tick for each region with each player's command, and sends each player the snapshots they hear of. The fight's rules have no clock but the ticks, and the foes' and the warden's dice come from their seeds. So the same commands at the same ticks give the same fight on the same machine.
pack_command packs a player's command for a tick into 12 bytes:
| Bytes | What they hold |
|---|---|
| 4 | The tick, lowest byte first |
| 4 | The buttons, one bit a button, in the order of buttons_of
|
| 4 | The move stick's forward and right, and the look stick's right and up, one byte each |
A stick's lean is kept in 255 steps: axis_byte gives 0 for -1, 127 at rest, and 254 for 1. unpack_command gives back the tick and the input, each stick within half a step of the one packed.
A message can be lost. InputHistory keeps each command a player sent until the server says it has it. InputHistory.send keeps a tick's command and gives every command kept, oldest first, for one message. InputHistory.ack forgets each command up to a tick. It keeps the last 64 at most.
state_of gives a FighterState of a character and its fighter. pack_snapshot packs it into 20 bytes:
| Part | Bytes | Kept as |
|---|---|---|
| The fighter's id | 2 | A whole number, to 65,535 |
| Where its feet are | 6 | Centimeters, to 327 meters from the middle |
| Its heading | 2 | A turn in 65,536 steps |
| Its health | 2 | A whole point |
| Its stamina and its mana | 2 | A share of its most, in 255 steps |
| Its special gauge | 1 | A fiftieth of a point |
| Its move | 1 | The move's value |
| Its clock in the move | 2 | Milliseconds |
| Its stance and its style | 1 | Two bits and three bits |
| Its combo | 1 | Up to 255 blows |
unpack_snapshot gives the snapshot back, each value within half a step. It refuses a move, a stance or a style that is not one. apply_state sets a client's copy of a character and its fighter from it.
A player does not hear of every fighter every tick. relevance says how often by how far the fighter is:
| Distance | Relevance |
Heard |
|---|---|---|
| To 30 m | EVERY_TICK |
Each tick |
| To 80 m | EVERY_THIRD |
Every third tick |
| To 160 m | EVERY_TENTH |
Every tenth tick |
| Further | UNHEARD |
Never |
heard says whether a player hears of a fighter at a tick. A fighter heard less often is heard at a tick of its own, by its id. So the far fighters spread over the ticks, and no tick carries them all.
A client draws the other fighters 3 ticks behind the newest tick it heard of, a tenth of a second. So it has two snapshots around the tick it draws, even when one snapshot is lost.
- Keep each fighter's snapshots in a
SnapshotBuffer.addkeeps a snapshot by its tick, and drops one that comes late. The buffer keeps the newest 32. - Each frame, ask
atfor the fighter at the newest tick, lessINTERPOLATION_TICKS, plus the time since that tick. - Put the result on the fighter's body with
apply_state.
blend_snapshots blends the place, the health, the stamina, the mana and the special gauge. The heading turns the short way. The move, the stance, the style and the combo come from the nearer snapshot. Before the first snapshot and after the last, the fighter stands as that snapshot says.
A client does not wait for the server to move its own player. A Predictor moves the player at once, and the server sets it right.
- Each tick, call
stepwith the tick, the input and the camera's yaw. The player moves at once. The predictor keeps the input, and the state it led to, for the newest 64 ticks. - When a snapshot of the player comes, call
reconcilewith its tick. The predictor takes the server's place and heading for that tick, and plays each later input again from there.
reconcile gives how far the player's place moved. It is zero when the server agrees with the prediction. A snapshot older than each tick kept changes nothing. A snapshot after each tick kept places the player where it says, and forgets the inputs.
A player's commands cost 360 bytes a second. A snapshot costs 20 bytes. So 40 fighters near a player, heard each tick, cost 24 kilobytes a second, before the headers of the messages.
- A client predicts how its player moves, not the player's fight. It shows the moves and the blows that the server sends.
- A snapshot carries no foe's brain, no missile and no popup. The client shows the missiles from the moves that send them.
- The sample layer has no sockets, no encryption and no lag measure. A game brings its own.
Tutorials
How-to
- Install
- Run the checks
- GPU backend
- Coverage
- Measure examples
- Add a feature
- Add a scanned building
- Write documentation
Reference
- Scene graph
- Rotations
- Cameras
- Geometry
- Geometry addons
- Lofts and clipping groups
- Sculptor
- Procedural generators
- Meshes and assets
- Lines
- Points and sprites
- Helpers
- Scene objects
- Animated objects
- Raycasting
- Curves and paths
- Animation
- Skinning
- Materials
- Node materials
- TSL functions
- Lights
- Lighting addons
- Fog
- Textures
- Renderer
- Renderer hooks and material flags
- Rasterization
- GPU backend
- Render target
- Post-processing
- GPU computation
- Compute nodes
- Progressive light map
- Gaussian splats
- Voxel global illumination
- SVG renderer
- Image files
- Image decode queue
- Model files
- More model files
- Exporters
- Scene JSON
- Windowing and controls
- C ABI and hosts
- Math
- Norm consumers
- Math addons
- Units
- Coverage tool
- Commands
- Examples
- Benchmarks
- Extensions
- Shared physics
- Physics query snapshots
- Continuous collision
- Tick force restoration
- Water
- Warp drives
- Vegetation
- Village
- Streamline
- Animals
-
Buildings
- Numerics
- Building topology
- Building model
- IFC exchange
- Procedural towers
- Floor plans and interiors
- Frame analysis
- Shell analysis
- Building energy
- Construction kits
- Bearing walls
- Scanned buildings
- SOG files
- Compressed PLY files
- Scan parts
- Cleaning and orienting scans
- Walking through a scan
- Virtual scans
- Castle
- CARLA
- Femur
- Tibia
- Fibula
- Patella
- Knee
- Muscles
- Vessels
- Lymph
- Nerves
- Integument
- Leg
- Foot
- Pelvis
- Torso
- Arm
- Hand
- Head
- Genome
- Game humanoid
- Humanoid fidelity
- Mixamo animations
- Audio-aligned game faces
- Converted assets
- Mesh quality
- Garments
- Outfits and armor
- Cloth and spring bones
- Attire estimates
- Segment inertia
- Process chemistry
- Thermochemical data
- Chemical equilibrium
- Process maps
- Rates and transport
- Electrolysis and film color
- Binary solutions
- Flowsheets and routes
- Materials and coatings
- Crystals and figures
Explanation
- Software rasterizer
- Scene graph as an array
- Linear color
- Normals are not colors
- Perspective correction
- Fixed-point coverage
- Per-fragment shading
- Mipmaps
- Sorted transparency
- Types and checks
- Render hooks as a trait
- Shared CPU and GPU code
- Bands and threads
- PNG structure
- Extensions beside the port
- Process temperatures
- Scans as building models
- CUDA teardown hang
- Mojo compiler hang