Jump to content
Sign in to follow this  
Guest

Some questions regarding public variables in MP and locality

Recommended Posts

Guest

Hello

I have a few questions about public variables and possible locality issues that I have not been able to completely figure out via forum searches, and I'd like to at least ask here, as my MP scripting abilities are very limited.

First, is regarding Object variables that are set on a clients end to an object that is local to them, and then accessed and even modified on server, where the object is not local to the server - in particular here the player. The below is the Init code, where the Eventhanlder is added to players:


if (IsServer) then
{

ArrayOfPlayers = [];

{
if (isPlayer _x) then
{
_x addEventHandler ["FIRED", {[_this select 0, _this select 1] execVM "PlayerFiredA.sqf"}];
ArrayOfPlayers = ArrayOfPlayers + [_x];
};
} ForEach AllUnits;

};

First off, the adding of the event handler. I want to do this in the Init file, can this be done on the server, and will it be actually added to each player on their client side, or does this need to be done locally on each client?

Next, in the script I set a variable to the player when they fire.

rivate ["_UnitPlayer","_Weapon","_UnitPlayerPos","_ControlScriptRan","_Suppressor","_WeaponItems"];

_UnitPlayer = _this select 0;

_Weapon = _this select 1;

_WeaponItems = weaponsItems _UnitPlayer;

_UnitPlayerPos = (GetPos _UnitPlayer);

_Suppressor = 1;

//Player sidechat Format ["Weapon is %1",_Weapon];

