Jump to content
Sign in to follow this  
NickThissen

Scripts in co-op multiplayer missions

Recommended Posts

Hi,

I have built some co-op missions and for the first time I'm using scripts to get some more advanced stuff going. Reading some snippets here and there I came to the conclusion that there is some difficulty with scripts in multiplayer environments, especially locality or on which client the scripts are run.

I've been looking for some more information on this but have not found anything helpful. Only a handful of forum posts that deal with one specific example or unrelated problems, I can't get much further than finding out that there are difficulties, but not exactly what they are and not even close to how to deal with them.

Is there any documentation or tutorial or something about multiplayer scripting, specifically in co-op missions?

I have a few questions that I'd like to get answered, if they are answered in such a document / tutorial I'll gladly read that, but I can't find one...

1. I've read that scripts are not automatically transfered to a MP version of the mission when you use Save As - Export to Multiplayer. I always write scripts in separate .sqf files which I place in the 'Documents\Arma 3\missions\<mission name>.<map>' folder. There is also a MPMissions folder but it's always empty, do I need that? So far I only play co-op missions with friends where one of us hosts the server locally (so not a dedicated server I believe), that has worked fine for missions without scripts, but I have not tried any custom missions with scripts yet.

So: do I just put the scripts in the 'missions' folder (that works fine when I test locally) and use the regular Save As - Export to Multiplayer function? Or do I need to do more?

2. That brings me to question 2: how do I test this properly? I have been testing by simply using 'Preview' and sometimes by hosting a local lan server with my mission, both seem to work fine. But I'm always alone in testing; I don't want to spoil the mission for my friends by testing every element in it with them because then we don't need to play it anymore ;)

3. When I call a script from a trigger (execVM in the onAct field), does this script fire on the server? Local to the player that triggered the trigger? Or for everyone separately?

4. What are some examples of things that could go wrong when not keeping MP scripting in mind? The only thing I can find is "for example" (it's always the same example) if you spawn vehicles with createVehicle, without using the "isServer" check, that it would spawn a vehicle for every client (and the server). Are there others? A specific example: I have a enemy helicopter that should get damaged from AI teammates (there aren't any AI teammates, I just script it that way to look like that). I wanted the helicopter to get damaged at a random time, but then I read that 'random' is local to each client. So suppose I wait for a random time and then damage the helicopter (via setDamage or setHit or whatever). Each client gets that command at a different time. Would that mean the helicopter gets damaged multiple times, at each random time that each client decided? Or is it only the shortest random time that determines when the command is executed (but then it is not executed again for each other client)? Things like this don't make sense to me; on one hand I read that these things are local to each player, but how can something like damaging a helicopter be local to each player. It's either damaged or not, surely it won't happen that one player sees a flaming wreck on the ground while the other sees a happy chopper flying around???

Well I'm tired of typing but have a lot more questions so I was hoping to get some good resources where I can learn it for myself... Thanks for any help!

Share this post


Link to post
Share on other sites

Resources: http://forums.bistudio.com/showthread.php?162048-Arma3-%28beta%29-Editing-Resources-thread-links-I-ve-compiled

Multiplayer scripting can be a very difficult process to understand. The most important aspect is locality... ie knowing which things need to run on the server (spawning enemies, creating objectives) and which things need to run on the clients (nametags, gear scripts etc)

1) If your SQF files are in the mission folder, exporting the mission WILL include them. MPMissions is where the exported mission end up as PBO's (kind of like zip files)

2) Preview will test fine, however it will not test for locality issues such as multiple createVehicle instances.

3) This will fire the script from all players (including the server) however you can just run the isServer or isPlayer checks inside the script file to limit which machines actually run the code.

4) Vehicle Service, CreateVehicle, 3D sound sources, tasks

Share this post


Link to post
Share on other sites

1)

There is no difference in where you put the scripts. The difference is how you script. In MP you need to keep track of on which machine each script is running. You also need to know how each command impacts the machines in the network. For example, if command createVehicle is executed on one machine, a vehicle will be created on all machines. So scripts that creates vehicles may be a good practice to run only on the server. As an opposite example, if the command createTrigger is executed, a trigger will only be created on the machine that executes the command (and the other machines will not have that particular trigger at all).

2)

Best way i guess is to get another copy of the game. Or maybe borrow a license key from a friend for test purposes only. To test server client code execution you ca set up and join a dedicated server on your developer machine. Check out Tophe's TADST for Arma 3.

3)

It will fire local to the player that has the trigger. However, if the trigger is added in editor, it is not actually "one trigger". It is "one trigger per each machine in the network". If you place a trigger in editor that creates a vehicle using command createVehicle when the leader of the group comes near, there will be as many vehicles created as machines in the network. That's because the trigger fires on all machines at the same time and createVehicle has "network global" impact. If the same trigger (or actually triggers) instead shows a hint (script command "hint") which has "network local" impact, it is shown once for all connected players. If you think of a trigger created by a script instead of placed in the editor, and that trigger is created on only one machine, it may be a little easier to understand. Then it is more obvious that the script is fired on the current machine only. One more thing, in an editor placed trigger, if you enter condition "isServer", then the trigger will exist on all machines, but there is only one machine that it fires on, and that is the server.

