Jump to content
Sign in to follow this  
osu_apoc

waitUntil Not Always Exiting

Recommended Posts

I've developed an airdrop system for Wasteland, the gist of which is a player makes a selection via a command menu, a heli and the object are spawned, object attaches to heli. The heli then flies to the drop point (through) and on to a final destination. I've got a waitUntil in the server function which should check a distance between the heli and drop point for release, OR if the heli's pilot's current waypoint is equal to the final waypoint, the object should detach. My issue is that on some servers, under load (not always though) the heli flies through the point and the object never detaches. This is not consistent behavior. On my test server I'd say it is 100% reliable when I've tried it.

My script follows this model:

Client uses command menu to initialize a client-sided script. This client sided script does some permission/funding checks, and passes arguments to a server-sided function (compiled at server start(compile preprocessFileLineNumbers)) via BIS_fnc_MP.

Typically everything works fine, but as I've said, sometimes the heli just overflies the drop point and continues on to its final destination without dropping the object.

Here's the code for the server function:

//Server Function for Airdrop Assistance
//Arguments passed from client script and command menus
//Author: Apoc
//Credits: Some methods taken from Cre4mpie's airdrop scripts, props for the idea!
//Starts off much the same as the client start.  This is to find information from config arrays


private ["_type","_selection","_player"]; //Variables coming from command menu and client side APOC_cli_startAirdrop
_type 				= _this select 0;
_selectionNumber 	= _this select 1;
_player 			= _this select 2;

diag_log format ["SERVER - Apoc's Airdrop Assistance - Player: %1, Drop Type: %2, Selection #: %3",name _player,_type,_selectionNumber];
//hint format ["Well we've made it this far! %1, %2, %3,",_player,_type,_selectionNumber];
_selectionArray = [];

switch (_type) do {
case "vehicle": {_selectionArray = APOC_AA_VehOptions};
case "supply": 	{_selectionArray = APOC_AA_SupOptions};
case "picnic":	{_selectionArray = APOC_AA_SupOptions};
default 		{_selectionArray = APOC_AA_VehOptions; diag_log "AAA - Default Array Selected - Something broke";};
};

_selectionName 	= (_selectionArray select _selectionNumber) select 0;
_selectionClass = (_selectionArray select _selectionNumber) select 1;
_price 			= (_selectionArray select _selectionNumber) select 2;

_playerMoney = _player getVariable ["bmoney", 0];
if (_price > _playerMoney) exitWith{};

_playermoney = _player setVariable ["bmoney", _playermoney - _price, true];
[_player] spawn fn_savePlayerData;

//OK, now the real fun

/////// Let's spawn us  an AI helo to carry the cargo /////////////////////////////////////////////////

_heliType = "B_Heli_Transport_03_unarmed_F";
_center = createCenter civilian;
_grp = createGroup civilian;
if(isNil("_grp2"))then{_grp2 = createGroup civilian;}else{_grp2 = _grp2;};
_flyHeight = 350;
_dropSpot = [(position _player select 0),(position _player select 1),_flyHeight];
_heliDirection = random 360;
_flyHeight = 200;  //Distance from ground that heli will fly at
_heliStartDistance = 5000;
_spos=[(_dropSpot select 0) - (sin _heliDirection) * _heliStartDistance, (_dropSpot select 1) - (cos _heliDirection) * _heliStartDistance, (_flyHeight+200)];

diag_log format ["AAA - Heli Spawned at %1", _spos];
_heli = createVehicle [_heliType, _spos, [], 0, "FLY"];
_heli allowDamage false;
_heli setVariable ["R3F_LOG_disabled", true, true];
[_heli] call vehicleSetup;

_crew = [_grp, _spos] call createRandomSoldierC;
_crew moveInDriver _heli;
_crew allowDamage false;

_heli setCaptive true;

_heliDistance = 5000;
_dir = ((_dropSpot select 0) - (_spos select 0)) atan2 ((_dropSpot select 1) - (_spos select 1));
_flySpot = [(_dropSpot select 0) + (sin _dir) * _heliDistance, (_dropSpot select 1) + (cos _dir) * _heliDistance, _flyHeight];

_grp setCombatMode "BLUE";
_grp setBehaviour "CARELESS";

