Lesson 3 - Locality

Locality is one of the most important topics in Arma 3 coding and one of the hardest to grasp for new Arma developers. I know this lesson is a long one, but it is somewhat complicated and I felt it was best to be covered in a single lesson. The lesson is separated into sections that you can read them separately as long as you understand the dependency of all the parts on the whole. You may have heard the term locality in relation to variables, this will not be covered in this lesson but will be covered in the future. This lesson is only covering the locality of objects in relation to Arma commands and coding. This lesson is a conglomerate of information from the references at the bottom of the lesson.

Introduction to understanding Locality -=- Section 1
When scripting for MP you must always, with every script, function, or command, keep in mind which machine(s) will be running the code and the locality of the arguments and effects.

Arma 3 does not have a strict client-server architecture. Instead, all clients including the server run code, both mission-specific user-created code and internal engine code. Although the concept of locality can be difficult to understand, if you keep in mind that we only need to determine the owner of an object or entity and that the owner will affect the execution of commands, you will be ahead of the curve.

I think the main reason that locality is difficult to understand is that developers, when testing their code, will often get different results when testing on their computer, in single-player or multiplayer hosted server versus when testing on a dedicated server. Even though a dedicated server is multiplayer, a multiplayer hosted server using the same Arma executable as a playing client is not a dedicated server. We refer to “clients” as those machines that run a player’s game, and “server” as the machine that hosts the game. On a hosted game, one client will be identical to the server, but on a dedicated host, this is not the case. For this lesson and locality in general, you need to remember that the server is just another client, but I will refer to it as its own entity, “Server”, for clarity. This gets even more complicated with headless clients (HC), but as NAK does not use headless clients at this time, we will not go into detail on HC’s, just be aware that a HC is just another client that connects to a dedicated server. A HC is often run on the same PC as the server but uses a separate Arma executable, therefore it is a separate client instance.

Before we get into the specifics of locality we need to provide a couple of definitions:

  • Object - In general the term object refers to the representation of an object (soldier, house, crate, vehicle, etc) that can be manipulated when in the 3D environment via the scripting language.
  • Owner - Each object in-game has an owner (with exception of map embedded objects). An owner is a connected PC. The server is also a connected PC so it can also be an owner. Each owner has an id. The Server PC for example has owner id: 2, the first connected human client has id: 3, etc. Created objects belong to the PC that created them, at least initially. Map embedded objects belong to everyone and no one and have owner id: 0. Some objects can change ownership back and forth while some never change. The player object always belongs to the PC of a person controlling it. A vehicle, like a car, for example, can belong to any client or the server, depending on if there is currently a driver and if there has been a driver.
  • Remote - If an object is owned by another client or the server it is “Remote” to my client.

Knowing the locality of an object is very important in multiplayer because there are commands that will only work if executed on the PC that owns the object. For example, commands like setHit and setFuel must be executed locally, i.e. on the PC that owns the vehicle object. So if you want to change the amount of fuel in a vehicle, you have to know what PC owns it first, then execute the setFuel command on that PC only. Arma 3 does provide methods to access a remote object, which will be covered in Section 4.

