Skip to content

Multiplayer

ffredyk edited this page Jul 24, 2026 · 2 revisions

Multiplayer

SQ# multiplayer is based on a simple idea: Scheduler = Machine. Each scheduler acts like an Arma machine — server, client, or headless client — communicating through remote execution and public variables.

Note: Multiplayer commands are stubs. Full networking (UDP/TCP, serialization, client tracking) is the host's responsibility. SQ# provides the scripting API — the host implements the network layer.

Scheduler = Machine

flowchart TB
    subgraph Host["HOST PROCESS"]
        subgraph Main["Scheduler Main (ID=1)"]
            M_isServer["isServer = true"]
            M_isDed["isDedicated = true"]
            M_hasUI["hasInterface = false"]
        end
        subgraph Client["Scheduler Client2 (ID=3)"]
            C_isServer["isServer = false"]
            C_isDed["isDedicated = false"]
            C_hasUI["hasInterface = true"]
        end
        subgraph HC["Scheduler HC (ID=4)"]
            H_isServer["isServer = false"]
            H_isDed["isDedicated = false"]
            H_hasUI["hasInterface = false"]
        end
        Main --> Bus
        Client --> Bus
        HC --> Bus
        subgraph Bus["MESSAGE BUS (host: network layer)"]
            RE["remoteExec"]
            PV["publicVariable"]
        end
    end
Loading

Within a single process, schedulers communicate via in-memory message passing. Across processes, the host provides the network transport.

Machine Identity

Every scheduler knows its role:

isServer       // true → this scheduler is the authority (server)
isDedicated    // true → server running without UI (dedicated)
hasInterface   // true → this scheduler has a player/UI attached
isClient       // true → this is NOT the server (!isServer)

Identity Combinations

isServer isDedicated hasInterface Arma Equivalent Use Case
Dedicated Server Authoritative game state, AI, mission logic
Player-Hosted Server Single-player or listen server
Player Client Rendering, input, UI, local effects
Headless Client Offloaded AI, physics, pathfinding

Setting Identity (C# Host)

var host = new SqHost();

// Dedicated server:
host.IsServer = true;
host.IsDedicated = true;
host.HasInterface = false;
host.DeclareMultiplayerCommands();

// Player client:
host.IsServer = false;
host.HasInterface = true;
host.DeclareMultiplayerCommands();

Important: In the default SQ# host, isServer is always true. Call DeclareMultiplayerCommands() to enable the multiplayer command set.

Remote Execution

remoteExec — Async Remote Call

<args> remoteExec [commandName, target, isJip]
Parameter Value Meaning
target 0 Everyone
2 Server only
-2 Everyone except server
clientId Specific client
isJip true Include Join-In-Progress clients
false Current clients only
// Execute everywhere
[params] remoteExec ["fn_process", 0, false];

// Execute on server only
[data] remoteExec ["fn_saveToDB", 2, false];

// Execute on specific client
[message] remoteExec ["fn_displayChat", _clientId, false];

remoteExecCall — Sync Remote Call

Same as remoteExec but uses call semantics (synchronous) instead of spawn (async). The target executes the command immediately — no new fiber is created.

[params] remoteExecCall ["fn_quickUpdate", 0, false];

Public Variables

publicVariable — Broadcast to All

myVar = 42;
publicVariable "myVar";        // broadcast to all clients

publicVariableServer — Send to Server

_playerAction = "ready";
publicVariableServer "_playerAction";  // client → server

publicVariableClient — Send to Specific Client

"_privateMsg" publicVariableClient _targetClientId;

Network Info (Stubs)

These return placeholder values — host must implement:

owner _unit;                   // 2.0 (placeholder — hardware machine ID)
netId _unit;                   // "0:0" (placeholder — network ID)
objectFromNetId "2:123";       // nil (placeholder — lookup by net ID)
didJIP;                        // false (placeholder — joined in progress?)
didJIPOwner;                   // false (placeholder — JIP owner?)

Player Identity

player;                        // 1 if hasInterface, else 0
allPlayers;                    // [1] (placeholder — all player IDs)

Host Implementation

The host must implement:

  1. Network transport — UDP/TCP, message serialization
  2. Client tracking — connected client IDs, join/leave events
  3. Message routing — deliver remoteExec and publicVariable to correct targets
  4. JIP queue — store messages for late-joining clients
  5. Object ID mapping — assign and resolve netId values

SQ# provides the scripting API surface. The host connects it to the network.

Common Patterns

Server-Authoritative Logic

if (isServer) then {
    // Only the server manages game state
    spawnAI();
    manageLoot();
    checkWinConditions();
};

Client-Server Communication

// Client requests action:
_playerRequest = ["spawn", _position];
publicVariableServer "_playerRequest";

// Server processes and broadcasts result:
_serverResponse = ["spawned", _unitId];
publicVariable "_serverResponse";

Dedicated Server Optimizations

if (isDedicated) then {
    // Skip UI rendering
    disableRendering();
    setHighPriority();
};

See Also

SQ# Wiki

Home

Engine Docs

Migration

Commands

Value Constructors

Arithmetic

Comparison

Logic

Array

String

Math

Random

Type & Introspection

HashMap

Code Execution

Concurrency

Scheduler

Thread Safety

Error

Output

Time

Multiplayer

Compiler

Clone this wiki locally