{_x disableAI "AUTOTARGET"; _x disableAI "TARGET";} forEach units _grp;

_wp0 = _grp addWaypoint [_dropSpot, 0, 1];
[_grp,1] setWaypointBehaviour "CARELESS";
[_grp,1] setWaypointCombatMode "BLUE";
[_grp,1] setWaypointCompletionRadius 20;
_wp1 = _grp addWaypoint [_flySpot, 0, 2];
[_grp,2] setWaypointBehaviour "CARELESS";
[_grp,2] setWaypointCombatMode "BLUE";
[_grp,2] setWaypointCompletionRadius 20;
_heli flyInHeight _flyHeight;

//////// Create Purchased Object //////////////////////////////////////////////
_object = switch (_type) do {
case "vehicle":
{
	_objectSpawnPos = [(_spos select 0), (_spos select 1), (_spos select 2) - 5];
	_object = createVehicle [_selectionClass, _objectSpawnPos, [], 0, "None"];
	diag_log format ["Apoc's Airdrop Assistance - Object Spawned at %1", position _object];
	_object setVariable ["A3W_purchasedStoreObject", true];
	_object setVariable ["A3W_purchasedVehicle", true, true];
	_object setVariable ["ownerUID", getPlayerUID _player, true];
	[_object, false] call vehicleSetup;
	if (_object getVariable ["A3W_purchasedVehicle", false] && !isNil "fn_manualVehicleSave") then
	{
		_object call fn_manualVehicleSave;
	};
	_object attachTo [_heli, [0,0,-5]]; //Attach Object to the heli
	_object
};	
case "supply":
{
_objectSpawnPos = [(_spos select 0), (_spos select 1), (_spos select 2) - 5];
_object = createVehicle ["B_supplyCrate_F", _objectSpawnPos, [], 0, "None"];
_object setVariable ["A3W_purchasedStoreObject", true];
[_object, _selectionClass] call fn_refillbox;
_object attachTo [_heli, [0,0,-5]]; //Attach Object to the heli
_object 
};
case "picnic":  //Beware of Bears!
{
_objectSpawnPos = [(_spos select 0), (_spos select 1), (_spos select 2) - 5];
_object = createVehicle ["B_supplyCrate_F", _objectSpawnPos, [], 0, "None"];
diag_log format ["Apoc's Airdrop Assistance - Object Spawned at %1", position _object];
_object setVariable ["A3W_purchasedStoreObject", true];
_object attachTo [_heli, [0,0,-5]]; //Attach Object to the heli
_object
}
};
_object allowDamage false; //Let's not let these things get destroyed on the way there, shall we?

diag_log format ["Apoc's Airdrop Assistance - Object at %1", position _object];  //A little log love to confirm the location of this new creature

//Wait until the heli is close to the drop spot, then move on to dropping the cargo and all of that jazz

// fix the drop distance between vehicles and ammo boxes - Creampie
if (_type == "vehicle") then {
WaitUntil{
	if ((([_heli, _dropSpot] call BIS_fnc_distance2D)<125)||(currentWaypoint _grp == 2)) exitWith{true};
	false
};
} else {
WaitUntil{
	if((([_heli, _dropSpot] call BIS_fnc_distance2D)<50)||(currentWaypoint _grp == 2)) exitWith {true};
	false 
};
};

detach _object;  //WHEEEEEEEEEEEEE
_objectPosDrop = position _object;
_heli fire "CMFlareLauncher";
_heli fire "CMFlareLauncher";

//Delete heli once it has proceeded to end point
[_heli,_grp,_flySpot,_dropSpot,_heliDistance] spawn {
	private ["_heli","_grp","_flySpot","_dropSpot","_heliDistance"];
	_heli = _this select 0;
	_grp = _this select 1;
	_flySpot = _this select 2;
	_dropSpot = _this select 3;
	_heliDistance = _this select 4;
	while{([_heli, _flySpot] call BIS_fnc_distance2D)>200}do{
		if(!alive _heli || !canMove _heli)exitWith{};
		sleep 5;
	};
	waitUntil{([_heli, _dropSpot] call BIS_fnc_distance2D)>(_heliDistance * .5)};
	{ deleteVehicle _x; } forEach units _grp;
	deleteVehicle _heli;
};

