Jump to content
celludriel

ContainerOpened EventHandler not triggering, locality issue ?

Recommended Posts

Hey,

 

I'm trying to trigger following eventhandler, that is attached to a "CargoNet_01_box_F":

 

			// create the eventhandler ContainerOpened that will reduce the supply drops and clean up the marker
			_drop addEventHandler ["ContainerOpened",{
					[["Supply drop opened"]] call CTISHR_fnc_ctiLog;
					_container = (_this select 0);
					_currentSupplyDrops = missionNamespace getVariable "CURRENT_AMOUNT_OF_SUPPLY_DROPS";
					missionNamespace setVariable ["CURRENT_AMOUNT_OF_SUPPLY_DROPS", (_currentSupplyDrops - 1)];
					_markerName = _container getVariable "supplydropMarkerName";
					deleteMarker _markerName;
				}];

This code runs serverside after spawning a "CargoNet_01_box_F", _drop contains that object.  Yet when I open the inventory the eventhandler never triggers.  This might be a clue, I believe the following appears in the serverlog when I do:

 

18:36:03 Error: Object(3 : 5) not found

I hope it is releated cause I do have a lot of logging flying by at the moment I try it.  Not that this error is saying much.  Can it be this eventhandler only lives on my dedicated servers and when a player opens the box the box on the client side doesn't have the eventhandler ?  I don't see any options to sync the eventhandler and MPEventHandler doesn't seem to be much of an option either.

 

another attempt but still a nogo

			_drop addEventHandler ["ContainerOpened",{
					[["Supply drop opened"]] call CTISHR_fnc_ctiLog;
					_container = (_this select 0);
					[_container] remoteExecCall ["SD_fnc_containerOpened", 2];
				}];

function body

if(!isDedicated) exitWith {};

params ["_container"];

[["Supply drop opened"]] call CTISHR_fnc_ctiLog;
_currentSupplyDrops = missionNamespace getVariable "CURRENT_AMOUNT_OF_SUPPLY_DROPS";
missionNamespace setVariable ["CURRENT_AMOUNT_OF_SUPPLY_DROPS", (_currentSupplyDrops - 1)];
_markerName = _container getVariable "supplydropMarkerName";
deleteMarker _markerName;

Same error appeared: 19:13:30 Error: Object(3 : 4) not found

 

