Jump to content
Sign in to follow this  
BEAKSBY

How do loop foreach allUnits+vehicles thoughout the game?

Recommended Posts

Correction: How to loop foreach allUnits+vehicles thoughout the game?

I am running a "Killed" event handler with foreach allUnits+vehicles in the initServer.sqf.

But new vehicles and units will be created during gameplay and the Killed" event handler must be executed on these.

SO, I need a way to ensure foreach allUnits+vehicles is continously run throughout the game without affecting gameplay performance (slowdowns).

What is the best way to do this? Also, this if for MP so it must be run even when the player is dead.

initServer.sqf

// add correct EVH's to vehicle types
{
switch (true) do {
case   (_x iskindof "Man") : { 
           _x addeventhandler ["killed",{_this execvm "inf.sqf";}];   
           }; 
case   (_x iskindof "Car") : { 
           _x addeventhandler ["killed",{_this execvm "veh.sqf";}];               
           }; 
}; //end switchcase

} foreach allUnits+vehicles;

Edited by BEAKSBY

Share this post


Link to post
Share on other sites

In the script which spawns the new unit or vehicle, so:

_veh = createVehicle blablabla

_veh addeventhandler ["killed",{_this execvm "veh.sqf";}];

Because looping allUnits constantly will make checks of every unit all the time, even the ones you already added the eventhandler. Not very useful.

Share this post


Link to post
Share on other sites

So, here is the initial way that I would tackle this problem. So you obviously need a infinite loop to continuously do this. The first thing that you will notice is that what you currently have will constantly add more and more event handlers until it will murder the server. So, what needs to happen is you need to be able to check whether or not you have already added an event handler to the unit. I believe you can do this with unit/vehicle ID's. So you need a persistent array (meaning is beyond the loop's scope so it is not deleted every loop) that holds the ID's of each unit or vehicle that you have added the event handler to. So each time you encounter a new ID not in your array, add the event handler. Everytime you encounter an ID inside your array, just ignore it. To make sure it does not eat up too much processing, probably want to run this in a separate thread as well as a small sleep everytime you check a vehicle. Should make the hit on the system extremely small.

Share this post


Link to post
Share on other sites
If you're using the createVehicle command, why not use an "Init" eventhandler to add the "Killed" EH? It should fire every time something is created with CreateVehicle.

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

So this is what I've done L3TUC3 and it works (so far), but to flyinpenguin's point, I'm wondering if all these units with EVHs being added will slow down the server? Should I be removing the EVHs after the unit is killed? I though it just decrements the EVH registry so didn't think it was worth it to save the server, also I thought it might cause problems too.

Share this post


Link to post
Share on other sites

it should not be _this, its should be _this select 0.

if you read the Event Handlers _this select 0 is the unit while _this select 1 is the person that killed the unit ;)

Share this post


Link to post
Share on other sites
So this is what I've done L3TUC3 and it works (so far), but to flyinpenguin's point, I'm wondering if all these units with EVHs being added will slow down the server? Should I be removing the EVHs after the unit is killed? I though it just decrements the EVH registry so didn't think it was worth it to save the server, also I thought it might cause problems too.

Infinte loop for this sounds like nonsense to me. But, you use the word "wondering" in your sentence above. Have you actually tried adding EHs to units as they're generated and tested it? From the sounds of it you haven't. Seeing as it's a quick job to test, you should always check it out before coming on a public forum and asking people who don't have access to your mission.

Just try it 3 times with adding EH's and 3 times without. Keep and eye on the FPS and compare. Then you will know and will not have to make guesstimations as to whether a method is bad or not. You need to be the arbiter and are perfectly placed to judge whether it is good or bad before checking things like infinite loops. (Which have their place but also their limitations).

EH's were introduced to avoid things like constant checks, but as I say, without testing it, you will not know.

Share this post


Link to post
Share on other sites

I am currently running my script with the Killed and epecontact EVHs attached to units created in game. My game consists mostly of creating units as you gererate constant money to buy them. It is a remake of The Outfit, from the xbox360. I did not notice any slow downs...but then I'm only testing on LAN, 1 vs 1 player.

