Jump to content
Ninjaisfast

Addaction with execVM, passing arguments incorrectly.

Recommended Posts

Me again. I'm trying to execute a script from an addAction. I simply want to cancel an active script (which is started from an action) by starting another script from an action, but that requires the script I want to cancel to have a script handle, which as far as I can tell, a script from an addAction doesn't have. 


All actions are on the same object. (and run from init field)
 
How I am starting the script that runs, which I want to cancel (runs and passes argument fine):

this addAction ["Yellow","script.sqf",yellow,1,false,true,"","",2];

What I've tried:

this addAction ["Green",{_scriptHandle = []execVM "script.sqf"},green,1,false,true,"","",2];

And putting things in the [ ] before execVM.
But I can't work out how to correctly pass the arguments. In the script.sqf, the destination (green in this case) is selected through:
_destinationName = _this select 3 ;

But this doesn't work properly if I try to execVM it.

script_cancel.sqf will just terminate the script handle.

this addAction ["CANCEL","script_cancel.sqf",nil,1,false,true,"","",2];


How do I pass the arguments correctly if I'm running the script from execVM, or can I just put something in the start of my script.sqf to give it a handle?

Thanks in advance ❤️

Share this post


Link to post
Share on other sites

Worked it out, posting solution in case anyone has similar troubles.

this addAction ["Green",{ScriptHandle = [_this select 0, _this select 1, _this select 2, green] execVM "script.sqf"},nil,1,false,true,"","",2];

Cancel via terminate ScriptHandle .

Share this post


Link to post
Share on other sites

Nice @Ninjaisfast but it is not very robust. If you ever need to use it on a separate object you're going to have issues with handles overwriting each other.

A safer place to store your handle is in the variable space of the object it's attached to.

//This is the code that goes into your action call
params ["_target", "_caller", "_id"];
private _handle = [_target, _caller, _id, green] execVM "someScript.sqf";
_target setVariable ["someScript_handle", _handle];

//In the cancel action you use
terminate (_this#0 getVariable ["someScript_handle", scriptNull]);

Also keep in mind what happens if the player activates the action again before cancel is used and make sure you handle that in your script.

 

---

 

Speaking of robustness. I wouldn't recommend the use of terminate, at least not as the go to make-awesome-script-go-stop tool.

When terminate is used the script will stop immediately and you often can't be 100% sure at which stage your initial script is at when it's terminated. A bit like pulling the plug.

 

Imagine a script controlling a set of traffic lights. While the light is on a script is running a loop switching the colors of the lights from green to yellow to red etc. When we want to simulate someone cutting the power the light is supposed to go dark. We don't need the script anymore since the light is off so we terminate it... and the light gets stuck on red, forever... but the power is supposed to be off? How the f...? Oh! We forgot to do cleanup on this particular set off lights... #VintageCurry

 

Of course this is just a silly example that a quick band-aid fix can sort out.

However in more complex work it is not always so trivial to 1. know what the last state was and 2. return to the Null-state.

 

The alternative practice: Build the termination procedure into the script instead and allow for a graceful shutdown.

Define variables and conditions that will trigger termination and use those to make sure the script terminates gracefully when the time comes.

More work I know but not that much, and besides, it keeps things self-contained, reusable and reduces the risk for bugs to sneak in.

 

Super-simple example of the alternative: 

Spoiler

 

The script:


/*
	This code assumes a call from addAction but can in essence be used anywhere as long as you provide a neat and isolated place to store the data.
    Object variable spaces are usually the best in my experience.
    
    In this case the kill-switch variable is stored in the _target's variable space
*/
params ["_target", "_caller", "_id", "_args"];

//Set up the kill-switch
_target setVariable ["TAG_arbitraryVarName", true];

//Run ze loop
while { _target getVariable "TAG_arbitraryVarName" } do {
  	//Light goes Green
  	//Light goes Yellow	
  	//Light goes Red
};

//Here you can clean up after yourself in a nice controllable fashion. No need for a R.U.D.
//Light goes Black

To cancel the script:


object setVariable ["TAG_arbitraryVarName", false];

 


Now don't get me wrong, terminate still has it uses. 

If there's no cleanup needed or the script in question is simple it can be a shortcut that saves a few cycles, or if something's gone horribly wrong terminate can save the day.

 

Anyway... *end rant*

  • Like 3

Share this post


Link to post
Share on other sites
5 hours ago, mrcurry said:

Speaking of robustness. I wouldn't recommend the use of terminate, at least not as the go to make-awesome-script-go-stop tool.

...

The alternative practice: Build the termination procedure into the script instead and allow for a graceful shutdown.

Define variables and conditions that will trigger termination and use those to make sure the script terminates gracefully when the time comes.

You knew exactly the problems I would run into before I ran into them. What I hard worked fine if I did things exactly as I wanted a person to, but once I started behaving more randomly, errors snuck in due to terminate not working how I wanted it to.

I ended up doing things pretty similar to how you recommended. My new cancel action sets a global variable tripCancelled to true, which is checked in every loop of the other script. If its true, it cancels things gracefully. The cancel action also resets the tripCancelled variable back to false a few seconds later, to stop issues if someone were to try to cancel before running the actual script.

On another note, terminate seems really weird. If a player runs the same looping script twice (lets just say a countdown from 100), then terminates the script handle, it seems to only terminate the last instance of it being run, and even scriptDone shows that the script is complete, even though the first instance is still running (visibly still counting down). Does a script handle change if its used twice, or something that I'm not grasping?

Share this post


Link to post
Share on other sites

a script handle is unique for each script instance. this means u need a different script handle variable (or better an array) for each started script instance...

terminate is not weird. it just does what you order ...

  • Like 1

Share this post


Link to post
Share on other sites

sure. And it will not end all spawned sub-scripts inside the one you want to terminate. They have their own "life" (in parallel).

  • Like 1

Share this post


Link to post
Share on other sites
19 hours ago, Ninjaisfast said:

If a player runs the same looping script twice

If you put the handle into a variable you will OVERWRITE the variable when you put the second handle into it. A variable can't know what values it might've had in the past. So your first handle is just gone.

  • Like 1

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

×