Home   Help Search Login Register  

Author Topic: How do servers save players info for games like Sahrani life and evolution?  (Read 9079 times)

0 Members and 1 Guest are viewing this topic.

Offline D007

  • Members
  • *
U da man Spon.. the arma world needs you...    :clap:
Me.. I read your money script and That's all it takes for me to go back to updating releases..
the project is just to broad.

There's just to much info for me to absorb in any kind of timely manner.
I just don't have the time to figure this out and
to update the mission releases and bug fixes.

Offline Mr.Peanut

  • Former Staff
  • ****
  • urp!
And he says something about like Net 1.1, or DSTS will not work, BUT he doesnt even say what Net is  :weeping: Im so confused... :(
As spooner said armalib is a more recent implementation than dsts so you should try using it. I had not heard of armalib until this thread. In addition dsts is not being developed anymore.
urp!

Offline i0n0s

  • Former Staff
  • ****

Offline Spooner

  • Members
  • *
  • Mostly useless
    • Community Base Addons
@Mr Peanut
Actually, all the SPON_player_* arrays are generated locally, so aren't actually updated across the network (this has advantages and disadvanges, of course; the main reason I have left it the way it is because this way allows stuff like the SPON Map addon to be client-side only).

Yes, I could do this, though perhaps not as "easily" as you imply, but I'm already busy with 100 other things (and I'd put it into SPON Money if I ever got around to doing it, but I've been promising to update SPON Money for a loooong time, so... ;P). I'll see what help I can put in on this thread, but I don't think I'm going to develop this whole system right now.
[Arma 2] CBA: Community Base Addons
[Arma 1] SPON Core (including links to my other scripts)

Offline Mr.Peanut

  • Former Staff
  • ****
  • urp!
@spoon
How do you determine the new players when they connect? You get _id and _name, but how do you determine what object they are? And is onPlayer(Dis)Connected run on all clients or just the server?

Your SPON_player_* arrays are generated locally, but they should be the same on all nodes, correct? I never realised the isPlayer command could be used to test whether any unit was a player, I thought it was equivalent to == player.
« Last Edit: 27 Aug 2008, 00:25:52 by Mr.Peanut »
urp!

Offline Spooner

  • Members
  • *
  • Mostly useless
    • Community Base Addons
The arrays are not consistent across machines, since they are updated only every 15 seconds and I'm sure this will become out of sync. Even if they are consistent in what objects they contain, the order of items might not be consistent.

Interestingly, if you team-switch (selectPlayer) out of your player object, then both the AI you leave behind and your new body are considered to be isPlayer. I suspect that playable AI on missions where "AIDisabled = 0" are isPlayers too, but I haven't checked that, since I hate having friendly AI in missions *sighs*
[Arma 2] CBA: Community Base Addons
[Arma 1] SPON Core (including links to my other scripts)

Offline Mr.Peanut

  • Former Staff
  • ****
  • urp!
I hate AI in game too, and the fact that JIP means you spawn into one. It is a sharp hot pain in the arse.

Does this mean it is impossible to have a list of players without using the onPlayer(Dis)Connected commands? I did not want to touch those since they do not stack (What was BI thinking?). Someone really needs to add a system to SPON CORE that manages a stack like this, similar to how the key event handles are managed. :P

I suppose one other solution is for each player, in the init.sqf, launch a script that checks an array of all playable units, and broadcasts which playable unit they are to the server using dynamic variables. The server would be initialised with a publicEventHandler for each possible client. But then, as you suggest, if would be impossible to detect players disconnecting. What a mess...
urp!

Offline Mr.Peanut

  • Former Staff
  • ****
  • urp!
(Yes a double post but its content should be independent of my last post)

Posting WIP code here for comments/criticism. Yes I know it is not commented. :P

What I really need to know is if I am on the right track.  The plan is to let any editor placed named in-game object have a data array.  If desired, I will add functions to add or remove objects from the array. There is an array called SMB_PDS_master that contains the names of all the players and npc objects. This array is scanned to match a name to an master index. Each player/npc has a data array that is referenced by that master index. At present, dead objects still hold data. I decided against using one huge array for all data for all objects, because it complicated matters, even if it might be the more proper way to implement. :P

You specify the "key" by which you want to call your data e.g. "money". Then use the get/set functions:
[_unit, "money", 10] call SMB_PDS_fSetValue
_value = [_unit, "money"] call SMB_PDS_fGetValue

[ASIDE]Will the following line:
Code: [Select]
SMB_PDS_playersConnected = SMB_PDS_playersConnected + [SMB_PDS_PLAYERS select _playerIndex];work as intended? Or will it store only the present player object and lose it between death and respawn? I don't rely on that line of code, but it sure would be handy. I suspect that it is crap though.  :shhh:[/ASIDE]

Adding in ArmALib to make data permanent on the server is pretty easy, though my (dis)connected functions would be different. Since ArmALib can detect itself, this would not be a problem. ArmALib is for windows servers only.


smb_pds\smb_pds_init.sqf
Call this near the top of your init.sqf with [] call compile preProcessFileLineNumbers "smb_pds\smb_pds_init.sqf"
Code: [Select]
#define SMB_PDS_PREFIX = "SMB_PDS_ARRAY_"
// User Defined
#define SMB_PDS_PLAYERS = [p1, p2, p3, p4, ...]
#define SMB_PDS_NPCS = [npc1, npc2, ...]
#define SMB_PDS_KEYS = ["Money","Pride",...]
#define SMB_PDS_DEFAULTS = [0, 10,...]
// End User Defined

//Global variables
//{SMB_PDS_names = SMB_PDS_names + [_x];} forEach SMB_PDS_PLAYERS;// crap
SMB_PDS_playersConnected = [];
SMB_PDS_namesConnected = [];
SMB_PDS_storeKeys = [];
SMB_PDS_storeData = [];
SMB_PDS_master = SMB_PDS_PLAYERS + [SMB_PDS_NPCS];

//Define functions
SMB_PDS_fGetData = compile preProcessFileLineNumbers "smb_pds\smb_pds_fGetData.sqf";
SMB_PDS_fSetData = compile preProcessFileLineNumbers "smb_pds\smb_pds_fSetData.sqf";

// Load store, if available, on server
if (isServer) then
{
    SMB_PDS_fOnPlayerConnected = compile preProcessFileLineNumbers "smb_pds\smb_pds_fOnPlayerConnected.sqf";
    SMB_PDS_fOnPlayerDisconnected = compile preProcessFileLineNumbers "smb_pds\smb_pds_fOnPlayerDisconnected.sqf";
    onPlayerConnected "[_name, _id] call SMB_PDS_fOnPlayerConnected;";
    onPlayerDisconnected "[_name, _id] call SMB_PDS_fOnPlayerDisconnected;";
};
nil;

smb_pds\smb_pds_fGetData.sqf
Code: [Select]
private ["_unit", "_key", "_value", "_nameList", "_masterIndex", "_keyIndex"];
_unit = _this select 0;
_key = _this select 1;
_nameList = [];
{_nameList = _nameList + [_x];} forEach SMB_PDS_master;
_masterIndex = _name find _nameList;
if (_masterIndex > -1) then
{
    _keyIndex = _key find SMB_PDS_KEYS;
    if (_keyIndex > -1) then
    {
        call compile format ["_value = " + SMB_PDS_PREFIX + "%1 select %2];", _masterIndex, _keyIndex];
    };
};
_value;

smb_pds\smb_pds_fSetData.sqf
Code: [Select]
private ["_unit", "_key", "_value", "_nameList", "_masterIndex", "_keyIndex"];
_unit = _this select 0;
_key = _this select 1;
_value = _this select 2;
_nameList = [];
{_nameList = _nameList + [_x];} forEach SMB_PDS_master;
_masterIndex = _name find _nameList;
if (_masterIndex > -1) then
{
    _keyIndex = _key find SMB_PDS_KEYS;
    if (_keyIndex > -1) then
    {
        call compile format [SMB_PDS_PREFIX + "%1 set [%2, %3];", _masterIndex, _keyIndex, _value];
        call compile format ["publicVariable """ + SMB_PDS_PREFIX + "%1"";", _masterIndex];
    };
};
nil;

smb_pds\smb_pds_fOnPlayerConnected.sqf
Code: [Select]
private ["_name", "_id", "_nameList", "_masterIndex", "_playerIndex", "_storeIndex"];
_name = _this select 0;
_id = _this select 1;
_nameList = [];
{_nameList = _nameList + [_x];} forEach SMB_PDS_PLAYERS;
_playerIndex = _name find _nameList;
if (_playerIndex > -1) then
{
    _nameList = [];
    {_nameList = _nameList + [_x];} forEach SMB_PDS_master;
    _masterIndex = _name find _nameList;
    SMB_PDS_playersConnected = SMB_PDS_playersConnected + [SMB_PDS_PLAYERS select _playerIndex];
    SMB_PDS_namesConnected = SMB_PDS_namesConnected + [_name];
    _storeIndex = _name find SMB_PDS_storeKeys;
    if (_storeIndex > -1) then
    {
        SMB_PDS_storeKeys = SMB_PDS_storeKeys - [_name]; 
        call compile format [SMB_PDS_PREFIX + "%1 = SMB_PDS_storeData select %2;", _masterIndex, _storeIndex];
        call compile format ["SMB_PDS_storeData = SMB_PDS_storeData - [[" + SMB_PDS_PREFIX + "%1]];", _storeIndex];
    }
    else
    {
        call compile format [SMB_PDS_PREFIX + "%1 = SMB_PDS_DEFAULTS;", _masterIndex];
    }
    call compile format ["{publicVariable _x} forEach [""" + \
        SMB_PDS_PREFIX + "%1"", ""SMB_PDS_playersConnected"", ""SMB_PDS_namesConnected""], masterIndex];"
};
nil;

smb_pds\smb_pds_fOnPlayerDisconnected.sqf
Code: [Select]
private ["_name", "_id", "_nameList", "_masterIndex", "_playerIndex"];
_name = _this select 0;
_id = _this select 1;
_nameList = [];
{_nameList = _nameList + [_x];} forEach SMB_PDS_PLAYERS;
_playerIndex = _name find _nameList;
if (_playerIndex > -1) then
{
    _nameList = [];
    {_nameList = _nameList + [_x];} forEach SMB_PDS_master;
    _masterIndex = _name find _nameList;
    SMB_PDS_playersConnected = SMB_PDS_playersConnected - [SMB_PDS_PLAYERS select _playerIndex];
    SMB_PDS_namesConnected = SMB_PDS_namesConnected - [_name];
    SMB_PDS_storeKeys = SMB_PDS_storeKeys + [_name]; 
    call compile format ["SMB_PDS_storeData = SMB_PDS_storeData + [[" + SMB_PDS_PREFIX + "%1]];", _masterIndex];
    {publicVariable _x} forEach ["SMB_PDS_playersConnected", "SMB_PDS_namesConnected"];
};
nil;
« Last Edit: 02 Sep 2008, 16:10:14 by Mr.Peanut »
urp!

Offline Spooner

  • Members
  • *
  • Mostly useless
    • Community Base Addons
You seem to be on the right sort of track with this.

Using separate 1-D arrays rather than a large data-structure is good, because it allows you to use find (otherwise you spend a lot of time in iterating through a big structure).

The immediate issue with this is that you can only ever set values on one machine, ideally the server. For example, should the player sell something and get then set, he might overwrite the get/set from someone else transferring money to him. If you are just using numbers, then an add and subtract could more safely replace set. You still have to make sure that subtractions are only run on one machine, or values could go below 0. Alternatively, having subtract with a callback function when it has been confirmed that the value can be reduced.

While I was scanning the scripts, I noticed a few things you might appreciate:

Since publicVariable just takes a string,
Code: (smb_pds\smb_pds_fSetData.sqf) [Select]
call compile format ["publicVariable """ + SMB_PDS_PREFIX + "%1"";", _masterIndex];
can be simplified to:
Code: (smb_pds\smb_pds_fSetData.sqf) [Select]
publicVariable (SMB_PDS_PREFIX + (str _masterIndex));

You can also simplify:
Code: (smb_pds\smb_pds_fGetData.sqf) [Select]
call compile format ["_value = " + SMB_PDS_PREFIX + "%1 select %2;", _masterIndex, _keyIndex];
to (and although I like using + for strings too, it is confusing, for me at least, when you use format and + at the same time).
Code: (smb_pds\smb_pds_fGetData.sqf) [Select]
_value = call compile format ["%1%2 select %3;", SMB_PDS_PREFIX, _masterIndex, _keyIndex];

Code: (smb_pds\smb_pds_fOnPlayerDisconnected.sqf) [Select]
_nameList = [];
{_nameList = _nameList + [_x];} forEach SMB_PDS_master;
is equivalent to
Code: (smb_pds\smb_pds_fOnPlayerDisconnected.sqf) [Select]
_nameList = [] + SMB_PDS_master;
Though when you do this, you just use the new array once for a find, so no reason to copy the array in the first place.
[Arma 2] CBA: Community Base Addons
[Arma 1] SPON Core (including links to my other scripts)

Offline h-

  • OFPEC Site
  • Administrator
  • *****
  • Formerly HateR_Kint
    • OFPEC
Ok, this is getting too MP:ey to be at the advanced section..  :P

Moved.
Project MCAR   ---   Northern Fronts   ---   Emitter 3Ditor
INFORMATIVE THREAD TITLES PLEASE. "PLEASE HELP" IS NOT ONE..
Chuck Norris can divide by zero.

Offline hoz

  • OFPEC Site
  • Administrator
  • *****
Sorry to butt in here but I saw this...

Quote
For a start, it is usual to set the mission as persistent ("persistent=1;" in description.ext)

Are you sure this isn't a server side setting?

Biki

I don't see it mentioned in the description.ext.

Sorry to hijack the discussion.
Xbox Rocks

Offline Spooner

  • Members
  • *
  • Mostly useless
    • Community Base Addons
Sorry, yes, I wasn't thinking :whistle:
[Arma 2] CBA: Community Base Addons
[Arma 1] SPON Core (including links to my other scripts)

Offline Mr.Peanut

  • Former Staff
  • ****
  • urp!
Thanks for the feedback. I've noticed a few other bugs as well. Christ what a quagmire! I was trying to avoid having to broadcast all data arrays every 15 seconds, but perhaps that is unavoidable. If it is unavoidable, it makes more sense to pack everything into one array.

The real question becomes how to stack requests to the server that fall in between the server broadcasting its status as locked or unlocked. Should data operations be serialized, or for maximum flexibility should compiled code strings be PV'd?

Other ponderables. When a player disconnects the AI retains the name until someone else spawns in?
« Last Edit: 02 Sep 2008, 20:15:34 by Mr.Peanut »
urp!

Offline B2KDragon

  • Members
  • *
    • Dogs of War
or u could look out for armalibery which does all that cant remeber were i got it though lol :scratch:

Offline D007

  • Members
  • *
as for weather arma considers ai you spawn into even with disableai =true isplayers.
I'd say yes..
Unintentionally ended up finding that one out..
Or I'm missing something.. which is entirely possible.

seems when someone joins this mission they may go into the cut scene,
which is reserved for dead players..

trigger executes at "not alive player" to setpos the body to a graveyard and a cut scene.
then hidebody the corpse in the CS.

players that join may sometimes go straight into this cut scene.
Even though they had not died at all.

am I crazy or does that mean that body is always present?
So the body is never seen until someone joins?
then it is killed and taken over by the player?

and that transition from alive, to not live and back to alive, causes them to go into the CS?

and hot dayum Peanut..lol..
you went right into it didn't ya. :)..
cheers man..

« Last Edit: 03 Sep 2008, 01:28:07 by D007 »