Jump to content
MrDj200

Save Loadout and Position on disconnect

Recommended Posts

Hello,

 

I need some help with this.

I want to save a players position, loadout and maybe group when a player leaves the server.

As we have some problems with people crashing or having IP resets or just needing to restart, this would be really neat.

 

I looked at onPlayerDisonnected and the EH PlayerDisconnected, but I don't really know where to save the data, and how to get the position.

 

Any help would be appreciated!

 

-Dj

Share this post


Link to post
Share on other sites

Not between server restarts, don't want to use a DB, or is the "profileNamespace" able to save things between game restarts(client)?

That'd be pretty neat to save some other things...

Share this post


Link to post
Share on other sites

Just leaving this here so I remember, I'll check back later tonight with solution. 

Share this post


Link to post
Share on other sites

Hmm, I've been playing around with namespaces and just noticed JIP players do not get the missionNameSpace synced?

How do I sync that?

Share this post


Link to post
Share on other sites

Here we go! Half a month late. Written up on December 15, forgotten about until today. Oops.

 

This should save a disconnecting players loadout, position and facing on the server and will broadcast it back to the player when he/she reconnects as long as the mission isn't restarted.

It does not handle group at this time since it was a maybe (just make sure you select the same group when reconnecting and you should be good). 


It's meant to go in the init.sqf, if you got the CfgRemoteExec whitelist enabled you need to add TAG_fnc_loadClientData to it.

You could also load TAG_fnc_loadClientData through functions library and put the mission eventhandlers in initServer.sqf instead.

 