Switching Locality
If you get in the driver’s seat of a car, you “own” it and the ownership of the car gets transferred to your PC. If you jump out, it still belongs to your PC unless someone else gets into the driver seat or you get disconnected from the server. In case of a disconnect, the ownership will immediately be transferred to the server. Getting in the passenger seat won’t change current ownership, however.

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, unlike AI, in a player-led group will remain local to its computer (see bullet point #1).

OBJECTS:

  • a driven vehicle is always local to the machine of its driver (not the commander or the gunners).
  • 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.
  • on NAK servers when missions are created they are created on the server. Therefore all mission created units and vehicles are local to the server.

Switching Locality

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

Arguments and Effects -=- Section 2

Because knowing the locality of an object is so important to Arma 3 coding, as you may have noticed, some commands on the BIS wiki have locality information present on their pages. This locality information is broken down into Arguments and Effects, both of which can be Local or Global.

Argument Local

:
If a commands Arguments are Local, ‘AL’ e.g. setFuel, it means is that the object (in this case a vehicle) has to be local to the machine issuing the command, whether server or client.

Argument Global

:
If a commands Arguments are Global, ‘AG’ e.g. driver the command accepts global arguments. The argument object does not need to be local (but can be) to the machine the code is running on. If the argument is a vehicle, the PC that owns the vehicle does not need to be found, the code can reference the vehicle and run on any PC.

Effect Local

:
If a commands Effects are Local, ‘EL’ e.g. createMarkerLocal The effect of the command is local, therefore the effect is limited to the PC that executed the command and will not be broadcast, only the PC that ran the code will see the effect. In the case of createMarkerLocal only, the client that ran the code would see the new marker.

Effect Global

:
If a commands Effects are Global, ‘EG’ e.g. setFuel, the effect of the command runs on all clients and the server, i.e the changes will be broadcast to and happen on every PC, and often on PCs that connect after the command is run, join in progress (JIP).

Server Execution

:
You will find a few scripting commands that are required to be executed on the server ‘SE’ in MP. These Server Execution scripting commands will either not function or return an error when executed on a player client. For example setFog has a Global Effect, but must be run on the server.

So let’s have another look at out setFuel command. The setFuel command arguments (vehicle in this case) must be local, so the code using the setFuel command has to run on the PC that owns the vehicle. The effect of the command is global, meaning this vehicle on all clients and server will be updated with a new fuel amount automatically.

Common Mistakes and misconceptions -=- Section 3
First, remember that locality is not about where the code is running but where the code needs to run in order to function. Many new developers believe that locality informs the server to run code on a specific client, this is not the case (This may be due to the debug console with the options to run code on the server, globally or locally). Code is run on the PC that triggers the code. Some triggers are event scripts. Event Scripts are scripts that are executed upon specific events. To use an Event Script, a file is created with a specific name in the mission directory. The following are some of the common event scripts and on which PC they run on:

  • initServer.sqf is executed only on the server when the mission is started.
  • initPlayerLocal.sqf is executed on the client PC when a client joins.
  • initPlayerServer.sqf is executed on the server when a client joins.
  • init.sqf is executed on each on mission start or mission join, local to each client or server running the code.
  • onPlayerRespawn.sqf executes on the player’s PC when they respawn.

Other examples of triggers that can cause code to run are actions taken why the mission is running, some examples of mission triggers are ‘Eden triggers’, displayEventHandlers, UI events, object event handlers, mission event handlers, and Action Menu selections.

Common Mistakes with Global Effect commands
It is vital to observe when a commands Effect is Global, the effect takes place every time the command is executed. Take the following example of an init.sqf script from a fictitious mission. We will assume there is a car in the mission called “NAK_playerCar” placed on the map, and you want to fill it up with 4 MX rifles and 10 magazines. init.sqf will run once for each client and the server.
/* init.sqf */
NAK_playerCar addWeaponCargoGlobal [“arifle_MX_F”,4];
NAK_playerCar addMagazineCargoGlobal [“30Rnd_65x39_caseless_mag”, 10];If you paid attention, you will immediately see what is wrong here. init.sqf is run at startup on all clients and the server. With the two lines of code we wanted to add 4 MX rifles and 10 magazines. But since the effect is global, something else entirely will happen.

  • On a hosted game with only a single player, or single-player for that matter, the effect will be as intended – there will be 4 rifles and 10 magazines.
  • On a dedicated server with a single player, there will be 8 rifles and 20 magazines.

Since the script runs on all machines, and the effect of the command is global, addWeaponCargoGlobal will add four rifles on all machines that connect. On a busy server, that vehicle will end up with a lot of rifles and magazines.

Common Mistakes with Local Effect commands
Obviously, one of the most serious issues that can occur with local effect commands is that they only have an effect on a single machine. createSimpleTask is an example of a command that takes a global argument but only has local effect. When creating tasks dynamically during the mission, if you use createSimpleTask it would fail to show the task for everyone unless the command is properly executed on all clients.

Likewise, certain commands that seem to be working correctly when you test the mission in single-player or on a hosted environment will fail to work on a dedicated server. For instance, if you want to override a vehicle’s color, you could use the setObjectMaterial command. You could run the command from the init.sqf or even in the vehicles Init. When the code is tested in single-player or a hosted server, it would appear as if the code ran correctly. The vehicle would be local to the server but in both SP and MP hosted my client and the server are the same. If you were to run the same code when a player gets into the vehicle, if the player is the driver, it would only run on the computer where the vehicle is Local and though the driver would see the color change, no one else would.

The other big mistake I see is when developers use the hint command for debugging or other purposes. Looking at the BIS Wiki you can see that hint has a local effect. Only the client or server that runs the code will see the hint. If testing in a SP or MP hosted server, if the code runs on the server you will see the hint just fine, but running the same code on a dedicated server and the hint would only be seen by the server.

How to avoid mistakes -=- Section 4
Arma 3 has multiple commands that you can use to help with locality and issues surrounding locality.

local
The first command is ‘local’ and its inverse ‘not local’. Local checks if a given unit is local on the computer. This can be used to determine which computer some code must be run. We will continue to use setFuel as an example. If we wanted to make sure that the code would only run on the correct client PC we would use:
if (local _veh) then {
_veh setFuel 1;
};This code would only run when the vehicle ‘_veh’ is local.

owner
Another command helpful when dealing with locality issues is the ‘owner’ command. The owner command is one of a handful of commands that will only run on the server as indicated by the Server Execution flag in the BIS Wiki. If you are running code on the server and need to find the owner of an object you can use ‘owner object’, which returns the machine network ID of the client to which the object is local.

setOwner
Similar to owner ‘setOwner’ must be run on the server and can be used to change the ownership of an object to a given client. For example, if you needed to set the owner of a vehicle created by the server to a specific client you could use ‘object setOwner clientID’.

Testing where code is running
We mentioned before “locality is not about where the code is running but where the code needs to run in order to function.”, but if you are unsure where a piece of code is running there are some commands that you can use.

isDedicated
Returns true if the machine (executing the command) is a dedicated multiplayer server. In single-player returns false.

hasInterface
Returns true if the computer has an interface (a real player). False for a dedicated server or a headless client.

Testing locally with a dedicated server
If a piece of code is intended to be run on a dedicated MP server, to properly test scripting locality issues, it is recommended to run a dedicated server and connect with at least 1 client if not 2. In order to do so in your servers server.cfg set:
kickduplicate = 0;
BattlEye = 0;In a future lesson support document, complete instructions will be provided to run a dedicated server and 2 clients all from the same computer. In the meantime information can be found online.

RemoteExec
The remoteExec command will be a lesson into itself in the future, but it does need to be touched on in regard to locality. If you know your code is not running local to the owner of an object and it is required, you can tell a client or the server to execute a scripted function or script command on a given target PC.

If from the initServer.sqf which we know from before is executed only on the server when the mission is started we wanted to send a hint:
// runs hint “hello” everywhere but server and with run the hint for everyone that joins after the mission starts.
“hello” remoteExec [“hint”, -2, true];
Or from a player interacting with a sign on the server we want to send a hint message to the player in the vehicle named ‘theA164’ who is in a group named _pilots.
First create a simple function
NAK_fnc_hintMyA164 = {
private [“_veh”,“_message”,_ReqName];
_veh = _this select 0;
_message = _this select 1;
_ReqName = _this select 2;
if (local _veh) then {
hint format [“%1 + _message”, _ReqName];
};
};Then we call that function with remoteExec to run it on all clients in the group ‘_pilots’, we do not have to run it on everyone because in this case, we know the player in the A-164 has to be in a specific group.
[“theA164”,“wants that A-164”,(name player)] remoteExec [“NAK_fnc_hintMyA164”,_pilots]
Another option would be to use remoteExec and set the target of the remoteExec to the server only. Then in the code use ‘owner’ to find the player that is the owner of the A-164. Then lastly send a hint to that owner from the server using remoteExec.
There are very often multiple ways to write your code. Although the second option maybe a little faster, it is generally better to write your code in a way that is easier to read and debug than it is to choose the faster code. There are exceptions to this rule and we will touch on code optimization in a future lesson.Conclusion -=- Section 5
I know that this lesson touched on many issues, but if you take away the following you will be in good shape in the future.

  • Knowing the locality of an object is very important in multiplayer because there are commands that will only work if executed on the PC that owns the object.
  • When testing code, the locality of an object in SP and MP hosted will be different than on a dedicated server.
  • Locality of an object can change as the mission is played.
  • A player’s unit is always local to the player’s machine.
  • There are multiple ways to test the locality of an object
  • If you are writing code for a dedicated server you should test your code on a dedicated server.
  • There are multiple ways to test where code is running.
  • Code can be run from one machine on another using RemoteExec.

Feel free to ask questions and comment.

REFERENCES

https://community.bistudio.com/wiki/Multiplayer_Scripting#Locality

https://community.bistudio.com/wiki/remoteExec

wonderful insight!