Jump to content
Sign in to follow this  
sickboy

ArmA (MP) Scripting

Recommended Posts

I created a MP Scripting/Editor Guide a couple of days ago, you can find it at the BIKI:

http://community.bistudio.com/wiki/6thSense.eu:EG

I would like to use this thread for discussing the content of the guide, or for questions/answers surrounding the wonders of MP Scripting etc.

In the end I hope to use the information to include in the wiki.

Edited by W0lle

Share this post


Link to post
Share on other sites

First Subject I would like to throw up:

TeamSwitch within Multiplayer

My experience with ArmA v1.01/1.02 (I think it was) tells me that teamSwitch does work in MP. With full option to switch between the different units you have setup as teamSwitchable.

Reading Doolittle posts and some others, it is claimed that teamSwitch does not work in MP. (Thread Link)

What are other people's findings?

I will be able to look into it myself in the next days, possibly weeks.

But I would like to supplement the Wiki page with informations on how to work out teamSwitch within MP etc ASAP.

Share this post


Link to post
Share on other sites

Can't say anything about that ts issue.

But still a good idea to open up a discussion here. thumbs-up.gif

Share this post


Link to post
Share on other sites

I found that when you switch to a unit (I believe that is NOT in your group) you will have no control over that unit. It is like you are "Being John Malkovich". Also your soul, face, stats and whatever are still part of the original unit.

--Ben

Share this post


Link to post
Share on other sites

I've had MP teamswitch (using a custom interface and selectplayer, not the BIS interface) within my own mission for a little while now, limiting use only to units local to the switcher's computer and within his own squad (and no players are ever in another player's squad, to prevent horrible bugs). From hearsay, I've heard that switching to a unit not local to your computer causes a crash. However, testers of my mission have occasionally experienced the "john malkovich" bug where they are accidentally inhabiting an AI which is local to the server (I suspect) and have no control over it.

if you 'addswitchableunit' more than one unit, the BIS dialog becomes enabled (also I think there's a command to enable or disable the dialog itself.) All of my tests have been done with a custom interface, so I can't comment on how BIS' works in MP.

It thus seems that if you wanted to enable teamswitch in MP missions, you should override the default BIS interface and write your own that changes a unit's locality before the switch, as long as the unit is not controlled by another player.

And as stated, the original player's identity stays with his server joined unit. In another thread it was pointed out to me that identity can be changed in MP, but I have been unsuccessful trying it myself. If you *never* use setidentity on a player's new unit, he retains his name in chat but not on his new unit's in game tag.

Another infuriating bug with teamswitch in SP or MP, is that when you change to another unit within the same vehicle (say, driver or gunner), the original player unit retains control of the vehicle. So if you 'move to cargo' from the driver position, and then teamswitch to the driver position, the cargo unit will retain control over the vehicle. Or, a player who gets in as cargo and then teamswitches to driver will also encounter this problem. Teamswitching within the same vehicle can thus cause problems when a player cannot retain control over his own vehicle, or can't switch to other positions (because he is no longer the effectivecommander). A clever combination of getin-getout actions to reset the commander of the vehicle to the original player works, but it's still an annoying workaround.

Share this post


Link to post
Share on other sites

I've never tested teamSwitch since people reported problems.. huh.gif I made a fake teamSwitch in my CTI where you switch positions and weapons.

The variable time is synced when you connect.. is param1/param2 synced?

What I'm curious about: when is onPlayerConnected run? When the new person is in the game and has a player variable?

Also, what I noticed for JIP clients... all objects since the start of the map seem to be created and then put into their present state! E.g. if something dies and is deleted, it will be created then deleted again. So with that in mind.. what if you setVehicleInit adding of ammo to ammo box.. and that ammo box is destroyed.. then JIP player joins (redundant to say that? heh) ... they create then destroy ammo box..do they also put ammo in it? Is it possible for setVehicleInit code to check "this" and see if it's dead yet before running a command on the object? (Seems you have to delay it)

It's interesting what Lester said about lock! I've been trying to figure out what's going on with that.

Share this post


Link to post
Share on other sites

Thank you all for responding.

@Raedor; Pitty, but thanks! smile_o.gif

@The Captain; Interesting. Will work those details out in the Guide, and maybe there's a chance we could figure out how to get around these weird issues. Custom dialog might be the way to go, as you did.

...
Quote[/b] ]I've never tested teamSwitch since people reported problems..  huh.gif I made a fake teamSwitch in my CTI where you switch positions and weapons.
Ok smile_o.gif

The captain seems to have some interesting info. I guess my little tests back then involved AI local to the player machine smile_o.gif

Quote[/b] ]The variable time is synced when you connect.. is param1/param2 synced?
param1 and param2 do not have to be synced as variables. param1 and param2 are set by admin or per mission default, if they were set by admin, I believe when the JIP player connects, when he looks at the param1/param2 setting in the server, it matches what was set by the admin / default. These settings are then used for param1 and param2 by the client. Just as an ordinary client would, when he was connected while setting the param(s) and starting the match smile_o.gif