Disclaimer: I have not done thorough testing. Just a syntax check and a basic logic check. So there might be some edge cases which might be bugged (It's also perfectly possible I missed something :P).

Other than that it should do it's job.

 

EDIT: The below code is broken, BIS_fnc_exportInventory has been updated and I missed to update position and direction after multiple disconnects.

See this post for working code.

TAG_fnc_loadClientData = {
	_this params ["_loadoutStr", "_positionASL", "_dir"];
	call compile _loadoutStr;
	player setDir _dir;
	player setPosASL _positionASL;
};

if(isServer) then {
	addMissionEventHandler [
		"HandleDisconnect", 
		{
			params ["_body", "_id", "_uid", "_name"];
			
			if(!isNull _body) then {			
				//Init storage var
				if(isNil "TAG_disconnectedLoadouts") then {
					TAG_disconnectedLoadouts = [];
				};
				
				//Save loadout as script for easy broadcast
				private _loadoutStr = [player, "script", false] call BIS_fnc_exportInventory;
				{
					private _index = _loadoutStr find _x;
					if(_index > -1) then {
						private _strArray = toArray _loadoutStr;
						_strArray deleteRange [_index, count _x];
						_loadoutStr = toString _strArray;
					};	
				} forEach ["// Remove existing items","// Add containers","// Add weapons", "// Add items", "// Set identity"];
					
				//Find in storage
				private _uidIndex = TAG_disconnectedLoadouts find _uid;
				if(_uidIndex > -1) then {
					//Found -> update
					private _loadoutIndex = _uidIndex + 1;
					TAG_disconnectedLoadouts set [_loadoutIndex, _loadoutStr];
				} else {
					//Not found -> Add new
					TAG_disconnectedLoadouts pushBack _uid;
					TAG_disconnectedLoadouts pushBack [_loadoutStr, getPosASL _body, getDir _body];
				};
			};
			false
		}
	];

	addMissionEventHandler [
		"PlayerConnected", 
		{
			params ["_id", "_uid", "_name", "_jip", "_owner"];
			if(_jip) then {
				private _clientData = missionNamespace getVariable ["TAG_disconnectedLoadouts", []];
				private _uidIndex = _clientData find _uid;
				if(_uidIndex > -1) then {
					private _loadoutIndex = _uidIndex + 1;
					(_clientData select _loadoutIndex) remoteExec ["TAG_fnc_loadClientData", _owner];
				};
			};
		}
	];
};

 

  • Thanks 2

Share this post


Link to post
Share on other sites
On 31/12/2017 at 6:39 AM, mrcurry said:

Here we go! Half a month late. Written up on December 15, forgotten about until today. Oops.

 

This should save a disconnecting players loadout, position and facing on the server and will broadcast it back to the player when he/she reconnects as long as the mission isn't restarted.

It does not handle group at this time since it was a maybe (just make sure you select the same group when reconnecting and you should be good). 


It's meant to go in the init.sqf, if you got the CfgRemoteExec whitelist enabled you need to add TAG_fnc_loadClientData to it.

You could also load TAG_fnc_loadClientData through functions library and put the mission eventhandlers in initServer.sqf instead.

 

Disclaimer: I have not done thorough testing. Just a syntax check and a basic logic check. So there might be some edge cases which might be bugged (It's also perfectly possible I missed something :P).

Other than that it should do it's job.


TAG_fnc_loadClientData = {
	_this params ["_loadoutStr", "_positionASL", "_dir"];
	call compile _loadoutStr;
	player setDir _dir;
	player setPosASL _positionASL;
};

if(isServer) then {
	addMissionEventHandler [
		"HandleDisconnect", 
		{
			params ["_body", "_id", "_uid", "_name"];
			
			if(!isNull _body) then {			
				//Init storage var
				if(isNil "TAG_disconnectedLoadouts") then {
					TAG_disconnectedLoadouts = [];
				};
				
				//Save loadout as script for easy broadcast
				private _loadoutStr = [player, "script", false] call BIS_fnc_exportInventory;
				{
					private _index = _loadoutStr find _x;
					if(_index > -1) then {
						private _strArray = toArray _loadoutStr;
						_strArray deleteRange [_index, count _x];
						_loadoutStr = toString _strArray;
					};	
				} forEach ["// Remove existing items","// Add containers","// Add weapons", "// Add items", "// Set identity"];
					
				//Find in storage
				private _uidIndex = TAG_disconnectedLoadouts find _uid;
				if(_uidIndex > -1) then {
					//Found -> update
					private _loadoutIndex = _uidIndex + 1;
					TAG_disconnectedLoadouts set [_loadoutIndex, _loadoutStr];
				} else {
					//Not found -> Add new
					TAG_disconnectedLoadouts pushBack _uid;
					TAG_disconnectedLoadouts pushBack [_loadoutStr, getPosASL _body, getDir _body];
				};
			};
			false
		}
	];

	addMissionEventHandler [
		"PlayerConnected", 
		{
			params ["_id", "_uid", "_name", "_jip", "_owner"];
			if(_jip) then {
				private _clientData = missionNamespace getVariable ["TAG_disconnectedLoadouts", []];
				private _uidIndex = _clientData find _uid;
				if(_uidIndex > -1) then {
					private _loadoutIndex = _uidIndex + 1;
					(_clientData select _loadoutIndex) remoteExec ["TAG_fnc_loadClientData", _owner];
				};
			};
		}
	];
};

 

 

Hello how are you, look I am still learning this scripting and I do not understand much how to put your script to work, could you if it is not too much trouble to explain briefly how to make it work with some quick steps of example?

 

Thanks

Share this post


Link to post
Share on other sites
On 15/09/2018 at 8:54 AM, redburn said:

 

Hello how are you, look I am still learning this scripting and I do not understand much how to put your script to work, could you if it is not too much trouble to explain briefly how to make it work with some quick steps of example?

 

Thanks


I'm pretty sure I mentioned this but it's no trouble. :)

 

Rereading my own instructions I'd say slap the code provided above in your init.sqf inside your mission folder and you should be good.

If you do not have your init.sqf then one will not be provided for you, you must create said init.sqf yourself inside your mission folder.

If you are unaware how to create a text-file and change it's extension then only The Google can help you.

If you are unaware how to google then may whatever diety you choose to worship have mercy on your soul.

 

 

 

 

 

P.s. Jokes aside, it should be pretty straightforward but let me know if you run into any trouble. ^^

  • Haha 2

Share this post


Link to post
Share on other sites
On 17/9/2018 at 4:29 AM, mrcurry said:


I'm pretty sure I mentioned this but it's no trouble. :)

 

