Jump to content

Recommended Posts

All,

 

Although I'm mostly retired as a mission maker, my players saw that HandleHeal is said to be fixed, so I knocked up this code.
But I'm getting nothing, no hints, nothing. The EH is being added, I'm sure of that I have a script called eventhandlers.sqf that takes care of this and all the other EHs added there work.
Help gratefully accepted

 

player addEventHandler ["HandleHeal", 
{
	[] spawn 
	{
		hint "handleheal active 1";
		params ["_injured", "_healer", "_isMedic", "_atVehicle", "_action"];
		private _damage = damage _injured;
		hint "handleheal active 2";
		if (_injured isNotEqualTo _healer) then 
		{
			waitUntil { (damage _injured isEqualTo 0) or (damage _injured isEqualTo 1) };
			hint "waituntil released";
			if (damage _injured isEqualTo 0) then
			{// give healer points
				[_healer,1] remoteExec ["addScore",2];
				hint "giving points";
			};
			

		};
	};
}];

 

Share this post


Link to post
Share on other sites

Hi, just a quick notice, you're using [] spawn {}, but that doesn't have any params itself. Try use _this spawn {} instead. That should pass event parameters and code should run. If not, there might be locality problem. Depends how you execute the script.

Share this post


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

Hi, just a quick notice, you're using [] spawn {}, but that doesn't have any params itself. Try use _this spawn {} instead. That should pass event parameters and code should run. If not, there might be locality problem. Depends how you execute the script.

Thanks for that - I've tried that before and it didn't work, but I tried it again just now to be absolutely sure and it still doesn't work. Not even getting the hints.
I don't think locality is the issue. The eventhandler is added in a file called eventhandlers.sqf which runs on all clients. The other EHs added here work OK.

Share this post


Link to post
Share on other sites

Right, I'm getting the active 2 hint when I heal myself, so the EH is working, sort of

Share this post


Link to post
Share on other sites

Right, so I tried the code with local hosted MP from Eden,  I've put your code into initPlayerLocal.sqf and had two playable characters, one medic (client 1) and other normal soldier (client 2). I noticed that it indeed didn't show the hints, except when injured player healed himself. That is because how the event is designed. It only triggers on PC where healer is local. But in that case initPlayerLocal only added event on connected PC, that caused only self heal to be registered because addEventHandler has local effect-no other client knows that you should be registered for healing.
So when I remoteExecuted addEventHandlers with JIP parameter as TRUE, It started to trigger when I used healing on the second client. That is, because client 1 at the moment client 2 connected received information to register HandleHeal event on client 2 selected character (unit), and client 2 also received information to register client1 unit event. That meant both PC had now registered HandleHeal to each other and might start to run the code properly.
Eh...I'm not good at explaining this 😄
Here is the code I tried:

//initPlayerLocal.sqf
[player,["HandleHeal", 
{
	_this spawn 
	{
		params ["_injured", "_healer", "_isMedic", "_atVehicle", "_action"];
                [str _this] remoteExec ["hint"];
		private _damage = damage _injured;
		if (_injured isNotEqualTo _healer) then 
		{
			waitUntil { (damage _injured isEqualTo 0) or (damage _injured isEqualTo 1) };
			if (damage _injured isEqualTo 0) then
			{// give healer points
				[_healer,1] remoteExec ["addScore",2];
				[("giving points to: " + str _healer)] remoteExec ["hint"];
			};
			

		};
	};
}]] remoteExec ["addEventHandler",0,TRUE];

Also this behaviour might be related to this specific event handler that needs to be registered on all clients (and possibly all the other units if player should get points for healing AIs) that should be able to heal each other. Probably because the local healer condition.
Most important information is that healer runs the code on his PC and needs to know the object that he is healing has HandleHeal event registered otherwise script won't trigger.
Also, this code has potential to get stucked in program flow on the healer's PC in case that he is not a medic and won't heal injured unit fully. It will wait in scheduler before some other medic heals the injured unit fully or unit dies and only after that it releases. Possibly revarding both healers (intended ?). Problem might be if client heals many units just partially, that will create many waiting scripts on that client PC that might potentially decrease performance of the mission for him. Might be better to have it handled by server as separate function.
I also believe that you have to re-register the same event every time a player dies. Transfering the event to new unit. Possibly also remove event from previous unit. Removing should also be done if player disconnects and AI switches in the role.
Those are some things to be considered. I'm definitely not sure how your mission is set up, so I just tried what I thought would be best. There are certainly people more experienced with multiplayer scripting than I am 😄. Still, I'm gonna hope that I helped you at least a little bit 🙂. And sorry if this explanation is a bit messy 😄.