if ((!(_Weapon == "Throw")) && (!(_Weapon == "Put"))) then
{

//When the player fires a non silenced weapon, the var will be set
{
if ((_Weapon == _x select 0) && ((_x select 1) == "")) then
{
_Suppressor = 0;
_UnitPlayer setVariable ["FiredPlayerPosLoudGun",[1,_UnitPlayer,_UnitPlayerPos,_Weapon],True];
//Player sidechat Format ["Player fired NON SILENCED WEAPON"];
};

} ForEach _WeaponItems;

if (_Suppressor == 1) then
{
_UnitPlayer setVariable ["FiredPlayerPosSilenced",[1,_UnitPlayer,_UnitPlayerPos,_Weapon],True];
//Player sidechat Format ["Player fired SILENCED WEAPON"];
};

My question here is, on a server ran control script, can I access these public variables (through GetVariable) that are set to various players by checking on each player object, as according to the Wiki, Players are Not local to the server?

Really, a big question as well here, is, can I access players and their units through server ran scripts, and furthermore do checks on them - like distance checks and alike, even though they are not considered local to the server scripts - and on the flip side, can non player AI units be accessed and be accessed/checked on via various commands in player run scripts?

I am aware of the fact that certain scripting commands only run on objects that are local to the computer that the command is being ran on, so my questions here are more generally based, knowing that certain script commands do have locality requirements.

Lastly, what is the "standard" global style non-object public variable form used - as all I have found is this :

SomeVar = 5; publicVariable "SomeVar";

And if the publicVariable is the main variable used in Mp for communicating variable data (non object): Can a publicvariable be created and declared on any client, not just server, and how is it accessed - that is, is a publicvariable accessed in script just like a global would be :SomeVar = 5; publicVariable "SomeVar"; this is done on one client, then on another client - where the variable has never been declared or used, just do - if (SomeVar == 5) then { //DoSomething}; - It seems to me that it would be desirable at least for most PublicVariables to declare/initialize them on server side in the Init to avoid script errors on clients that check on them - if I'm reading this all correctly.

Thanks in advance for any help :)

Edited by Guest

Share this post


Link to post
Share on other sites
Lastly, what is the "standard" global style non-object public variable form used - as all I have found is this :

SomeVar = 5; publicVariable "SomeVar";

And if the publicVariable is the main variable used in Mp for communicating variable data (non object): Can a publicvariable be created and declared on any client, not just server, and how is it accessed - that is, is a publicvariable accessed in script just like a global would be :SomeVar = 5; publicVariable "SomeVar"; this is done on one client, then on another client - where the variable has never been declared or used, just do - if (SomeVar == 5) then { //DoSomething}; - It seems to me that it would be desirable at least for most PublicVariables to declare/initialize them on server side in the Init to avoid script errors on clients that check on them - if I'm reading this all correctly.

Thanks in advance for any help

You can use publicVariable on every client. The value of the variable gets spread through all entities, server, clients etc.

I can only suggest you to read some of KK's blog entries about multiplayer scripting BLOG

Share this post


Link to post
Share on other sites

Server does not have a "player" variable.

Player is a local variable assigned on clients to the current man that the user/client is controlling. It is a quick refrence, just like "this" on the editor.

Your first script wouldn't work.

If you are not using AI in the player slots, a "playableunits" array would contain all the units that are currently occupied by players. You have to check on the wiki, if the command you use can be used locally or globally, and if the effects are local or global. As said, KK blog has usefull info and easy to understand explanations about local/global.

Share this post


Link to post
Share on other sites

The Event Scripts may help with some of this as well (i.e the EH added to all players I would put in the initPlayerLocal.sqf):

https://community.bistudio.com/wiki/Event_Scripts

This way (in initPlayerLocal) you assign the EH to all players at missions start and then for each JIP. Now for the script ran within the EH you may want to look into BIS_fnc_MP to take in the "local" parameters, but execute only on the server (not really sure this is 100% true though in terms of the local being passed).

Share this post


Link to post
Share on other sites

The "addEventHandler" command has global arguments and local effect. You can add an event handler to any unit, but the actual handler will only execute on the machine where the eventhandler was added. In the first code block, all the eventhandlers would be added from the server's perspective, and so the eventhandler would only run on the server as well. if you wanted each client to have the event handler as well, you'd need to remove the "isServer" restriction

yes you can access remote units, such as other players, if the commands you run support global parameters. "distance" is global so that would work to calculate, for example, the distance from a remote player to an object, executed on the server

likewise AI can be manipulated by clients, again, if the commands support global parameters. an alternative is to use bis_fnc_mp

publicvariables are accessed through their names.

if i run x = 5; publicVariable "x"; on the server, then clients would then have an "x" variable equating to 5 locally, that they could access like any other variable. if an "x" variable already existed, then it would be overwritten with the broadcast value

if your client scripts rely on a publicvariable, then execution timing is important. you have to make sure the publicvarible is broadcast before clients try to access it. if you're running your "publicVariable" command in a scheduled envrionment(via spawn or execVM), then code execution order isn't guaranteed, and you should use "waitUntil {!isNil "var"}" in your client code, which halts execution until the variable exists. or you can use a publicvariableeventhandler on clients which will automatically launch code when it detects that a public variable has changed

Share this post


Link to post
Share on other sites

Keep in mind the not all EHs can take a global argument. I believed "Fired" is one of the few that can't.

Share this post


Link to post
Share on other sites
Guest

First off, thanks everyone for the very helpful responses, and links :)

You can use publicVariable on every client. The value of the variable gets spread through all entities, server, clients etc.

I can only suggest you to read some of KK's blog entries about multiplayer scripting BLOG

I appreciate the link there. I did some reading, very clear and to point stuff.

Server does not have a "player" variable.

Player is a local variable assigned on clients to the current man that the user/client is controlling. It is a quick refrence, just like "this" on the editor.

Your first script wouldn't work.

If you are not using AI in the player slots, a "playableunits" array would contain all the units that are currently occupied by players. You have to check on the wiki, if the command you use can be used locally or globally, and if the effects are local or global. As said, KK blog has usefull info and easy to understand explanations about local/global.

Gotcha. And indeed it seems more clear that those event handlers really should just be added locally, and then use the object variable with public set to true for communicating results from within the script.

The Event Scripts may help with some of this as well (i.e the EH added to all players I would put in the initPlayerLocal.sqf):

https://community.bistudio.com/wiki/Event_Scripts

This way (in initPlayerLocal) you assign the EH to all players at missions start and then for each JIP. Now for the script ran within the EH you may want to look into BIS_fnc_MP to take in the "local" parameters, but execute only on the server (not really sure this is 100% true though in terms of the local being passed).

I appreciate the link there, and I checked out the BIS_fnc_MP command. I certainly have come to the conclusion that simplifying the whole thing to just executing the event handlers locally would be best, and I can avoid getting 'in over my head' in some complex stuff that shouldn't be needed, when all that is needed is just PublicVariable communication from within scripts.

Keep in mind the not all EHs can take a global argument. I believed "Fired" is one of the few that can't.

Thanks for the heads up there :)