Rereading my own instructions I'd say slap the code provided above in your init.sqf inside your mission folder and you should be good.

If you do not have your init.sqf then one will not be provided for you, you must create said init.sqf yourself inside your mission folder.

If you are unaware how to create a text-file and change it's extension then only The Google can help you.

If you are unaware how to google then may whatever diety you choose to worship have mercy on your soul.

 

 

 

 

 

P.s. Jokes aside, it should be pretty straightforward but let me know if you run into any trouble. ^^

 

I only mentioned it because you said that if there were functions you had to do something else, but it´s ok :)

Share this post


Link to post
Share on other sites

Hi, 
i tried to use your code, and the positioning thing is just working for the first time. The loadout save isn`t working at all.
Do you know how to fix it?

Share this post


Link to post
Share on other sites
On 3/11/2020 at 9:54 PM, Sergeant_Emerald said:

Hi, 
i tried to use your code, and the positioning thing is just working for the first time. The loadout save isn`t working at all.
Do you know how to fix it?

TAG_fnc_loadClientData = {
	_this params ["_loadout", "_positionASL", "_dir"];
	player setUnitLoadout _loadout;
	player setDir _dir;
	player setPosASL _positionASL;
};

if(isServer) then {
	addMissionEventHandler [
		"HandleDisconnect", 
		{
			params ["_body", "_id", "_uid", "_name"];
			
			if(!isNull _body) then {			
				//Init storage var
				if(isNil "TAG_disconnectedLoadouts") then {
					TAG_disconnectedLoadouts = [];
				};
				
				//Get data
				private _loadout = getUnitLoadout _body;
				private _position = getPos _body;
				private _direction = getDir _body;
					
				//Find in storage
				private _uidIndex = TAG_disconnectedLoadouts find _uid;
				if(_uidIndex > -1) then {
					//Found -> update
					private _loadoutIndex = _uidIndex + 1;
					TAG_disconnectedLoadouts set [_loadoutIndex, [_loadout, _position, _direction]];
				} else {
					//Not found -> Add new
					TAG_disconnectedLoadouts pushBack _uid;
					TAG_disconnectedLoadouts pushBack [_loadout, _position, _direction];
				};
			};
			false
		}
	];

	addMissionEventHandler [
		"PlayerConnected", 
		{
			params ["_id", "_uid", "_name", "_jip", "_owner"];
			if(_jip) then {
				private _clientData = missionNamespace getVariable ["TAG_disconnectedLoadouts", []];
				private _uidIndex = _clientData find _uid;
				if(_uidIndex > -1) then {
					private _loadoutIndex = _uidIndex + 1;
					(_clientData select _loadoutIndex) remoteExec ["TAG_fnc_loadClientData", _owner];
				};
			};
		}
	];
};
  • Like 1
  • Thanks 2

Share this post


Link to post
Share on other sites
On 3/18/2020 at 9:06 AM, mrcurry said:

TAG_fnc_loadClientData = {
	_this params ["_loadout", "_positionASL", "_dir"];
	player setUnitLoadout _loadout;
	player setDir _dir;
	player setPosASL _positionASL;
};

