Triggers, scripts and addAction in multiplayer

Triggers, Scripts and addAction in MultiPlayer v1.10
by Baddo

Recommended for OFP version 1.46.
This piece of old, but still interesting (and for some issues, hair-raising!) information is taken from the LDD Kyllikki forums:
http://kyllikki.tiimipeli.net/cgi/ib/ikonboard.cgi?s=47362bd75f91a11e1451959d6cd4ce42;act=ST;f=7;t=9
It was originally written in Finnish by Kegetys. With the permission of Kegetys, Baddo has modified and translated the most interesting parts of the text into English to be readable by a wider audience. Many thanks to Kegetys for the grassroots level research he has done and for giving permission to submit this to OFPEC.

TRIGGERS
Triggers work in a multiplayer environment a bit differently from how people usually think. Triggers, like everything else in OFP, are executed locally on every client. This can lead to “desync”, which means that players experience different things because the mission is not synchronized anymore between the clients and the server.
Example scenario:
Trigger with Condition: East Present

Let player X be a west soldier and player Y be an east soldier. When player Y walks into the zone of the trigger, his computer doesn’t send any particular information about the trigger event to player X’s computer. But in a normal situation the trigger also executes on player X’s computer because player Y’s character is also standing in the trigger zone on X’s computer.
But what if there is a lag peak, or player X’s / player Y’s connection lags slightly behind the other players’ connections just at the moment when the trigger should execute on all computers? That might cause player Y warping over the trigger on player X’s computer and thus the trigger will never fire for player X, even though it does so for player Y. These kinds of problems lead to entriely different situations in missions between players. Some players may have objectives marked done but others won’t etc…
To ensure this will not become a problem in your missions, you can implement a “safety net” for all mission critical triggers. This solution makes use of 2 triggers, set up as in the following example:
1st Trigger:
- Condition: East Present
- OnActivation: trig1Fired = True; publicVariable "trig1Fired"
2nd Trigger:
- Condition: trig1Fired
- OnActivation: hint "This is a more secure way to get things done in OFP."; [] exec "bringintheWOMD.sqs"
How this works:
1. Player Y walks into the 1st trigger’s zone and the trigger executes at least (but not necessarily only) on player Y’s computer, setting the trig1Fired variable to True and sending a publicVariable message about the trig1Fired variable to all computers in the session. Now all computers should have variable trig1Fired variable set to True.
2. On all computers in the session the 2nd trigger notices that the trig1Fired variable has become True and executes whatever was in 2nd trigger’s OnActivation line.
This method ensures that even if the 1st trigger only fires on player Y’s computer, the information is sent to all computers in the session thus leading to a synchronized game flow with the help of the 2nd trigger. It might be that some or even all computers in the session already saw the 1st trigger event and thus don’t need the additional publicVariable message, but this method takes into account the possibility of desync/lag etc. problems.

SCRIPTS
Scripts (started with exec, addAction) are also executed locally. This isn’t usually a problem but there are issues that must be taken into account when designing MP missions, especially when using the addAction command.
Lag and different speeds of client computers causes scripts to rarely start and end at the exact same time in multiplayer missions. This is why, for example, at the start of multiplayer missions some players may be able to start moving while others are still watching the intro. This issue has no workable solution – we have to live with the fact that people have different hardware and different connection speeds to the server.

Baddo’s thoughts: You could try to reduce this effect with a waiting loop at the end of your intro. Every client could tell the server that they have reached the end of the intro and after all clients have reported to be finished with the intro, the server would tell all clients to start the mission. This would increase the waiting time for some players, but would make the situation fairer to others who suffer from slower internet connection to the server or have slower hardware. In general, this is not worth doing imho, because there would still be differences in when the scripts start/end. This method might reduce the differences but would never remove them completely.

ADDACTION / PLAYER

A script started from addAction is only executed locally i.e. only on the computer of the player who uses the action. You must use the publicVariable command and a True/False variable to activate a trigger if you want your script to run on all computers in the session. Example:

this addAction ["Bring in the WOMD","activateWOMD.sqs"]

Script: activateWOMD.sqs

trigWOMDFire = True

publicVariable "trigWOMDFire"

exit

Trigger:
- Condition: trigWOMDFire
- OnActivation: hint "This is a more secure way to get things done in OFP."; [] exec "bringintheWOMD.sqs"

The player command can be very useful when using the addAction command. If you want to add an action to all players, you might be tempted to put this in Init field of each playable unit:
this addaction["Eat dust","eatdust.sqs"]
But this is not a good way to do it. All client computers in the session will execute this line of code for each playable unit. This leads to a situation where players will see the added actions of other playable units in their action menus if they are close enough to each other. In other words, players will see actions that are not meant for them at all or they will see multiple actions of the same type in their action menu.
A way to fix this problem is to execute this line of code at the start of the mission:
player addAction["Eat dust","eatdust.sqs"]
This code should go into the init.sqs file or an equivalent place where it will only be executed once for each player. Do not put this line in the Init fields of the units because then all players will add the action to themselves multiple times. Example:

In a playable unit’s Init field:

this exec "initunit.sqs"

Script initunit.sqs:

if not(local _this) then {exit}

_this addAction["Eat dust","eatdust.sqs"]

exit

Happy editing.

Revision History

v1.10 - 2006/09/29 - edits, bonus examples and reformat to html - Mr.Peanut