It's indeed the time variable that is synced, but afaik a jip player's dayTime gets set like: current daytime (mission time set in editor) + (synced) time = real current daytime on all clients+server. (Unless the time was artificially manipulated with setDate, skipTime, in this case, the jip player will have his time still said to what I wrote above, but is out of sync with the rest because their time was manipulated)

Quote[/b] ]What I'm curious about: when is onPlayerConnected run? When the new person is in the game and has a player variable?
My Findings:

[*] onPlayerConnected fires after the Briefing for all players that are ingame

[*] onPlayerConnected fires when a jip player's world is initialized, but it does not have to mean that the player object itself is already initialized.

I must say that these have not been confirmed by the proper testing chain... Maybe someone should do it, so we can confirm smile_o.gif

Quote[/b] ]Also, what I noticed for JIP clients... all objects since the start of the map seem to be created and then put into their present state! E.g. if something dies and is deleted, it will be created then deleted again. So with that in mind.. what if you setVehicleInit adding of ammo to ammo box.. and that ammo box is destroyed.. then JIP player joins (redundant to say that? heh) ... they create then destroy ammo box..do they also put ammo in it? Is it possible for setVehicleInit code to check "this" and see if it's dead yet before running a command on the object? (Seems you have to delay it)
Samething I noticed here, and also one of the reasons why I stopped using setVehicleInit and now work with a Network Service that uses a publicVariable per channel (clients or server), and addPublicVariableEventHandler. I send an array with: Destination, code to execute, and extra parameters like object references etc, which can be accessed from within the code to execute.

The problem is only that for your example, ammo boxes, you would need setVehicleInit because you want the boxes to be filled with the right ammo for the JIP players smile_o.gif

But this actually creates a desynchronized environment I believe, because you fill it to 100% when the vehicleInit is ran at JIP, while the box contents could already have been changed.

Testing object for alive could require a short sleep so the object is initialized, and if destroyed, this should 've propagated by then aswell. I can remember however a test where I set a vehicleInit with a player sidechat str(this), deleted the object, and let a jip player join. He said he received "<Null Object>" in the chat.

Which would indicate that the init is ran after initialization+synchronization of the objects, not directly after initialization.

I don't remember if alive check on an objNull gives true or false, I believe false, so using an alive check in the code would be ok.

Quote[/b] ]It's interesting what Lester said about lock! I've been trying to figure out what's going on with that.
I guess I missed that tounge2.gif going to look into it smile_o.gif

Update Ok, took a look.

Some functions must be executed where the object is local, and do not have effect from every machine.

Maybe those functions should all be listed and marked, in their BIKI pages aswell as the EG I believe.

The solution is to use a network service or a simple publicVariable with addPublicVariable like in my example above. The code to execute should only check if the object is local to the machine, and then execute the command.

In my opinion, such network service should be common practice for MP missions, as many more advanced features require this kind of communication possibilities

Share this post