if(isServer) then {
	addMissionEventHandler [
		"HandleDisconnect", 
		{
			params ["_body", "_id", "_uid", "_name"];
			
			if(!isNull _body) then {			
				//Init storage var
				if(isNil "TAG_disconnectedLoadouts") then {
					TAG_disconnectedLoadouts = [];
				};
				
				//Get data
				private _loadout = getUnitLoadout _body;
				private _position = getPos _body;
				private _direction = getDir _body;
					
				//Find in storage
				private _uidIndex = TAG_disconnectedLoadouts find _uid;
				if(_uidIndex > -1) then {
					//Found -> update
					private _loadoutIndex = _uidIndex + 1;
					TAG_disconnectedLoadouts set [_loadoutIndex, [_loadout, _position, _direction]];
				} else {
					//Not found -> Add new
					TAG_disconnectedLoadouts pushBack _uid;
					TAG_disconnectedLoadouts pushBack [_loadout, _position, _direction];
				};
			};
			false
		}
	];

	addMissionEventHandler [
		"PlayerConnected", 
		{
			params ["_id", "_uid", "_name", "_jip", "_owner"];
			if(_jip) then {
				private _clientData = missionNamespace getVariable ["TAG_disconnectedLoadouts", []];
				private _uidIndex = _clientData find _uid;
				if(_uidIndex > -1) then {
					private _loadoutIndex = _uidIndex + 1;
					(_clientData select _loadoutIndex) remoteExec ["TAG_fnc_loadClientData", _owner];
				};
			};
		}
	];
};

Thank You!

Share this post


Link to post
Share on other sites

I tried to use the script above over the weekend:

- blank mission with one playable character, an arsenal and a vehicle.

- The code above copied into in it.sqf.

- on joining I walked about 15 metres and to the right, turned 180 from starting orientation, and dripped numerous weapons and items from inventory.

- I then disconnected and logged back on. 

 

I spawned back in original position, with original gear.

 

Can anyone confirm this script still works, and if there are any other server side settings I need to make it work?

 

Thanks!

Share this post


Link to post
Share on other sites

First of all, this code is supposed to save loadout/position/direction on server during the mission on server. So, in multiplayer session, during the session (as far as the server is running the mission).

 

If you want to save something in SP or even hosted MP at home, you must save (what you want) in your profileNameSpace. You can use the MEHs  "handleDisconnect" & "playerConnected" as above but your data must be saved in profileNameSpace, not missionNameSpace.

  • Like 1

Share this post


Link to post
Share on other sites

So i tried again, changing missionNamespace to profileNamespace. no joy. 

 

as per above, hosted server. There is stuff mentioned above about whitelists, etc, which i do not understand. Is there anyone who uses this script that can help?

 

thanks

Share this post


Link to post
Share on other sites
3 hours ago, Noel Collins said:

So i tried again, changing missionNamespace to profileNamespace. no joy. 

 

as per above, hosted server. There is stuff mentioned above about whitelists, etc, which i do not understand. Is there anyone who uses this script that can help?

 

thanks

did you change it also in handledisconnect EH ? Without your final code, difficult to help.

Share this post


Link to post
Share on other sites

Original Attempt was with code in init.sqf exactly as above on hosted server, with nothing else done.

This attempt the same code was as below, only change marked in red.:

i dont see a namespace in the handle disconnect EH?

 

thanks

 

 

 

TAG_fnc_loadClientData = {

        _this params ["_loadout", "_positionASL", "_dir"];

        player setUnitLoadout _loadout;

        player setDir _dir;

        player setPosASL _positionASL;

};

 