Share this post


Link to post
Share on other sites

Thank you for doing this. Much appreciated.

I know the waitUntil is a potential problem.  Partial heals, healer getting killed during revive, different healers to name just a few. My plan was to get the EH and remoteexec parts working and assured before adding security and otther features. Medic gets more points for a revive, for example.

I think I know what you mean, although the execution is local, every instance of each player needs to have the EH, so client 1 must have the EH and code on their entity locally and remotely. It's this bit of locality I rarely, if ever grasp. 🙂
I don't have to remove the EH from disconnects - AI don't take control, but it is something to note. Also, it's not clear, as you point out, if this EH is respawn persistent. Only one way to find out 🙂
I've actually got an animation based system in mind to do the points-for-revive thing, rather than Handleheal. Let's see how this goers first though.
I'll try your code now and report back. Again, many thanks.

Share this post


Link to post
Share on other sites

I can't make it work reliably. Your code has worked a couple of times, then at other times, under seemingly identical circumstances, it doesn't. 😞
 

Share this post


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

I don't have to remove the EH from disconnects - AI don't take control

Well, since remoteExec has JIP parameter filled, when player disconnects, his unit is destroyed and becomes objNull unit dies, but JIP command persists and when new player connects he receives basically objNull deadUnit addEventhandler... not sure if that would trow error or not doesn't trow error, but it's not needed anymore, so I think it will be better to remove that request when player disconnects. That would also need some array of JIP IDs managed by the server.

 

5 hours ago, Tankbuster said:

Also, it's not clear, as you point out, if this EH is respawn persistent.

I believe, it's connected to the unit, so when unit dies, eventhandler is bound to that dead unit. After testing respawning seems to work even with respawned unit. So it seems to be persistent...huh...so, if I get it right,if addEventHandler is called with object player, eventhandlers are transfered to player every time from dead unit to new unit when he respawns ? I would need someone to explain it to me how it actually works. 
 

3 hours ago, Tankbuster said:

under seemingly identical circumstances, it doesn't.

Hmmm, strange. I would need more informations. When it didn't work, did the first hint show up, how did you healed other player, was the unit same as you connected or new unit (respawned) and so on. Video might help.

tested with respawn instant, so it might also work differently with other respawn types.