The "addEventHandler" command has global arguments and local effect. You can add an event handler to any unit, but the actual handler will only execute on the machine where the eventhandler was added. In the first code block, all the eventhandlers would be added from the server's perspective, and so the eventhandler would only run on the server as well. if you wanted each client to have the event handler as well, you'd need to remove the "isServer" restriction

yes you can access remote units, such as other players, if the commands you run support global parameters. "distance" is global so that would work to calculate, for example, the distance from a remote player to an object, executed on the server

likewise AI can be manipulated by clients, again, if the commands support global parameters. an alternative is to use bis_fnc_mp

publicvariables are accessed through their names.

if i run x = 5; publicVariable "x"; on the server, then clients would then have an "x" variable equating to 5 locally, that they could access like any other variable. if an "x" variable already existed, then it would be overwritten with the broadcast value

if your client scripts rely on a publicvariable, then execution timing is important. you have to make sure the publicvarible is broadcast before clients try to access it. if you're running your "publicVariable" command in a scheduled envrionment(via spawn or execVM), then code execution order isn't guaranteed, and you should use "waitUntil {!isNil "var"}" in your client code, which halts execution until the variable exists. or you can use a publicvariableeventhandler on clients which will automatically launch code when it detects that a public variable has changed

Thanks for all of this Naizarak :)

I have just a few questions (I know.. again! lol)

In the first code block, all the eventhandlers would be added from the server's perspective, and so the eventhandler would only run on the server as well.

Does this mean that in this case, where the EventHandlers are being added to the players only on the server side, that this would actually work, where they would still execute when any of the players fired, but the actual code would run on the server? Or would the event handlers not even work, due to the fact they are not being added locally?

/Edit - I should note that I'm just asking about this out of curiosity, as it certainly seems that it would be preferable to add the event handlers locally to each client in the init, and then just use the object variable set to public state.

if your client scripts rely on a publicvariable, then execution timing is important. you have to make sure the publicvarible is broadcast before clients try to access it. if you're running your "publicVariable" command in a scheduled envrionment(via spawn or execVM), then code execution order isn't guaranteed, and you should use "waitUntil {!isNil "var"}" in your client code, which halts execution until the variable exists. or you can use a publicvariableeventhandler on clients which will automatically launch code when it detects that a public variable has changed

I have to ask, when you say that timing is important, is this because of the possible delays encountered due to client lag, or is there some sort of actual bug that can be induced by a PublicVariable being set and not being detected correctly in MP?

Example :

In the Init, on server I do: X = 5; PublicVariable "X";

And hence on mission start, within a short time all clients should then know that X = 5, and is a PublicVariable. So it is now declared.

Then later, on server I do: X = 15; PublicVariable "X";