if(isServer) then {

        addMissionEventHandler [

               "HandleDisconnect",

               {

                       params ["_body", "_id", "_uid", "_name"];

                      

                       if(!isNull _body) then {                    

                               //Init storage var

                               if(isNil "TAG_disconnectedLoadouts") then {

                                      TAG_disconnectedLoadouts = [];

                               };

                              

                               //Get data

                               private _loadout = getUnitLoadout _body;

                               private _position = getPos _body;

                               private _direction = getDir _body;

                                     

                               //Find in storage

                               private _uidIndex = TAG_disconnectedLoadouts find _uid;

                               if(_uidIndex > -1) then {

                                      //Found -> update

                                      private _loadoutIndex = _uidIndex + 1;

                                      TAG_disconnectedLoadouts set [_loadoutIndex, [_loadout, _position, _direction]];

                               } else {

                                      //Not found -> Add new

                                      TAG_disconnectedLoadouts pushBack _uid;

                                      TAG_disconnectedLoadouts pushBack [_loadout, _position, _direction];

                               };

                       };

                       false

               }

        ];

 

        addMissionEventHandler [

               "PlayerConnected",

               {

                       params ["_id", "_uid", "_name", "_jip", "_owner"];

                       if(_jip) then {

                               private _clientData = profileNamespace getVariable ["TAG_disconnectedLoadouts", []];

                               private _uidIndex = _clientData find _uid;

                               if(_uidIndex > -1) then {

                                      private _loadoutIndex = _uidIndex + 1;

                                      (_clientData select _loadoutIndex) remoteExec ["TAG_fnc_loadClientData", _owner];

                               };

                       };

               }

        ];

};

Share this post


Link to post
Share on other sites

TAG_disconnectedLoadouts also needs to be changed, the script in its current form relies on that global variables are defined in missionNamespace unless otherwise specified.

 

But just changing out which namespace is used is gonna cause undefined behaviour when the same server runs multiple missions running the script.

 

I've been planning to rewrite this using hashmaps anyway so I'll see about writing up a version for profileNamespace too

  • Like 2

Share this post


Link to post
Share on other sites

Here is my contrib. I added some log lines. Seems to work, but should be tested deeper:


 

TAG_fnc_loadClientData = {
      params ["_loadout", "_positionASL", "_dir"];
      waitUntil {!isNull player};
      player setUnitLoadout _loadout;
      player setDir _dir;
      player setPosASL _positionASL;
    };

if (isServer) then {
  //  profileNameSpace setVariable ["TAG_disconnectedLoadouts",nil];  // only for resetting the variable on server. Important if not previously a hashmap !

  addMissionEventHandler ["HandleDisconnect",
    {
      params ["_body", "_id", "_uid", "_name"];
      if(!isNull _body) then {
          private _loadout = getUnitLoadout _body;
        private _position = getPos _body;
        private _direction = getDir _body;
        if(isNil {profileNameSpace getVariable "TAG_disconnectedLoadouts"}) then {
          profileNameSpace setVariable ["TAG_disconnectedLoadouts",createHashMapFromArray [[_uid,[_loadout,_position,_direction]]]];
        } else {
          (profileNameSpace getVariable "TAG_disconnectedLoadouts") set [_uid,[_loadout,_position,_direction]];
        };
diag_log [_uid,(profileNameSpace getVariable "TAG_disconnectedLoadouts")];
      };
      false
    }
  ];

  addMissionEventHandler ["PlayerConnected",
    {
      params ["_id", "_uid", "_name", "_jip", "_owner"];
      if(_jip) then {
        private _clientData = profileNamespace getVariable ["TAG_disconnectedLoadouts", []];
        if (_clientData isEqualTo []) exitWith {};
        private _value = _clientData get _uid;
diag_log ["<<<<<after connect>>>>",_value,_this];
      [_value,TAG_fnc_loadClientData] remoteExec ["spawn", _owner];
      };
    }
  ];
};

 

  • Like 1

Share this post


Link to post
Share on other sites
On 11/7/2021 at 12:06 AM, Noel Collins said:


thanks @pierre MGI

 

the above is my experience, I can script, but im a little out of my depth on this one. what am i missing here?

 

thanks

 

 

 

 

So I just test ran @pierremgi's code and had a hiccup.
When I copied from his forum post to the init.sqf a "ghost character" showed up which would prevent the script from working. This is why running test-benches with startup parameter -showScriptErrors is important.

See image: https://www.dropbox.com/s/c2zewaykupzgwba/mysterycharacter.png?dl=0

Pierre could be messing with us... :suspicious: 

...but more likely its an artifact from the forum formatting of the code.

 

So check if you are also getting this in your init-file.

To be safe retype "profileNamespace getVariable".

 

