Saving player's progress

Saving player's progress

« posted: Nov 20, 2013, 06:07 PM »
Hello! I am using this mission on my dedicated sandbox server but the problem is that I want to save all server progress such as in DayZ.
Can someone post scripts for saving player position/loadout on every connect/disconnect to server and for vehicles/bases positions/standings also.
I am trying to use inidbi but i dont have the right code for saving all of the above.
  • Offline AgentRev
  • Developer
  • Veteran
  • ******
  • Posts: 2490

Re: Saving player's progress

« Reply #1 posted: Nov 20, 2013, 08:36 PM »
The mission only has scripts to save player inventory and base objects.

To enable player saving, you must edit the config.sqf file inside the mission, and set config_player_saving_enabled to compileFinal "1"

To enable basesaving, you must edit the main_config.sqf file in the A3Wasteland_settings directory on your server, or if you don't have it, the server\init.sqf file inside the mission, and set A3W_baseSaving to 1

Hope it helps.

Re: Saving player's progress

« Reply #2 posted: Nov 21, 2013, 07:05 AM »
I found this parameters, thank you. I set all like you sad but saving still does not work.
System writes an account that is loaded successfully but nothing saves.
  • Offline JoSchaap
  • Developer
  • Mercenary
  • ******
  • Posts: 481
  • Had a life.. Got a modem.. (~1996)

Re: Saving player's progress

« Reply #3 posted: Nov 21, 2013, 01:32 PM »
whats inside the @inidb mod savefiles?

could you post a screenshot of the @inidb/db/ folder contents?

Re: Saving player's progress

« Reply #4 posted: Nov 21, 2013, 04:35 PM »
yes, sure. My @inidb folder is default. I tried to use inidbI v1.2 and tried to use inidb v1.0
Same result :/
  • Offline Caffeind
  • Insurgent
  • Fresh Spawn
  • *
  • Posts: 14

Re: Saving player's progress

« Reply #5 posted: Nov 22, 2013, 01:13 AM »
I might be incorrect here but I think that the problem is you're expecting player saves to be automatic when in fact, they are only performed when a player is in prone position and selects save player from the action menu.
Thread softly and carry a big sig!

Re: Saving player's progress

« Reply #6 posted: Nov 22, 2013, 09:01 AM »
OMG It's works! Thank you very much but is there any way to automate saving of the player, such as every 60 seconds?
  • Offline Caffeind
  • Insurgent
  • Fresh Spawn
  • *
  • Posts: 14

Re: Saving player's progress

« Reply #7 posted: Nov 22, 2013, 11:39 AM »
Here is one solution, it's probably not the best but it works and will give you something to go from.

Make a copy of \persistence\players\c_savePlayerToServer.sqf and paste it into \server\functions\

Rename the copied file (\server\functions\c_savePlayerToServer.sqf) to playerSaves.sqf

Open playerSaves.sqf in a text editor.

Find:
Code: [Select]
if(playerSetupComplete) thenReplace with:
Code: [Select]
while {true} do
Find:
Code: [Select]
player globalChat "Player saved!";
};
Replace with:
Code: [Select]
sleep 10;
};
Change the sleep 10; to sleep X; where X is the amount of time in seconds between saves.

Open \client\init.sqf

Find:
Code: [Select]
player switchMove "AmovPpneMstpSnonWnonDnon";
};
Add after (on a new line):
Code: [Select]
// Start Player Saves.
[] execVM "server\functions\playerSaves.sqf";

This will start player saves once they finish initialising (after they have been loaded from iniDB).

If you want to save the extra player info (magazines etc) then you'll probably want to recompile the iniDB dll to allow string length longer than 512 (I have mine up to 2048 so it can save all ammuntionWithCount variables).