//Time based deletion of the heli, in case it gets distracted
[_heli,_grp] spawn {
	private ["_heli","_grp"];
	_heli = _this select 0;
	_grp = _this select 1;
	sleep 30;
	if (alive _heli) then
	{
		{ deleteVehicle _x; } forEach units _grp;
		deleteVehicle _heli;
		diag_log "AIRDROP SYSTEM - Deleted Heli after Drop";
	};
};

WaitUntil {(((position _object) select 2) < (_flyHeight-20))};
	_heli fire "CMFlareLauncher";
	_objectPosDrop = position _object;
	_para = createVehicle ["B_Parachute_02_F", _objectPosDrop, [], 0, ""];
	_object attachTo [_para,[0,0,-1.5]];

	_smoke1= "SmokeShellGreen" createVehicle getPos _object;
	_smoke1 attachto [_object,[0,0,-0.5]];
	_flare1= "F_40mm_Green" createVehicle getPos _object;
	_flare1 attachto [_object,[0,0,-0.5]];

	if (_type == "vehicle") then {_object allowDamage true;}; //Turn on damage for vehicles once they're in the 'chute.  Could move this until they hit the ground.  Admins choice.

WaitUntil {((((position _object) select 2) < 1) || (isNil "_para"))};
	detach _object;
	_smoke2= "SmokeShellGreen" createVehicle getPos _object;
	//_smoke2 attachto [_object,[0,0,-0.5]];
	_flare2= "F_40mm_Green" createVehicle getPos _object;
	//_flare2 attachto [_object,[0,0,-0.5]];
	sleep 2;
	if (_type == "picnic") then {  //So let's go ahead and delete that ugly ammo pallet and create a wonderful picnic basket/barrel
		_objectLandPos = position _object;
		deleteVehicle _object;	
		_object2 = switch (_selectionClass) do {
			case "Land_Sacks_goods_F": {
				_object2 = createVehicle [_selectionClass, _objectLandPos, [], 0, "None"];
				_object2 setVariable ["food", 50, true];
				_object2
			}; //A very big picnic, no?
			case "Land_BarrelWater_F": {
				_object2 = createVehicle [_selectionClass, _objectLandPos, [], 0, "None"];
				_object2 setVariable ["water",50, true];
				_object2
			};		
		};
	};

Does anyone have any tips on how I can sort out this issue? I've captured no errors in client or server logs that point to any issue with this system.

Share this post


Link to post
Share on other sites

Yes waitUntil and while conditions are pretty unreliable.

What I do is have a while command with an exitwith to replace the waituntil

For example;


// REPLACE WAITUNTIL
while {true} do {
sleep 0.1;
if (((((position _object) select 2) < 1) || (isNil "_para"))) exitWith {};
};
// CONTINUE CODE HERE AS USUAL

Share this post


Link to post
Share on other sites

Make sure you take the flying altitude into consideration when doing distance checks for aircrafts,

if the waituntil loop has to finish when the chopper is at 50m distance, but the chopper itself is flying at 60m altitude the condition will never return true, even if the chopper is hovering above the target location.

If that's the solution to your problem, it certainly has been for me, heh.

Share this post


Link to post
Share on other sites
Yes waitUntil and while conditions are pretty unreliable.

What makes you say that? To my knowledge and experience this is not true.

---------- Post added at 19:24 ---------- Previous post was at 19:13 ----------

Does anyone have any tips on how I can sort out this issue? I've captured no errors in client or server logs that point to any issue with this system.

Something I quickly noticed:

switch (_type) do {
case "vehicle": {_selectionArray = APOC_AA_VehOptions};
case "supply": 	{_selectionArray = APOC_AA_SupOptions};
case "picnic":	{_selectionArray = APOC_AA_SupOptions};
default 		{_selectionArray = APOC_AA_VehOptions; diag_log "AAA - Default Array Selected - Something broke";};
};

You allow for _type be something other than "vehicle", "supply" or "picnic" here ^^^^. But here:

_object = switch (_type) do {
....

You have no default {} statements. Switch is strange and if you have no 'default' the switch would return boolean, while as I can see you want object. This would most definitely introduce some unexpected behaviour.

Also

_grp = createGroup civilian;

I assume you know you can do this only 144 times after which you will start getting <group-NULL> for _grp with all the consequences.

Edited by Killzone_Kid

Share this post


Link to post
Share on other sites

Setting a small completion radius on a helicopter waypoint cannot be guaranteed. Helicopters have a hardcoded default completion radius.

I know back in beta completion radius was 500m, although just quickly testing a MH9 it is completing at 100m so it maybe dependent on heli type.

Depending the angle of approach, speed etc to drop position and angle to next waypoint your heli distance to WP1 may never become smaller than 50 meters and as it already has its next watpoint it will carry on to it.

You would be better off not giving it the second waypoint until it has definitely reached your desired position/distance to the first waypoint and dropped its cargo. e.g

_wp = _heli addWaypoint [ _dropPos, 0 ];

waitUntil { [ waypointPosition _wp, _heli ] call BIS_fnc_distance2D < 50 };

//Drop cargo

_wp = _heli addWaypoint [ _deletePos, 0 ];

Share this post


Link to post
Share on other sites

Because as i said in my previous post completion will happen at the hardcoded distance of 100m and from reading the OP's script they would like it nice and close. But yes that would be the default solution and would save headaches, just drop the lot when it gets as close as its definitely likely to.

Share this post


Link to post
Share on other sites
Because as i said in my previous post completion will happen at the hardcoded distance of 100m and from reading the OP's script they would like it nice and close. But yes that would be the default solution and would save headaches, just drop the lot when it gets as close as its definitely likely to.

You can set waypoint completion radius AFAIK https://community.bistudio.com/wiki/setWaypointCompletionRadius doesn't have to be hardcoded.

Share this post


Link to post
Share on other sites
You can set waypoint completion radius AFAIK https://community.bistudio.com/wiki/...mpletionRadius doesn't have to be hardcoded.

Yes you can set a completion radius when setting up a waypoint BUT certain vehicle classes have a default hardcoded completion in their configs (think it maybe the precision value but not really looked into it).

e.g MH9 is 100m

It does not matter what you put in a waypoint, whether inserted in editor or by script. These vehicles will always complete their waypoints if they get closer than hardcoded value.

You can see this yourself by placing an MH9 in the editor, give it a waypoint with completion radius of 20m. Then in the waypoint onAct place....

hint format [ "completed\nDis : %1", [waypointPosition [group this,currentWaypoint group this] , this] call bis_fnc_distance2d];

it will always fire off just under 100m

Makes sense i suppose you dont want someone placing a fast moving jet with a default waypoint (default has completion of 0) as the jet would just keep circling trying to hit the spot before it could move on.

Edited by Larrow
spelling

Share this post


Link to post
Share on other sites

it will always fire off just under 100m

Yep, seems to be hardcoded to max 100, you can set it to less and heli will obey. Still for cargo drop 100 and less is perfect, no real need for waituntil nonsense.

Share this post


Link to post
Share on other sites

I know its hardcoded at 100m, ive been telling you that since 8.30 this evening even though you chose to ignore what i was saying, but thanks for the confirmation :rolleyes:

So i will read that as

"I see what you mean Larrow, in that case i will ignore the OPs original idea and announce default is the solution and continue to debunk and ridicule other options as nonsense"

Jeez whatever chap.

Share this post


Link to post
Share on other sites

"I see what you mean Larrow, in that case i will ignore the OPs original idea and announce default is the solution and continue to debunk and ridicule other options as nonsense"

Jeez whatever chap.

Huh? What do you mean?

Share this post


Link to post
Share on other sites
What makes you say that? To my knowledge and experience this is not true.

_object = switch (_type) do {
....

You have no default {} statements. Switch is strange and if you have no 'default' the switch would return boolean, while as I can see you want object. This would most definitely introduce some unexpected behaviour.

Yeah, was adding stuff in and apparently got lazy. I'll fix that right up.

_grp = createGroup civilian;

I assume you know you can do this only 144 times after which you will start getting <group-NULL> for _grp with all the consequences.

Thankfully there's a cleanup script running in the base Wasteland code that will delete the empty groups fairly routinely, so I'm not terribly concerned with this point yet. However, I'll add in the code to delete the group once the heli has done its job and has been removed.

---------- Post added at 00:05 ---------- Previous post was at 23:58 ----------

Larrow and Killzone_Kid, seems like you are contradicting each other. Is the config not allowing the waypoint completion beyond 100m, or ALWAYS completing once within 100m?

For my purposes, this is not much of an issue, though when dropping vehicles their inertia can be an issue, so I've had to drop them at around 125m for them to be near to the player. For this case, I may just have the chopper sit at the waypoint and then detach, then add the new waypoint for an exit point.

I'll try using the waypoint statements method (I was not familiar with this previously) to execute the drop. While using the waitUntil, my second conditional statement should have picked up the change in waypoint, even at the end of the flight path. However, the heli would proceed to waypoint 2 and just hover. It seems like the waitUntil, at some point, just stopped being evaluated. Though, none of the subsequent code seemed to be either.

Share this post


Link to post
Share on other sites
Is the config not allowing the waypoint completion beyond 100m, or ALWAYS completing once within 100m?

You can set it to what ever you like BUT some vehicles ( maybe all, have not looked ) have a hardcoded minimum. e.g the helicopters have a minimum of 100m.

So if you set a waypoint completion at 250m it will complete at 250m.

If you set it to 20m, a helicopter will complete at 100m.

Share this post


Link to post
Share on other sites
You can set it to what ever you like BUT some vehicles ( maybe all, have not looked ) have a hardcoded minimum. e.g the helicopters have a minimum of 100m.

So if you set a waypoint completion at 250m it will complete at 250m.

If you set it to 20m, a helicopter will complete at 100m.

Interesting. Thanks for that info!

It seems as if you cannot actually execute any real code within the setWaypointStatements statement. No local objects seem to be recognized there. The only thing I can get to be recognized are the group leader, and I suspect I could list the units in the group, per the wiki. I tried a basic log entry for the position of the heli in the waypointStatement, to no avail. Though when I used position of the group leader, the log entry showed just fine.

---------- Post added at 02:40 ---------- Previous post was at 01:41 ----------

Well, Larrow, I looked back upon a post of yours from 2013, which showed how the support module was running the paradrop system.

Wow... that thing is clunky. I think I'll give the While {true} method a go and see if that helps the other servers out.

Share this post


Link to post
Share on other sites
What makes you say that? To my knowledge and experience this is not true.

---------- Post added at 19:24 ---------- Previous post was at 19:13 ----------

Its probably just my trial and error approach to building scripts. But I have noticed some irregularities with the while condition. I try to avoid Waituntil commands where possible so i don't have as much experience with them. I assumed they would behave similarly

Share this post


Link to post
Share on other sites
......

I set completion radius to 20m for MH9 yesterday and it literally landed to complete, so making radius too small is not ideal. You can always move waypoint back if you know direction from where the drop is coming so it drops it a bit earlier.

Share this post


Link to post
Share on other sites
It seems as if you cannot actually execute any real code within the setWaypointStatements statement. No local objects seem to be recognized there.

This is to be expected, it is no different to an eventHandler, as the code provided is deferred till the event of the waypoint completing and is run un-scheduled within its own scope.

The only thing I can get to be recognized are the group leader, and I suspect I could list the units in the group, per the wiki. I tried a basic log entry for the position of the heli in the waypointStatement, to no avail.

The group leader that is given the waypoint is passed into the code provided as this. Therefor the vehicle should be available as vehicle this.

Well, Larrow, I looked back upon a post of yours from 2013, which showed how the support module was running the paradrop system.

Wow... that thing is clunky. I think I'll give the While {true} method a go and see if that helps the other servers out.

Wouldn't describe it as clunky myself, again it is what it is, code ( passed as a string ) deferred to run un-scheduled within its own scope when the waypoint completes. Which is no different to any eventHandler other than the code is passed as a string (even eventHandler code can be given as a string). Which is no different to passing a script file which is basically a stream of text characters that the engine compiles to code.

myScript.sqf

hint format [ "this - %1\npos - %2\nveh - %3\npos - %4", typeOf this, getPosATL this, typeOf ( vehicle this ) , getPosATL ( vehicle this ) ];

init.sqf

_wp = group myObject addWaypoint [ [ myobject, 50, getDir myObject ] call BIS_fnc_relPos, 0 ];
_wp setWaypointStatements [ "true", loadFile "myScript.sqf" ];

use preProcess if the file has comments and other stuff that need processing.

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  

×