I will check the fps as a baseline, never thought of that, thanks.

Edited by BEAKSBY

Share this post


Link to post
Share on other sites

Okay so if there's a limitation with explosions then I see your problem.

You could check for isNull so that code doesn't get executed on units that have been deleted before dying.

While {true} do {
   {
       if (not isNull _x) then {
           // your code here
       };
   } forEach allUnits + vehicles;
   sleep 1;
};

Could slow down with a LOT of units though.

Or have a global array to work from which you add to and delete from as units are spawned and killed. (Might be problematic if units are spawned in by different computers and you need to PV).

But then there is no other reliable way to check who killed a unit anyway - "hit" EH doesn't fire sometimes when unit is killed and also "handleDamage" doesn't return the source all of the time. I'm off to work soon but will have a think about this one.

EDIT: Deffo don't add EH's in a loop on units as you will add them multiple times to the same unit and then your FPS will definitely suffer. Just add them as the units are spawned as mentioned by someone above.

Edited by Das Attorney

Share this post


Link to post
Share on other sites

I'm using something like this:

while {true} do {
{

if (isNil {_x getvariable "somefancyvariable"}) then {

	[_x] call GOM_fnc_somefancyfunction;
	_x setvariable ["somefancyvariable",true,true];
};

} foreach allunits;

sleep 5;
};

Got a few of these loops running (for various selections of units) and didn't notice any performance difference between running the check for a good chunk of units or not.

Is there an actual better way of doing this?

Share this post


Link to post
Share on other sites

I normally play it by ear when it comes to these things - for stuff like checking positions of units, there's not really any other options than a loop. I guess you could use triggers but then they're doing a 0.5 second loop check anway.

A large part of it will depend on the frequency of checks and the code you're calling.

Share this post


Link to post
Share on other sites

Just for a "Killed" EH it is much better to add it when the unit is created.

Share this post


Link to post
Share on other sites

Question: an EH is not removed if you deleteVehicle the unit??????

I thought yes.

So, if yes, adding EH when you create the unit + a cleanup script is the best way, isn't it?

Share this post


Link to post
Share on other sites

It is part of the unit that is created, so if you delete this unit, the EH will be deleted too.

Share this post


Link to post
Share on other sites

vehicles will return a lot more than just ground\sea\air vehicles, it will also include ammo boxes and lots of other objects which you will need to filter out

1) So to optimise this you would initially filter out actual vehicles and add the KilledEH to them

2) At this point, you have now attached a Killed EH to all known vehicles in the vehicle array

3) Save the vehicles array as something else, you will later use this to compare to any later call on the vehicles array and this will allow you to quickly reduce the number of elements in the array to scan through

NB>>

1) As per previously stated, the better way to do this is to add it to the init fields of the vehicles/units when they are getting created

2) Always call a function in an eventhandler, eg Precompiled and saved to memory