Hope this helps. Sing out if you need anything else (I'm still learning myself, 4 weeks in and knuckle deep).

Cheers,
Caffeind.
Thread softly and carry a big sig!

Re: Saving player's progress

« Reply #8 posted: Nov 22, 2013, 07:34 PM »
Caffeind thank you very much for your post! Really useful. Can you please post your inidb.dll here?
Also money saving is not work for me :\
And also i want to ask how to add vehicle and boxes saving?

And about objects saving: i placed some objects and locked them. Then i made a restart and objects were kept but when i made a next restart, they are gone. I mean that i need to lock the objects every server restart to save them, this is not good :(
  • Offline Caffeind
  • Insurgent
  • Fresh Spawn
  • *
  • Posts: 14

Re: Saving player's progress

« Reply #9 posted: Nov 23, 2013, 12:13 AM »
Caffeind thank you very much for your post! Really useful. Can you please post your inidb.dll here?
I have attached the dll to this post. Please keep a backup of the original dll file in case you encounter issues with this one.

Also money saving is not work for me :\

Open playerSaves.sqf (in your \server\functions\ folder).
Find:
Code: [Select]
sleep 10;Before that line, insert:
Code: [Select]
moneyLevel = player getVariable "cmoney";
[_uid, _uid, "Money", moneyLevel] call fn_SaveToServer;

If you are allowing your players to save their characters when in prone, then you will need to make these changes to \persistence\c_SavePlayerToServer.sqf as well.

Open \persistence\players\c_playerDBSetup.sqf
Find:
Code: [Select]
if(_varName == 'AssignedItems') then {Above that line, add:
Code: [Select]
if(_varName == 'Money') then {
moneyLevel = _varValue;
_player setVariable ["cmoney", _varValue, true];
moneyLoaded = 1;
};

And also i want to ask how to add vehicle and boxes saving?

And about objects saving: i placed some objects and locked them. Then i made a restart and objects were kept but when i made a next restart, they are gone. I mean that i need to lock the objects every server restart to save them, this is not good :(
When you are testing this, make sure you give the server enough time to find, and save all the objects. I'd suggest leaving it running for 10-15 minutes to ensure it has finished loading the objects, and has performed at least one save of them before restarting. If you don't have 10-15 minutes, open your anyObjects.ini scroll to the last entry, note what its Obj number it is. The object count starts at 0, so add 1 to this number and change the count= field at the top of the anyObject.ini file to the new number we just calculated.

Hope this is some help.

Cheers,
Caffeind.
Thread softly and carry a big sig!

Re: Saving player's progress

« Reply #10 posted: Nov 23, 2013, 06:17 AM »
Continuous saving isnt implemented for a good reason. It's hard to make a system where item duping isnt a major issue. ie. if you save every 60 seconds, then someone can just drop all their items, relog and pick up all the (now duplicated) items. While this isn't as important as dayz, things like duplicating cash can essentially give an unlimited amount of supplies to one team and make things completely unfair for the other team.

It's your server so we wont tell you how to run it, this is simply why we have implemented it the way we have.

MF

Re: Saving player's progress

« Reply #11 posted: Nov 23, 2013, 08:59 AM »
I think would be great to set the timer for 30 seconds to exit and set character saving every 25 seconds. It would be a solution with duping and combat logging. Thus it is possible to realize the continuous saving.
  • Offline Caffeind
  • Insurgent
  • Fresh Spawn
  • *
  • Posts: 14

Re: Saving player's progress

« Reply #12 posted: Nov 23, 2013, 01:42 PM »
You can achieve an 'abort' button timer with the following:
Code: [Select]
disableSerialization;

while {true} do
{
waitUntil {!(isNull (findDisplay 49))};
((findDisplay 49) displayCtrl 104) ctrlEnable false;
hint "Abort button will be available in 30 seconds.";
_time = time;
waitUntil {(isNull (findDisplay 49)) || time >= _time + 30};
if (!(isNull (findDisplay 49))) then
{
((findDisplay 49) displayCtrl 104) ctrlEnable true;
waitUntil {isNull (findDisplay 49)};
};
}

The problem is, however, that a player can still Alt-F4 (as that's controlled by Windows, not ArmA). That said, with the current save system, and using persistent player files, a player can go prone and save, then dump their items on the ground and re-log, resulting in them having the gear they had when they performed the save.

Another option would be to include a forced save when the player presses the Esc key (bringing up the menu with the Abort option). You could instead use:
Code: [Select]
disableSerialization;

while {true} do
{
waitUntil {!(isNull (findDisplay 49))};
[] execVM "persistence\players\c_savePlayerToServer.sqf";
waitUntil {isNull (findDisplay 49)};
}

Providing your persistence\players\c_savePlayerToServer.sqf saves the same information as your server\playerSaves.sqf (if you've used the examples I've posted in this thread).

Another point of note, iniDB really isn't up to this task. For persistent player saves we really need to be using a mySQL interface of some kind.

Cheers,
Caffeind.
Thread softly and carry a big sig!

Re: Saving player's progress

« Reply #13 posted: Nov 23, 2013, 03:40 PM »
Still easy to dupe due to how the connections are handled. If you use task manager to kill it, you again can duplicate. I may be wrong as this is definitely not my area of expertise, but the whole player save concept really takes a lot of effort to avoid it (think about how hard dayz have tried). We don't have that kind of effort to spend on it as it seems like a lot of servers don't use player persistence.

Re: Saving player's progress

« Reply #14 posted: Jan 12, 2014, 04:01 PM »
an easy way to fix the player duping would be to include a script on the script which drops money
so when the player drops money, the save script gets executed and the server tells the database that the player has no money and that gets saved

so when the player rejoins, he no longer has the money that he dropped

also, my money saving on my server doesn't seem to be working, here is my c_savePlayerToServer.sqf

Code: [Select]
private["_uid"];
if(playerSetupComplete) then
{
_uid = getPlayerUID player;
[_uid, _uid, "Health", damage player] call fn_SaveToServer;
[_uid, _uid, "Side", str(side player)] call fn_SaveToServer;
[_uid, _uid, "Account Name", name player] call fn_SaveToServer;

{
_keyName = _x select 0;
_value = _x select 1;
[_uid, _uid, _keyName, _value] call fn_SaveToServer;
} forEach call mf_inventory_all;

[_uid, _uid, "Vest", vest player] call fn_SaveToServer;
[_uid, _uid, "Uniform", uniform player] call fn_SaveToServer;
[_uid, _uid, "Backpack", backpack player] call fn_SaveToServer;
[_uid, _uid, "Goggles", goggles player] call fn_SaveToServer;
[_uid, _uid, "HeadGear", headGear player] call fn_SaveToServer;

[_uid, _uid, "UniformItems", uniformItems player] call fn_SaveToServer;
[_uid, _uid, "VestItems", vestItems player] call fn_SaveToServer;
[_uid, _uid, "BackpackItems", backpackItems player] call fn_SaveToServer;

[_uid, _uid, "Position", getPosATL vehicle player] call fn_SaveToServer;
[_uid, _uid, "Direction", direction vehicle player] call fn_SaveToServer;

[_uid, _uid, "PrimaryWeapon", primaryWeapon player] call fn_SaveToServer;
[_uid, _uid, "PrimaryWeaponItems", primaryWeaponItems player] call fn_SaveToServer;
//[_uid, _uid, "PrimaryWeaponMagazine", primaryWeaponMagazine player] call fn_SaveToServer;

[_uid, _uid, "SecondaryWeapon", SecondaryWeapon player] call fn_SaveToServer;
[_uid, _uid, "SecondaryWeaponItems", secondaryWeaponItems player] call fn_SaveToServer;
//[_uid, _uid, "SecondaryWeaponMagazine", secondaryWeaponMagazine player] call fn_SaveToServer;

[_uid, _uid, "HandgunWeapon", handgunWeapon player] call fn_SaveToServer;
[_uid, _uid, "HandgunItems", handgunItems player] call fn_SaveToServer;
//[_uid, _uid, "HandgunMagazine", handgunMagazine player] call fn_SaveToServer;

[_uid, _uid, "Items", items player] call fn_SaveToServer;
[_uid, _uid, "AssignedItems", assignedItems player] call fn_SaveToServer;

magsWithAmmoCounts = [];
{
_class = _x select 0;
_count = _x select 1;
_elem = [_class, _count];
magsWithAmmoCounts set [count magsWithAmmoCounts, _elem];
} forEach (magazinesAmmoFull player);

[_uid, _uid, "MagazinesWithAmmoCount", magsWithAmmoCounts] call fn_SaveToServer;
//[_uid, _uid, "Weapons", Weapons player] call fn_SaveToServer;
moneyLevel = player getVariable "cmoney";
[_uid, _uid, "Money", moneyLevel] call fn_SaveToServer;
player globalChat "Player saved! DO NOT SAVE EXTRA GUN IN BACKPACK!";
};

// Possible new methods


and here is my c_playerDBSetup.sqf

Code: [Select]
//===========================================================================
applyPlayerDBValues =
{
private ["_array","_varName","_varValue","_i","_in","_exe","_backpack","_sendToServer","_uid"];
diag_log format["applyPlayerDBValues called with %1", _this];
_array = _this;
_uid = _array select 0;
_varName = _array select 1;

if (count _array == 3) then {
_varValue = _array select 2;
} else {
//diag_log format["applyPlayerDBValues failed to get a value for %1", _varName];
};

// Donation error catch
if(((getPlayerUID player) != _uid) AND ((getPlayerUID player) + "_donation" != _uid)) exitWith {if(_varName == 'DonationMoney') then {donationMoneyLoaded = 1;};};

//if there is not a value for items we care about exit early
if(isNil '_varValue') exitWith
{
//diag_log format["applyPlayerDBValues early termination with nil value for %1", _varName];
if(_varName == 'Position') then {positionLoaded = 1;};
if(_varName == 'DonationMoney') then {donationMoneyLoaded = 1;};

if(_varName == 'PrimaryWeapon') then {primaryLoaded = 1;};
if(_varName == 'HandgunWeapon') then {handgunLoaded = 1;};
if(_varName == 'SecondaryWeapon') then {secondaryLoaded = 1;};

if(_varName == 'Backpack') then { backpackLoaded = 1;};
if(_varName == 'Vest') then { vestLoaded = 1;};
if(_varName == 'Uniform') then { uniformLoaded = 1;};
if(_varName == 'Items') then { itemsLoaded = 1;};
};

if(_varName == 'DonationMoney') then {player setVariable["donationMoney",_varValue,true]; donationMoneyLoaded = 1;};

// Inventory item section. Use mf_inventory_all as set up by the mf_inv system
{
_itemID = _x select 0;
if (_varName == _itemID) then {
// Special call to mf_inventory_add specifying an absolute value
[_varName, _varValue, true] call mf_inventory_add;
};
} forEach call mf_inventory_all;

if(_varName == 'Health') then {player setDamage _varValue;};

//if(_varName == 'Magazines') then {{player addMagazine _x;}foreach _varValue;};

if (_varName == 'MagazinesWithAmmoCount') then {
{
_className = _x select 0; // eg. 30Rnd_65x39_caseless_mag
_ammoCount = _x select 1; // Magazine current ammo count

if ([player, _className] call fn_fitsInventory) then
{
player addMagazine [_className, _ammoCount];
};
} forEach _varValue;
};

if((_varName == 'Items')) then
{
for "_i" from 0 to (count _varValue) - 1 do
{
_name = _varValue select _i;
_backpack = unitBackpack player;
_inCfgWeapons = isClass (configFile >> "cfgWeapons" >> _name);

// Optics seems to denote an 'item' if 1 or 'weapon' is 0
_cfgOptics = getNumber (configFile >> "cfgWeapons" >> _name >> "optics");

if (_inCfgWeapons && _cfgOptics == 0 && (!isNil '_backpack')) then {_backpack addWeaponCargoGlobal [_name,1];}
else
{
if ([player, _name] call fn_fitsInventory) then
{
player addItem _name;
};
};
};
if(_varName == 'Items') then {itemsLoaded = 1;};
};

if(_varName == 'PrimaryWeaponItems') then
{
{
if (_x != "") then
{
player addPrimaryWeaponItem _x;
};
}foreach _varValue;
};
if(_varName == 'SecondaryWeaponItems') then
{
{
if (_x != "") then
{
player addSecondaryWeaponItem _x;
};
}foreach _varValue;
};
if(_varName == 'HandgunItems') then
{
{
if (_x != "") then
{
player addHandgunItem _x;
};
}foreach _varValue;
};

if(_varName == 'Uniform') then {removeUniform player; player addUniform _varValue; uniformLoaded = 1;};
if(_varName == 'Vest') then {removeVest player; player addVest _varValue; vestLoaded = 1;};
if(_varName == 'Backpack') then {removeBackpack player; player addBackpack _varValue; backpackLoaded = 1;};
if(_varName == 'HeadGear') then {removeHeadgear player; player addHeadgear _varValue;};
if(_varName == 'Goggles') then {player addGoggles _varValue};

if(_varName == 'Position') then {player setPos _varValue; player setVariable["playerWasMoved",1,true]; positionLoaded = 1;};
if(_varName == 'Direction') then {player setDir _varValue;};

if(_varName == 'PrimaryWeapon') then{player addWeapon _varValue; primaryLoaded = 1;};
if(_varName == 'HandgunWeapon') then{player addWeapon _varValue; handgunLoaded = 1;};
if(_varName == 'SecondaryWeapon') then {player addWeapon _varValue; secondaryLoaded = 1;};
if(_varName == 'Money') then {
moneyLevel = _varValue;
_player setVariable ["cmoney", _varValue, true];
moneyLoaded = 1;
};
if(_varName == 'AssignedItems') then {
{
_inCfgWeapons = isClass (configFile >> "cfgWeapons" >> _x);
if (_inCfgWeapons) then {
// Its a 'weapon'
player addWeapon _x;
} else {
player linkItem _x;
};
} foreach _varValue;
};
};

//===========================================================================
_sendToServer =
"
accountToServerLoad = _this;
publicVariableServer 'accountToServerLoad';
";

sendToServer = compile _sendToServer;
//===========================================================================
"accountToClient" addPublicVariableEventHandler
{
(_this select 1) spawn applyPlayerDBValues;
};
//===========================================================================

statFunctionsLoaded = 1;

i've looked over all the changes and made sure that its right, for a new arma 3 dev i'm a bit lost as to if i did anything wrong on the implementation.


EDIT: i found the little bugger, in the code you mentioned (c_playerDBSetup.sqf) there was a "_" attached before the player, so this was the fix (using this)
Code: [Select]
if(_varName == 'Money') then {
moneyLevel = _varValue;
player setVariable ["cmoney", _varValue, true];
moneyLoaded = 1;
};
this could be the fix for those having problems with the code.