After I busted the ghost the code ran fine on my test-bench.

 

If it still doesn't work here's a couple of things you could do:

 1. Check your CfgRemoteExec so that "spawn" is allowed.

 2. Check the .rpt if some other error is being printed when you disconnect / connect.

 3. If running with mods, give it a try without them to see if they are interfering somehow.

Share this post


Link to post
Share on other sites

Don't forget to enable the line resetting profileNameSpace setVariable ["TAG_disconnectedLoadouts",nil];  (just a comment above). 

why?

If your profileNameSpace already have a (bad) variable "TAG_disconnectedLoadouts", that could lead to wrong array. So, reset it,  then modify the code as above (comment) in order to increment this variable along with new players.

Share this post


Link to post
Share on other sites
On 11/7/2021 at 7:24 PM, mrcurry said:

 

 

So I just test ran @pierremgi's code and had a hiccup.
When I copied from his forum post to the init.sqf a "ghost character" showed up which would prevent the script from working. This is why running test-benches with startup parameter -showScriptErrors is important.

p.png?fv_content=true&size_mode=5

Pierre could be messing with us... :suspicious: 

...but more likely its an artifact from the forum formatting of the code.

 

So check if you are also getting this in your init-file.

To be safe retype "profileNamespace getVariable".

 

After I busted the ghost the code ran fine on my test-bench.

 

If it still doesn't work here's a couple of things you could do:

 1. Check your CfgRemoteExec so that "spawn" is allowed.

 2. Check the .rpt if some other error is being printed when you disconnect / connect.

 3. If running with mods, give it a try without them to see if they are interfering somehow.

Thanks, trying that now but no avail.

 

the image you posted has not come through, so i cannot see what you are trying to show.

 

as its sits now in my init.sqf:

 

TAG_fnc_loadClientData = {
      params ["_loadout", "_positionASL", "_dir"];
      waitUntil {!isNull player};
      player setUnitLoadout _loadout;
      player setDir _dir;
      player setPosASL _positionASL;
    };

if (isServer) then {
  profileNamespace setVariable ["TAG_disconnectedLoadouts",nil];  // only for resetting the variable on server. Important if not previously a hashmap !

  addMissionEventHandler ["HandleDisconnect",
    {
      params ["_body", "_id", "_uid", "_name"];
      if(!isNull _body) then {
        private _loadout = getUnitLoadout _body;
        private _position = getPos _body;
        private _direction = getDir _body;
        if(isNil {profileNamespace getVariable "TAG_disconnectedLoadouts"}) then {
        profileNamespace setVariable ["TAG_disconnectedLoadouts",createHashMapFromArray [[_uid,[_loadout,_position,_direction]]]];
        } else {
          (profileNamespace getVariable "TAG_disconnectedLoadouts") set [_uid,[_loadout,_position,_direction]];
        };
diag_log [_uid,(profileNamespace getVariable "TAG_disconnectedLoadouts")];
      };
      false
    }
  ];

  addMissionEventHandler ["PlayerConnected",
    {
      params ["_id", "_uid", "_name", "_jip", "_owner"];
      if(_jip) then {
        private _clientData = profileNamespace getVariable ["TAG_disconnectedLoadouts", []];
        if (_clientData isEqualTo []) exitWith {};
        private _value = _clientData get _uid;
diag_log ["<<<<<after connect>>>>",_value,_this];
      [_value,TAG_fnc_loadClientData] remoteExec ["spawn", _owner];
      };
    }
  ];
};

Share this post


Link to post
Share on other sites

Read above.

If you are writing:

profileNamespace setVariable ["TAG_disconnectedLoadouts",nil];

you are resetting every time (at launch) your variable. So, the profileNameSpace is just useless!

You must reset this at the first time (just because you can't say what this variable is made of after several trials),   then delete or unable this line (by comment) in order to keep your data.

I'd rather remove this line and warn for running that once, because any further test.

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

×