Armed with the knowledge above, the rest of this guide will try to give advice on how to handle common situations. This is by no means complete, and some of it might not be the optimal solution, but so far, it has (mostly) worked for me.
As mentioned above, a good number of scripting commands related to briefings and tasks are local effect only. Obviously this is a necessity, since you do not always want all players/groups/sides to have the same briefing. However, this means that getting a briefing that works reliable will have its challenges.
Luckily, there is a very easy solution to this issue: Use a task tracker. I am not trying to promote FHQ Task Tracker here, use whatever you like. FHQ and Shuko’s are two example of working task trackers that will make the creation, both statical and dynamical, of briefings and tasks so much easier. Both of them are robust and proven to work. Really, there is no reason whatsoever to reinvent the wheel – use a task tracker for briefings, and save yourself a lot of headaches.
As we have seen above, triggers might be tricky to handle correctly, especially if you do not want them to behave erratically like the Detected By type. There are a couple of things you can do. One method I had frequently used in the past is Game Logic objects and waypoints. If you synchronize a trigger to a waypoint, the group associated with the waypoint will move to it but do not continue until the trigger fires. This can be used to e.g. run a script or commands in the waypoints completion field. However, I do advice against this method, or rather, I don’t use it anymore since I have found that (at least last time I checked) logic waypoints (AND and OR types) are broken (AND waypoints behaved like OR waypoints).
What I mostly do myself these days is to use a mission flow FSM. FSM stands for Finite State Machine, and what it means is a graph of nodes that can either be states or conditions. When a state is reached, the FSM execution will halt in that state until one of the attached conditions evaluates to true. For more on FSM’s, see the chapter “FSM Crash Course” below.
The major point with triggers is making them reliable and determined, meaning that you want to make sure that the trigger works and its statements are only executed the right amount of times (mostly, one time, or one time per client). For this purpose, any method works if it first determines that the trigger fired on a machine, and then executes the correct statements. Using the above waypoint method will make the script execution only depend on the game logic (which only exists once, and always on the server) and therefore will fold one or many firings of the trigger to a single one which can then continue the processing in a deterministic way – you will be absolutely sure that the code will only be executed once, so you can use whatever methods to make sure it runs were it is supposed to run.
Triggers can be used effectively using a condition field containing a variable. For example, to end a mission and make sure that it runs on all clients, place a trigger in the mission and put the following in the Trigger Condition:
In the On Activation field, place the following:
[“end1”] call BIS_fnc_endMission;
Now, to reliably end the mission, any client or the server can simply run
myVariable = true; publicVariable “myVariable”;
Obviously, you can pick any variable name, in fact, you are encouraged to use a sensible one. You should also set the variable to false in your init.sqf to make sure it is defined. Triggers like this are very easy to make and generally work well.
remoteExec is a script command that was recently added and should be favored over earlier methods. The command simply executes a script, function or scripting command on a given subset of clients and/or the server. Its details are described here. The command accepts anything on the left side and an array on the right side. The left side can be empty if the called “thing” does not require any arguments. The Bohemia Interactive Wiki page lists an extensive list of examples.
For most missions, it isn’t obvious how to run something on a specific machine in a multiplayer environment. The standard function set of Bohemia Interactive’s function module, however, contains a function that is tailored to handle this specific issue: BIS_fnc_MP.
The basic premise of the function is to run a piece of code (mostly a function, i.e. a pre-compiled variable containing code) on a number of machines. In its simplest form, it takes the arguments and the name of the function (or a command name) and runs it on all clients and the server:
[“Hello World”, “systemChat”, true] call BIS_fnc_MP;
The first parameter is the argument to the remotely executed command or function. In this case, since the systemChat command accepts a single string, it is just that. More complex examples will likely have an array here.
The second parameter is the command or function to run. If it is a function, it must exist as a variable on the remote machine(s). The systemChat is a command that displays a message in grey (running systemChat on the server of course doesn’t make sense, since the server doesn’t have a display).
The third parameter may vary wildly depending on what you want to do. In this case, it is a boolean, which indicates that you want to run the command on the server and (since it is true) on all clients. Were it false, you would only run on the server.
There are other possibilities for this parameter. For example, specifying an object will run the code only were the object is local and nowhere else. Likewise, you can specify a side or a group to run it only on clients where the player is on that specific side, or in that specific group.
There are more options to this function, check the Wiki page for more details.
Ending a mission needs to be done on all machines, not only one. Missions that adhere to the new mission presentation guidelines put out by Bohemia Interactive should end the mission by calling BIS_fnc_endMission, but again, this needs to be called on all machines. This can easily be done using any of the above methods, the reason I make specific mention here is that it needs to be done and is often forgotten.
A Crash Course on FSM’s
This part has been removed from this guide and moved to its own post.
I hope that this guide produced some useful information. The most important aspects of successful multiplayer mission making is to be aware of locality and what effects they have on the statements you are executing. The most common issues when running on a dedicated server is that there is no player object on the server, and any script running that references player might or might not have an effect, whether desired or not. This guide should have shown how to avoid these problems, and how to achieve a consistent execution based on global or local events. The FSM as a central entity for controlling the mission flow is a useful method to ensure single execution even if an event is triggered multiple times, but again, it must be observed how execution is handled. For example, just calling BIS_fnc_endMission in the FSM is not enough; it will then ONLY run on the server. If you are just testing the mission by yourself on a hosted server, that will work because the server and you are the same entity and therefore there is no such thing as a disjoint context, but as soon as you run this on a dedicated server, even if you are testing alone, the problem will become immediately apparent.
It is highly recommended that you set up a dedicated server for testing. I have a separate machine running Linux that I use as a gateway for my home network which I have set up a dedicated server on for testing; to the best of my knowledge, it is possible to run a dedicated server and a client on the same machine at the same time. Famously, testing only proves the presence of bugs, not their absence, but it is a good (and so far, pretty much the only method) to ensure your mission runs on a dedicated server.
The methods presented herein work on single player as well. You can run the test mission we created in single player as well as multiplayer.
If you have any feedback, please don’t hesitate to contact me.
Much of this document is based on personal experience and collected evidence from the Bohemia Interactive Forum and the Bohemia Interactive Community Wiki. Although the author has made any reasonable attempt to achieve accuracy of the content of this document, he will assume no responsibility for commissions or errors. Usage of this information is at your own risk. The author makes no representation of completeness or fitness for any particular purpose and shall in no event be liable for any loss of profit or other damage, including but not limited to special, incidental, consequential or other damages.
All trademarks, service marks, product names, or named features are assumed to be the property of their respective owners and are only used for reference, without any implied endorsement when using any of the terms.
© 2014-2016 by Hans-Jörg Frieden. All Rights Reserved.