And suppose all clients are running a script that is on a 5 second delayed loop that checks on X to be 15. if (X == 15) then {//DoStuff};

My question is, if the PublicVariable was already declared, then it should be no issue here as far as errors go, and the only repercussion to having the delayed loop is that of course, all clients will pick up the PublicVariable change to 15 at different times? If this is all correct, then I understand, and it's all good :)

/Edit

One last question -

From what I read on KillZone Kids site, when a player hosts a Mp game, their computer becomes the server. Does this mean that on the Hosts computer, both the Server code and the client code runs, as it would seem that in this case the host would actually be a client as well?

Edited by Guest

Share this post


Link to post
Share on other sites

the eventhandler accepts global arguments, so even though players aren't local to the server, you can still use them for your event handlers. whenever a player shoots their gun, the handler will only execute on the server however, because that's where the eventHandler was added.

another example is multiplayer event handlers. they have both global arguments and effects. what this means is that your handler will be executed on all machines in the game, regardless of where it was actually added. going further, if you add the same MPeventhandler from multiple machines, then the handler will be executed that many times. a common mistake is adding an MPEventHandler in the init.sqf, which causes it to be added on every machine, and so in a game with X players, the handler code will be executed X times on each player's machine

https://community.bistudio.com/wiki/Arma_3:_Event_Handlers

your publicVariable implementation seems sound, there shouldn't be any problems. publicVariable itself is a reliable command, there's no bug with its implementation. i was just referring to a hypothetical situation where clients try to access public variables that haven't been broadcast yet, in which case you'd have to use waitUntil or a public variable event handler to launch code

Share this post


Link to post
Share on other sites
First, is regarding Object variables that are set on a clients end to an object that is local to them, and then accessed and even modified on server, where the object is not local to the server - in particular here the player
Any Object variables that are set are added locally to the object from where ever the command is run. Much like global variables with publicVariable# commands they can be globally set on all machines by using the third parameter..

 myObject setVariable [ "myVariable", value, true ]; 

True here transmits the object variable to all connected machines copies of the object.

First off, the adding of the event handler. I want to do this in the Init file, can this be done on the server
Yes it can be done in the init file and can be added on the server. As you have written it this is what is currently happening due to the if (isServer) then {.
will it be actually added to each player on their client side, or does this need to be done locally on each client?
No as written you are adding the event to the servers copy of the object. If you need to add it locally to the client then you would have to either remove the isServer and check that the object isPlayer and local, or as others have mentioned run it from the initPlayerLocal.sqf event script.
Next, in the script I set a variable to the player when they fire.
"Next, in the script I set a variable on the servers copy of the player object when they fire and transmit the objects variable to all machines"
My question here is, on a server ran control script, can I access these public variables (through GetVariable) that are set to various players by checking on each player object, as according to the Wiki, Players are Not local to the server?
Again as written your event is already firing on the server so the object variables have been set on the servers copies of the player object, so would already be available server side. Although generally yes, if the objects variable has been set on the player, locally to the player, as long as the object variable has been globally transmitted (see reply to first quote) then the object variable will be available on all machines.
Really, a big question as well here, is, can I access players and their units through server ran scripts, and furthermore do checks on them - like distance checks and alike, even though they are not considered local to the server scripts - and on the flip side, can non player AI units be accessed and be accessed/checked on via various commands in player run scripts?
As others have said, this is possible but you have to take into account the commands locality and whether it can except remote objects as parameters.
Can a publicvariable be created and declared on any client, not just server, and how is it accessed - that is, is a publicvariable accessed in script just like a global would be :SomeVar = 5; publicVariable "SomeVar"; this is done on one client, then on another client - where the variable has never been declared or used, just do - if (SomeVar == 5) then { //DoSomething}; -
Think of it this way,

There is no such thing as a publicVariable.

There are global variables and local variables.

PublicVariable is a command for syncing global variables across the network.

On a machine receiving a sync request...

if the global variable currently exists on the receiving machine then it is updated with the synced value. if it does not exist it is created with the synced value.

So yes any machine can create a global variable. It is accessed as a global variable. A global variable can be synced across the network to other machines using publicVariable# commands.

It seems to me that it would be desirable at least for most PublicVariables to declare/initialize them on server side in the Init to avoid script errors on clients that check on them
You could define your global variables on all machines to avoid undefied errors or as naizarak explains in the quote below just make sure the variables exist before using them.
you should use "waitUntil {!isNil "var"}" in your client code, which halts execution until the variable exists. or you can use a publicvariableeventhandler on clients which will automatically launch code when it detects that a public variable has changed
To add to this, If your script is not running in a scheduled environment making waitUntil unusable and you dont need to know exactly when the variable changes (publicVariableEventHandler). As per your example

if (SomeVar == 5) then { //DoSomething};

If SomeVar has not been previously defined on the client, the above would throw a undefined error. Instead first check that it exists..

if (!isnil "SomeVar" && { SomeVar == 5 }) then { //DoSomething};

Due to the SomeVar == 5 being wrapped in {} causes lazy evaluation, meaning the condition will quit if SomeVar is nil (not defined) and not even evaluate == 5, preventing the undefined error being thrown.

Share this post


Link to post
Share on other sites
Keep in mind the not all EHs can take a global argument. I believed "Fired" is one of the few that can't.

It can.

Also OP, instead of !(a == b) you can just a != b

Share this post


Link to post
Share on other sites
Guest

You guys are awesome :)

Thanks so much for the great replies, I really got it this time :)

Pretty cool actually, that the whole MP thing is not really that big of a deal, at least just sticking within the realm of general MP script stuff usage - Publicvariables in particular. I really thought things were much more difficult to simply get variables communicated between server and clients, so this is good news.

I suspect this thread may end up helping out quite a few others out there as well with similar questions.

I can't really guarantee my little 'project' will ever actually get done and released, but if it ever does, everyone that chipped in here will be rightfully added to the credits list.

One remaining question that I had earlier, if anyone might have the answer :

From what I read on KillZone Kids site, when a player hosts a Mp game, their computer becomes the server. Does this mean that on the Hosts computer, both the Server code and the client code runs, as it would seem that in this case the host would actually be a client as well?

Thanks again. :)

Share this post


Link to post
Share on other sites

From what I read on KillZone Kids site, when a player hosts a Mp game, their computer becomes the server. Does this mean that on the Hosts computer, both the Server code and the client code runs, as it would seem that in this case the host would actually be a client as well?

Correct ;)

Share this post


Link to post
Share on other sites
Guest

Thanks SilentSpike for the answer to that :)