4)

Personally I think of four kinds of "locality categories": 1. Obvious global commands, 2. Local commands that should be obvious global commands, 3. Commands that you don't know are local or global, 4. Obvious local commands. An obvious global command (1) is the familiar example "createVehicle". It simply makes no sense that a tank would exist on one machine but not on another. The mission would be broken, so BI has designed the command for global impact. Local commands that should be global (2) are commands that BI in my opinion need to fix. In more previous versions of Arma one and the same soldier could look east on one machine and west on another. That is one thing that they have fixed. In Arma 2 OA (I don't know about Arma 3) i had a friend that I could see just walk through the walls of a house. It appeared that the house on his machine was razed, but on my machine it was not. A command that is local but you don't know (3) may be the "setRain" command. How come it sometimes rains for me but not for my friend sitting two meters from me in the game? However if the map is 10 km wide it is certanly possible that it rains in one end of the map but not in the other. So this is up to the mission developer to have the correct weather behavour. An obvious local command (4) is "hint". If hint is executed on one machine, why should the same message show to others, and who would that be? Players in the same group? On the same side? Everyone?

You may wonder, how do I know about the locality for commands? Here are your available scripting commands:

http://community.bistudio.com/wiki/Category:Scripting_Commands_ArmA2

http://community.bistudio.com/wiki/Category:Arma_3:_New_Scripting_Commands_List

Most of the commands have documentation regarding MP scripting. For example, look at command "setVariable" on the following link, and note the Additional Information section and the header Multiplayer.

http://community.bistudio.com/wiki/setVariable

