-
Notifications
You must be signed in to change notification settings - Fork 0
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.
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
Within a single process, schedulers communicate via in-memory message passing. Across processes, the host provides the network transport.
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)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 |
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,
isServeris alwaystrue. CallDeclareMultiplayerCommands()to enable the multiplayer command set.
<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];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];myVar = 42;
publicVariable "myVar"; // broadcast to all clients_playerAction = "ready";
publicVariableServer "_playerAction"; // client → server"_privateMsg" publicVariableClient _targetClientId;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; // 1 if hasInterface, else 0
allPlayers; // [1] (placeholder — all player IDs)The host must implement:
- Network transport — UDP/TCP, message serialization
- Client tracking — connected client IDs, join/leave events
-
Message routing — deliver
remoteExecandpublicVariableto correct targets - JIP queue — store messages for late-joining clients
-
Object ID mapping — assign and resolve
netIdvalues
SQ# provides the scripting API surface. The host connects it to the network.
if (isServer) then {
// Only the server manages game state
spawnAI();
manageLoot();
checkWinConditions();
};// Client requests action:
_playerRequest = ["spawn", _position];
publicVariableServer "_playerRequest";
// Server processes and broadcasts result:
_serverResponse = ["spawned", _unitId];
publicVariable "_serverResponse";if (isDedicated) then {
// Skip UI rendering
disableRendering();
setHighPriority();
};- Concurrency — scheduler architecture
- Thread Safety — data sharing between schedulers
- spawnOn — cross-scheduler spawn
- remoteExec — remote execution command
- remoteExecCall — remote call command
- publicVariable — broadcast variable
- Getting Started
- Language Guide
- Type System
- Control Flow
- Functions & Code
- Strings & Text
- Arrays & Collections
- Concurrency
- Promise System
- Thread Safety
- Host API & Embedding
- CLI Tool
- Bytecode Reference
- Multiplayer
- Syntax Sugar
- Optimization Guide
- Benchmarks
- count
- select
- pushBack
- append
- deleteAt
- deleteRange
- resize
- reverse
- sort
- find
- in
- forEach
- freeze
- thaw
- isFrozen
- currentScheduler
- clientOwner
- allSchedulers
- schedulerName
- schedulerExists
- schedulerBudget
- setSchedulerBudget
- fiberCount
- readyFiberCount
- waitingFiberCount
- schedulerLoad
- sendTo