ExecVM means that each time the script is called for, it will compile then run, you want Eventhandler code to run as efficiently as possible so use a precompiled function because that is already compiled and stored in memory and will instantly run. (If there are sleeps or waituntil's in the vehicle scripts, then still precompile the function but spawn it rather than call it

so something like

  // Use an MP Eventhandler and only run this loop on the server
 if!(IsServer)exitwith{};

 // filter out actual vehicles, in this example, if it has a driver slot
 // You could filter using alternative methods, eg  IsKindof "AIR" etc
 // You could also filter for anything that is locked, e.g non enterable
 {
if (getNumber (configfile >> "CfgVehicles" >> typeof _x >> "hasDriver")==1)then
 	{
	_x addMPeventhandler ["MPkilled",{_this call MyTag_VehKilled;}];
	_x setvariable ["KilledEH",TRUE];
};
 }foreach Vehicles;

 // Now save this array for later use
 _CheckedVehicles = Vehicles;

 // the following variable will contain any new vehicles that you were not aware of on a previous loop
 _newVehicles = [];

 // We now have all vehicles that started on the map with their Eventhandlers
 // We can now set up a slow loop and monitor for anything new being created
 // in this case every 30 seconds
 while{TRUE}do
 {
sleep 30;
_NewVehicles = [];
// Remove checked vehicles from the updated vehicle array to create a list of new vehicles
_NewVehicles = Vehicles - _CheckedVehicles;
// Now if there are new vehicles add an eventhandler to those that have a driver position
if(Count _NewVehicles >0)then
{
  	{
		if (getNumber (configfile >> "CfgVehicles" >> typeof _x >> "hasDriver")==1)then
 			{
			_x addMPeventhandler ["MPkilled",{_this call MyTag_VehKilled;}];
			_x setvariable ["KilledEH",TRUE];
 			};
		// Update the checkedvehicles array to include the new found vehicles;
		// so they wont exist in the NewVehicles array on the next loop
		_CheckedVehicles = _CheckedVehicles + [_x];
                       sleep 0.2;

	}foreach _NewVehicles;
};
 };

I havent given you all the code, you can learn from this.

I also added a setvariable, you can check for this before adding a KilledEH if you so wished

Run 2 looping scripts, one for vehicles and then do something similar for AllUnits

Edited by Terox

Share this post


Link to post
Share on other sites

OK Thanks Terox,

I'll need some time to digest this and adapt into my script. I will measure the FPS and compare with my current scripts that adds the EVHs in the initServer.sqf for existing allUntis + vehicles and in the scripts below createVehicle / createUnit for new units created during the game.

My MP game will be able up to support up to 4 Vs 4 maximum, but each player will be creating one or two vehicles at a time before they are killed and cleaned-up with a number of StaticWeapons and units each...so it could get quite busy. SO far I'm only tesing in LAN 1V1 and over the INTERNET with a friend (monthly).

I also use a clean-up scipt below that removes vehicles including their crew as well as allDead...

For the most part, the game generally slows down with particle effects like smoke and fire...I try and limit this to 1 minute before I delete the vehicle emmitting it.

cleanup.sqf

/*private ["_delcode", "_currentworld", "_oldworld", "_newworld", "_notalive"];
_delcode = {
private ["_crew"];
sleep 100;
{
	if (not isNull _x) then {
		if (_x isKindOf "Ship" or _x isKindOf "Air" or _x isKindOf "LandVehicle") then {
			_crew = nearestObjects [_x, ["Man"], 20];
			_crew = _crew + crew _x;
			deleteVehicle _x;
			{
				if (not alive _x) then {deleteVehicle _x};
			} forEach _crew;
		} else {
			deleteVehicle _x;
		};
	};
} forEach _this;
};
_currentworld = [];
while {true} do {
sleep 3;
_oldworld = _currentworld;
_currentworld = + list wholeworld;
_newworld = _oldworld - _currentworld;
_notalive = [];
{
	if (not alive _x) then {_notalive = _notalive + [_x]};
} forEach _newworld;
if (count _notalive > 0) then {_notalive spawn _delcode};
};*/

while {true} do
{
sleep 10;

	{
	_sandGlass = _x getVariable "RYD_DeathTime";
	if (isNil "_sandGlass") then 
		{
		_x setVariable ["RYD_DeathTime",time]
		}
	else
		{
		if ((time - _sandGlass) > 60) then
			{
			deleteVehicle _x
			}

		}
	}
foreach AllDead;
};

Share this post


Link to post
Share on other sites

Yup Terox, your a beast. Only addition is to add a small sleep function inside the foreach that is in the while loop. Will balance the load to make sure it will not stutter the game even under heavy load. Something like a 0.2 or even a 0.1

Edited by flyinpenguin

Share this post


Link to post
Share on other sites
Yup Terox, your a beast. Only addition is to add a small sleep function inside the foreach that is in the while loop. Will balance the load to make sure it will not stutter the game even under heavy load. Something like a 0.2 or even a 0.1

Maybe, but I wouldnt expect the _newvehicles array to have any more than 2 or 3 elements, so that shouldnt be an issue, but I updated the code with your suggestion

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  

×