Jump to content
twakkie

Advice on using addAction, ExecVM and remoteExec in script (i.e. locality and that Jazz)

Recommended Posts

Hi all
 
In a dedicated server MP environment, I created a mission where the players have to interact with a satphone to call in reinforcements.
The idea is to defend themselves until help arrives but I am struggling putting all the pieces together of the script to form a single low resource script.
 
The scripts must do the following:
1) Start a countdown timer which will show on the players UI  i.e. hintsilent
2) When the timer reach 0 after x amount of time reinforcements spawns.
 
I think I have most of it figured out. I added an MP add action with a modified version of mikie boy's add action script:
 
addActionMP.sqf
fnc_addActionMP = {
params["_object","_screenMsg","_scriptToCall"];
if(isNull _object) exitWith {};
_object addAction [_screenMsg,_scriptToCall,_object];
sleep 1;
};

params["_object","_screenMsg","_scriptToCall"];
[_object,_screenMsg,_scriptToCall] remoteExec ["fnc_addActionMP", 0];
Which calls the following script. 
 
countDown.sqf
private "_time";
_time = 10;

while {_time > 0} do {
_time = _time - 1;  
hintSilent (format["Time Left: \n %1", [((_time)/60)+.01,"HH:MM"] call BIS_fnc_timetostring]); 
sleep 1;
};
All these scripts, including the spawning script, works perfect on their own but I am struggling to put them all together due to locality.
I want to directly call in countDown.sqf from the addActionMP.sqf script but due to remoteExec I suspect either the countDown.sqf will not launch on the server or when the countdown reach zero and fires the reinforce script it will fire on all clients instead of only on the server.
I came to the conclusion that I will probably need another script satPhoneAct.sqf which will do 2 thing. Launch the UI countdown.sqf wich doesnt fire the reinforce script and another server side countdown.sqf which will only count down and fire the reinforce script. 
 

satPhoneAct.sqf 

null = execVM "countDownUI.sqf"
if !(isServer) exitWith {};
null = execVM "countDownServer.sqf"

Finally my question is this, what pitfalls should I watch out for?

Since countDown.sqf have a sleep in it, will it cause the script to either fail or not fire or cause massive performance issues when I launch it with execVM? Should I use another way to execute the script?

I will test and continue researching it, but I would be gratefull for any advice.

 

Regards

Twak

 

Share this post


Link to post
Share on other sites

Actually, i have a pretty simillar problem, but no ability to post new threads, so i will be watching this thread carefully

Share this post


Link to post
Share on other sites

Probably best start again, looking always at the big picture. :)

You want to sync a countdown between multiple players, some of which might have (possibly) joined in progress, some of which will have slower computers (delaying the scheduled "sleep" more than others), .. and if not, the clock will drift slowly anyway. So the easiest thing is to just run the countdown on the server and simply send notifications to clients.

Though first, we have the satphone with an Action, which you'd presumably want to hide if anyone calls in reinforcements. This can be done with by specifying a "condition" to addAction like

isNil "satphone_used"
The _scriptToCall would then simply tell the server to execute the countdown script and would set this used variable for everyone else, so the satphone couldn't be used again.

{ null = execVM "startCountDown.sqf" } remoteExec ["call", 2];
satphone_used = true;
publicVariable "satphone_used";
So together, it could look like (on the satphone init line)

this addAction ["Call Reinforcements",
    {
        { null = execVM "startCountDown.sqf" } remoteExec ["call", 2];
        satphone_used = true;
        publicVariable "satphone_used";
    },
    nil,
    1.5,
    true,
    true,
    "",
    "isNil ""satphone_used"""
];
The actual startCountdown.sqf would then cycle the sleeps (or diag_ticktime if you want to be super precise) and display notifications for all players, something like

for [{_x = 300}, {_x > 0}, {_x = _x - 1}] do {
    if (_x % 10 == 0) then {
        (format ["Reinforcements: %1 secs left", _x]) remoteExec ["hintSilent", -2];
    };
    sleep 1;
};

"Reinforcements are here!" remoteExec ["hintSilent", -2];

... your server-only reinforcements code here (or execVM with its script) ...
This isn't very much optimized, but the regular "for .. from x to y" loop cannot count downwards.

Sorry I can't test all of the above right now (might have some syntax errors), but it hopefully should help. It's not the most efficient way of doing things (that would be using serverTime, working around its "features", checking satphone_used on JIP, etc.), but it should work reliably.

  • Like 1

Share this post


Link to post
Share on other sites

I think I understand what you want.  Cut the problem down in a few parts.

 

- make sure the satphone has the add action and this is available to all players

- run the timer on server only that broadcasts the timer value each second to all players

- when the timer ends launch reinforcement on server only

 

The first one you seem to have covered so that is fine.  Now for the second one, first of all you will need an evil global to tell the server a timer is running.  Next you will need a loop that checks every second if the timer has ended and notify all clients of the new timer time.

 

Take this "conceptional example"

if(!isServer) exitWith {};
_timerSeconds = 0;
TIMER_RUNNING = true;
if(TIMER_RUNNING) then {
	while(_timerSeconds < 60) then {
		sleep 1;
		_timerSeconds++;
		[_timerSeconds] remoteExec ["LIB_fnc_clientShowSeconds", -2, false];
	};

	[] execVM "callReinforcements.sqf";
	TIMER_RUNNING = false;
}

