Greetings! I’d like to express my gratitude to a number of people before I start, and also apologise for being late with my appreciation, as I haven’t checked my PayPal in awhile… Big thanks to Daniel!
u r fucking awesome! thx alot for ur “real_date.dll v3.0” its far aways from my skills… thx again and greetz from Berlin 😀
You’re awesome too! Big thanks to Michael!
Hey man. It’s not much, but your tutorials and guides have given me a lot of my information in the world of ARMA. I am new to the ARMA world and it’s a unique kind of beast. I am an engineer for IBM and work with a lot of complex software and ARMA is still somewhat of a beast to work with…. Either way, thanks for your awesome guides and keep up the great work!
Yeah man, there is so much more in Arma I want to learn too :). Also big thanks to George and Richard! Thank you all guys, you make it worthwhile.
To continue my previous efforts, I meant to address this for quite sometime. I think anyone who tries to make an MP mission should understand how unit “init” works and what are the consequences when used without caution.
Any object such as a unit or a vehicle placed in editor will be saved into mission.sqm file as a class. This file will be read by every client connecting to the server including server itself. Objects saved in mission.sqm will then be created once on every connecting to the server client including server itself. Basically, every client will have local copy of objects saved in mission.sqm. These copies will all be syncronised across network with one master copy, but only one PC will have control over this master copy, only one client will “own” it. Hence the terminology “local” and “owner”, that also happen to have corresponding SQF commands.
The editor also allows mission maker to perform additional INITIALIZATION of an object, by providing editor form field where mission maker can easily enter some scripting commands. This field will save in mission.sqm as “init” param and the code in it will be executed when the local copy of the object is created on connecting PC. Let’s call it object “init” for simplicity.
Arma fans have been using this object “init” for ages. It has many advantages. One doesn’t need to know scripting language in order to copy/paste some code in “init” field. It is convenient and easy to edit and many used this for changing default loadout on units added in editor. In SP one can put any command in “init” and not worry about the locality issues, since everything is local in SP. Even in MP, prior to Arma 3, this technique was working just fine, due to specifics of old inventory commands.
In Arma 3 inventory got changed, as well as how it is handled in MP. There are many many many new commands for it and they are somewhat different from old commands, even the commands with the same name in Arma 3 may behave differently. I’m trying to keep track of the differences on Arma BIKI, but the developer does not always announce major changes in functionality, which makes it rather difficult.
In order to explain how this works in Arma 3 I am going to use an example. Say I want to strip a player of all weapons, uniform, vest and default backpack and give him a handgun, 3 clips and empty backpack to start with:
Maybe I want the player to find the rest of the gear on the map instead of having everything from the start. Adding a few scripting commands in the “init” field in the editor should do the trick, right? This is how it saves in mission.sqm:
Now let’s examine the code I added:
Commands in black are commands added in Arma 3, commands in blue are commands that have been available prior to Arma 3. I wrote a comment next to each command about command locality -> arguments and effects. These can be found on BIKI command pages as well. Argument means (in this case) the entity to which command will be applied. When argument is “local”, the command needs to be executed on the client that “owns” the entity, i.e. on client with the master copy. If argument is “global”, it doesn’t matter where you execute your command. Effect indicates if the changes made by the command execution occur on one client (local) or on all clients (global).
For example, removeUniform is arg:global/eff:global. This means it can be executed on any client regardless of who “owns” the entity and the effect of uniform removal will broadcast over network, so it will be removed from all copies of the entity on all clients. Backpack command, removeBackpack, on the other hand, has arg:local/eff:global, meaning that the command has to be executed on the client that “owns” the master copy to remove backpack from every client. If command is executed elsewhere, nothing would happen.
Now, before I continue with this example, I need to mention how player joins a server. 1st thing that happens is player avatar is created on the server. It is empty and server “owns” it. Then the server assigns some random default AI identity to the avatar, so it has face, voice and name. The avatar’s “init” is executed, if there is one. At this point server already knows for whom this avatar is prepared. Copies of the avatar are created on all connected clients including player’s client. When the avatar initialisation is finished on player’s PC, player identity gets moved into this avatar, at which point server transfers the ownership of the avatar to player’s PC, and player’s copy becomes the master copy.
As you can notice, when avatar’s “init” runs on player’s PC, it is still the server that “owns” it. So if “init” contains any commands that have arg:local/eff:global, they will successfully execute on server but not on client. And that goes for other connected clients too, these commands will not work there, as avatar will not be local to any client. This is one of the reasons in pre Arma 3 it was quite safe to use inventory commands in “init”.
Now let’s get back to the example. So I join the server. All inventory tweaking is done to my avatar even before I get to move in. So I’m in the game, I have a backpack, a gun and 3 clips. I move around and find a uniform and a helmet. Hurrah! I put it on. Some other player joins the server, and I instantly lose my newly found pants and helmet but gain 3 more clips in my backpack! What just happened?
When another player joins the server, he gets a copy of my avatar created on his client. As “init” is not empty, this “init” runs on another player’s PC during my avatar’s copy creation on his PC. All arg:local/eff:global commands in “init” fail because he doesn’t own my avatar, but all arg:global/eff:global commands successfully execute. This means my uniform and helmet get removed and 3 clips are added to my inventory. And this will happen to me again when another player joins the server and so on and so forth with every new player joining.
Is this a bad design? Not really. arg:global commands allow operations on remote units. This means that at any point in the game one can change remote unit’s inventory from the server, for example, even though the server doesn’t “own” the unit. Of course this could also be done from another client not just server. While it allows more flexibility to a mission maker, it is not very secure in case someone gain access to unauthorised script execution on their client. Allowing arg:global on server only while leaving it arg:local on clients for the same command, would have solved 2 problems, additional security and safe “init”. Not sure if this is ever going to change.
But for now there is a workaround for “init” problem. If one restricts the “init” execution explicitly to the server, it is guaranteed to run once only at the moment of unit creation on the server, thus eliminating any duplicated runs. This is how my code should have looked in the first place:
!isServer means any client that is not a server, which excludes both, dedicated server and hosted server. So when another player joins the server, my “init” code will abort immediately on his PC because he is not a server and the code won’t run again on the server because a copy of my avatar has already initialised on the server before. Win win. And as you can see it is not a big deal as long as you know what is going on.
Of course a better solution would be to use init.sqf, initPlayerLocal.sqf, initPlayerServer.sqf and onPlayerRespawn.sqf scripts as well as CfgFunctions to have better control over your code. But for now…
Enjoy,
KK
EDIT: Alternatively, instead of if (!isServer) exitWith {}; you can use if (!local this) exitWith {}; Both isServer and local this are true during unit init on the server, but I’ve had reports that one works better then the other. Also noticed BIS started using local over isServer as well, maybe they know something too?