{"name":"Multiplayer Scripting","title":"Multiplayer Scripting","type":"topic","url":"https://community.bistudio.com/wiki/Multiplayer_Scripting","revid":376938,"updated":"2025-12-31T22:33:57Z","aliases":["JIP","Join In Progress","Locality in Multiplayer","Multiplayer scripting"],"markdown":"This page aims to explain notions and specificities of Multiplayer Scripting and its differences with Singleplayer Scripting.\n\nIt features three parts:\n1. #Notions and general knowledge\n1. #Structuring Multiplayer code\n1. Testing locally\n\n## Notions and general knowledge\n\n### The basics\n\n- In Multiplayer, players are connected to a **server**.\n  - The server is a machine that distributes information among clients, such as unit positions, vehicle speeds, etc.\n  - The server can be either a dedicated machine, or hosted by a player; in the latter it means the server can have a player unit.\n- A machine connected to a server is called a **client**.\n  - Clients can join a mission *after* it started: they are considered \"JIP\" and scripting should be adapted to them. See the #Join In Progress chapter for more information.\n\n> [!NOTE]\n> In Singleplayer, the game acts as a player-hosted server (isServer returns true).\n\n> [!WARNING]\n> Some Multiplayer commands do **not** work in Singleplayer, such as netId!\n>\n> The other way around, some Singleplayer commands do not work in Multiplayer (e.g  setAccTime).\n\n### Locality\n\n#### Definitions\n\n- **LOCAL** is an attribute for the machine where the command/script/function is executed or the AI calculated.\n  - to check if an argument is local, use the local command\n- **REMOTE** means **non local** - on another machine. All player-controlled soldiers are remote units to a dedicated server, for example.\n  - to check if an argument is remote, simply use `not local _myObject`\n- commands arguments and effects can be either **local** or **global**:\n\n|  |  |\n|---|---|\n| Local Argument | a **local** argument means an argument that is processed on the machine where the command is executed |\n| Global Argument | a **global** argument means any argument, even a remote one |\n| Local Effect | a **local** effect means that the effect will only happen on the machine where the command is executed |\n| Global Effect | a **global** effect means that all the computers will receive it |\n| Server Execution | **server exec** means that the command or function has to be executed on the server machine, either mission host or dedicated server |\n\n> [!NOTE]\n> See also:\n> - Scripting Commands: Local Effect\n> - Scripting Commands: Global Effect\n> - Scripting Commands: Server Execution\n> - Functions: Local Effect\n> - Functions: Global Effect\n> - Functions: Server Execution\n\n#### Different machines and how to target them\n\n> [!NOTE]\n> - For obvious reasons, a server cannot be JIP.\n> - Use didJIP to determine if a client joined during the game.\n\n| Scripting Commands | Server | Player client | Headless client | Note |  |\n|---|---|---|---|---|---|\n| Dedicated | Player-Hosted |  |  |  |  |\n| ```sqf<br>isDedicated<br>``` | ✓ | ✗ | ✗ | ✗ | Dedicated server |\n| ```sqf<br>hasInterface && isServer<br>``` | ✗ | ✓ | ✗ | ✗ | Player server |\n| ```sqf<br>hasInterface && not isServer<br>``` | ✗ | ✗ | ✓ | ✗ | Player client |\n| ```sqf<br>not hasInterface && not isDedicated<br>``` | ✗ | ✗ | ✗ | ✓ | Headless client |\n| ```sqf<br>isServer<br>``` | ✓ | ✓ | ✗ | ✗ | Any server |\n| ```sqf<br>hasInterface<br>``` | ✗ | ✓ | ✓ | ✗ | Any player |\n| Miscellaneous |  |  |  |  |  |\n| ```sqf<br>not isServer<br>``` | ✗ | ✗ | ✓ | ✓ | All server clients, including headless clients, but excluding player host |\n| ```sqf<br>not isDedicated<br>``` | ✗ | ✓ | ✓ | ✓ | Everything but a dedicated server |\n| ```sqf<br>not hasInterface<br>``` | ✓ | ✗ | ✗ | ✓ | Headless clients and dedicated server |\n\n**For an extended version for all games, click**\n\n> [!NOTE]\n> Before Operation Flashpoint v1.99 and the backport of isServer, the server could be identified by placing a Game Logic in the editor (that would always remain local to the server), then using the following: `local myLogic`.\n\n| Targeted machine | Operation Flashpoint | Armed Assault | Arma 2 | Arma 3 |\n|---|---|---|---|---|\n| **Dedicated Server**<br>A server without a human player behind it | ```sqf<br>isServer && isNull player<br>``` | ```sqf<br>isServer && isNull player<br>``` | ```sqf<br>isDedicated<br>``` | ```sqf<br>isDedicated<br>``` |\n| **Player Server**<br>A server hosted by a player | ```sqf<br>isServer && not isNull player<br>``` | ```sqf<br>isServer && not isNull player<br>``` | ```sqf<br>isServer && not isDedicated<br>``` | ```sqf<br>hasInterface && isServer<br>``` |\n| **Server**<br>Any server, dedicated or hosted | ```sqf<br>isServer<br>``` | ```sqf<br>isServer<br>``` | ```sqf<br>isServer<br>``` | ```sqf<br>isServer<br>``` |\n| **Player Client**<br>A player connected since the lobby | ```sqf<br>not isServer && not isNull player<br>``` | ```sqf<br>not isServer && not isNull player<br>``` | ```sqf<br>not isServer && not isNull player<br>``` | ```sqf<br>hasInterface && not isServer<br>``` |\n| **JIP Player Client**<br>A player connected in the middle of a mission | N/A | ```sqf<br>not isServer && isNull player<br>``` | ```sqf<br>not isServer && isNull player<br>``` | ```sqf<br>hasInterface && didJIP<br>``` |\n| **Headless Client**<br>A client without a human player behind it<br>(used to offload server calculations) | colspan=\"3\" N/A | ```sqf<br>not hasInterface && not isServer<br>``` |  |  |\n| **JIP Headless Client**<br>Headless client connected during the ongoing mission | colspan=\"3\" N/A | ```sqf<br>not hasInterface && not isServer && didJIP<br>``` |  |  |\n\n- If you want to know if the current machine is a server (Dedicated Server or Player Server), use isServer.\n- If you want to know if the current machine has a player (Player Server included), use hasInterface.\n\n> [!NOTE]\n> **Arma 3:** Since Arma 3, a server-only init file is available: **initServer.sqf** (see Event Scripts)\n\n> [!NOTE]\n> **Arma 3:** **BIS_fnc_getNetMode** can be used to have a string value representing the local machine status (\"DedicatedServer\", \"Server\", \"HeadlessClient\", \"Client\", \"SinglePlayer\" ).\n>\n> This function doesn't make the JIP distinction and should be used along didJIP.\n\n#### General information about locality\n\n- about player:\n  - a player's unit is **always** local to the (human) player's machine\n  - a dedicated server doesn't have a player unit (`isNull player` returns true)\n  - to know if a certain unit is *a* player, use isPlayer\n- groups and AI:\n  - an AI group with a player-leader will be local to the player's computer\n  - a subordinate player being in a player-lead group will remain local to its computer (see bulletpoint #1)\n- objects:\n  - a driven vehicle is always local to the machine of its driver (not the commander's, not the gunner's)\n  - terrain objects (buildings, vegetation, roads etc.) are local everywhere (`local nearestBuilding player` returns true on every client)\n  - editor-placed objects and empty vehicles are local to the server\n  - editor-placed **triggers** are created on every machine unless specified otherwise (\"Server Only\" Eden Editor option ticked)\n  - units created with createUnit will be local to the computer that issued the command\n  - objects and vehicles created with createVehicle will be local to the computer that issued the command\n\n#### Locality changes\n\n- if the player-leader dies, the AI is transferred to the machine where the new leader is local (server in case of AI, client in case of another player)\n- joining AI units will change locality to the new leader's computer\n- locality will change from server to client when a player gets in an empty vehicle\n- locality can also change in the event of a Team Switch or usage of selectPlayer\n\n> [!NOTE]\n> **Arma 3:** Since Arma 3 the \"Local\" Event Handler triggers when an object's locality is changed.\n\n#### Code examples\n\n| Code | Argu-ments | Effect | Description |\n|---|---|---|---|\n| ```sqf<br>remoteUnit setDamage 1;<br>``` | Global Argument | Global Effect | Any unit (local or remote) will die, and all the computers will know |\n| ```sqf<br>localUnit addMagazine \"30Rnd_556x45_STANAG\";<br>``` | Local Argument | Global Effect | Only a local unit can have its inventory edited with this command, but all the machines will be notified: if a player looks into the *localUnit* inventory, the added magazine will be there |\n| ```sqf<br>remoteUnit setFace \"Miller\";<br>``` | Global Argument | Local Effect | Any unit (local or remote) can get its face changed, but the new face will not be propagated through the network. Only the local machine's player will see the effect of the command<br>> [!NOTE]<br>> This command should ideally be **executed on every machine**, for example with:<br>><br>> ```sqf<br>> [remoteUnit, \"Miller\"] remoteExec [\"setFace\", 0, true];<br>> ```<br>><br>> See the #Remote Execution chapter for more information. |\n| ```sqf<br>localCamera cameraEffect [\"Internal\", \"Back\"];<br>``` | Local Argument | Local Effect | Only a local camera can be entered, and of course only the local machine will see through this camera |\n\n### Network ID\n\nNetwork IDs identify machines and network objects. They can be used to target proper machines with remote execution; see the #Remote Execution chapter for more information.\n\n> [!WARNING]\n> **A machine ID** (ownerID) is not to be confused with **an object's network ID** (netId).\n\n#### Machine network ID\n\nEvery machine, including the server, has a **network ID**.\n\nA server always has an ID of **2**, every new client has the next number (the first client will be number **3**, second client number **4**, etc.)\n- To get the current machine's ID, use clientOwner.\n- To get the machine ID of an object's owner, use owner *object*  (MP only).\n- To get the machine ID of a group's owner, use groupOwner *object* (MP only).\n\n> [!NOTE]\n> **0** and **1** are special values:\n> - **0** means **everyone** (including the server)\n> - **1** means **current machine** but **is not implemented** and should **not** be used.\n\n#### Object network ID\n\nEvery object synchronised through the network has a network ID, shortened to **netId**.\n- To get an object's netId, use netId (MP only). **SP & MP variant:** BIS_fnc_netId\n- To get an object from a netId, use objectFromNetId (MP only). **SP & MP variant:** BIS_fnc_objectFromNetId\n- To get a group from a netId, use groupFromNetId (MP only). **SP & MP variant:** BIS_fnc_groupFromNetId\n\n### Sending information across network\n\n#### Remote Execution\n\nSince Arma 3 v1.50, remoteExec and remoteExecCall efficient engine network commands are available:\n\n```sqf\n\"Message to everyone, including JIP players!\" remoteExec [\"hint\", 0, true];\n```\n\nSee Arma 3: Remote Execution for more information.\n\nArma 3 alpha introduced BIS_fnc_MP (**obsolete** since Arma 3 v1.50, use the above version):\n\n```sqf\n[\"Message to everyone, including JIP  players!\", \"hint\", true, true] call BIS_fnc_MP;  // obsolete since Arma 3 v1.50, use remoteExec or remoteExecCall\n```\n\nArma 2 introduced the Arma 2: Multiplayer Framework (not present in Arma 3):\n\n```sqf\n// having the Functions module placed in the editor\nwaitUntil { not isNil \"BIS_MPF_InitDone\"; };\n[nil, nil, rHINT, \"Message to everyone!\"] call RE;\n```\n\n#### PublicVariable commands\n\nPublicVariable commands allow to send **global** variables to specified machines.\n- Network reception is guaranteed\n- Short variable names should be used for network performance\n- These commands shouldn't be used intensely for the same reason\n\n| Command | Effect | JIP synchronised |\n|---|---|---|\n| publicVariable | Set/update a variable value from the local machine to all the other machines (including server) | ✓ |\n| publicVariableServer | Set/update a variable value **from a client to the server** | ✗ |\n| publicVariableClient | Set/update a variable value **from the local machine** (not necessarily the server) **to a specific client** | ✗ |\n\n##### How it works\n\nThe server has the variable `ABC_Score` to broadcast:\n\n```sqf\nABC_Score = 5;                // sets the value\npublicVariable \"ABC_Score\"    // publishes the variable (do not forget the quotes!)\n```\n\nThis sends the variable name and value to the clients.\n- If the variable is not yet declared on their machine, it will declare it as well.\n- If the variable already exists, **the value is overridden** by the server's value.\n- If the variable changes again on the server, it has to be manually publicVariable'd once again!\n- A JIP player will synchronise **server's** publicVariable'd variables **before** executing **init.sqf**. See Initialisation Order for more information.\n  - A JIP player will **not** synchronise publicVariableClient-received variables after he disconnects/reconnects. Only server-known public variables sent with publicVariable will be synchronised.\n\n#### setVariable command\n\nAvailable since Arma 2, the **public** version of the setVariable command (alternative syntax) allows you to store a variable into an object and spread this variable across the network.\n\n> [!NOTE]\n> **Arma 3:** In Arma 3 it is possible to broadcast the nil value, thus deleting the variable from the target.\n\n### Player connection events\n\nThe following commands will execute given code when a player is connecting or disconnecting. This code will run for players connecting in the lobby **and** for JIP players!\n- \"PlayerConnected\" mission Event Handler\n- \"PlayerDisconnected\" mission Event Handler\n\n> [!NOTE]\n> Before Arma 3 v1.58, use BIS_fnc_addStackedEventHandler for onPlayerConnected/onPlayerDisconnected.\n\n### Client state\n\nA client state is the client's connection state. It can be obtained with getClientState and getClientStateNumber commands on both server and clients.\n\n| getClientStateNumber | getClientState | Description |\n|---|---|---|\n| 0 | \"NONE\" | No client (or singleplayer) |\n| 1 | \"CREATED\" | Client is created |\n| 2 | \"CONNECTED\" | Client is connected to server, message formats are registered |\n| 3 | \"LOGGED IN\" | Identity is created |\n| 4 | \"MISSION SELECTED\" | Mission is selected |\n| 5 | \"MISSION ASKED\" | Server was asked to send / not send mission |\n| 6 | \"ROLE ASSIGNED\" | Role was assigned (and confirmed) |\n| 7 | \"MISSION RECEIVED\" | Mission received |\n| 8 | \"GAME LOADED\" | Island loaded, vehicles received |\n| 9 | \"BRIEFING SHOWN\" | Briefing was displayed |\n| 10 | \"BRIEFING READ\" | Ready to play mission |\n| 11 | \"GAME FINISHED\" | Game was finished |\n| 12 | \"DEBRIEFING READ\" | Debriefing read, ready to continue with next mission |\n\n### Join In Progress (Armed Assault v1.00)\n\n**Join In Progress** (JIP) is the ability for a player to connect while a game is running, unlike in Operation Flashpoint where one had to wait for the game to be over.\n\nIt was introduced in  on Xbox and Armed Assault on PC.\n\nA player that joined in progress will be referred to as **JIP player**.\n\n> [!NOTE]\n> To disable JIP on a server:\n> - the server admin can disable playable slots on the role selection screen\n> - the mission designer can disable AI within mission config\n> However, JIP remains available if respawn is enabled in the mission.\n\n#### JIP Synchronisation\n\nA JIP player will have a lot of information synchronised (publicVariable'd and setVariable server variables for example) by the game **before** accessing the mission itself; see below:\n\n| Information | Arma 1 | Arma 2 | Arma 3 |\n|---|---|---|---|\n| date/time | ✓ | ✓ | ✓ |\n| date/time + setDate/skipTime | ✗ | ✗ | ✓ |\n| weather (overcast, fog) | ✗ | ✓ | ✓ |\n| weather + setOvercast/setFog/setRain/setLightnings | ✗ | ✗ | ✓ |\n| time passed since mission start (time command) |  |  | ✓ |\n| publicVariable-sent variables (Number, String, Structured Text, Array, Code) | ✓ | ✓ | ✓ |\n| publicVariable-sent variables (nil) | ✗ | ✗ | ✓ |\n| setVariable-assigned variables (when alternative syntax's *public* parameter is set to true) | N/A | ✓ | ✓ |\n| remoteExec- and remoteExecCall-executed code if *JIP* prerequisites are met | colspan=\"2\" N/A | ✓ |  |\n\n> [!WARNING]\n> All the **initialisation fields** code get executed again for **each and every JIP connecting player**.\n>\n> This means that code with global effect Global Effect (such as setDamage) **WILL** be re-executed (depending on locality)!\n\n#### Related Commands\n\n- remoteExec\n- remoteExecCall\n- didJIP\n- didJIPOwner\n- exportJIPMessages\n- isRemoteExecutedJIP\n\n### See also\n\n- Headless Client\n- Initialisation Order\n- Arma 3: Remote Execution\n\n## Structuring Multiplayer code\n\n### Responsibilities\n\n- The **server** initialises and works with **variables**\n- A connecting or connected **player-client** gets the mission data from the server\n- Once an objective is reached, **the server tells the players** via remoteExec/remoteExecCall, publicVariable or setVariable\n- Players can get their client-side UI code triggered by the server\nSee this earlier chapter to know how to target specific machines.\n\n#### Server\n\nThe **Server** must be considered as the one ruler of the mission. He is the game's referee and all the decisions should come from it. All the values on the server should be considered as **true**.\n\nConsider the server as a powerful machine where you can put all the mission conditions and heavy scripting on.\nIn case of a very heavy mission, the server can be assisted by Headless Clients (this involves a per-mission specific design).\n\nServer-side code must gather or wait all the variables (potentially from client-side scripts and publicVariableServer) and take and broadcast its decisions accordingly using publicVariable.\n\nServer-side code should ideally **not** make any reference to a player variable considering a server can potentially be **dedicated**, unless you want to force this mission to be player-hosted (such as Arma 2's campaign).\n\n> [!NOTE]\n> Use allPlayers, BIS_fnc_listPlayers, playableUnits (and switchableUnits for SP compatibility) to refer to players.\n\n#### Client\n\nThe **Client** must be considered as an average machine that can leave the game at any moment.\n**No mission-vital code must be executed on its side** and **no decisions must be taken client-side**.\n\nAgain, **one must expect that any client can disconnect at any moment**.\n\nThe client executes all the (local) UI code (Post Process Effects, Dialogs, etc.)\n\nThe player's machine should not deal with a great piece of code.\n\n> [!IMPORTANT]\n> Do not forget that a server **can** be a player, and therefore should execute player code in that case.\n\nThe proper way to check for player-side condition is\n\n```sqf\nif (hasInterface) then { /* (...) */ };\n```\n\nNote that the following example is **incorrect**:\n\n```sqf\nif (isServer) then\n{\n    // ...\n}\nelse // WRONG: a server CAN be a player\n{\n    // ...\n};\n```\n\n### Code writing\n\nThe usual four steps of coding are the following:\n- Think it well\n  - If you cannot figure out how to write your code, say what you want to do out loud or write it down in your language, then replace it by code little by little.\n- Make it work\n  - use the -showScriptErrors startup parameter to see your code errors.\n- Make it readable\n  - name your variables properly, as if you had to give it to someone that should get it without the need of explaining\n- Optimise then\n  - use more performance-friendly commands, reduce your search radius and loop frequencies once everything works.\n\nSee Code Optimisation#Rules for advices and more information.\n\n### See also\n\n- Event Scripts\n- Code Optimisation\n\n## Local Multiplayer Testing\n\nTo ensure all cases, your mission should ideally work properly:\n- in Singleplayer\n- in Player-hosted Multiplayer\n- in a Dedicated Server-hosted game\n\nThe following chapters explain how to mimic multiple clients on one computer. Of course, nothing prevents you from inviting friends to play your mission and report bugs!\n\n### Dedicated Server\n\nTo properly test MP scripting locality issues, it is recommended to run a **dedicated server** and connect with **two** clients. In order to do so:\n- In your server executable's server.cfg: \n\n```cpp\nloopback = true;    // force LAN-only server\nkickDuplicate = 0;    // disable duplicate Arma kick\n\n// needed in case of Headless Client test only\nheadlessClients[]    = { \"127.0.0.1\" };\nlocalClient[]        = { \"127.0.0.1\" };\n```\n\n- Run the game twice from Steam and connect each (with `-showScriptErrors` flag)\n  - If you use one screen for the first client and the other screen for the second client, use `-noPause -window` launcher options. This will not pause render if the window doesn't have focus.\n\n> [!IMPORTANT]\n> Be sure to disable BattlEye or it will close any additional instances.\n\n- Try your mission and check for script errors:\n  - on client's screens and RPT files\n  - in your server's RPT files\n\n| Pros | Cons |\n|---|---|\n| - Tests almost all \"can go wrong\" scenarios<br>- Can test Headless Clients, persistent server, server restart, admin login/logout | - Misses cases where isDedicated is used instead of isServer<br>- \"Big\" setup |\n\n### Player-Hosted Server\n\nWhile this method covers less cases than the #Dedicated Server Test method above, this method is faster:\n- Open the Arma 3: Launcher and disable BattlEye\n- Select the mods that the host will use (if any)\n- Launch the game using the `Play`/`Play with Mods` button\n- Select the mods that clients will use\n- Launch the game again using the `Play`/`Play with Mods` button\n- In the first game instance (server), host the mission either through local hosting or with Eden Editor MP mission type launch\n- In the second game instance (client), join the session using the MP tab LAN section and entering the mission hosted by the server\n\n| Pros | Cons |\n|---|---|\n| - Fast setup (with quick modlist support)<br>- Helps find cases where isDedicated is used instead of isServer | - May miss locality issues (code that happens to run on the server because it runs on the server's *client* like e.g hideObjectGlobal)<br>- No headless client testing (this can be solved by using a custom server.cfg) |\n\n### See Also\n\n- Arma 3: Dedicated Server\n- server config","sections":[{"heading":null,"level":1,"markdown":"This page aims to explain notions and specificities of Multiplayer Scripting and its differences with Singleplayer Scripting.\n\nIt features three parts:\n1. #Notions and general knowledge\n1. #Structuring Multiplayer code\n1. Testing locally"},{"heading":"Notions and general knowledge","level":2,"markdown":""},{"heading":"The basics","level":3,"markdown":"- In Multiplayer, players are connected to a **server**.\n  - The server is a machine that distributes information among clients, such as unit positions, vehicle speeds, etc.\n  - The server can be either a dedicated machine, or hosted by a player; in the latter it means the server can have a player unit.\n- A machine connected to a server is called a **client**.\n  - Clients can join a mission *after* it started: they are considered \"JIP\" and scripting should be adapted to them. See the #Join In Progress chapter for more information.\n\n> [!NOTE]\n> In Singleplayer, the game acts as a player-hosted server (isServer returns true).\n\n> [!WARNING]\n> Some Multiplayer commands do **not** work in Singleplayer, such as netId!\n>\n> The other way around, some Singleplayer commands do not work in Multiplayer (e.g  setAccTime)."},{"heading":"Locality","level":3,"markdown":""},{"heading":"Definitions","level":4,"markdown":"- **LOCAL** is an attribute for the machine where the command/script/function is executed or the AI calculated.\n  - to check if an argument is local, use the local command\n- **REMOTE** means **non local** - on another machine. All player-controlled soldiers are remote units to a dedicated server, for example.\n  - to check if an argument is remote, simply use `not local _myObject`\n- commands arguments and effects can be either **local** or **global**:\n\n|  |  |\n|---|---|\n| Local Argument | a **local** argument means an argument that is processed on the machine where the command is executed |\n| Global Argument | a **global** argument means any argument, even a remote one |\n| Local Effect | a **local** effect means that the effect will only happen on the machine where the command is executed |\n| Global Effect | a **global** effect means that all the computers will receive it |\n| Server Execution | **server exec** means that the command or function has to be executed on the server machine, either mission host or dedicated server |\n\n> [!NOTE]\n> See also:\n> - Scripting Commands: Local Effect\n> - Scripting Commands: Global Effect\n> - Scripting Commands: Server Execution\n> - Functions: Local Effect\n> - Functions: Global Effect\n> - Functions: Server Execution"},{"heading":"Different machines and how to target them","level":4,"markdown":"> [!NOTE]\n> - For obvious reasons, a server cannot be JIP.\n> - Use didJIP to determine if a client joined during the game.\n\n| Scripting Commands | Server | Player client | Headless client | Note |  |\n|---|---|---|---|---|---|\n| Dedicated | Player-Hosted |  |  |  |  |\n| ```sqf<br>isDedicated<br>``` | ✓ | ✗ | ✗ | ✗ | Dedicated server |\n| ```sqf<br>hasInterface && isServer<br>``` | ✗ | ✓ | ✗ | ✗ | Player server |\n| ```sqf<br>hasInterface && not isServer<br>``` | ✗ | ✗ | ✓ | ✗ | Player client |\n| ```sqf<br>not hasInterface && not isDedicated<br>``` | ✗ | ✗ | ✗ | ✓ | Headless client |\n| ```sqf<br>isServer<br>``` | ✓ | ✓ | ✗ | ✗ | Any server |\n| ```sqf<br>hasInterface<br>``` | ✗ | ✓ | ✓ | ✗ | Any player |\n| Miscellaneous |  |  |  |  |  |\n| ```sqf<br>not isServer<br>``` | ✗ | ✗ | ✓ | ✓ | All server clients, including headless clients, but excluding player host |\n| ```sqf<br>not isDedicated<br>``` | ✗ | ✓ | ✓ | ✓ | Everything but a dedicated server |\n| ```sqf<br>not hasInterface<br>``` | ✓ | ✗ | ✗ | ✓ | Headless clients and dedicated server |\n\n**For an extended version for all games, click**\n\n> [!NOTE]\n> Before Operation Flashpoint v1.99 and the backport of isServer, the server could be identified by placing a Game Logic in the editor (that would always remain local to the server), then using the following: `local myLogic`.\n\n| Targeted machine | Operation Flashpoint | Armed Assault | Arma 2 | Arma 3 |\n|---|---|---|---|---|\n| **Dedicated Server**<br>A server without a human player behind it | ```sqf<br>isServer && isNull player<br>``` | ```sqf<br>isServer && isNull player<br>``` | ```sqf<br>isDedicated<br>``` | ```sqf<br>isDedicated<br>``` |\n| **Player Server**<br>A server hosted by a player | ```sqf<br>isServer && not isNull player<br>``` | ```sqf<br>isServer && not isNull player<br>``` | ```sqf<br>isServer && not isDedicated<br>``` | ```sqf<br>hasInterface && isServer<br>``` |\n| **Server**<br>Any server, dedicated or hosted | ```sqf<br>isServer<br>``` | ```sqf<br>isServer<br>``` | ```sqf<br>isServer<br>``` | ```sqf<br>isServer<br>``` |\n| **Player Client**<br>A player connected since the lobby | ```sqf<br>not isServer && not isNull player<br>``` | ```sqf<br>not isServer && not isNull player<br>``` | ```sqf<br>not isServer && not isNull player<br>``` | ```sqf<br>hasInterface && not isServer<br>``` |\n| **JIP Player Client**<br>A player connected in the middle of a mission | N/A | ```sqf<br>not isServer && isNull player<br>``` | ```sqf<br>not isServer && isNull player<br>``` | ```sqf<br>hasInterface && didJIP<br>``` |\n| **Headless Client**<br>A client without a human player behind it<br>(used to offload server calculations) | colspan=\"3\" N/A | ```sqf<br>not hasInterface && not isServer<br>``` |  |  |\n| **JIP Headless Client**<br>Headless client connected during the ongoing mission | colspan=\"3\" N/A | ```sqf<br>not hasInterface && not isServer && didJIP<br>``` |  |  |\n\n- If you want to know if the current machine is a server (Dedicated Server or Player Server), use isServer.\n- If you want to know if the current machine has a player (Player Server included), use hasInterface.\n\n> [!NOTE]\n> **Arma 3:** Since Arma 3, a server-only init file is available: **initServer.sqf** (see Event Scripts)\n\n> [!NOTE]\n> **Arma 3:** **BIS_fnc_getNetMode** can be used to have a string value representing the local machine status (\"DedicatedServer\", \"Server\", \"HeadlessClient\", \"Client\", \"SinglePlayer\" ).\n>\n> This function doesn't make the JIP distinction and should be used along didJIP."},{"heading":"General information about locality","level":4,"markdown":"- about player:\n  - a player's unit is **always** local to the (human) player's machine\n  - a dedicated server doesn't have a player unit (`isNull player` returns true)\n  - to know if a certain unit is *a* player, use isPlayer\n- groups and AI:\n  - an AI group with a player-leader will be local to the player's computer\n  - a subordinate player being in a player-lead group will remain local to its computer (see bulletpoint #1)\n- objects:\n  - a driven vehicle is always local to the machine of its driver (not the commander's, not the gunner's)\n  - terrain objects (buildings, vegetation, roads etc.) are local everywhere (`local nearestBuilding player` returns true on every client)\n  - editor-placed objects and empty vehicles are local to the server\n  - editor-placed **triggers** are created on every machine unless specified otherwise (\"Server Only\" Eden Editor option ticked)\n  - units created with createUnit will be local to the computer that issued the command\n  - objects and vehicles created with createVehicle will be local to the computer that issued the command"},{"heading":"Locality changes","level":4,"markdown":"- if the player-leader dies, the AI is transferred to the machine where the new leader is local (server in case of AI, client in case of another player)\n- joining AI units will change locality to the new leader's computer\n- locality will change from server to client when a player gets in an empty vehicle\n- locality can also change in the event of a Team Switch or usage of selectPlayer\n\n> [!NOTE]\n> **Arma 3:** Since Arma 3 the \"Local\" Event Handler triggers when an object's locality is changed."},{"heading":"Code examples","level":4,"markdown":"| Code | Argu-ments | Effect | Description |\n|---|---|---|---|\n| ```sqf<br>remoteUnit setDamage 1;<br>``` | Global Argument | Global Effect | Any unit (local or remote) will die, and all the computers will know |\n| ```sqf<br>localUnit addMagazine \"30Rnd_556x45_STANAG\";<br>``` | Local Argument | Global Effect | Only a local unit can have its inventory edited with this command, but all the machines will be notified: if a player looks into the *localUnit* inventory, the added magazine will be there |\n| ```sqf<br>remoteUnit setFace \"Miller\";<br>``` | Global Argument | Local Effect | Any unit (local or remote) can get its face changed, but the new face will not be propagated through the network. Only the local machine's player will see the effect of the command<br>> [!NOTE]<br>> This command should ideally be **executed on every machine**, for example with:<br>><br>> ```sqf<br>> [remoteUnit, \"Miller\"] remoteExec [\"setFace\", 0, true];<br>> ```<br>><br>> See the #Remote Execution chapter for more information. |\n| ```sqf<br>localCamera cameraEffect [\"Internal\", \"Back\"];<br>``` | Local Argument | Local Effect | Only a local camera can be entered, and of course only the local machine will see through this camera |"},{"heading":"Network ID","level":3,"markdown":"Network IDs identify machines and network objects. They can be used to target proper machines with remote execution; see the #Remote Execution chapter for more information.\n\n> [!WARNING]\n> **A machine ID** (ownerID) is not to be confused with **an object's network ID** (netId)."},{"heading":"Machine network ID","level":4,"markdown":"Every machine, including the server, has a **network ID**.\n\nA server always has an ID of **2**, every new client has the next number (the first client will be number **3**, second client number **4**, etc.)\n- To get the current machine's ID, use clientOwner.\n- To get the machine ID of an object's owner, use owner *object*  (MP only).\n- To get the machine ID of a group's owner, use groupOwner *object* (MP only).\n\n> [!NOTE]\n> **0** and **1** are special values:\n> - **0** means **everyone** (including the server)\n> - **1** means **current machine** but **is not implemented** and should **not** be used."},{"heading":"Object network ID","level":4,"markdown":"Every object synchronised through the network has a network ID, shortened to **netId**.\n- To get an object's netId, use netId (MP only). **SP & MP variant:** BIS_fnc_netId\n- To get an object from a netId, use objectFromNetId (MP only). **SP & MP variant:** BIS_fnc_objectFromNetId\n- To get a group from a netId, use groupFromNetId (MP only). **SP & MP variant:** BIS_fnc_groupFromNetId"},{"heading":"Sending information across network","level":3,"markdown":""},{"heading":"Remote Execution","level":4,"markdown":"Since Arma 3 v1.50, remoteExec and remoteExecCall efficient engine network commands are available:\n\n```sqf\n\"Message to everyone, including JIP players!\" remoteExec [\"hint\", 0, true];\n```\n\nSee Arma 3: Remote Execution for more information.\n\nArma 3 alpha introduced BIS_fnc_MP (**obsolete** since Arma 3 v1.50, use the above version):\n\n```sqf\n[\"Message to everyone, including JIP  players!\", \"hint\", true, true] call BIS_fnc_MP;  // obsolete since Arma 3 v1.50, use remoteExec or remoteExecCall\n```\n\nArma 2 introduced the Arma 2: Multiplayer Framework (not present in Arma 3):\n\n```sqf\n// having the Functions module placed in the editor\nwaitUntil { not isNil \"BIS_MPF_InitDone\"; };\n[nil, nil, rHINT, \"Message to everyone!\"] call RE;\n```"},{"heading":"PublicVariable commands","level":4,"markdown":"PublicVariable commands allow to send **global** variables to specified machines.\n- Network reception is guaranteed\n- Short variable names should be used for network performance\n- These commands shouldn't be used intensely for the same reason\n\n| Command | Effect | JIP synchronised |\n|---|---|---|\n| publicVariable | Set/update a variable value from the local machine to all the other machines (including server) | ✓ |\n| publicVariableServer | Set/update a variable value **from a client to the server** | ✗ |\n| publicVariableClient | Set/update a variable value **from the local machine** (not necessarily the server) **to a specific client** | ✗ |"},{"heading":"How it works","level":5,"markdown":"The server has the variable `ABC_Score` to broadcast:\n\n```sqf\nABC_Score = 5;                // sets the value\npublicVariable \"ABC_Score\"    // publishes the variable (do not forget the quotes!)\n```\n\nThis sends the variable name and value to the clients.\n- If the variable is not yet declared on their machine, it will declare it as well.\n- If the variable already exists, **the value is overridden** by the server's value.\n- If the variable changes again on the server, it has to be manually publicVariable'd once again!\n- A JIP player will synchronise **server's** publicVariable'd variables **before** executing **init.sqf**. See Initialisation Order for more information.\n  - A JIP player will **not** synchronise publicVariableClient-received variables after he disconnects/reconnects. Only server-known public variables sent with publicVariable will be synchronised."},{"heading":"setVariable command","level":4,"markdown":"Available since Arma 2, the **public** version of the setVariable command (alternative syntax) allows you to store a variable into an object and spread this variable across the network.\n\n> [!NOTE]\n> **Arma 3:** In Arma 3 it is possible to broadcast the nil value, thus deleting the variable from the target."},{"heading":"Player connection events","level":3,"markdown":"The following commands will execute given code when a player is connecting or disconnecting. This code will run for players connecting in the lobby **and** for JIP players!\n- \"PlayerConnected\" mission Event Handler\n- \"PlayerDisconnected\" mission Event Handler\n\n> [!NOTE]\n> Before Arma 3 v1.58, use BIS_fnc_addStackedEventHandler for onPlayerConnected/onPlayerDisconnected."},{"heading":"Client state","level":3,"markdown":"A client state is the client's connection state. It can be obtained with getClientState and getClientStateNumber commands on both server and clients.\n\n| getClientStateNumber | getClientState | Description |\n|---|---|---|\n| 0 | \"NONE\" | No client (or singleplayer) |\n| 1 | \"CREATED\" | Client is created |\n| 2 | \"CONNECTED\" | Client is connected to server, message formats are registered |\n| 3 | \"LOGGED IN\" | Identity is created |\n| 4 | \"MISSION SELECTED\" | Mission is selected |\n| 5 | \"MISSION ASKED\" | Server was asked to send / not send mission |\n| 6 | \"ROLE ASSIGNED\" | Role was assigned (and confirmed) |\n| 7 | \"MISSION RECEIVED\" | Mission received |\n| 8 | \"GAME LOADED\" | Island loaded, vehicles received |\n| 9 | \"BRIEFING SHOWN\" | Briefing was displayed |\n| 10 | \"BRIEFING READ\" | Ready to play mission |\n| 11 | \"GAME FINISHED\" | Game was finished |\n| 12 | \"DEBRIEFING READ\" | Debriefing read, ready to continue with next mission |"},{"heading":"Join In Progress (Armed Assault v1.00)","level":3,"markdown":"**Join In Progress** (JIP) is the ability for a player to connect while a game is running, unlike in Operation Flashpoint where one had to wait for the game to be over.\n\nIt was introduced in  on Xbox and Armed Assault on PC.\n\nA player that joined in progress will be referred to as **JIP player**.\n\n> [!NOTE]\n> To disable JIP on a server:\n> - the server admin can disable playable slots on the role selection screen\n> - the mission designer can disable AI within mission config\n> However, JIP remains available if respawn is enabled in the mission."},{"heading":"JIP Synchronisation","level":4,"markdown":"A JIP player will have a lot of information synchronised (publicVariable'd and setVariable server variables for example) by the game **before** accessing the mission itself; see below:\n\n| Information | Arma 1 | Arma 2 | Arma 3 |\n|---|---|---|---|\n| date/time | ✓ | ✓ | ✓ |\n| date/time + setDate/skipTime | ✗ | ✗ | ✓ |\n| weather (overcast, fog) | ✗ | ✓ | ✓ |\n| weather + setOvercast/setFog/setRain/setLightnings | ✗ | ✗ | ✓ |\n| time passed since mission start (time command) |  |  | ✓ |\n| publicVariable-sent variables (Number, String, Structured Text, Array, Code) | ✓ | ✓ | ✓ |\n| publicVariable-sent variables (nil) | ✗ | ✗ | ✓ |\n| setVariable-assigned variables (when alternative syntax's *public* parameter is set to true) | N/A | ✓ | ✓ |\n| remoteExec- and remoteExecCall-executed code if *JIP* prerequisites are met | colspan=\"2\" N/A | ✓ |  |\n\n> [!WARNING]\n> All the **initialisation fields** code get executed again for **each and every JIP connecting player**.\n>\n> This means that code with global effect Global Effect (such as setDamage) **WILL** be re-executed (depending on locality)!"},{"heading":"Related Commands","level":4,"markdown":"- remoteExec\n- remoteExecCall\n- didJIP\n- didJIPOwner\n- exportJIPMessages\n- isRemoteExecutedJIP"},{"heading":"See also","level":3,"markdown":"- Headless Client\n- Initialisation Order\n- Arma 3: Remote Execution"},{"heading":"Structuring Multiplayer code","level":2,"markdown":""},{"heading":"Responsibilities","level":3,"markdown":"- The **server** initialises and works with **variables**\n- A connecting or connected **player-client** gets the mission data from the server\n- Once an objective is reached, **the server tells the players** via remoteExec/remoteExecCall, publicVariable or setVariable\n- Players can get their client-side UI code triggered by the server\nSee this earlier chapter to know how to target specific machines."},{"heading":"Server","level":4,"markdown":"The **Server** must be considered as the one ruler of the mission. He is the game's referee and all the decisions should come from it. All the values on the server should be considered as **true**.\n\nConsider the server as a powerful machine where you can put all the mission conditions and heavy scripting on.\nIn case of a very heavy mission, the server can be assisted by Headless Clients (this involves a per-mission specific design).\n\nServer-side code must gather or wait all the variables (potentially from client-side scripts and publicVariableServer) and take and broadcast its decisions accordingly using publicVariable.\n\nServer-side code should ideally **not** make any reference to a player variable considering a server can potentially be **dedicated**, unless you want to force this mission to be player-hosted (such as Arma 2's campaign).\n\n> [!NOTE]\n> Use allPlayers, BIS_fnc_listPlayers, playableUnits (and switchableUnits for SP compatibility) to refer to players."},{"heading":"Client","level":4,"markdown":"The **Client** must be considered as an average machine that can leave the game at any moment.\n**No mission-vital code must be executed on its side** and **no decisions must be taken client-side**.\n\nAgain, **one must expect that any client can disconnect at any moment**.\n\nThe client executes all the (local) UI code (Post Process Effects, Dialogs, etc.)\n\nThe player's machine should not deal with a great piece of code.\n\n> [!IMPORTANT]\n> Do not forget that a server **can** be a player, and therefore should execute player code in that case.\n\nThe proper way to check for player-side condition is\n\n```sqf\nif (hasInterface) then { /* (...) */ };\n```\n\nNote that the following example is **incorrect**:\n\n```sqf\nif (isServer) then\n{\n    // ...\n}\nelse // WRONG: a server CAN be a player\n{\n    // ...\n};\n```"},{"heading":"Code writing","level":3,"markdown":"The usual four steps of coding are the following:\n- Think it well\n  - If you cannot figure out how to write your code, say what you want to do out loud or write it down in your language, then replace it by code little by little.\n- Make it work\n  - use the -showScriptErrors startup parameter to see your code errors.\n- Make it readable\n  - name your variables properly, as if you had to give it to someone that should get it without the need of explaining\n- Optimise then\n  - use more performance-friendly commands, reduce your search radius and loop frequencies once everything works.\n\nSee Code Optimisation#Rules for advices and more information."},{"heading":"See also","level":3,"markdown":"- Event Scripts\n- Code Optimisation"},{"heading":"Local Multiplayer Testing","level":2,"markdown":"To ensure all cases, your mission should ideally work properly:\n- in Singleplayer\n- in Player-hosted Multiplayer\n- in a Dedicated Server-hosted game\n\nThe following chapters explain how to mimic multiple clients on one computer. Of course, nothing prevents you from inviting friends to play your mission and report bugs!"},{"heading":"Dedicated Server","level":3,"markdown":"To properly test MP scripting locality issues, it is recommended to run a **dedicated server** and connect with **two** clients. In order to do so:\n- In your server executable's server.cfg: \n\n```cpp\nloopback = true;    // force LAN-only server\nkickDuplicate = 0;    // disable duplicate Arma kick\n\n// needed in case of Headless Client test only\nheadlessClients[]    = { \"127.0.0.1\" };\nlocalClient[]        = { \"127.0.0.1\" };\n```\n\n- Run the game twice from Steam and connect each (with `-showScriptErrors` flag)\n  - If you use one screen for the first client and the other screen for the second client, use `-noPause -window` launcher options. This will not pause render if the window doesn't have focus.\n\n> [!IMPORTANT]\n> Be sure to disable BattlEye or it will close any additional instances.\n\n- Try your mission and check for script errors:\n  - on client's screens and RPT files\n  - in your server's RPT files\n\n| Pros | Cons |\n|---|---|\n| - Tests almost all \"can go wrong\" scenarios<br>- Can test Headless Clients, persistent server, server restart, admin login/logout | - Misses cases where isDedicated is used instead of isServer<br>- \"Big\" setup |"},{"heading":"Player-Hosted Server","level":3,"markdown":"While this method covers less cases than the #Dedicated Server Test method above, this method is faster:\n- Open the Arma 3: Launcher and disable BattlEye\n- Select the mods that the host will use (if any)\n- Launch the game using the `Play`/`Play with Mods` button\n- Select the mods that clients will use\n- Launch the game again using the `Play`/`Play with Mods` button\n- In the first game instance (server), host the mission either through local hosting or with Eden Editor MP mission type launch\n- In the second game instance (client), join the session using the MP tab LAN section and entering the mission hosted by the server\n\n| Pros | Cons |\n|---|---|\n| - Fast setup (with quick modlist support)<br>- Helps find cases where isDedicated is used instead of isServer | - May miss locality issues (code that happens to run on the server because it runs on the server's *client* like e.g hideObjectGlobal)<br>- No headless client testing (this can be solved by using a custom server.cfg) |"},{"heading":"See Also","level":3,"markdown":"- Arma 3: Dedicated Server\n- server config"}],"categories":["Arma Scripting Tutorials"],"groups":["Scripting"]}