This script runs on the server, while the client has a function library with LIB_fnc_clientShowSeconds as a function to draw the seconds on screen.  Considering this script only runs on the server the callReinforcements will only run there.  This might not run out of the box but might give you an idea.

 

- script on client side to remoteExec this script

- script on serverside (what I just put above here)

- script on client side to draw timer seconds on screen

  • Like 1

Share this post


Link to post
Share on other sites

How about this?

[_object, ["CALL REINFORCEMENTS", {
	[(_this select 0), (_this select 2)] remoteExec ["removeAction", 0]; // IF YOU WANT TO REMOVE THE ACTION AFTER USE
	private "_time";
	_time = 10;

	while {_time > 0} do {
		_time = _time - 1;  
		(format["Time Left: \n %1", [((_time)/60)+.01,"HH:MM"] call BIS_fnc_timetostring]) remoteExec ["hintSilent", -2];
		Sleep 1;
	};
	["PARAMETERS FOR REINFORCEMENT SCRIPT", "PATH TO REINFORCEMENT SCRIPT"] remoteExec ["execVM", 2];
}]] remoteExec ["addAction", 0];
  • Like 2

Share this post


Link to post
Share on other sites

 

How about this?

[_object, ["CALL REINFORCEMENTS", {
	[(_this select 0), (_this select 2)] remoteExec ["removeAction", 0]; // IF YOU WANT TO REMOVE THE ACTION AFTER USE
	private "_time";
	_time = 10;

	while {_time > 0} do {
		_time = _time - 1;  
		(format["Time Left: \n %1", [((_time)/60)+.01,"HH:MM"] call BIS_fnc_timetostring]) remoteExec ["hintSilent", -2];
		Sleep 1;
	};
	["PARAMETERS FOR REINFORCEMENT SCRIPT", "PATH TO REINFORCEMENT SCRIPT"] remoteExec ["execVM", 2];
}]] remoteExec ["addAction", 0];

 

Personally I have run into problems with using remoteExec ["script", 2].  JIP keeps complaining you can't use 2 with remoteExec as target.  I don't know why, but my logs have been full with those errors.  only when it's releated to JIP though.  Could be I'm doing something wrong.

Share this post


Link to post
Share on other sites

Personally I have run into problems with using remoteExec ["script", 2].  JIP keeps complaining you can't use 2 with remoteExec as target.  I don't know why, but my logs have been full with those errors.  only when it's releated to JIP though.  Could be I'm doing something wrong.

 

I can't speak from experience, but there's an additional parameter that handles JIP, a boolean. Not sure what it does but you can check it out, RemoteExec Wiki

Share this post


Link to post
Share on other sites

Here's another countdown script.

/*
    Author: Revo

    Description:
    Shows a global countdown.

    Parameter(s): - number (Countdown in seconds)

    Returns:
    Revo_countdownFinished/true in missionNamespace, false on clients
*/

if (!isServer) exitWith {false};

_countdown = param [0,120,[0]];
_format = param [1,nil,[""]];

missionNamespace setVariable ["Revo_countdownFinished",false];

for "_i" from 1 to _countDown do
{
    _timeleft = format ["Time left: %1",[(_countdown - _i),_format] call BIS_fnc_secondsToString];
    _timeLeft remoteExec ['hintSilent',0];
    sleep 1;
};

missionNamespace setVariable ["Revo_countdownFinished",true];

true;
  • Like 1

Share this post


Link to post
Share on other sites

Wow thanks everyone!! I will go through all the suggestions and scripts and will come back with some feedback. Cheers!

Share this post


Link to post
Share on other sites

Just a little feedback.

 

I used Fiddi's approach since it had everything I wanted nicely compact. It was local to server and executed the scripts I wanted without broadcasting it to everyone while displaying the hint to everyone..

Thanks man, I just had one error. The [(_this select 0), (_this select 2)] remoteExec ["removeAction", 0]; did not remove the add action. I am unsure why though. Nevertheless, just told the okes not to use it more than once.

 

Revo thanks for that countdown script. Will use it in the future.

Cheers!

Share this post


Link to post
Share on other sites

Mostly I ve no luck with my thoughts about remoteExec but try this:

[[_this select 0, _this select 2], {_this select 0 removeAction _this select 1}] remoteExec ["call", 0];

Share this post


Link to post
Share on other sites

It's weird though, it should work. I have never experienced that it did not remove the action, I have never tried it on dedicated server though. Are you sure you wrote it correctly?

[(_this select 0), (_this select 2)] remoteExec ["removeAction", 0];

Share this post


Link to post
Share on other sites

It's weird though, it should work. I have never experienced that it did not remove the action, I have never tried it on dedicated server though. Are you sure you wrote it correctly?

[(_this select 0), (_this select 2)] remoteExec ["removeAction", 0];
I think the problem is that remoteExec will not pass the values of _this select 0 and _this select 2 to the client. the command is executed as it is and on client _this will be unknown. Therefore I suggested to pass _this to a function and call it with remoteExec...

Another time I had no luck with remoteExec and all I said about it is rubbish.

Share this post


Link to post
Share on other sites

 

It's weird though, it should work. I have never experienced that it did not remove the action, I have never tried it on dedicated server though. Are you sure you wrote it correctly?

[(_this select 0), (_this select 2)] remoteExec ["removeAction", 0];

 

Almost 100% sure I wrote it correctly but I will double check it when I am home.

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

×