Regarding your helicopter script. I would solve it by letting the server, and only the server, control the helicopter with a script. Where I execute the script, I would test using command isServer, to make sure that the script is not run on any other machine than a dedicated or hosted server. The script can then pick a random time to the helicopter to "be hit". I would assume that commands "setHit" and "setDamage" has "network global" impact (the above category 1) since it makes no sense to have a burning heli for one player but not all. The assumption is correct then it will work, and the helicopter will fall apart for all players at the same time. If the assumpsion is wrong and the commands are in the category 2 above, I need to fix it myself in some way. Luckily category 2 is not so common any more, and since the documentation says nothing about Multiplayer (http://community.bistudio.com/wiki/setDamage) it will probably work as expected (i.e. setDamage is in category 1). But if in category 2, I need to have communication between the machines (command publicVariable) to somehow make it happen on all machines at the same time.

Another hint on the locality subject and script execution. Your init.sqf file runs automatically on all machines when mission starts (or for a JIP when he/she enters). Use the following code to separate server and client code already from start. This code will have the file ServerInit.sqf execute only on the server (hosted or dedicated). And the script ClientInit.sqf will execute on alla machines that are a client (including a hosted server which is also a client). Maybe ServerInit.sqf is a good file to have your helicopter script executed in?

if (isServer) then {
   execVM "ServerInit.sqf";
};
if (!isDedicated) then {
   execVM "ClientInit.sqf";
};

Edited by Engima

Share this post


Link to post
Share on other sites

Don't know if mentioned but most biki articles have two icons which shows if commands needs to be executed locally or not and if their effect is local/global

Share this post


Link to post
Share on other sites
1. I've read that scripts are not automatically transfered to a MP version of the mission when you use Save As - Export to Multiplayer. I always write scripts in separate .sqf files which I place in the 'Documents\Arma 3\missions\<mission name>.<map>' folder. There is also a MPMissions folder but it's always empty, do I need that? So far I only play co-op missions with friends where one of us hosts the server locally (so not a dedicated server I believe), that has worked fine for missions without scripts, but I have not tried any custom missions with scripts yet.

So: do I just put the scripts in the 'missions' folder (that works fine when I test locally) and use the regular Save As - Export to Multiplayer function? Or do I need to do more?

2. That brings me to question 2: how do I test this properly? I have been testing by simply using 'Preview' and sometimes by hosting a local lan server with my mission, both seem to work fine. But I'm always alone in testing; I don't want to spoil the mission for my friends by testing every element in it with them because then we don't need to play it anymore ;)

3. When I call a script from a trigger (execVM in the onAct field), does this script fire on the server? Local to the player that triggered the trigger? Or for everyone separately?

4. What are some examples of things that could go wrong when not keeping MP scripting in mind? The only thing I can find is "for example" (it's always the same example) if you spawn vehicles with createVehicle, without using the "isServer" check, that it would spawn a vehicle for every client (and the server). Are there others? A specific example: I have a enemy helicopter that should get damaged from AI teammates (there aren't any AI teammates, I just script it that way to look like that). I wanted the helicopter to get damaged at a random time, but then I read that 'random' is local to each client. So suppose I wait for a random time and then damage the helicopter (via setDamage or setHit or whatever). Each client gets that command at a different time. Would that mean the helicopter gets damaged multiple times, at each random time that each client decided? Or is it only the shortest random time that determines when the command is executed (but then it is not executed again for each other client)? Things like this don't make sense to me; on one hand I read that these things are local to each player, but how can something like damaging a helicopter be local to each player. It's either damaged or not, surely it won't happen that one player sees a flaming wreck on the ground while the other sees a happy chopper flying around???

1. Everything in the mission folder is exported when you export it using either of those options.

2. For pretty much everything except locality issues, you're fine to test it on your own. For anything which you think needs to be tested with multiple players, find a buddy who you can trust not to spoil things. Ideally, one who is also into scripting, etc.

Also, it's only slightly related, but I find that MCC Sandbox is a great tool for testing missions.

I am not experienced enough to help you with either 3 or 4.

Share this post


Link to post
Share on other sites

I'm not sure that's completely true, Iceman. The basics of it are pretty well explained, in the link you posted and here. Certainly some people understand things better when they read them, some understand better when they do them, different people, different ways of learning. Stating that explaining it is impossible seems wrong to me.

There really is nothing complicated in the basics of locality, it's always the implementation that can get a bit tricky when you're entering more complex systems.

The general idea is to always have in mind what scope you're working in: if dealing with a unit, where is that unit local. If dealing with a script, where is my script executed. Then, as cuel mentioned, keep the biki opened in a tab and regularly check where the commands I need to use should be executed from relatively to that particular scope.

BIS_fnc_MP is way better than the MP_framework from A2 and allows to easily handle any locality issues by executing code where you need it to be executed. Example: I want one vehicle to change texture ten minutes in the game.

Obviously you don't want each player to have its own countdown, as it would result in everybody having the texture change at a different time, depending on who loads first, etc.

So you make it server-side only:

if isServer then { sleep 600;

On the other hand, now you have a problem as setObjectTexture as th AG (Global Argument) and EL (Local Effects) icons: It means that you can change with that command the texture on a remote object (not handled by the player's computer), but this needs to be executed on your computer for you to see it.

So you need to execute the actual texture change on everybody's computer.

[vehicle, TAG_fnc_changeTexture, true, true] spawn BIS_fnc_MP;
}; 

where vehicle is the object you want to change, the first true meaning everybody will execute that code, the second meaning it will be executed by JIPs as well.

Now you need to define that TAG_fnc_changeTexture function.

TAG_fnc_changeTexture = {
if !hasInterface exitWith {};    // on computers that don't have an interface (a dedicated server or an Headless Client), the script is aborted: we don't need them to change a texture they cannot see or display.
_veh = _this;		//retrieves the parameter you gave it in BIS_fnc_MP, the vehicle
_veh setObjectTexture [1,"\pboname\texture2.paa"]; //Do whatever you needed to do locally
};

Now remember, that function will be executed by everybody, so it needs to be defined everywhere: the easy way is to stick it in the init.sqf, as you know it will be executed by every client.

Obviously this is a very basic example, but at first, I believe this is the way to go, and you should repeat that analysis for every script command you use.

Then, when stuff is working, you can start optimizing all this, mostly by reducing as much as possible the amount of times you need to "artificially" switch locality (using BIS_fnc_MP).

Share this post


Link to post
Share on other sites

For sure. Bis_fnc_mp is great. I don't even see why anyone would ever use the old framework.

Share this post


Link to post
Share on other sites

Thanks all, that's quite helpful actually. It seems the best course of action for my mission currently is to let every script run only on the server and abort it otherwise.

The only problem I then still have in my current scripts is things like hints; they are all only executed on the server right now so that means nobody would ever see a hint. However, in local testing I do see the hints. Is that because I am the server at that point? Or is it more complicated? (I know the solution is just to move the hint commands above the 'abort if not on server' check, just checking my own logic).

---------- Post added at 18:02 ---------- Previous post was at 17:49 ----------

I have fixed some of my scripts, I believe. But perhaps one more relevant example then...

In my mission a trigger is fired when two stationary vehicles are destroyed (condition !alive ...). The trigger fires a script. The script should do the following:

1. Hint/sidechat some text

2. Wait a while

3. Hint/sidechat more text

4. Wait a while

5. Hint/sidechat more text

6. Spawn enemy units

7. Call the next script

Obviously the first 5 commands should be issued on every client so that everyone sees the hints. After that, I have the 'isServer' check so the rest is only executed on the server. The AI units are spawned with createUnit which I am assuming is global so they should spawn for every client (even though only executed on the server). The next script (step 7) involves spawning a friendly helicopter extraction and (besides spawning the vehicle) it issues some hint commands.

The problem is that in step 7 there are also hint commands. They will now not be seen because the script is only called on the server? Is that correct? Should I then use BIS_fnc_MP to show the hints on every client?

Share this post


Link to post
Share on other sites

For a global hint, look into BIS_fnc_MP

Set up a function in init.sqf to call from your server-side scripts

TAG_hint = {
   hint _this;
};

Your server scripts would use something like this

["Hello, world","TAG_hint",true,false] spawn BIS_fnc_MP;

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×