# Multiplayer Set it up single-player, ship multiplayer. The IC is **server-authoritative** and replicates its running state: there is no separate multiplayer mode to opt into, only settings to leave alone or tune. ```{image} ../_static/screenshots/shot-multiplayer-two-clients-mockup.svg :alt: multiplayer two clients (placeholder mockup) :width: 100% ``` Save the real screenshot as: `shot-multiplayer-two-clients.png` ## Turning it on `bReplicateActions` (default **true**) on the IC, plus the owning actor having **Replicates** set. That's the whole opt-in, no per-Group or per-Cycle flag to remember. ## Server-authoritative execution + multicast The server runs interactions and **multicasts** group start/stop to every client. Visible motion on a client is driven by replicated data, not a local guess re-simulating the same tween, so a slow or lagging client still sees the door open at the same rate and phase as everyone else. ## Late join: `ReplicatedActionStates` Running groups (a spinning fan, a half-open door) live in `ReplicatedActionStates` on the IC. A client that joins mid-game reconstructs each of them at the **correct timeline phase** via `OnRep_ActionStates`: a late joiner sees the fan already spinning, not restarting from zero. ## Route client requests via the Interactor ```{important} **Client requests must go via the player-owned Interactor, never call `Server_*` on a world-placed actor directly.** A Server RPC called straight on a server-owned interactable is **dropped with no error**: Unreal only routes a Server RPC from the actor's **owning connection**, and a world-placed door is owned by the server, not by any client. ``` MovaInteract routes every client request (interaction, typed command, proximity) via the **player-owned** `UMovaIxInteractorComponent` instead: - `Server_TargetInteraction(IC, ChoiceIndex, AnchorName)` - `Server_TargetAction(IC, GroupName, TargetName)` - `Server_TargetTags(IC, Tags)` Each takes the target IC as a **parameter**, so the RPC itself travels over the pawn's connection (which the client owns) while still naming an arbitrary world actor to act on. A collapsed multi-choice pick is addressed by its **source point** (`ResolveServerAddress(Point, ChoiceIndex, OutAnchor, OutServerIndex)`) so the server re-resolves the raw point by anchor and rebuilds it identically from tags, instead of trusting a client-computed collapsed index. ```{mermaid} sequenceDiagram participant C as Client (pawn) participant I as Interactor (owned by client) participant S as Server (IC authority) participant O as Other clients C->>I: TargetInteraction(ChoiceIndex) I->>S: Server_TargetInteraction(IC, ChoiceIndex, Anchor) Note over S: WithValidation + range check S->>S: Execute group, update ReplicatedActionStates S-->>C: Multicast group start/stop S-->>O: Multicast group start/stop Note over C,O: Every client renders the same phase ``` ## Validated + range-verified All the `Server_*` RPCs above are `WithValidation`. Beyond that, every networked interaction is **distance-verified server-side** so a modified client cannot trigger an object across the map by sending a raw RPC with a fabricated `IC` reference: - `bServerVerifyRange` (Project Settings → Plugins → Mova Interact → Network, default on) enables the check. - `ServerRangeSlack` widens the allowed distance to absorb normal network lag: a client's aim can have moved a little by the time the RPC lands. - The reject is **soft**: out-of-range requests are dropped and logged, **never** a kick or a hard disconnect. A laggy but honest player never gets punished for it. Only a request that's implausible even with slack gets ignored. ## Replicated grab Grab / carry / throw (see {doc}`grab-component`) follows the identical pattern: the client aims and requests locally, the server re-validates (`IsGrabbable` + `IsGrabInServerRange` + a clamped throw strength) and owns the physics handle, and `HeldComponent`'s replication drives `OnGrabbed` / `OnReleased` / `OnThrown` on every machine via `OnRep_Held`. The carried object itself still needs to be a replicated actor with replicated movement to visibly move on remote clients. ## Verifying it To eyeball it in PIE with 2+ clients, turn on the debug overlay's net-role banner, it always shows while `mova.ix.Debug` is on, so it doubles as your multiplayer diagnostic view (see {doc}`debugging`): ```{image} ../_static/screenshots/shot-debug-overlay-net-banner-mockup.svg :alt: debug overlay net banner (placeholder mockup) :width: 100% ``` Save the real screenshot as: `shot-debug-overlay-net-banner.png` ## See also - {doc}`grab-component`: the grab system's own multiplayer section, in more depth. - {doc}`../concepts/tag-grammar`: `%Actor^` delegation, relevant when a networked tag hands off execution to another actor's IC. - {doc}`debugging`: the `mova.ix.Debug` net-role banner (SERVER / CLIENT) is invaluable when diagnosing "works in single-player, not in PIE with 2 clients."