Skip to content

jengine-lockstep

Two-player lockstep turns over jengine-relay, and Two Shores (Reversi) on top.

KindProject package: linked into the builds of the projects that list it
Sourcepackages/jengine-lockstep
Version0.1.0
Entry pointjengine_lockstep::LockstepPackage
Script namespaceLockstep
Depends onjengine-net
Listed byprojects/arcade-classics

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.

Scene keyRust type
ls_gameLocksteppackage only: needs a v2 scene

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.

CallFlat 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
  • 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 }
CallDoes
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

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.