jengine-lockstep
Two-player lockstep turns over jengine-relay, and Two Shores (Reversi) on top.
| Kind | Project package: linked into the builds of the projects that list it |
| Source | packages/jengine-lockstep |
| Version | 0.1.0 |
| Entry point | jengine_lockstep::LockstepPackage |
| Script namespace | Lockstep |
| Depends on | jengine-net |
| Listed by | projects/arcade-classics |
Using it
Section titled “Using it”List it in the top-level "packages" array of project.jengine, together with every package it depends on: a crate dependency compiles a package in but does not register it, and packages_a_project_lists_cover_their_dependencies fails a project that lists only the outer one:
{ "packages": [ { "name": "jengine-lockstep", "source": { "type": "path", "path": "../../packages/jengine-lockstep" } }, { "name": "jengine-net", "source": { "type": "path", "path": "../../packages/jengine-net" } } ]}Then rebuild the editor viewport for the project (jengine viewport-build projects/<name>, or the
editor’s Rebuild viewport banner). Native and web builds link it on their own.
A scene that uses its components must be a v2 scene ("version": 2): a v1 scene has no field
for a package component and keeps it as inert custom data.
A script can check that the package is linked with if Lockstep == nil then return end.
Components
Section titled “Components”| Scene key | Rust type | |
|---|---|---|
ls_game | Lockstep | package only: needs a v2 scene |
Script commands
Section titled “Script commands”Commands run in every runtime and return nothing; a package publishes what scripts read onto components (see the notes below). The flat name also works.
| Call | Flat name |
|---|---|
Lockstep.ls_local(e) | ls_local |
Lockstep.online(e, room, name) | ls_online |
Lockstep.play(e, cell) | ls_play |
Lockstep.leave(e) | ls_leave |
Scenes that use it
Section titled “Scenes that use it”projects/arcade-classics/assets/scenes/shores.scene
From packages/jengine-lockstep/src/lib.rs.
Lockstep turns over the network - and Two Shores, the first game on it.
jengine-net moves strings. This is the layer its own docs said was
missing (“no rooms, no replication… that is the layer above, and it
wants a game exercising it before its decisions get locked in”): a
two-player turn session that joins a room on jengine-relay, applies only
the moves the relay sends back, rebuilds from the log on join, and checks
every move against a hash of the board it was made on. See session.
It talks to jengine-net through the world, not the crate
Section titled “It talks to jengine-net through the world, not the crate”The network package’s script channel is a Net custom component - url,
outbox, inbox, state - and this package reads and writes the same
fields from Rust. So it needs jengine-net present (listed by the
project, which registers its systems) but never links it, which is why
jengine-net is a runtime dependency in PackageMetadata and a dev
dependency in Cargo.toml: the test that pins the channel’s field names
against jengine_net::channel is the only code that needs both.
Scheduling is what makes that work in one frame: jengine-net fills the
inbox at PreUpdate, this runs at ScriptExec priority -1, and
jengine-net drains the outbox at ScriptExec 0 - so a move clicked this
frame leaves this frame.
"ls_game": { "relay": "ws://127.0.0.1:9001", "bot_delay": 0.6 }Script commands
Section titled “Script commands”| Call | Does |
|---|---|
Lockstep.ls_local(e) | a game against the bot, here, with no network |
Lockstep.online(e, room, name) | connect to the relay and take a seat in room |
Lockstep.play(e, cell) | place a stone - played at once alone, sent online |
Lockstep.leave(e) | give the seat up and disconnect |
What scripts read
Section titled “What scripts read”One ls_game bag: mode (idle / local / online), board (64
numbers, 0 empty, 1 dark, 2 light), to_play, my_seat, my_turn,
legal, dark, light, over, winner, peers, net, status,
desync and events.