update2:
After further testing, it seems that events added on client PC is persistent only for him after respawning (that's why code above still triggers on self heal but nothing else), and for the other clients it's tied to previous unit as events cease to work on new unit. That might be fixed by removing previous event and reexecuting it when new unit spawns. That's why I don't do multiplayer scripting 😄.

Edited by soldierXXXX
update after testing respawn
  • Thanks 1

Share this post


Link to post
Share on other sites

It's all very odd and rather than mess with code and locality that I don't really understand despite your best efforts, for which I'm extremely grateful, I've spent the afternoon writing and testing a server side, jipsafe, respawnsafe system for awarding points to players who revive incapacitated players using lifeState and animationState. I'm testing, trying to break and improve it now.

 

 

Share this post


Link to post
Share on other sites

Both scripts launched from initserver.sqf

incapacitatedplayers variable set to [] in initserver

 

../includes.sqf does macros and other stuff, remove it and the __tky lines if using this code

 

// by tankbuster
 #include "..\includes.sqf"
_myscript = "rewardreviversmanager.sqf";
__tky_starts
while {true} do
{
    sleep 4;

    {
        if (((lifestate _x) isEqualTo "INCAPACITATED") and (not (_x in incapacitatedplayers))) then
        {
            incapacitatedplayers pushBackUnique _x;
            [_x] execVm "server\rewardrevivers.sqf";
        };
    }foreach allPlayers - (entities "HeadlessClient_F");
};

__tky_ends
// by tankbuster
 #include "..\includes.sqf"
_myscript = "rewardrevivers.sqf";
__tky_starts
params ["_inj"];
diag_log format ["###rr gets %1", _inj];
waitUntil {(lifeState _inj)isNotEqualTo "INCAPACITATED"};
incapacitatedplayers = incapacitatedplayers - [_inj];
sleep 0.5;
if ((lifeState _inj) isEqualTo "HEALTHY") then// player is no longer incapacitated
{// check if player have been revived
    private _nearpotentialhealers = (_inj nearEntities ["SoldierWB", 3]) select {"medicend" in (animationState _x)};
    if (_nearpotentialhealers isEqualTo []) exitwith 
    {
        diag_log format ["### RR thinks no-one healing nearby and quits"];
    };
    private _nearestpotentialhealers = [_nearpotentialhealers, [], {_inj distanceSqr _x}, "ASCEND"] call BIS_fnc_sortBy;
    private _actualhealer = _nearestpotentialhealers #0;
    _actualhealer addScore 1;
    if (_actualhealer getUnitTrait "MEDIC") then 
    {
        _actualhealer addscore 1;
    };
    diag_log format ["### revived!! npf %1, npf2 %2, ah %3 , score %4", _nearpotentialhealers, _nearestpotentialhealers,_actualhealer, getPlayerScores _actualhealer ];
}
else
{
    diag_log format ["### RR thinks player wasn't revived and quits"];
};


__tky_ends

 

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

Glad to hear that you got your system working 🙂

  • Thanks 1

Share this post


Link to post
Share on other sites

For peace of mind, I want to share the code I put together for anyone that might come across this thread in the future. This is actually my first time messing with eventhandlers in the multiplayer script, so I spend about three days observing, adjusting and learning how events behave in multiplayer environment. I really wanted to make the event work since it was the original idea of this thread, and I believe, I managed to make it functional. This event is a bit complicated as it has to run on each client and be updated after unit respawns. Learned quite a lot I have to say as previously I only read about multiplayer scripting in theory. Still it's not my cup of tea, so if someone experienced points out something I forgot about, I'll gladly hear it. Otherwise I consider this thread resolved 🙂. I tested it with instant respawn, so I hope it works on other types as well. I had one client as player hosted server and other as joined client. Tried both clients in lobby ready to play as well as JIP, and it worked in my tests.

Code with debug and description:

Spoiler

//initPlayerLocal.sqf
//Idea is to make reward system for healing players
params ["_player", "_didJIP"];
soldierXXXX_fnc_addHandleHeal = {
//this function is executed locally on each client
//must be called on every PC separately
scriptName "soldierXXXX_fnc_addHandleHeal";
params ["_unit"];
private _var = _unit getVariable "HandleHealEH";
if !(isNil "_var") exitWith {
[(str name _unit + " already has attached HandleHeal EH, Number of heals:" + str ((_unit getEventHandlerInfo ["HandleHeal",0]) select 2))] remoteExec ["systemChat"];//prevention to not duplicate event on one client
};
private _userHandler = _unit addEventHandler ["HandleHeal",{
_this spawn 
	{
		params ["_injured", "_healer", "_isMedic", "_atVehicle", "_action"];
        [str _this] remoteExec ["hint"];
		[(str name _healer + ": " + str (_this select 1 getEventHandlerInfo ["HandleHeal",0]))] remoteExec ["systemChat"];//debug healer triggers message with event info
		if (_injured isNotEqualTo _healer) then 
		{
			waitUntil { (damage _injured isEqualTo 0) or (damage _injured isEqualTo 1) };
			if (damage _injured isEqualTo 0) then
			{// give healer points
				[_healer,1] remoteExec ["addScore",2];
				[("giving points to: " + str _healer)] remoteExec ["hint"];
			};
		};
	};
}];
_unit setVariable ["HandleHealEH",_userHandler,false];//variable must be set locally-might cause not adding eventhandler otherwise for playing client, for other clients might also cause multiple eventhandlers as it would rewrite their actual local value
systemChat ("eventHandler added to: " + name _unit);
};

soldierXXXX_fnc_removeHandleHeal = {
//must be called on every PC separately
scriptName "soldierXXXX_fnc_removeHandleHeal";
params ["_unit"];
private _var = _unit getVariable "HandleHealEH";
if (isNil "_var") exitWith {systemChat ("Cannot remove eventhandler from " + (name _unit))};
_unit removeEventHandler ["HandleHeal",_var];
_unit setVariable ["HandleHealEH",nil,false];
systemChat ("eventHandler removed from: " + name _unit);
};

//Killed is persistant event and automatically assigns to new player unit-code is executed on machine where event was added-here just for testing
player addEventHandler ["Killed",
{
 params ["_unit", "_killer", "_instigator", "_useEffects"];
 [(str _unit + " killed!")] remoteExec ["hint"];
}];

//Respawn is persistant event and automatically assigns to new player unit-code is executed locally, so function it calls must be executed on each machine-basically client sends request for each machines to update function locally
player addEventHandler ["Respawn",{
	params ["_unit", "_corpse"];
	[(str name _unit + "Respawned, Number of HandleHeals: " + str ((_unit getEventHandlerInfo ["HandleHeal",0]) select 2))] remoteExec ["systemChat"];
	//Update send to each client
	//Eventhandlers needs to be reattached-it's persistent on client that died, but for the other clients respawned unit is new object-update everywhere just to be sure
	[_unit] remoteExecCall ["soldierXXXX_fnc_removeHandleHeal",0];
	[_unit] remoteExecCall ["soldierXXXX_fnc_addHandleHeal",0];
	}];

//initial HandleHeal-added to everyone already playing-automatically added to player hosting server as he's basically the first client
[_player] remoteExec ["soldierXXXX_fnc_addHandleHeal",0];//JIP doesn't do anything here and it's not needed


//allPlayers Visibility test
[("visible Players to " + name _player + ": " + str allPlayers)] remoteExec ["systemChat"];//test which units client sees at the time of join-allPlayers
private _allPlayers = (allUnits + allDead) select {isPlayer _x};//much faster than allPlayers
[("visible scripted Players to " + name _player + ": " + str _allPlayers)] remoteExec ["systemChat"];//test which units client sees at the time of join-scripted _allPlayers

//locally adding events-fixed HandleHeal initialization for joined clients
//this loop needs to be executed on each joined client-otherwise he doesn't receive the request to update from already joined clients-joining client must add event for each player already connected.
{
 if (isServer) exitWith {};//server already receives all players eventhandlers-not needed to execute this loop on the server-he already received requests to update from all connected clients
 [(name _player + " had variable defined before for " + name _x + ": " +(str (_x getVariable ["HandleHealEH","didn't"])))] remoteExec ["systemChat"];//check if variable exists on the machine at the time he joined
 [_x] spawn soldierXXXX_fnc_addHandleHeal;
} forEach _allPlayers - [_player];

//Problems:
//Should be working if I'm not mistaken. Maybe the code would require further tweaking for different types of respawn.
//Tested on player hosted server with one client joined

 


Cleaner code no debug, no description:
 

Spoiler

//initPlayerLocal.sqf
//Idea is to make reward system for healing players
params ["_player", "_didJIP"];
soldierXXXX_fnc_addHandleHeal = {
scriptName "soldierXXXX_fnc_addHandleHeal";
params ["_unit"];
private _var = _unit getVariable "HandleHealEH";
if !(isNil "_var") exitWith {
};
private _userHandler = _unit addEventHandler ["HandleHeal",{
_this spawn 
	{
		params ["_injured", "_healer", "_isMedic", "_atVehicle", "_action"];
        ["Handler works !"] remoteExec ["hint"];
		if (_injured isNotEqualTo _healer) then 
		{
			waitUntil { (damage _injured isEqualTo 0) or (damage _injured isEqualTo 1) };
			if (damage _injured isEqualTo 0) then
			{// give healer points
				[_healer,1] remoteExec ["addScore",2];
				[("giving points to: " + str _healer)] remoteExec ["hint"];
			};
		};
	};
}];
_unit setVariable ["HandleHealEH",_userHandler,false];
};

soldierXXXX_fnc_removeHandleHeal = {
scriptName "soldierXXXX_fnc_removeHandleHeal";
params ["_unit"];
private _var = _unit getVariable "HandleHealEH";
if (isNil "_var") exitWith {systemChat ("Cannot remove eventhandler from " + (name _unit))};
_unit removeEventHandler ["HandleHeal",_var];
_unit setVariable ["HandleHealEH",nil,false];
};

//Respawn update
player addEventHandler ["Respawn",{
	params ["_unit", "_corpse"];
	[_unit] remoteExecCall ["soldierXXXX_fnc_removeHandleHeal",0];
	[_unit] remoteExecCall ["soldierXXXX_fnc_addHandleHeal",0];
	}];

//initial HandleHeal
[_player] remoteExec ["soldierXXXX_fnc_addHandleHeal",0];

{
 if (isServer) exitWith {};
 [_x] spawn soldierXXXX_fnc_addHandleHeal;
} forEach _allPlayers - [_player];

 

 

  • Thanks 1

Share this post


Link to post
Share on other sites

Thank you. Well done!

  • 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

×