Link to post
Share on other sites

I've added 3 notes recently as tips directly relevant to JIP:

- player - waiting until player is not null

- playerSide - equals west during JIP

- publicVariable - prevent overwriting during initialisation

Not sure whether they may be of use to your discussion.

I've only learnt these things recently. Pretty simple but I wish I knew them 6 months ago.

Edit:

Also, the script to 'get a list of all (alive)player-objects' doesn't take into consideration that the player may be a crew member inside a vehicle (with multiple crew members) and hence will be excluded. You need to add something like:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE"> if ([_vehicle] call fn_IsVehicle) then

{

{

if (isPlayer _x) then

{

_playerList = _playerList+[_x];

};

} forEach (crew _vehicle);

}

...

Edited by W0lle
code tags fixed

Share this post


Link to post
Share on other sites

Re: time:

Time seems to be synchronized well enough for players who start at mission start and players who JIP. However, time is NOT synced for players who are stuck in lobby or briefing when the game starts ("If you start mission you may leave players behind. Would you like to continue?" or such). Naturally these players also do not get additional updates since mission start, and I wish there was a simple way to 1) figure out which players are in this state, and 2) give them an accurate time value from the server to replace their timelagged value.

Re: network engine:

I just want to chime in that I wrote a network engine similar to sickboy's which has publicVariableEventHandlers on the server to recieve info from each client, and clients that wait for broadcasts from the server. This method is far easier to implement & cleaner than setvehicleinit as sickboy has pointed out, especially now that we can send arrays (been waiting for this since OFP..).

Also, in terms of publicVariables being synced for JIP players: The client seems to receive the variables either before or while his init.sqf is running (ie, before sleeps). So, if in your scripts you initialize a publicVariable (say, so you can wait for variables to be sent to that client later), your client will end up with his variable initialized without the value that was sent as he connected. This means you either need to check for an existing value when you initialize client variables on JIP, or resend publicvariables from the server after a player is connected to ensure they get the value. As well, I think the publicvariable that is synced when a player connects will not trigger the publicVariableEventHandler (unless the handler is added in the init, before any waits), but I have not tested this specific case. (ack, looks like DrEyeball pointed this very fact out on the wiki)

And to chime in about 'lock': I'm happy that command is local! In my own mission I lock east's crew restricted vehicles to west, and vice versa. (Tanks, helis, etc). This would not be possible if the command were global...

Share this post


Link to post
Share on other sites
....
Quote[/b] ]I've added 3 notes recently as tips directly relevant to JIP
Nice one!
Quote[/b] ]I've only learnt these things recently. Pretty simple but I wish I knew them 6 months ago.
I know where you're coming from smile_o.gif
Quote[/b] ]Also, the script to 'get a list of all (alive)player-objects' doesn't take into consideration that the player may be a crew member inside a vehicle (with multiple crew members) and hence will be excluded
Good point! Resolved.
...
Quote[/b] ]However, time is NOT synced for players who are stuck in lobby or briefing when the game starts
Good point. Maybe we should bring this to BI's attention as I believe it's a bug or at least no good exception handling smile_o.gif

We could always create a heartbeat and make the server send out it's time every 60 (or so) seconds and let clients sync with it. Or as Victor Farbau wrote a script; use a global equally named object and use setPos on the server to keep track of hours, minutes, seconds smile_o.gif

Quote[/b] ]The client seems to receive the variables either before or while his init.sqf is running (ie, before sleeps)
Yep, it's important to check if the variable is nil or not before initializing them, incase of publicVariable.
Quote[/b] ]especially now that we can send arrays (been waiting for this since OFP..).
Oh yes! Boy public Arrays rock biggrin_o.gif (Sending arrays as strings was such a shitty solution, as when you wanted to send objects, they had to be named the same on all machines, through a variable, vehicleVarName (or in the editor).
Quote[/b] ]And to chime in about 'lock': I'm happy that command is local! In my own mission I lock east's crew restricted vehicles to west, and vice versa. (Tanks, helis, etc). This would not be possible if the command were global...
Agreed, it's a good thing smile_o.gif

