Continuing with MP coding, it is very important to understand the concept of object “locality”. So here are a few points to get started…

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. Server PC for example has owner id: 2, first connected human client has id: 3, etc.

Created objects belong to PC that created them. 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. For example, player object always belongs to PC of a person controlling it. A vehicle, like a car for example, can belong to any PC, depending on who is driving.

If you get in a driver 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 driver seat or you get disconnected from the server. In case of disconnect, the ownership will immediately be transferred to the server PC. Getting in passenger seat won’t change current ownership however.

Knowing 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 object. The actual changes to the object will have global effect and will be broadcast to every PC.  So if you want to change amount of fuel in a vehicle, you have to know what PC owns it first, then execute setFuel command on that PC only, which then will automatically propagate changes over network and will update all connected PCs.

Because of that, as you may have noticed, some commands on BIS wiki have locality information present on their respectful pages.

AL Command arguments must be local. If the argument is my player unit, I can only issue the command on my PC as on other PCs my player object is REMOTE so it won’t work.

AG Command accepts global arguments. If the argument is a vehicle, I do not have to find the PC that owns it, I can just reference it from anywhere.

EL The effect of the command is local, i.e the effect is limited to the PC that executed the command and will not be broadcast.

EG The effect of the command is global, i.e the changes will be broadcast to and happen on every PC, and often on JIP PCs too.

So let’s have another look at out setFuel command. Command arguments (vehicle in this case) must be local, so it has to happen on the PC that owns the vehicle. The effect of the command is global, meaning all instances of this vehicle on other PCs will be updated with new fuel amount automatically.

To know if an object is local to a PC you run your script on, use local command. It will return true if the tested object belongs to this PC. Converting object to string will also reveal object locality. Non local objects will have REMOTE suffix. To demonstrate this I hinted the object representing my player on the server. Since player is always local to my PC the server variant of it will contain REMOTE suffix:

REMOTE

Arma 3 also has a handy event handler “local”, which will fire as soon as the object it is assigned to changes locality. For example when you spawn a vehicle on the server then jump in the driver seat. Vehicle ownership will change from the server to your PC, the vehicle will become local to your PC and the event handler will fire.

“Local” event handler was used in DayZ Mod in Arma 2 in order to track client zombies to make them continue to exist on the server if client disconnected. This attempt was later abandoned by the creator but on our private DayZ server I have made a few major changes to the leftover code and it worked the treat.

troll

Players who got used to coward Alt+F4-ing to escape zombies were for a pleasant surprise when they got greeted by a bunch of aggroed zombies, patiently awaiting the player return on reconnect!

Anyway, the correct procedure for finding ownership of an object would be:

  1. Find out if the object belongs to you already by using local command
  2. If the object is not local, find out who owns it. This can only be done on the server PC. Only server knows which PC owns what
  3. Send request to the server with the object in question
  4. Check if the object is local to the server, in case the server PC owns it
  5. If the server is not the owner, you can ask the server who is the owner by executing owner command on the server
  6. Execute your desired command on the PC that owns the object

And this is how it would look in code if we were to make setFuel global:

//init.sqf KK_fnc_setFuel = { private ["_veh","_fuel"]; _veh = _this select 0; _fuel = _this select 1; if (local _veh) then { _veh setFuel _fuel; } else { PVsetFuel = _this; if (isDedicated) then { (owner _veh) publicVariableClient "PVsetFuel"; } else { publicVariableServer "PVsetFuel"; }; }; }; "PVsetFuel" addPublicVariableEventHandler { (_this select 1) call KK_fnc_setFuel; };

There is another way, though not completely optimal as it leads to increase in network traffic. You can simply have all clients including the server waiting for a broadcast and then check locality of the broadcast vehicle on every PC. Only one PC will own the vehicle and execute setFuel. If in the previous code example there would be a maximum of 2 extra network calls made at any time to find the owner, in the next code example the maximum extra amount of network calls could be equal to number of players on the server.

//init.sqf KK_fnc_setFuel = { private ["_veh","_fuel"]; _veh = _this select 0; _fuel = _this select 1; if (local _veh) then { _veh setFuel _fuel; } else { PVsetFuel = _this; publicVariable "PVsetFuel"; }; }; "PVsetFuel" addPublicVariableEventHandler { _veh = _this select 1 select 0; _fuel = _this select 1 select 1; if (local _veh) then { _veh setFuel _fuel; }; };

The second example will run fine on both dedicated and player hosted servers. In both cases you can call KK_fnc_setFuel from any PC, for example:

[vehicles select 0, 0.5] call KK_fnc_setFuel;

Enjoy,
KK