Jump to content
SnowmaneYT

[HELP] - SC logic fails after X time

Recommended Posts

Hello,

 

I'm making a SC mission and in the begining the game logic runs fine, but after a random amount of time (5-10 mins of play) the sectors stop working and also got a massive FPS drops (clear sign of something wrong).

I checked the code several times and I don' t find any clue.

 

Here's the code (only the relevant to the topic):

 

init.sqf (All OK here):

{_respawns = _x addMPEventHandler ["MPRespawn", {
	params ["_unit", "_corpse"];
	_moveLoop = _unit execVM "unitMove.sqf";
	}];
} foreach allUnits;

unitMove.sqf (All OK here):

params ["_unit"];

if (side _unit == west) then {_unit execVM "unitMoveWest.sqf"};
if (side _unit == east) then {_unit execVM "unitMoveEast.sqf"};
if (side _unit == independent) then {_unit execVM "unitMoveIndi.sqf"};

unitMoveWest.sqf (ploblem is here. Only post one, the other are the same just changing the side):

params ["_unit"];
sleep 1;
_targetSector_1 = sectornum_1;
_targetSector_2 = sectornum_2;
_targetSector_3 = sectornum_3;
_targetSector_4 = sectornum_4;
_targetSector_5 = sectornum_5;
_targetSector_6 = sectornum_6;
_sectorsArray = [_targetSector_1, _targetSector_2, _targetSector_3, _targetSector_4, _targetSector_5, _targetSector_6];
_sectorSelected = _sectorsArray call BIS_fnc_selectRandom;
_sectorLoc = getPos _sectorSelected;

// I think the ploblem is in here
if (_sectorSelected getVariable "owner" == west) then {_unit execVM "unitMoveWest.sqf"};

_unit doMove _sectorLoc;
waitUntil {unitReady _unit};

// ToDo Sector owner check
sleep 30;
_unit execVM "unitMoveWest.sqf";

 

Thanks!

Share this post


Link to post
Share on other sites
if (_sectorSelected getVariable "owner" == west) then {_unit execVM "unitMoveWest.sqf"};

_unit doMove _sectorLoc;
waitUntil {unitReady _unit};

sleep 30;
_unit execVM "unitMoveWest.sqf";

 