Share this post


Link to post
Share on other sites

The setpos trick is quite handy... I just might have to use that! My clients use the elapsed time to calculate 'time left' in various screens (server sends the end time value only once). In the rare cases the elapsed time is out of sync, the clients' timers are all screwy. Handy trick noted...

To be fair, missions can't be guaranteed to work correctly if the admin or players leave others in the lobby when the game warns them.. but I'd like to make a system that's nearly foolproof regardless. I don't like blaming the user biggrin_o.gif

As an aside, something I haven't written yet but have in the pipeline is a TK script that disconnects players if they reach or surpass the server-set threshold, using the (finddisplay 46) closedialog 0 trick (which I believe Doolittle also uses in the ACS, but I haven't checked for sure). Disconnect-on-TK might be a much nicer server script than building in hacks into each mission piecemeal (bananaphone room, self damage, weapon removal, etc). Such a simple, portable TK protection script that runs serverside might be something for MP scripters to consider (perhaps even combined with a 'forgive' feature or a time/name based lockout (5 mins or so) to prevent quick rejoins...)

Share this post


Link to post
Share on other sites

For the anti-cheat I just fire an end trigger on the client. They disconnect but everyone else keeps playing. I also use this in my CTI map where if you want the map to limit to a certain side you have the trigger disconnect you and say "please pick another side". Great way to release one version of a map with 1, 2, or 3 teams supported.

Share this post


Link to post
Share on other sites

Are unused player slots undefined variables during MP ?

Share this post


Link to post
Share on other sites
Are unused player slots undefined variables during MP ?

If AI is disabled, then empty player slots do not initialize and thus their variables or inits, neither, until they join.

Share this post


Link to post
Share on other sites

holy arma bug .. ewrrr..feature nullification... thanks

there is very little MP dedicated server scripting info out there.

hoo-rah!

Share this post


Link to post
Share on other sites

edit: Internet went bad crazy_o.gif .

Quote[/b] ]

[*] To only run something on dedicated server or serverClient: isServer

[*] To only run something on clients, and never on dedicated server or serverClient: !isServer

[*] To only run something on clients or server Clients: !(isNull player)

Im calling a intro script from init.SQF like this:

<span style='color:blue'>//StartCam

[]spawn

{

waitUntil {local player};

[]execVM"intro.sqf";

};</span>

to make sure it works on both dedi and non dedicated servers should i use:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">!(isNull player)

instead? I understand intro must be local now because of JIP smile_o.gif .

Share this post


Link to post
Share on other sites

Meow!

Your current code is basicly ok.

"Local player" will only become true on machines where the player object is not a null-object, and the object is local. That condition will never be met on a dedicated server.

I would however make this a more general system.

Put to start of init.sqf:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">/*

 Script by Sickboy (sb _at_ 6thSense.eu)

 Version: v0.1

*/

T_INIT = false;

T_Server = false; T_Client = false; T_JIP = false;

if (playersNumber east + playersNumber west + playersNumber resistance + playersNumber civilian > 0) then { T_MP = true } else { T_MP = false };

if (isServer) then

{

 T_Server = true;

 if (!(isNull player)) then { T_Client = true };

 T_INIT = true;

} else {

 T_Client = true;

 if (isNull player) then

 {

     T_JIP = true;

     [] spawn { waitUntil { !(isNull player) }; T_INIT = true };

 } else {

     T_INIT = true;

 };

};

And change your code for the intro:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

//StartCam

[] spawn

{

 waitUntil {T_INIT};

 if (T_CLIENT) then { execVM "intro.sqf" };

};

You can use the above code for as much stuff as you like.

It will ensure that the script-instances will terminate, if the condition isn't met. Where-as your example, on a deddy-server, would keep the scripthandle active for the whole duration of the game.

Which isn't a problem by itself, however as there are AFAIK limits to the amount of script-handles that can be open at one time, I guess it's never too bad to put it down to a minimum