Wish I had more information about that error :(

Share this post


Link to post
Share on other sites

How is the supply crate being created? With CreateVehicle or CreateVehicleLocal? Is there only 1 supply crate that all clients see, or does each client have their own version of the supply crate?

 

The addHandler call should be run on the machine that 'owns' the crate. That means if the server created the crate, this could should only run on the server.

 

What does CTISHR_fnc_ctiLog do? Is it just logging using diag_log? if there's other stuff happening here you need to check the locality of it.

Share this post


Link to post
Share on other sites

Vehicle creation

private _obj = createVehicle [_objectType, _supplyDropStart, [], 0, "NONE"];

Server is creating the supply create with this, I would think this crate is synced to each client ?

Don't worry about ctiLog it's the simplest of functions

params ["_logdata"];

if (DEBUG_ENABLED) then { diag_log format _logdata };

I guess the eventHandler isn't know on all the clients then, only explaination I have.  I can solve the problem another way, but just for once I liked to see the eventHandlers of arma 3 do what they are supposed to do !  I guess they will work in a singleplayer environment when you make the handler on the client and run it on the client, but bleh so limiting.

Share this post


Link to post
Share on other sites

Looking at the ContainerOpened event on the wiki, the arguments you pass in don't have to be local, but the effect of the add handler call is local to the machine. So as you have it setup now it should work, but I think this note gives a clue:

 

Quote

Triggers when cargo container is accessed by player. This event handler is similar to "InventoryOpened" EH, but needs to be assigned to the container rather than the player and cannot be overridden. Note: will trigger only for the unit opening container..

 

Maybe try adding the handler on all machines? The description seems a bit contradictory (what does it mean override? You should be able to add multiple handlers to this event).

 

Do you see it logging the message at least when you run it currently?

 

  • Like 1

Share this post


Link to post
Share on other sites
9 hours ago, igneous01 said:

The addHandler call should be run on the machine that 'owns' the crate. That means if the server created the crate, this could should only run on the server.

Although the crate is created on the server the event needs to be added on each clients machine for it to trigger when a client opens it.

Crate is added on the server. Client has event added so crate would be non local ( Global Argument ), when player opens the crate code is run ( Effect Local ).

Something like...

//Server
TAG_fnc_suppliesOpened = {
	params[ "_container" ];
	
	//Remove function call for container from JIP queue
	remoteExec [ "", format[ "crate_%1", _container call BIS_fnc_netId ] ];
	
	//Tell all clients to remove their EH
	[ "REM", _container ] remoteExec [ "TAG_fnc_handleContainerEH", 0 ]; //No JIP they ether have the event or do not
	
	[["Supply drop opened"]] call CTISHR_fnc_ctiLog;
	_currentSupplyDrops = missionNamespace getVariable "CURRENT_AMOUNT_OF_SUPPLY_DROPS";
	missionNamespace setVariable ["CURRENT_AMOUNT_OF_SUPPLY_DROPS", (_currentSupplyDrops - 1)];
	_markerName = _container getVariable "supplydropMarkerName";
	deleteMarker _markerName;
};

//Where you create supply drop
private _obj = createVehicle [_objectType, _supplyDropStart, [], 0, "NONE"];
_obj setVariable [ "supplydropMarkerName", /*name_of_marker*/ ];

//RE with Unique JIP queue name based off of object NetID
[ "ADD", _obj ] remoteExec [ "TAG_fnc_handleContainerEH", 0, format[ "crate_%1", _obj call BIS_fnc_netId ] ];



//Clients
TAG_fnc_handleContainerEH = {
	params[ "_mode", "_crate" ];
	
	switch ( toUpper _mode ) do {
		
		case "ADD" : {
			
			//Add EH locally to client
			_openedEH = _crate addEventHandler [ "ContainerOpened", {
				params[ "_container", "_unit" ];
				
				//handle opening on server
				[ _container ] remoteExec [ "TAG_fnc_suppliesOpened", 2 ];				
			}];
			
			//Store local EH handle on the container
			_crate setVariable[ "openedEH", _openedEH ];
		};
		
		case "REM" : {
			
			//Get local EH handle
			_EH = _crate getVariable "openedEH";
			if !( isNil "_EH" ) then {
				//Remove the local EH
				_crate removeEventHandler [ "ContainerOpened", _EH ];
			};
		};
	};
};

Untested.

  • Like 1

Share this post


Link to post
Share on other sites

Ok Igneous and Larrow I'm see how it works then.  I have a few concerns though, most importantly JIT.  New clients need to get the event handlers, now this is probably not to hard to accomplish.  It does seems a lot of data that needs to be synced all the time though with a lot that can go wrong.  I'm getting the feeling although I can get it to work like this I have to look for a better solution to the issue.

 

I'm doing all this just to lower the counter for current drops if a player "finds" one of the supply drops.  Preferably I want the server to handle this logic, the clients shouldn't have to do any work for this.  So sending over eventhandlers to clients ... sounds ... yukkie ...  I could spawn a monitor thread at server side that checks every five seconds if there is a player near to the drop, when it is the players have "found" the drop and I can do cleanup severside.  Sounds cleaner then the monkeywork I'm doing now.  However I'm sure I might have to do other eventhandling at one point where this knowledge comes in handy.

 

Thanks for brainstorming with me !

Share this post


Link to post
Share on other sites
1 hour ago, celludriel said:

I have a few concerns though, most importantly JIT.  New clients need to get the event handlers, now this is probably not to hard to accomplish.

As easy as adding a unique String for the JIP part of remoteExec. Changed code above to handle JIP by adding a unique JIP queue name based of the objects NetID.

1 hour ago, celludriel said:

 It does seems a lot of data that needs to be synced all the time though with a lot that can go wrong.

I dont see a lot of data that needs to be kept in sync, you send a reference of an Object and a String across the network via remoteExec, any JIP also receive the Object and String with function call and the engine just handles the crate EH. Once opened by someone all clients remove their events and the server removes the RE call from the queue.

1 hour ago, celludriel said:

Preferably I want the server to handle this logic, the clients shouldn't have to do any work for this.

Would not call it much work, its not like their keeping track of anything, all they do is either add or remove an event. You could even move the remove event from the clients ContainerOpened EH and add it to the server function instead (see changed code).

1 hour ago, celludriel said:

So sending over eventhandlers to clients ... sounds ... yukkie ...  I could spawn a monitor thread at server side that checks every five seconds if there is a player near to the drop, when it is the players have "found" the drop and I can do cleanup severside.

Its a object reference and a String that is pretty much forgotten about until the engine comes to fire it off. I would much prefer to add a client event than add another thread to the servers scheduler. Why start up a scheduler thread on the server when you can just let the engine handle an event for you.

  • Like 1

Share this post


Link to post
Share on other sites

You make some valid points, guess I've always been client shy, the less responsibility to my clients the happier I am.  I do this in my day job as well, although I have to admit, there I have loadbalancing and server sharding ... so yeah ... programming sqf is a whole other beast

Share this post


Link to post
Share on other sites

Thanks Larrow,

 

I got it to work with your implementation.  For those interested you can see what I wrote here:

 

https://github.com/Celludriel/Simple_CTI/tree/develop/custom/modules/SupplyDropModule

 

I wonder though, syncing eventhandlers to clients is something that happens often in code.  I think this can be written more generic where we send the handling of the event to the client in the form of a parameter.  Then the function to add or remove eventhandlers on client side can be totally generic and reusable.  For now I'll keep it as is, but if I have to do this more often in the future I might refactor this to something generic if possible.

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

×