diff --git a/docs/assets/images/multiplayer-network.png b/docs/assets/images/multiplayer-network.png new file mode 100644 index 0000000..6975d47 Binary files /dev/null and b/docs/assets/images/multiplayer-network.png differ diff --git a/docs/assets/images/multiplayer-scene-network.png b/docs/assets/images/multiplayer-scene-network.png new file mode 100644 index 0000000..819f411 Binary files /dev/null and b/docs/assets/images/multiplayer-scene-network.png differ diff --git a/docs/editor/multiplayer.md b/docs/editor/multiplayer.md new file mode 100644 index 0000000..7dc90ab --- /dev/null +++ b/docs/editor/multiplayer.md @@ -0,0 +1,109 @@ +# Multiplayer + +psxsplash games can link up to 10 consoles. The engine talks over the PlayStation's second serial port (SIO1, the link-cable port), and the scene's Lua sends and receives with the [`Net` table](../lua/api-reference.md#net). In the editor you set the scene's network id, and **Play** can boot up to 10 linked consoles at once. + +![The Network view during a Play with 3 consoles: console 1 is in slot 1 and is the authority](../assets/images/multiplayer-network.png) + +## Scene > Network + +With no object selected, the inspector shows the [scene settings](scene-settings.md); **Network** is one of their sections. + +![The Network section: the Network id locked to the scene's name, and Play set to boot 4 consoles](../assets/images/multiplayer-scene-network.png) + +**Network id**: consoles linked with `Net.Connect` only connect when their scenes have the same id. + +Left empty, the id is derived at run time from the scene index and the object count. That number changes when objects are added, so consoles running different builds of the same scene may not connect. Different scenes can also collide on the same derived id. While the id is empty, a **Use "<scene>"** button sets it to the scene's name; Ctrl+Z puts it back. A set id is shown as **Locked: hash 0x...**. The hash depends only on the id, so later builds of the scene keep it. + +**Play boots** sets how many consoles the next **Play** starts, from **1 Console** to **10 Consoles**. The **Relay** switch sends 2 consoles through the editor's relay; with it off they link directly. 3 or more always use the relay, so the switch is hidden. Both settings are saved in your editor settings. The scene file does not store them. + +The same choices are on the **Players** button right of **Play** in the toolbar (also **Build > Players**). Its label is **1 player**, **2 linked**, **2 via relay** or **4 players**, and its list ends with **Use the Relay for 2 Consoles**. + +## Playing with several consoles + +Each console is a separate pcsx-redux. + +- **2 consoles, linked directly.** The emulators are joined over SIO1, the way PlayStations are joined by a link cable. Console 1 is the link's server, console 2 its client. +- **3 to 10 consoles, through the relay.** The editor runs a relay on 127.0.0.1 and each console connects to it. The relay groups consoles into rooms by network id, gives each one a slot from 1 to 10 (slot 0 is the relay) and passes events, data and positions between them. + +The console that has the keyboard is outlined. + +## The status line + +Under each console, a line shows what the engine reports about the link: + +| Status line | Meaning | +|---|---| +| **Not linked: the scene has not called Net.Connect()** | No script has started a session. | +| **Connecting...** | `Net.Connect` ran and is waiting for the other side. | +| **Linked · slot 0 · host · 2 players** | Connected: this console's slot, whether it is the host, and how many players there are. | +| **Link refused: the other console runs another engine version** | The engines use different protocol versions. | +| **Link refused: the other console runs another scene** | The network ids differ. | + +With an empty Network id, the line ends with **no Network id: set one in Scene > Network**. Through the relay, it starts with the relay slot and the authority: **Relay slot 2 · authority slot 1**, or **Relay slot 1 · authority** on the authority. + +## The Network view + +When the relay runs, the output panel has an **Output of console N** chip (what the console with the keyboard printed) and a **Network** chip, the packets the relay routed. + +The head line looks like **Relay on 127.0.0.1:34613 · 3 of 3 consoles in a room · authority: slot 1**. **Pause** freezes the list; the relay keeps routing. + +- **Slot** filters by console. A chip shows the console's slot and **authority** on the room's authority, or **no slot** / **gone**. **All** shows them all. +- **Type** turns packet types on and off: Hello, HelloAck, Snapshot, Event, EventAck, Ping, Pong, Bye, ObjectState, AppData. Each chip shows a count. + +The list is newest last, with Time, From, To, Type, Seq and Bytes. Snapshots, Pings and Pongs a console sends within a second share a row. When the authority's console quits, the hand-off gets a row, such as **Authority slot 2 (was 1)**. + +## Host authority + +One console in a session is the **host**. It owns the shared world objects: the actors a script registered with `Net.RegisterActor`. The host sends their position and facing in its snapshots. On the other consoles, those actors are moved to the host's values. `Net.SyncActor` follows the same rule: only the host's sync is applied. + +Which console is host depends on the link: + +- **Direct link:** both consoles roll a random number during the handshake. The higher number becomes host in slot 0; the other console gets slot 1. +- **Relay:** the first console in a room is the authority. When it quits, the lowest slot still in the room takes over. A console that joins later into a freed lower slot does not. + +Player avatars are not host-owned: each console sends the position of its player. + +## The Lua side + +`Net.Connect()` starts the session. The functions that take an actor want a handle from `Actor.Find`. + +- `Net.Send(id, arg)` sends a pair of integers reliably. The other consoles' scene scripts receive them in `onNetEvent(id, arg)`. +- `Net.SendData(str)` sends a string of up to 256 bytes reliably and in order. The others get it in `onNetData(str)`. +- `Net.SetLocalAvatar(actor)` sets which actor this console sends as its player (the player by default). `Net.SetRemoteAvatar(slot, actor)` sets the local object that shows another slot's player. +- `Net.RegisterActor(actor)` makes an actor host-owned; up to 48 per scene. `Net.SyncActor(actor)` sends that object's `self.sync` table. +- `Net.IsHost()`, `Net.LocalSlot()` and `Net.PlayerCount()` describe this console's place in the session. `Net.State()` returns 2 once connected. + +This object script registers a crate, and each 60 frames the host pushes it along and the other consoles see it move. Each console also sends an event: + +```lua +local crate, frames = nil, 0 + +function onCreate(self) + Net.Connect() + crate = Actor.Find("Crate (3)") + if crate then Net.RegisterActor(crate) end +end + +function onUpdate(self, dt) + if Net.State() ~= 2 or not crate then return end + frames = frames + 1 + if frames < 60 then return end + frames = 0 + if Net.IsHost() then + Actor.SetPosition(crate, Vec3.add(Actor.GetPosition(crate), Vec3.new(2, 0, 0))) + end + Net.Send(7, Net.LocalSlot()) +end +``` + +And in the scene script: + +```lua +function onNetEvent(id, arg) + Debug.Log("event " .. id .. " from slot " .. arg) +end +``` + +## Real hardware + +On a PlayStation the engine uses the same serial port, so a pair of consoles join with a link cable. The relay listens only on 127.0.0.1 for Play's emulated consoles. diff --git a/docs/lua/api-reference.md b/docs/lua/api-reference.md index afd8936..2a08b86 100644 --- a/docs/lua/api-reference.md +++ b/docs/lua/api-reference.md @@ -2,7 +2,55 @@ Every function the engine gives your Lua scripts, as of the current psxsplash source. Numbers that carry fractions are [fixed-point](fixed-point.md). -Not listed: the `Net` table. The engine has it, but the editor has no way to author a network link yet. +## Net + +Linking consoles over the serial port (SIO1). See [Multiplayer](../editor/multiplayer.md) for how sessions, slots and the host work, and for running several consoles from the editor. + +### Session + +```lua +Net.Connect([baud [, rxMode]]) -- start a session -> true +Net.Disconnect() -- leave it (tells the others if connected) +Net.IsConnected() -- -> true once connected +Net.State() -- -> 0 not connected, 1 connecting, 2 connected, + -- 3 version mismatch, 4 scene mismatch +Net.IsHost() -- -> true on the console that owns world objects +Net.LocalSlot() -- -> this console's slot (255: none yet) +Net.PlayerCount() -- -> players in the session +Net.Stats() -- -> table of link counters +``` +`Net.Connect` uses the scene's Network id to check that every console runs the same scene. `baud` defaults to 57600. `rxMode` is `0` Auto (the default), `1` Polled or `2` Interrupt. + +`Net.Stats` fields include `bytesRx`, `bytesTx`, `frames`, `crcErrors`, `resyncs`, `serialErrors`, `rttMillis`, `goodput`, `queueDepth`, `baud`, `rxMode` and `heapKB`. + +### Avatars and world objects + +```lua +Net.SetLocalAvatar(actor) -- the actor this console sends as its player +Net.SetRemoteAvatar(slot, actor) -- the local actor that shows slot's player +Net.SetReplicationEnabled(enabled) -- send this console's avatar at all (default true) +Net.RegisterActor(actor) -- host owns this actor -> false if 48 are registered +Net.UnregisterActor(actor) +Net.SyncActor(actor) -- send the object's self.sync table -> bool +``` +Each takes an actor handle from [`Actor.Find`](#actor). The local avatar defaults to the player. Turn replication off in scenes with no avatar, such as a menu or a lobby. `Net.SyncActor` returns `false` if the object has no `self.sync` table or it does not fit in 254 bytes; the other consoles apply it only for a registered actor, and only when it comes from the host. + +### Messages + +```lua +Net.Send(eventId [, arg]) -- two integers, reliable -> bool +Net.SendData(str) -- a string of 1 to 256 bytes, reliable and in order -> bool +Net.ReliableQueueDepth() -- -> queued, capacity +``` +The other consoles receive these in the scene script's `onNetEvent(id, arg)` and `onNetData(str)`. Both send functions return `false` when not connected or when the reliable queue is full, and the message is dropped; check the result, or compare `Net.ReliableQueueDepth()` before sending a batch. + +### Across scene loads + +```lua +Net.SetPersistent(enabled) -- keep the session through the next Scene.Load +Net.IsPersistent() -- -> bool +``` +Call `Net.SetPersistent(true)` before `Scene.Load`, then `Net.Connect()` again in the new scene: the console keeps its slot. Avatar mappings and registered actors are still dropped with the old scene. --- diff --git a/docs/reference/contributing.md b/docs/reference/contributing.md index 26876f2..cc54c30 100644 --- a/docs/reference/contributing.md +++ b/docs/reference/contributing.md @@ -19,7 +19,7 @@ Report a problem from **Help > Report a Problem** in the editor, or open an issu - Bug reports with a project that reproduces them. - Fixes to anything in [Known Issues](known-issues.md). -- Editor controls for engine features that have none yet: a network link and the runtime-light switch. +- Editor controls for engine features that have none yet: the runtime-light switch. - Corrections and examples for these docs. ## Building the editor diff --git a/docs/reference/known-issues.md b/docs/reference/known-issues.md index 1de2781..2883e40 100644 --- a/docs/reference/known-issues.md +++ b/docs/reference/known-issues.md @@ -27,9 +27,6 @@ Runtime point lights exist in the engine, the `Light` API and timeline tracks, b ### Fonts and finer nav settings live in the scene file The UI tab picks from the scene's fonts but does not add them, and the nav bake's cell size and region merging keep their defaults unless you edit the scene file. -### No network link in the editor -The engine has a `Net` table, but the editor has no way to author a network link yet, so it is not in these docs. - ### Scene type is stored but not used **Exterior** and **Interior** change nothing. Culling follows the Room objects. diff --git a/mkdocs.yml b/mkdocs.yml index 5d5cff7..78313b3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -93,6 +93,7 @@ nav: - Audio: editor/audio.md - Scene Settings: editor/scene-settings.md - World Streaming: editor/world-streaming.md + - Multiplayer: editor/multiplayer.md - Building & Running: editor/building.md - Scripting: - lua/index.md