The spawn for the intro check is only necessary if the rest of the script must continue processing, even while the intro is waiting for the conditions.

If the intro is execVM'ed at the end of a file, or if you must have more things ran if this machine is a Client, just leave the spawn out.

Share this post


Link to post
Share on other sites

Thanks Sickboy, its very nice of you to share all that knowledge/info with us smile_o.gif .

Share this post


Link to post
Share on other sites
Quote[/b] ]

Triggers created in editor exist on all machines (a trigger is created per machine, local to it), and they run on all machines (conditions checked, onActivation/onDeActivation executed when condition is true etc)

* Because the trigger is created on each machine, changing the trigger properties (statements, onActivation, etc. etc) has only local effects.

Oh oh, i just thought about this.

If a jip client joins a mission he will re-check the conditions (comands + variables) and relaunch whatever the trigger(s) is/are meant to activate, this is nasty if i only want my triggers to activate once lol.

How do i work around this? Do i have to use a script running on the server that passes all the stuff to the clients instead?

pistols.gif JIP!

Share this post


Link to post
Share on other sites
Quote[/b] ]

Triggers created in editor exist on all machines (a trigger is created per machine, local to it), and they run on all machines (conditions checked, onActivation/onDeActivation executed when condition is true etc)

   * Because the trigger is created on each machine, changing the trigger properties (statements, onActivation, etc. etc) has only local effects.

Oh oh, i just thought about this.

If a jip client joins a mission he will re-check the conditions (comands + variables) and relaunch whatever the trigger(s) is/are meant to activate, this is nasty if i only want my triggers to activate once lol.

How do i work around this? Do i have to use a script running on the server that passes all the stuff to the clients instead?

pistols.gif JIP!

Meow!

The quickest I could come up with atm:

If you use the Machine Check solution I provided earlier,

and you dont want a certain trigger to execute _at_all on a jip client

Add this to the trigger condition:

T_INIT && !T_JIP

example, if the condition was: T_Objective1, then it would become: T_Objective1 && T_INIT && !T_JIP

if the condition was: this, then it would become: this && T_INIT && !T_JIP

It gets somewhat more nasty, when you still want triggers to execute on jip clients, only when the trigger wasn't activated yet in the running game. However, surely not impossible.

Share this post


Link to post
Share on other sites
If you use the Machine Check solution I provided earlier..

Ofcourse smile_o.gif .

It gets somewhat more nasty, when you still want triggers to execute on jip clients, only when the trigger wasn't activated yet in the running game. However, surely not impossible.

I have a bunch of triggers that activate and deactivate DAC zones in a sequence, basically when you clear one (condition) it deactivates it and activates the next, so i guess i cant use triggers here because of the nasty JIP.

Share this post


Link to post
Share on other sites

I have a NON-ANSWERED question... how do i get AI to respawn on a multiplayer map. I know this is a broad question but, I want whole (non playable AI) groups aircraft and vehicles to respawn and do exactly what they were doing before they die... I got the blufor empty vehicles to respawn and players to respawn and all that... but the enemy ai i am having trouble with... can yo help? sad_o.gif

Share this post


Link to post
Share on other sites

Have you made a respawn_west or repsawn_east or respawn_guerilla marker? THe marker is put where you want that aproapriate team to respawn at. wink_o.gif guerilla is resistance.

Share this post


Link to post
Share on other sites

Interesting notes I've found:

<ul>

[*]Server can't removeAllWeapons _unit on a player (_unit = the players unit); however it can move the unit around via SetPos and change almost all other relevant player statistics.

[*]Server can't addEventHandlers to players.

Share this post


Link to post
Share on other sites

Does the setVehicleInit overwrite the mission.sqm init string for the unit? Or it is another "init"?

I'm asking because the article says "Unit init line from editor runs", just before the init.sqf. And this is the behaviour of JIP setVehicleInit statements. But in "Join in Progress" they are named "vehicleInits", like if they were a different one.

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  

×