You have created a loop where, if the sector is owned by a west unit, unitMoveWest.sqf is run repeatedly until the condition evaluates false (which it won't), as fast as the script can be run (which is very fast). Each of those also starts another instance of the loop ~30 seconds later (the unit is already at the sector, so it does the waitUntil first). If you have multiple units owning the sector, there could be a cascade of hundreds of thousands (or more?) of instances running at a time, each telling its relevant unit to move to where it already is. 

 

It seems you need a different approach. I haven't had coffee yet, so I'll have to think on it. Also, there are plenty of smarter folks than me here who are likely to jump in.

  • Like 1

Share this post


Link to post
Share on other sites

What is an SC mission? That line you wrote a comment about definitely is a problem. It looks like it just recursively calls itself so as soon as 1 unit respawns, you will start creating hundreds of runs of unitMoveWest. ExecVM runs in a scheduled environment so what happens is you end up with hundreds of scripts all waiting for their turn to run. I'm kind of interested to see what would happen if this was a scheduled environment. Ah, I figured out what you were trying to do while I was typing out something else heheh. You want to select another array element if it is side west. Why not:

_sectorSelected = _sectorsArray call BIS_fnc_selectRandom;

while {_sectorSelected getVariable "owner" == west} do {
	_sectorSelected = _sectorsArray call BIS_fnc_selectRandom;
};

_sectorLoc = getPos _sectorSelected;

That should take care of the issue I think; but you have more code, so, as long as it's here, I'd like to offer some critiques and improvements to it as well:

then {_unit execVM "unitMoveWest.sqf"};
then {_unit execVM "unitMoveEast.sqf"};
then {_unit execVM "unitMoveIndi.sqf"};

"unitMoveWest.sqf"
"unitMoveEast.sqf"
"unitMoveIndi.sqf"

I am seeing this and instantly smelling copy-pasted code. The naming scheme alone implies that this should be a parameter of 1 script, instead of just 3 different scripts. Unless unitMove.sqf is doing more than just switching which script gets run, it would make much more sense to just remove it entirely and directly run a different script. The best part is that you can even move the side check into the script since you are passing the unit:

//the copy-pasted code, etc
switch (side _this) do {
	case west: {
	};
	case east: {
	};
	case independent: {
	};
	default {
	};
}

I think you should combine the 3 scripts into just 1 and then you can use a switch for side-specific logic as seen above

_targetSector_1 = sectornum_1;
_targetSector_2 = sectornum_2;
_targetSector_3 = sectornum_3;
_targetSector_4 = sectornum_4;
_targetSector_5 = sectornum_5;
_targetSector_6 = sectornum_6;
_sectorsArray = [_targetSector_1, _targetSector_2, _targetSector_3, _targetSector_4, _targetSector_5, _targetSector_6];
_sectorSelected = _sectorsArray call BIS_fnc_selectRandom;
// ^ this is all noise
//why not:
_sectorsArray = [sectornum_1, sectornum_2, sectornum_3, sectornum_4, sectornum_5, sectornum_6];
_sectorSelected = _sectorsArray call BIS_fnc_selectRandom;
//And if you only use _sectorsArray 1 time maybe just get rid of that too:
_sectorSelected = [sectornum_1, sectornum_2, sectornum_3, sectornum_4, sectornum_5, sectornum_6] call BIS_fnc_selectRandom;

If you only use a variable 1 time and have no intention of expanding on it that will require you to use it more than once, I see very little value in bothering to save it as a variable

_unit doMove _sectorLoc;
waitUntil {unitReady _unit};

// ToDo Sector owner check
sleep 30;
_unit execVM "unitMoveWest.sqf";

I think you are now familiar with why many people don't like recursion and how it can easily cause issues so what if we make this not recursive by combining it with the first bit of code from this post:

while {true} do {
	_sectorSelected = _sectorsArray call BIS_fnc_selectRandom;

	while {_sectorSelected getVariable "owner" == west} do {
		_sectorSelected = _sectorsArray call BIS_fnc_selectRandom;
	};
	_sectorLoc = getPos _sectorSelected;

	_unit doMove _sectorLoc;
	waitUntil {unitReady _unit};

	// ToDo Sector owner check
	sleep 30;
};

Hope that helps

  • Like 3
  • Thanks 1

Share this post


Link to post
Share on other sites

Helped a lot, thanks so much.

I was using execVM because I thought the Script restarts instead of creating a new instance.
Just for curiosity, how to stop a running script? ... and, when unit dies the script is stopped?

Share this post


Link to post
Share on other sites

Happy to help, as always

 

17 minutes ago, SnowmaneYT said:

Just for curiosity, how to stop a running script? ... and, when unit dies the script is stopped?

ExecVM and spawn both will return a Script Handle object. Then you can terminate it:

_script1 = [] spawn {};
systemChat str _script1;

_script2 = [] execVM "myScript.sqf";
systemChat str _script2;

{
	terminate _x;
} foreach [_script1, _script2];

Also no, the script is not attached to the unit in any way so it would keep running

  • Like 2

Share this post


Link to post
Share on other sites

Sorry for asking so much, i'm just starting in Arma 3 scripting...

 

My question now is related to:

{_respawns = _x addMPEventHandler ["MPRespawn", {
	params ["_unit", "_corpse"];
	_moveLoop = _unit execVM "unitMove.sqf";
	}];
} foreach allUnits;

If I have understood well, i don't need to execVM on respawn because the script is already running, in my case used in editor code on each unit:

nul = this execVM "unitMove.sqf";

 

Share this post


Link to post
Share on other sites

Init fields in editor-placed units do not carry over to respawn.

  • Like 1

Share this post


Link to post
Share on other sites
45 minutes ago, Harzach said:

Great post, @dreadedentity

Thanks, I try to be helpful!

35 minutes ago, SnowmaneYT said:

Sorry for asking so much, i'm just starting in Arma 3 scripting...

Not a problem, we wouldn't answer questions here if it bothered us 😀

35 minutes ago, SnowmaneYT said:

If I have understood well, i don't need to execVM on respawn because the script is already running, in my case used in editor code on each unit:

To be honest, I feel like I never truly mastered MP scripting, so I can't say with certainty but I believe the game gives you a new unit when you respawn, instead of re-using the previous unit. Someone might have to answer that for me. Regardless, it would be far easier to replace the true with alive _unit in the while loop to make the old script die off when the player dies, and just leave the script adding on respawn

35 minutes ago, SnowmaneYT said:

And... using you very nice help unirMove.sqf is, i think, nice and clear:

You're still doing some copy-pasting. The only thing that seems to be changing is this 1 line of code, so that's all you would need to do a switch on:

while {_sectorSelected getVariable "owner" == west} do {
while {_sectorSelected getVariable "owner" == east} do {
while {_sectorSelected getVariable "owner" == independent} do {

Would be something like this:

params ["_unit"];
sleep 1;
_sectorsArray = [sectornum_1, sectornum_2, sectornum_3, sectornum_4, sectornum_5, sectornum_6];
while {true} do {
	_sectorSelected = _sectorsArray call BIS_fnc_selectRandom;
	
	switch (side _unit) do {
		case west: {
			while {_sectorSelected getVariable "owner" == west} do {
				_sectorSelected = _sectorsArray call BIS_fnc_selectRandom;
			};
		};
		case east: {
			while {_sectorSelected getVariable "owner" == east} do {
				_sectorSelected = _sectorsArray call BIS_fnc_selectRandom;
			};
		};
		case independent: {
			while {_sectorSelected getVariable "owner" == independent} do {
				_sectorSelected = _sectorsArray call BIS_fnc_selectRandom;
			};
		};
	};
	
	_sectorLoc = getPos _sectorSelected;

	_unit doMove _sectorLoc;
	waitUntil {unitReady _unit};

	// ToDo Sector owner check
	sleep 30;
};

Now hold on, we still have some repeated code, and yes, we can even get rid of that too:

params ["_unit"];
sleep 1;
_sectorsArray = [sectornum_1, sectornum_2, sectornum_3, sectornum_4, sectornum_5, sectornum_6];
while {alive _unit} do { //loop only runs while unit is alive, when it dies the script will end after it gets to sleep 30;
	_sectorSelected = _sectorsArray call BIS_fnc_selectRandom;
	
	_unitSide = side _unit; //maybe not a huge issue but cache the value so arma doesn't have to get it over and over
	while {_sectorSelected getVariable "owner" == _unitSide} do {
		_sectorSelected = _sectorsArray call BIS_fnc_selectRandom;
	};
	_sectorLoc = getPos _sectorSelected;

	_unit doMove _sectorLoc;
	waitUntil {unitReady _unit};

	// ToDo Sector owner check
	sleep 30;
};

Turns out we didn't need a switch after all!

  • Like 3
  • Thanks 1

Share this post


Link to post
Share on other sites
22 hours ago, dreadedentity said:

while {_sectorSelected getVariable "owner" == _unitSide} do {
	_sectorSelected = _sectorsArray call BIS_fnc_selectRandom;
};

 

_sectorSelected = selectRandom ( _sectorArray select{ _x getVariable "owner" != _unitSide } );

Make a selection from those that are not currently owned by the side of the unit.

  • Like 2

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

×