Share this post


Link to post
Share on other sites

CASE

Spawning AI units on headless client.

Calling AIs unitname via actionmenu on a player client.

PROBLEM

Works well in editor and hosted MP game where player is host.

However, in dedicated server environment with AI spawned on dedi, user calling the action menu scripts gets an error, not finding the units name.

SCRIPTS

convoySpawn.sqf (ran on HC if available, if not on server):

_veh = createVehicle [_typeOfVeh, (getMarkerPos _Spawn1), [], 0, "NONE"];
createVehicleCrew _veh;
{
	diag_log [_x, faction _x, side _x, side group _x];
} forEach crew _veh;
c1 = _veh; //Spawning five units up to c5

Player units init:

this addAction ["<t color='#266A2E'>START THE CONVOY</t>", "Raw\cStart.sqf"];

/Raw/cStart.sqf:

openMap true;
private ["_id"];
_id = ["click", "onMapSingleClick",
{
	"cTarget" setMarkerPos _pos;
	{_x enableAI "MOVE"} foreach [c1,c2,c3,c4,c5];
	c1 move getMarkerPos "cTarget";
	openMap false;
	titleText ["Convoy is on the move", "PLAIN"];
	["click", "onMapSingleClick"] call BIS_fnc_removeStackedEventHandler; 
}] call BIS_fnc_addStackedEventHandler;

SOLUTION

I need to find a way to make the unit names global or public, so they can be reached with triggers on server or scripts on clients. Any help on this issue would be appreciated!

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  

×