Multiplayer Scripting: Difference between revisions

From Bohemia Interactive Community
Jump to navigation Jump to search
m (Add BE disable advice)
m (Text replacement - "{{Inline code|" to "{{ic|")
(27 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{SideTOC|0.9}}
{{TOC|side|0.9}}


This page aims to explain notions and specificities of [[Multiplayer Scripting]] and its differences with Singleplayer Scripting.<br>
This page aims to explain notions and specificities of [[Multiplayer Scripting]] and its differences with Singleplayer Scripting.<br>
Line 9: Line 9:


= Notions and general knowledge =
= Notions and general knowledge =


== The basics ==
== The basics ==
Line 17: Line 18:
** 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.
** 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.
* A machine connected to a server is called a '''client'''.
* A machine connected to a server is called a '''client'''.
** Clients can join a mission ''after'' it started: they are considered "[[Join In Progress|JIP]]" and scripting should be adapted to them. See the [[#Join In Progress|Join In Progress]] chapter for more information.
** Clients can join a mission ''after'' it started: they are considered "[[#Join In Progress|JIP]]" and scripting should be adapted to them. See the {{HashLink|#Join In Progress}} chapter for more information.
<br>
<br>


{{Informative | In Singleplayer, the game acts as a player-hosted server ({{Inline code|[[isServer]]}} returns {{Inline code|[[true]]}}).}}
{{Feature | Informative | In Singleplayer, the game acts as a player-hosted server ({{ic|[[isServer]]}} returns {{ic|[[true]]}}).}}
{{Warning | Some Multiplayer commands do '''not''' work in Singleplayer, such as {{Inline code|[[netId]]}} !<br>
{{Feature | Warning | Some Multiplayer commands do '''not''' work in Singleplayer, such as {{ic|[[netId]]}} !<br>
The other way around, some Singleplayer commands do not work in Multiplayer (e.g  {{Inline code|[[setAccTime]]}}).}}
The other way around, some Singleplayer commands do not work in Multiplayer (e.g  {{ic|[[setAccTime]]}}).}}




Line 32: Line 33:
** to check if an argument is local, use the [[local]] command
** to check if an argument is local, use the [[local]] command
* '''REMOTE''' means '''non local''' - on another machine. All player-controlled soldiers are remote units to a dedicated server, for example.
* '''REMOTE''' means '''non local''' - on another machine. All player-controlled soldiers are remote units to a dedicated server, for example.
** to check if an argument is remote, simply use {{Inline code|[[not]] [[local]] ''myObject''}}
** to check if an argument is remote, simply use {{ic|[[not]] [[local]] ''myObject''}}
* [[:Category:Scripting_Commands|commands]] arguments and effects can be either '''local''' or '''global''':
* [[:Category:Scripting_Commands|commands]] arguments and effects can be either '''local''' or '''global''':
{|
{|
| style="padding-right: 0.5em;" | {{EffArg|cmd|arg|local}}
| style="padding-right: 0.5em;" | {{Icon|localArgument|32}}
| a '''local''' argument means an argument that is processed on the machine where the command is executed
| a '''local''' argument means an argument that is processed on the machine where the command is executed
|-
|-
| {{EffArg|cmd|arg|global}}
| {{Icon|globalArgument|32}}
| a '''global''' argument means any argument, even a remote one
| a '''global''' argument means any argument, even a remote one
|-
|-
| {{EffArg|cmd|eff|local}}
| {{Icon|localEffect|32}}
| a '''local''' effect means that the effect will only happen on the machine where the command is executed
| a '''local''' effect means that the effect will only happen on the machine where the command is executed
|-
|-
| {{EffArg|cmd|eff|global}}
| {{Icon|globalEffect|32}}
| a '''global''' effect means that all the computers will receive it
| a '''global''' effect means that all the computers will receive it
|-
| {{Icon|serverExec|32}}
| '''server exec''' means that the command or function has to be executed on the server machine, either mission host or dedicated server
|}
|}


=== Different machines and how to target them ===
=== Different machines and how to target them ===


{| class="bikitable" style="text-align: center"
{{Feature | Informative | For obvious reason, a server cannot be [[#Join In Progress|JIP]].}}
 
{| class="wikitable" style="text-align: center"
|+ Valid for {{arma3}}
|+ Valid for {{arma3}}
! Machine
! Machine
Line 56: Line 62:
! Is server
! Is server
! Is player
! Is player
! Is JIP
! Is [[#Join In Progress|JIP]]
|-
|-
| Dedicated Server
| Dedicated Server
| <code>[[isDedicated]]</code>
| <code>[[isDedicated]]</code>
| {{task/}}
| {{Icon|checked}}
| {{task}}
| {{Icon|unchecked}}
| {{task}}
| {{Icon|unchecked}}
|-
|-
| Player Server
| Player Server
| <code>[[hasInterface]] && [[isServer]]</code>
| <code>[[isServer]] && [[hasInterface]]</code>
| {{task/}}
| {{Icon|checked}}
| {{task/}}
| {{Icon|checked}}
| {{task}}
| {{Icon|unchecked}}
|-
|-
| Player Client
| Player Client
| <code>[[hasInterface]] && not [[isServer]]</code>
| <code>not [[isServer]] && [[hasInterface]]</code>
| {{task}}
| {{Icon|unchecked}}
| {{task/}}
| {{Icon|checked}}
| {{task}}
| {{Icon|unchecked}}
|-
|-
| [[Join In Progress|JIP]] Player Client
| [[#Join In Progress|JIP]] Player Client
| <code>[[hasInterface]] && [[didJIP]]</code>
| <code>[[hasInterface]] && [[didJIP]]</code>
| {{task}}
| {{Icon|unchecked}}
| {{task/}}
| {{Icon|checked}}
| {{task/}}
| {{Icon|checked}}
|-
|-
| [[Arma 3 Headless Client|Headless Client]]
| [[Arma 3 Headless Client|Headless Client]]
| <code>not [[hasInterface]] && not [[isServer]]</code>
| <code>not [[hasInterface]] && not [[isServer]]</code>
| {{task}}
| {{Icon|unchecked}}
| {{task}}
| {{Icon|unchecked}}
| {{task}}
| {{Icon|unchecked}}
|-
|-
| JIP Headless Client
| JIP Headless Client
| <code>not [[hasInterface]] && not [[isServer]] && [[didJIP]]</code>
| <code>not [[hasInterface]] && [[didJIP]]</code>
| {{task}}
| {{Icon|unchecked}}
| {{task}}
| {{Icon|unchecked}}
| {{task/}}
| {{Icon|checked}}
|}
|}
'''For an extended version for all games, click''' <spoiler>
'''For an extended version for all games, click''' <spoiler>
{{Informative | Before {{GVI|ofp|1.99}} and [[isServer]], server could be detected by placing a [[Game Logic]] in the editor that would remain local to the server, then using the following:<code>[[local]] myLogic</code>}}
{{Feature | Informative | Before {{GVI|ofp|1.99}} and [[isServer]], server could be detected by placing a [[Game Logic]] in the editor that would remain local to the server, then using the following:<code>[[local]] myLogic</code>}}




{| class="bikitable" style="line-height: normal; text-align: center"
{| class="wikitable" style="line-height: normal; text-align: center"
! Targeted machine
! Targeted machine
! {{ofp}}
! {{ofp}}
Line 145: Line 151:
</spoiler>
</spoiler>


* If you want to know if the current machine is a server (Dedicated Server or Player Server), use {{Inline code|[[isServer]]}}.
* If you want to know if the current machine is a server (Dedicated Server or Player Server), use {{ic|[[isServer]]}}.
* If you want to know if the current machine has a player (Player Server included), use {{Inline code|[[hasInterface]]}}.
* If you want to know if the current machine has a player (Player Server included), use {{ic|[[hasInterface]]}}.


{{Feature arma3|Since {{arma3}}, a server-only init file is available: '''initServer.sqf''' (see [[Event Scripts]])}}
{{Feature|arma3|Since {{arma3}}, a server-only init file is available: '''initServer.sqf''' (see [[Event Scripts]])}}


{{Feature arma3 | '''[[BIS_fnc_getNetMode]]''' can be used to have a string value representing the local machine status ("DedicatedServer", "Server", "HeadlessClient", "Client", "SinglePlayer" ).<br>
{{Feature|arma3 | '''[[BIS_fnc_getNetMode]]''' can be used to have a string value representing the local machine status ("DedicatedServer", "Server", "HeadlessClient", "Client", "SinglePlayer" ).<br>
This function doesn't make the JIP distinction and should be used along [[didJIP]].}}
This function doesn't make the JIP distinction and should be used along [[didJIP]].}}


Line 157: Line 163:
* about [[player]]:
* about [[player]]:
** a [[player]]'s unit is '''always''' local to the (human) player's machine
** a [[player]]'s unit is '''always''' local to the (human) player's machine
** a dedicated server doesn't have a [[player]] unit ({{Inline code|[[isNull]] [[player]]}} returns [[true]])
** a dedicated server doesn't have a [[player]] unit ({{ic|[[isNull]] [[player]]}} returns [[true]])
** to know if a certain unit is ''a'' player, use [[isPlayer]]
** to know if a certain unit is ''a'' player, use [[isPlayer]]
* groups and AI:
* groups and AI:
Line 164: Line 170:
* objects:
* objects:
** a driven vehicle is always local to the machine of its driver (not the commander's, not the gunner's)
** a driven vehicle is always local to the machine of its driver (not the commander's, not the gunner's)
** terrain objects (buildings, vegetation, roads etc.) are local everywhere ({{Inline code|[[local]] [[nearestBuilding]] [[player]]}} returns [[true]] on every client)
** terrain objects (buildings, vegetation, roads etc.) are local everywhere ({{ic|[[local]] [[nearestBuilding]] [[player]]}} returns [[true]] on every client)
** editor-placed objects and empty vehicles are local to the server
** editor-placed objects and empty vehicles are local to the server
** editor-placed '''triggers''' are created on every machine unless specified otherwise ("Server Only" [[Eden Editor]] option ticked)
** editor-placed '''triggers''' are created on every machine unless specified otherwise ("Server Only" [[Eden Editor]] option ticked)
Line 176: Line 182:
* locality will change from server to client when a player gets in an empty vehicle
* locality will change from server to client when a player gets in an empty vehicle
* locality can also change in the event of a [[Team Switch]] or usage of [[selectPlayer]]
* locality can also change in the event of a [[Team Switch]] or usage of [[selectPlayer]]
{{Feature arma3|Since {{arma3}} the [[Arma 3: Event Handlers#Local|"Local" Event Handler]] triggers when an object's locality is changed.}}
{{Feature|arma3|Since {{arma3}} the [[Arma 3: Event Handlers#Local|"Local" Event Handler]] triggers when an object's locality is changed.}}


=== Code examples ===
=== Code examples ===


{| class="bikitable"
{| class="wikitable"
! Code
! Code
! Argu-ments
! Argu-ments
Line 187: Line 193:
|-
|-
| <code>remoteUnit [[setDamage]] 1;</code>
| <code>remoteUnit [[setDamage]] 1;</code>
| align="center" | {{EffArg|cmd|arg|global}}
| align="center" | {{Icon|globalArgument|32}}
| align="center" | {{EffArg|cmd|eff|global}}
| align="center" | {{Icon|globalEffect|32}}
| Any unit (local or remote) will die, and all the computers will know
| Any unit (local or remote) will die, and all the computers will know
|-
|-
| <code>localUnit [[addMagazine]] "30Rnd_556x45_STANAG";</code>
| <code>localUnit [[addMagazine]] "30Rnd_556x45_STANAG";</code>
| align="center" | {{EffArg|cmd|arg|local}}
| align="center" | {{Icon|localArgument|32}}
| align="center" | {{EffArg|cmd|eff|global}}
| align="center" | {{Icon|globalEffect|32}}
| 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
| 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
|-
|-
| <code>remoteUnit [[setFace]] "Miller";</code>
| <code>remoteUnit [[setFace]] "Miller";</code>
| align="center" | {{EffArg|cmd|arg|global}}
| align="center" | {{Icon|globalArgument|32}}
| align="center" | {{EffArg|cmd|eff|local}}
| align="center" | {{Icon|localEffect|32}}
| 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
| 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
{{Informative | This command should ideally be '''executed on every machine''', for example with:
{{Feature | Informative | This command should ideally be '''executed on every machine''', for example with:
  [remoteUnit, "Miller"] [[remoteExec]] ["setFace", 0, true];
  [remoteUnit, "Miller"] [[remoteExec]] ["setFace", 0, true];
See [[#Remote Execution|Remote Execution]] paragraph for more information.}}
See [[#Remote Execution|Remote Execution]] paragraph for more information.}}
|-
|-
| <code>localCamera [[cameraEffect]] ["Internal", "Back"];</code>
| <code>localCamera [[cameraEffect]] ["Internal", "Back"];</code>
| align="center" | {{EffArg|cmd|arg|local}}
| align="center" | {{Icon|localArgument|32}}
| align="center" | {{EffArg|cmd|eff|local}}
| align="center" | {{Icon|localEffect|32}}
| Only a local camera can be entered, and of course only the local machine will see through this camera
| Only a local camera can be entered, and of course only the local machine will see through this camera
|}
|}
Line 214: Line 220:


Network IDs identify machines and network objects. They can be used to target proper machines with remote execution; see [[#Remote Execution|Remote Execution]] chapter for more information.
Network IDs identify machines and network objects. They can be used to target proper machines with remote execution; see [[#Remote Execution|Remote Execution]] chapter for more information.
{{Warning | '''A machine ID''' ([[owner|ownerID]]) is not to be confused with '''an object's network ID''' ([[netId]]).}}
{{Feature | Warning | '''A machine ID''' ([[owner|ownerID]]) is not to be confused with '''an object's network ID''' ([[netId]]).}}


=== Machine network ID ===
=== Machine network ID ===


Every machine, including the server, has a '''network ID'''.<br>
Every machine, including the server, has a '''network ID'''.<br>
A server has an ID of '''2''', every new client has the next number (the first client will be number '''3''', second client number '''4''', etc.)
A 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.)
* To get the current machine's ID, use [[clientOwner]].
* To get the current machine's ID, use [[clientOwner]].
* To get the machine's ID of an object's owner, use [[owner]] ''object''  (MP only).
* To get the machine ID of an object's owner, use [[owner]] ''object''  (MP only).
* To get the machine's ID of a group's owner, use [[groupOwner]] ''object'' (MP only).
* To get the machine ID of a group's owner, use [[groupOwner]] ''object'' (MP only).
 
{{Feature | Informative | '''0''' and '''1''' are special values:
* '''0''' means '''everyone''' (including the server)
* '''1''' means '''current machine''' but '''{{Color|darkred|is not implemented}}''' and should '''not''' be used.}}


=== Object network ID ===
=== Object network ID ===
Line 237: Line 247:


Since {{arma3}} v1.50, [[remoteExec]] and [[remoteExecCall]] efficient engine network commands are available:
Since {{arma3}} v1.50, [[remoteExec]] and [[remoteExecCall]] efficient engine network commands are available:
  ["Message to everyone, including JIP players!", 0, true] [[remoteExec]] ["hint"];
  "Message to everyone, including JIP players!" [[remoteExec]] ["hint", 0, true];
See [[Arma 3 Remote Execution]] for more information.
See [[Arma 3 Remote Execution]] for more information.


Line 243: Line 253:
  ["Message to everyone, including JIP  players!", "hint", true, true] call BIS_fnc_MP;  {{cc|obsolete since {{arma3}} v1.50, use [[remoteExec]] or [[remoteExecCall]]}}
  ["Message to everyone, including JIP  players!", "hint", true, true] call BIS_fnc_MP;  {{cc|obsolete since {{arma3}} v1.50, use [[remoteExec]] or [[remoteExecCall]]}}


{{arma2}} introduced the [[Multiplayer framework]] (not present in {{arma3}}):
{{arma2}} introduced the [[Arma 2: Multiplayer Framework]] (not present in {{arma3}}):
  {{cc|having the Functions module placed in the editor}}
  {{cc|having the Functions module placed in the editor}}
  waitUntil { not isNil "BIS_MPF_InitDone"; };
  waitUntil { not isNil "BIS_MPF_InitDone"; };
Line 254: Line 264:
* Short variable names should be used for network performance
* Short variable names should be used for network performance
* These commands shouldn't be used intensely for the same reason
* These commands shouldn't be used intensely for the same reason
{| class="bikitable"
{| class="wikitable"
! Command
! Command
! Effect
! Effect
! [[Join In Progress|JIP]] synchronised
! [[#Join In Progress|JIP]] synchronised
|-
|-
| [[publicVariable]]
| [[publicVariable]]
| Set/update a variable value from the local machine to all the other machines (including server)
| Set/update a variable value from the local machine to all the other machines (including server)
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
|-
|-
| [[publicVariableServer]]
| [[publicVariableServer]]
| Set/update a variable value '''from a client to the server'''
| Set/update a variable value '''from a client to the server'''
| align="center" | {{task}}
| align="center" | {{Icon|unchecked}}
|-
|-
| [[publicVariableClient]]
| [[publicVariableClient]]
| Set/update a variable value '''from the local machine''' (not necessarily the server) '''to a specific client'''
| Set/update a variable value '''from the local machine''' (not necessarily the server) '''to a specific client'''
| align="center" | {{task}}
| align="center" | {{Icon|unchecked}}
|}
|}


==== How it works ====
==== How it works ====
The server has the variable {{Inline code|ABC_Score}} to broadcast:
The server has the variable {{ic|ABC_Score}} to broadcast:
  ABC_Score = 5; {{cc|sets the value}}
  ABC_Score = 5; {{cc|sets the value}}
  [[publicVariable]] "ABC_Score" {{cc|publishes the variable (do not forget the quotes!)}}
  [[publicVariable]] "ABC_Score" {{cc|publishes the variable (do not forget the quotes!)}}
Line 280: Line 290:
* If the variable already exists, '''the value is overridden''' by the server's value.
* If the variable already exists, '''the value is overridden''' by the server's value.
* If the variable changes again on the server, it has to be manually [[publicVariable]]'d once again!
* If the variable changes again on the server, it has to be manually [[publicVariable]]'d once again!
* A [[Join In Progress|JIP]] player will synchronise '''server's''' [[publicVariable]]'d variables '''before''' executing '''init.sqf'''. See [[Initialization Order]] for more information.
* A [[#Join In Progress|JIP]] player will synchronise '''server's''' [[publicVariable]]'d variables '''before''' executing '''init.sqf'''. See [[Initialization Order]] for more information.
** A JIP player will '''not''' synchronise [[publicVariableClient]]-received variables after he disconnects/reconnects. Only server-known public variables sent with [[publicVariable]] will be synchronised.
** A JIP player will '''not''' synchronise [[publicVariableClient]]-received variables after he disconnects/reconnects. Only server-known public variables sent with [[publicVariable]] will be synchronised.


Line 286: Line 296:


Available since {{arma2}}, 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.
Available since {{arma2}}, 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.
{{Feature arma3|In {{arma3}} it is possible to broadcast the [[nil]] value, thus deleting the variable from the target.}}
{{Feature|arma3|In {{arma3}} it is possible to broadcast the [[nil]] value, thus deleting the variable from the target.}}




== Player connection events ==
== Player connection events ==


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 [[Join In Progress|JIP]] players!
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 [[#Join In Progress|JIP]] players!
* [[Arma 3: Event Handlers/addMissionEventHandler#PlayerConnected|"PlayerConnected" mission Event Handler]]
* [[Arma 3: Event Handlers/addMissionEventHandler#PlayerConnected|"PlayerConnected" mission Event Handler]]
* [[Arma 3: Event Handlers/addMissionEventHandler#PlayerDisconnected|"PlayerDisconnected" mission Event Handler]]
* [[Arma 3: Event Handlers/addMissionEventHandler#PlayerDisconnected|"PlayerDisconnected" mission Event Handler]]
{{Informative | Before {{arma3}} v1.57, use [[BIS_fnc_addStackedEventHandler]] for [[onPlayerConnected]]/[[onPlayerDisconnected]].}}
{{Feature | Informative | Before {{arma3}} v1.57, use [[BIS_fnc_addStackedEventHandler]] for [[onPlayerConnected]]/[[onPlayerDisconnected]].}}




Line 300: Line 310:


A client state is the client's connection state. It can be obtained with [[getClientState]] and [[getClientStateNumber]] commands on both server and clients.
A client state is the client's connection state. It can be obtained with [[getClientState]] and [[getClientStateNumber]] commands on both server and clients.
{| class="bikitable"
{| class="wikitable"
! [[getClientStateNumber]]
! [[getClientStateNumber]]
! [[getClientState]]
! [[getClientState]]
Line 359: Line 369:




== Join In Progress ==
{{ArgTitle|Join In Progress|2|{{GVI|arma1|1.00}}|{{GVI|ofpe|1.00}}}}
 
'''Join In Progress''' (JIP) is the ability for a player to connect while a game is running, unlike in {{ofp}} where one had to wait for the game to be over.<br>
It was introduced in [[:Category:{{ofpe}}|{{ofpe}}]] on XBox and [[{{arma1}}]] on PC.
 
A player that joined in progress will be referred to as '''JIP player'''.
 
{{Feature | Informative |
To disable JIP on a server:
* the server admin can disable playable slots on the role selection screen
* the mission designer can [[Description.ext#disabledAI|disable AI]] within mission config
However, JIP remains available if respawn is enabled in the mission.
}}
 
=== JIP Synchronisation ===
 
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:


A [[Join In Progress|JIP]] player will have many information synchronised ([[publicVariable]]'d and [[setVariable]] server variables for example) by the game before accessing the mission itself. JIP was introduced in [[:Category:{{ofpe}}|{{ofpe}}]] on XBox and [[{{arma}}]] on PC.
{| class="wikitable"
{| class="bikitable"
! Information
! Information
! Arma 1
! Arma 1
Line 369: Line 394:
|-
|-
| date/time
| date/time
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
|-
|-
| date/time + [[setDate]]/[[skipTime]]
| date/time + [[setDate]]/[[skipTime]]
| align="center" | {{task}}
| align="center" | {{Icon|unchecked}}
| align="center" | {{task}}
| align="center" | {{Icon|unchecked}}
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
|-
|-
| weather ([[overcast]], [[fog]])
| weather ([[overcast]], [[fog]])
| align="center" | {{task}}
| align="center" | {{Icon|unchecked}}
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
|-
|-
| weather + [[setOvercast]]/[[setFog]]/[[setRain]]/[[setLightnings]]
| weather + [[setOvercast]]/[[setFog]]/[[setRain]]/[[setLightnings]]
| align="center" | {{task}}
| align="center" | {{Icon|unchecked}}
| align="center" | {{task}}
| align="center" | {{Icon|unchecked}}
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
|-
|-
| time passed since mission start ([[time]] command)
| time passed since mission start ([[time]] command)
| align="center" | ''unknown''
| align="center" | ''unknown''
| align="center" | ''unknown''
| align="center" | ''unknown''
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
|-
|-
| [[publicVariable]]-sent variables ([[Number]], [[String]], [[Text]], [[Array]], [[Code]])
| [[publicVariable]]-sent variables ([[Number]], [[String]], [[Text]], [[Array]], [[Code]])
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
|-
|-
| [[publicVariable]]-sent variables ([[nil]])
| [[publicVariable]]-sent variables ([[nil]])
| align="center" | {{task}}
| align="center" | {{Icon|unchecked}}
| align="center" | {{task}}
| align="center" | {{Icon|unchecked}}
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
|-
|-
| [[setVariable]]-assigned variables (when alternative syntax's ''public'' parameter is set to true)
| [[setVariable]]-assigned variables (when alternative syntax's ''public'' parameter is set to true)
| {{n/a}}
| {{n/a}}
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
|-
|-
| [[remoteExec]]- and [[remoteExecCall]]-executed code if ''JIP'' prerequisites are met
| [[remoteExec]]- and [[remoteExecCall]]-executed code if ''JIP'' prerequisites are met
| colspan="2" {{n/a}}
| colspan="2" {{n/a}}
| align="center" | {{task/}}
| align="center" | {{Icon|checked}}
|}
|}
See also [[Join In Progress]].
 
{{Warning | All the '''initialisation fields''' code get executed again for '''each and every JIP connecting player'''.<br>
{{Feature | Warning | All the '''initialisation fields''' code get executed again for '''each and every JIP connecting player'''.<br>
This means that code with global effect {{EffArg|cmd|eff|global}} (such as [[setDamage]]) '''WILL''' be re-executed (depending on locality)!}}
This means that code with global effect {{Icon|globalEffect|32}} (such as [[setDamage]]) '''WILL''' be re-executed (depending on locality)!}}
 
=== Related Commands ===
 
* [[remoteExec]]
* [[remoteExecCall]]
* [[didJIP]]
* [[didJIPOwner]]
* [[exportJIPMessages]]
* [[isRemoteExecutedJIP]]




Line 420: Line 454:


* [[Arma 3 Headless Client|Headless Client]]
* [[Arma 3 Headless Client|Headless Client]]
* [[:Category:Commands by effects and arguments locality|Commands by effects and arguments locality]]
** [[:Category:Commands requiring server side execution|Server-only commands]]
* [[Initialization Order]]
* [[Initialization Order]]
* [[Arma 3 Remote Execution]]
* [[Arma 3 Remote Execution]]
Line 446: Line 478:


Server-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 {{arma2}}'s campaign).
Server-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 {{arma2}}'s campaign).
{{Informative | Use [[allPlayers]], [[BIS_fnc_listPlayers]], [[playableUnits]] (and [[switchableUnits]] for SP compatibility) to refer to players.}}
{{Feature | Informative | Use [[allPlayers]], [[BIS_fnc_listPlayers]], [[playableUnits]] (and [[switchableUnits]] for SP compatibility) to refer to players.}}


=== Client ===
=== Client ===
Line 456: Line 488:
The client executes all the (local) UI code ([[Post process effects]], [[Arma: GUI Configuration|Dialogs]], etc.)<br>
The client executes all the (local) UI code ([[Post process effects]], [[Arma: GUI Configuration|Dialogs]], etc.)<br>
The player's machine should not deal with a great piece of code.
The player's machine should not deal with a great piece of code.
{{Important | Do not forget that a server '''can''' be a player, and therefore should execute player code in that case.}}
{{Feature | important | Do not forget that a server '''can''' be a player, and therefore should execute player code in that case.}}
The proper way to check for player-side condition is
The proper way to check for player-side condition is
  [[if]] ([[hasInterface]]) [[then]] { (…) };
  [[if]] ([[hasInterface]]) [[then]] { (…) };
Line 504: Line 536:
localClient[] = { "127.0.0.1" };
localClient[] = { "127.0.0.1" };
</syntaxhighlight>
</syntaxhighlight>
* Run Arma 3 twice from Steam and connect each (with {{Inline code|-showScriptErrors}} flag)
* Run Arma 3 twice from Steam and connect each (with {{ic|-showScriptErrors}} flag)
** If you use one screen for the first client and the other screen for the second client, use {{Inline code|-noPause -window}} launcher options. This will not pause render if the window doesn't have focus.<!--
** If you use one screen for the first client and the other screen for the second client, use {{ic|-noPause -window}} launcher options. This will not pause render if the window doesn't have focus.<!--
-->{{Important | Be sure to disable [[BattlEye]] or it will close any additional instances.}}
-->{{Feature | important | Be sure to disable [[BattlEye]] or it will close any additional instances.}}
* Try your mission and check for script errors:
* Try your mission and check for script errors:
** on client's screens and [[arma.RPT|RPT files]]
** on client's screens and [[arma.RPT|RPT files]]
Line 515: Line 547:
** in a Dedicated Server-hosted game
** in a Dedicated Server-hosted game


{{Informative | Of course, nothing prevents you to invite friends to play your mission and report bugs!}}
{{Feature | Informative | Of course, nothing prevents you to invite friends to play your mission and report bugs!}}




Line 524: Line 556:




[[Category: Scripting Topics]]
[[Category:Arma Scripting Tutorials]]

Revision as of 19:12, 27 February 2021

This page aims to explain notions and specificities of Multiplayer Scripting and its differences with Singleplayer Scripting.
It features three parts:

  1. Notions and general knowledge
  2. Structuring Multiplayer code
  3. Testing locally


Notions and general knowledge

The basics

Client-Server relationship
  • In Multiplayer, players are connected to a server.
    • The server is a machine that distributes information among clients, such as unit positions, vehicle speeds, etc.
    • 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.
  • A machine connected to a server is called a client.
    • 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.


In Singleplayer, the game acts as a player-hosted server (isServer returns true).
Some Multiplayer commands do not work in Singleplayer, such as netId !
The other way around, some Singleplayer commands do not work in Multiplayer (e.g setAccTime).


Locality

Definitions

  • LOCAL is an attribute for the machine where the command/script/function is executed or the AI calculated.
    • to check if an argument is local, use the local command
  • REMOTE means non local - on another machine. All player-controlled soldiers are remote units to a dedicated server, for example.
    • to check if an argument is remote, simply use not local myObject
  • commands arguments and effects can be either local or global:
LALocal a local argument means an argument that is processed on the machine where the command is executed
GAGlobal a global argument means any argument, even a remote one
LELocal a local effect means that the effect will only happen on the machine where the command is executed
GEGlobal a global effect means that all the computers will receive it
SEServer server exec means that the command or function has to be executed on the server machine, either mission host or dedicated server

Different machines and how to target them

For obvious reason, a server cannot be JIP.
Valid for Arma 3
Machine Conditions Is server Is player Is JIP
Dedicated Server isDedicated Checked Unchecked Unchecked
Player Server isServer && hasInterface Checked Checked Unchecked
Player Client not isServer && hasInterface Unchecked Checked Unchecked
JIP Player Client hasInterface && didJIP Unchecked Checked Checked
Headless Client not hasInterface && not isServer Unchecked Unchecked Unchecked
JIP Headless Client not hasInterface && didJIP Unchecked Unchecked Checked

For an extended version for all games, click

Before Logo A0.png1.99 and isServer, server could be detected by placing a Game Logic in the editor that would remain local to the server, then using the following:local myLogic


Targeted machine Operation Flashpoint Arma 1 Arma 2 Arma 3
Dedicated Server
A server without a human player behind it
isServer && isNull player isServer && isNull player isDedicated isDedicated
Player Server
A server hosted by a player
isServer && not isNull player isServer && not isNull player isServer && not isDedicated hasInterface && isServer
Server
Any server, dedicated or hosted
isServer isServer isServer isServer
Player Client
A player connected since the lobby
not isServer && not isNull player not isServer && not isNull player not isServer && not isNull player hasInterface && not isServer
JIP Player Client
A player connected in the middle of a mission
N/A not isServer && isNull player not isServer && isNull player hasInterface && didJIP
Headless Client
A client without a human player behind it
(used to offload server calculations)
N/A not hasInterface && not isServer
JIP Headless Client
Headless client connected during the ongoing mission
N/A not hasInterface && not isServer && didJIP
↑ Back to spoiler's top

  • If you want to know if the current machine is a server (Dedicated Server or Player Server), use isServer.
  • If you want to know if the current machine has a player (Player Server included), use hasInterface.
Arma 3
Since Arma 3, a server-only init file is available: initServer.sqf (see Event Scripts)
Arma 3
BIS_fnc_getNetMode can be used to have a string value representing the local machine status ("DedicatedServer", "Server", "HeadlessClient", "Client", "SinglePlayer" ).
This function doesn't make the JIP distinction and should be used along didJIP.

General information about locality

  • about player:
    • a player's unit is always local to the (human) player's machine
    • a dedicated server doesn't have a player unit (isNull player returns true)
    • to know if a certain unit is a player, use isPlayer
  • groups and AI:
    • an AI group with a player-leader will be local to the player's computer
    • a subordinate player being in a player-lead group will remain local to its computer (see bulletpoint #1)
  • objects:
    • a driven vehicle is always local to the machine of its driver (not the commander's, not the gunner's)
    • terrain objects (buildings, vegetation, roads etc.) are local everywhere (local nearestBuilding player returns true on every client)
    • editor-placed objects and empty vehicles are local to the server
    • editor-placed triggers are created on every machine unless specified otherwise ("Server Only" Eden Editor option ticked)
    • units created with createUnit will be local to the computer that issued the command
    • objects and vehicles created with createVehicle will be local to the computer that issued the command

Locality changes

  • 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)
  • joining AI units will change locality to the new leader's computer
  • locality will change from server to client when a player gets in an empty vehicle
  • locality can also change in the event of a Team Switch or usage of selectPlayer
Arma 3
Since Arma 3 the "Local" Event Handler triggers when an object's locality is changed.

Code examples

Code Argu-ments Effect Description
remoteUnit setDamage 1; GAGlobal GEGlobal Any unit (local or remote) will die, and all the computers will know
localUnit addMagazine "30Rnd_556x45_STANAG"; LALocal GEGlobal 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
remoteUnit setFace "Miller"; GAGlobal LELocal 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
This command should ideally be executed on every machine, for example with:
[remoteUnit, "Miller"] remoteExec ["setFace", 0, true];
See Remote Execution paragraph for more information.
localCamera cameraEffect ["Internal", "Back"]; LALocal LELocal Only a local camera can be entered, and of course only the local machine will see through this camera


Network ID

Network IDs identify machines and network objects. They can be used to target proper machines with remote execution; see Remote Execution chapter for more information.

A machine ID (ownerID) is not to be confused with an object's network ID (netId).

Machine network ID

Every machine, including the server, has a network ID.
A 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.)

  • To get the current machine's ID, use clientOwner.
  • To get the machine ID of an object's owner, use owner object (MP only).
  • To get the machine ID of a group's owner, use groupOwner object (MP only).
0 and 1 are special values:
  • 0 means everyone (including the server)
  • 1 means current machine but is not implemented and should not be used.

Object network ID

Every object synchronised through the network has a network ID, shortened to netId.


Sending information across network

Remote Execution

Since Arma 3 v1.50, remoteExec and remoteExecCall efficient engine network commands are available:

"Message to everyone, including JIP players!" remoteExec ["hint", 0, true];

See Arma 3 Remote Execution for more information.

Arma 3 alpha introduced BIS_fnc_MP (obsolete since Arma 3 v1.50, use the above version):

["Message to everyone, including JIP  players!", "hint", true, true] call BIS_fnc_MP;  // obsolete since Arma 3 v1.50, use remoteExec or remoteExecCall

Arma 2 introduced the Arma 2: Multiplayer Framework (not present in Arma 3):

// having the Functions module placed in the editor
waitUntil { not isNil "BIS_MPF_InitDone"; };
[nil, nil, rHINT, "Message to everyone!"] call RE;

PublicVariable commands

PublicVariable commands allow to send global variables to specified machines.

  • Network reception is guaranteed
  • Short variable names should be used for network performance
  • These commands shouldn't be used intensely for the same reason
Command Effect JIP synchronised
publicVariable Set/update a variable value from the local machine to all the other machines (including server) Checked
publicVariableServer Set/update a variable value from a client to the server Unchecked
publicVariableClient Set/update a variable value from the local machine (not necessarily the server) to a specific client Unchecked

How it works

The server has the variable ABC_Score to broadcast:

ABC_Score = 5;				// sets the value
publicVariable "ABC_Score"	// publishes the variable (do not forget the quotes!)

This sends the variable name and value to the clients.

  • If the variable is not yet declared on their machine, it will declare it as well.
  • If the variable already exists, the value is overridden by the server's value.
  • If the variable changes again on the server, it has to be manually publicVariable'd once again!
  • A JIP player will synchronise server's publicVariable'd variables before executing init.sqf. See Initialization Order for more information.
    • A JIP player will not synchronise publicVariableClient-received variables after he disconnects/reconnects. Only server-known public variables sent with publicVariable will be synchronised.

setVariable command

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.

Arma 3
In Arma 3 it is possible to broadcast the nil value, thus deleting the variable from the target.


Player connection events

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!


Client state

A client state is the client's connection state. It can be obtained with getClientState and getClientStateNumber commands on both server and clients.

getClientStateNumber getClientState Description
0 "NONE" No client (or singleplayer)
1 "CREATED" Client is created
2 "CONNECTED" Client is connected to server, message formats are registered
3 "LOGGED IN" Identity is created
4 "MISSION SELECTED" Mission is selected
5 "MISSION ASKED" Server was asked to send / not send mission
6 "ROLE ASSIGNED" Role was assigned (and confirmed)
7 "MISSION RECEIVED" Mission received
8 "GAME LOADED" Island loaded, vehicles received
9 "BRIEFING SHOWN" Briefing was displayed
10 "BRIEFING READ" Ready to play mission
11 "GAME FINISHED" Game was finished
12 "DEBRIEFING READ" Debriefing read, ready to continue with next mission


2

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.
It was introduced in Operation Flashpoint: Elite on XBox and Armed Assault on PC.

A player that joined in progress will be referred to as JIP player.

To disable JIP on a server:
  • the server admin can disable playable slots on the role selection screen
  • the mission designer can disable AI within mission config
However, JIP remains available if respawn is enabled in the mission.

JIP Synchronisation

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:

Information Arma 1 Arma 2 Arma 3
date/time Checked Checked Checked
date/time + setDate/skipTime Unchecked Unchecked Checked
weather (overcast, fog) Unchecked Checked Checked
weather + setOvercast/setFog/setRain/setLightnings Unchecked Unchecked Checked
time passed since mission start (time command) unknown unknown Checked
publicVariable-sent variables (Number, String, Text, Array, Code) Checked Checked Checked
publicVariable-sent variables (nil) Unchecked Unchecked Checked
setVariable-assigned variables (when alternative syntax's public parameter is set to true) N/A Checked Checked
remoteExec- and remoteExecCall-executed code if JIP prerequisites are met N/A Checked
All the initialisation fields code get executed again for each and every JIP connecting player.
This means that code with global effect GEGlobal (such as setDamage) WILL be re-executed (depending on locality)!

Related Commands


See also


Structuring Multiplayer code

Responsibilities

  • The server initialises and works with variables
  • A connecting or connected player-client gets the mission data from the server
  • Once an objective is reached, the server tells the players via remoteExec/remoteExecCall, publicVariable or setVariable
  • Players can get their client-side UI code triggered by the server

See earlier chapter about how to target specific machines.

Server

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.
Consider the server as a powerful machine where you can put all the mission conditions and heavy scripting on. In case of a very heavy mission, the server can be assisted by Headless Clients (this involves a per-mission specific design).

Server-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.

Server-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).

Use allPlayers, BIS_fnc_listPlayers, playableUnits (and switchableUnits for SP compatibility) to refer to players.

Client

The Client must be considered as an average machine that can leave the game at any moment. No mission-vital code must be executed on its side and no decisions must be taken client-side.
Again, one must expect that any client can disconnect at any moment.

The client executes all the (local) UI code (Post process effects, Dialogs, etc.)
The player's machine should not deal with a great piece of code.

Do not forget that a server can be a player, and therefore should execute player code in that case.

The proper way to check for player-side condition is

if (hasInterface) then { (…) };

Note that the following example is incorrect:

if (isServer) then
{
	(…)
}
 else // WRONG: a server CAN be a player
{
	(…)
};


Code writing

The usual four steps of coding are the following:

  • Think it well
    • 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.
  • Make it work
  • Make it readable
    • name your variables properly, as if you had to give it to someone that should get it without the need of explaining
  • Optimise then
    • use more performance-friendly commands, reduce your search radius and loop frequencies once everything works.

See Code Optimisation - Rules for advices and more information.


See also


Testing locally

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:

loopback = true;	// force LAN-only server
kickDuplicate = 0;	// disable duplicate Arma kick

// needed in case of Headless Client test only
headlessClients[]	= { "127.0.0.1" };
localClient[]		= { "127.0.0.1" };
  • Run Arma 3 twice from Steam and connect each (with -showScriptErrors flag)
    • 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.
      Be sure to disable BattlEye or it will close any additional instances.
  • Try your mission and check for script errors:
  • To ensure all cases, your mission should ideally work properly:
    • in Singleplayer
    • in Player-hosted Multiplayer
    • in a Dedicated Server-hosted game
Of course, nothing prevents you to invite friends to play your mission and report bugs!


See Also