Aller au contenu

Photo

RE: Sending a PC to a Stored Location After Server Reset - FAIL


  • Veuillez vous connecter pour répondre
24 réponses à ce sujet

#1
Pstemarie

Pstemarie
  • Members
  • 2 745 messages
 Help....
I've got this weird issue with returning characters to their previous location when they reenter the server.
I store the character's location into the campaign database in the OnClientLeave event. Then in the OnClientEnter hook I check if the db has a stored location for that player and if it does I jump them to that location. Everything works fine as long as I don't restart the server. 
When I restart the server the jump never occurs even though the location is still stored. I thought the player's public cd-key remained the same so I'm sure what the issue is...
I have done some digging and came up with an alternate - storing the location on a player database item. Works great as long as I do not restart the server. Do I need to export the character in the OnClientLeave event to get these variables to stick around after a server reset or is the problem with the stored location?

#2
henesua

henesua
  • Members
  • 3 863 messages
The solution is to store the location in its constituent parts. BUT instead of storing the area as an object, store it as the area's tag. The position vector can be stored as a float for each piece of it X, Y, Z and rotation.

In NBDE Knat does just this when storing a location to the database.

#3
virusman

virusman
  • Members
  • 282 messages
Player's location is automatically stored by the server without any scripting. External database is necessary to save/restore player's location across restarts. So probably your script is just not working. Check if it saves anything at all.

#4
henesua

henesua
  • Members
  • 3 863 messages
Virusman, he's talking about this being a problem across server resets.

The problem is that the game object for the area can change across restarts. And thus the Location data is not good after a restart.

The way around this is to come up with a better way to store the area. This will allow you to have the correct area when you recreate the location from its parts when you need it.

[edit] Thanks for posting this, BTW, it reminded me of this problem. I need to adjust my contribution t this month's CCC.

Modifié par henesua, 27 janvier 2013 - 11:30 .


#5
Sadira of Tyr

Sadira of Tyr
  • Members
  • 172 messages
We use this script to save the PC's location, and it works fine through a server reset. An external database is not needed.


void main()
{
    object oPC = GetPCSpeaker();
    object oArea = GetArea(oPC);
    string sPCName = GetName(oPC);  // Gets the name of the PC
    string CDKey = GetPCPublicCDKey(oPC); // Gets the public CD Key of the player . . . adds to quality of check
    string sID = GetStringLeft(sPCName,10); // Indivudual Character Used
    string sHID = sID+CDKey; // HCR Style;
    string sAreaName = GetName(oArea);  // Sets up a string using the name of the Area
    string sAreaTag = GetTag(oArea); // Sets up a string using the Tag of the Area
    if(GetIsPC(oPC)||(GetIsDM(oPC) && !GetIsDMPossessed(oPC)))  // Script will fire for PCs and DMs
    {
        vector vPosition = GetPositionFromLocation(GetLocation(oPC));
        float nX = vPosition.x;
        float nY = vPosition.y;
        float nZ = vPosition.z;
        int nAreaX = FloatToInt(nX);
        int nAreaY = FloatToInt(nY);
        int nAreaZ = FloatToInt(nZ);
        string sAreaX = IntToString(nAreaX);
        string sAreaY = IntToString(nAreaY);
        string sAreaZ = IntToString(nAreaZ);
        // Set an Int so we can check for a saved location in the portal or trigger
        SetCampaignInt("PlayerStartLoc", "StartLocSet_" + sHID, 1, oPC);
        // Create Persistent Location Data
        SetCampaignInt("PlayerStartLoc", "AreaX_" + sHID, nAreaX, oPC);
        SetCampaignInt("PlayerStartLoc", "AreaY_" + sHID, nAreaY, oPC);
        SetCampaignInt("PlayerStartLoc", "AreaZ_" + sHID, nAreaZ, oPC);
        SetCampaignString("PlayerStartLoc", "AreaTag_"+ sHID, sAreaTag, oPC);
        // Tell the player what the Saved Area will be for verification
        SendMessageToPC(oPC, "Persistent Saved Location is " + sAreaName);
        // Apply a visual effect.
        effect eVFX;
        eVFX = EffectVisualEffect(VFX_FNF_STRIKE_HOLY);
        ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVFX, GetLocation(oPC));
    }
}

Modifié par Sadira of Tyr, 27 janvier 2013 - 11:32 .


#6
Pstemarie

Pstemarie
  • Members
  • 2 745 messages
Wow, Sadira that saves me a ton of work. Thanks :) Now how to I get him back there when the server restarts and the Player enters?

#7
Sadira of Tyr

Sadira of Tyr
  • Members
  • 172 messages
You are welcome. Here is our port back script, used with an area trigger.

// This script pulls data for location and jumps PC to last save point
// Use this in the onused event of a portal or onenter of a trigger or a conversation
void main()
{
    // uncomment the proper definition of the player object for your use!!!
    object oPC = GetEnteringObject();
    //object oPC = GetLastUsedBy();
    //ojbect oPC = GetLastSpeaker();
    if(GetIsPC(oPC)||(GetIsDM(oPC) && !GetIsDMPossessed(oPC)))  // Script will fire for PCs and DMs
    {
    string sPCName = GetName(oPC);  // Gets the name of the PC
    string CDKey = GetPCPublicCDKey(oPC); // Gets the public CD Key of the player . . . adds to quality of check
    string sID = GetStringLeft(sPCName,10); // Indivudual Character Used
    string sHID = sID+CDKey; // HCR Style user check
    // Read Persistent Location Data
    int nPlayerBeenHere = GetCampaignInt("PlayerStartLoc", "StartLocSet_" + sHID, oPC);  // The check to see if PC has a saved location
    int nAreaX = GetCampaignInt("PlayerStartLoc", "AreaX_" + sHID, oPC);
    int nAreaY = GetCampaignInt("PlayerStartLoc", "AreaY_" + sHID, oPC);
    int nAreaZ = GetCampaignInt("PlayerStartLoc", "AreaZ_" + sHID, oPC);
    string sAreaName = GetCampaignString("PlayerStartLoc", "AreaName_" + sHID, oPC);
    string sAreaTag = GetCampaignString("PlayerStartLoc", "AreaTag_"+ sHID, oPC);
    // Set up the location from Database info
    float y = IntToFloat(nAreaY);
    float x = IntToFloat(nAreaX);
    float z = IntToFloat(nAreaZ);
    vector vTargetLoc = Vector(x, y, z);
    location lTargetLoc = Location(GetArea(GetObjectByTag(sAreaTag)), vTargetLoc, 0.0);
    if(nPlayerBeenHere == 1) // if the player has a saved location lets run this scripts
        {
            if (GetCurrentHitPoints(oPC)<=0)  // See if the PC has 0 or less HPs . . . added this for HCR death system
            {
            SendMessageToPC(oPC,"You are still be dead...");
            }
            else // If the PC is above 0 HPs, send them to their last save location
            {
            SendMessageToPC(oPC,"Portaling to your Saved Location in 3 Seconds...");
            DelayCommand(3.0, AssignCommand(oPC, ActionJumpToLocation(lTargetLoc)));
            }
        }
    }
}

#8
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
I have to run so I will make this fast.

I see no need to break the entire location down for storage. The only problem with the stored location is the area, and that is only if the module was worked on and the area order changes.

Simply storing the location and the area tag should be good enough to rebuild a valid location.

location lLoc = Get**Location();
string sArea = Get**AreaTag();
lLoc = location (GetObjectByTag(sArea), GetpositionFromLocation(lLoc), GetFacingFromLocation(lLoc));

May be errors above, Just typing from memory to give an Idea.

As far as the problem you are having, I would have to check and see if the location in the OnClientLeave event is valid. You may need to store the location before that event fires. After all does not the Area Exit Event fire first? Once the pC leaves the area there location is no longer valid.

Modifié par Lightfoot8, 28 janvier 2013 - 12:11 .


#9
Pstemarie

Pstemarie
  • Members
  • 2 745 messages
Lightfoot, this sounds more promising. I couldn't get Sadira's scripts to work correctly - probably caused by my implementing them through OnClientLeave and the OnClientEnter hook script (x3_mod_pre_enter).

Tried moving the store location script to Area OnExit and still no go. It appears that nothing is being stored in the database for the area, making me certain that the area or location is no long valid even from that event.

Modifié par Pstemarie, 28 janvier 2013 - 01:30 .


#10
henesua

henesua
  • Members
  • 3 863 messages
 this is how i do it in my spell focus system - storing a location on an item


location GetSpellFocusLocation(object oFocus)
{


return Location( GetObjectByTag(GetLocalString(oFocus, SPELLFOCUS_LOCATION_TAG)),
Vector( GetLocalFloat(oFocus, SPELLFOCUS_LOCATION_X),
GetLocalFloat(oFocus, SPELLFOCUS_LOCATION_Y),
GetLocalFloat(oFocus, SPELLFOCUS_LOCATION_Z)
),
GetLocalFloat(oFocus, SPELLFOCUS_LOCATION_F)
);
}

// Stores a location on a spell focus. - [FILE: spellfocus_inc]
void StoreSpellFocusLocation(object oFocus, location lLoc)
{
vector vPos = GetPositionFromLocation(lLoc);

SetLocalString(oFocus, SPELLFOCUS_LOCATION_TAG, GetTag(GetAreaFromLocation(lLoc)));
SetLocalFloat(oFocus, SPELLFOCUS_LOCATION_X, vPos.x);
SetLocalFloat(oFocus, SPELLFOCUS_LOCATION_Y, vPos.y);
SetLocalFloat(oFocus, SPELLFOCUS_LOCATION_Z, vPos.z);
SetLocalFloat(oFocus, SPELLFOCUS_LOCATION_F, GetFacingFromLocation(lLoc));
}

#11
Pstemarie

Pstemarie
  • Members
  • 2 745 messages
Ok here's what I'm basically looking for -

1. The PW is a LAN PW and although I could leave my computer on all the time I really don't want to pay the higher electricity bill to do so - besides we only play once every other week or so.

2. The Server must be able to be reset (see above)

3. Therefore, I want to be able to store a PCs location when they leave the module and I want this to be persistent across resets.

4. I'd like to avoid using a trigger or conversation to do so. I just want the player to be able to exit and when they do the game stores their location in the database.

5. I'm using bioware's database because NWNX is so far over my head I feel like Lurch whenever I look at it.

#12
henesua

henesua
  • Members
  • 3 863 messages

Pstemarie wrote...
I want to be able to store a PCs location when they leave the module and I want this to be persistent across resets.


I store this on module heartbeat and it works for me. Module Heartbeat typically runs between 4 and 6 times a minute as far as I can tell. I'll post my code on pastebin.

Pstemarie wrote...
I'm using bioware's database because NWNX is so far over my head I feel like Lurch whenever I look at it.


Bio DB is adequate. But I recommend using Knat's NBDE to increase its speed. I fixed a few functions in it which you should have access to now.

Modifié par henesua, 28 janvier 2013 - 04:31 .


#13
Pstemarie

Pstemarie
  • Members
  • 2 745 messages
Knat's NDBE doesn't exactly play nicely with the systems I've got in place - Alternate Horse Scripts and Killer Death System.

#14
henesua

henesua
  • Members
  • 3 863 messages
I am sure I can make it work for you just fine if you are willing to let me help. Let me know.

Otherwise, I can write a non-NBDE system for this if you would like. In fact... the one I posted above could easily be adapted.

#15
Pstemarie

Pstemarie
  • Members
  • 2 745 messages
Don't get me wrong - I need the help badly. I plan on looking into NBDE again. I just haven't gotten to it. Your previous post gave me some ideas so I'm going to try those, but please post your code cause I'm skeptical my idea will work.

#16
The Amethyst Dragon

The Amethyst Dragon
  • Members
  • 1 878 messages
Part of your problem may be that the PC object often becomes "invalid" when a player logs out, before any scripting can typically capture the PC object for any sort of use.

Using the heartbeat script to store the location to the BioWare database is a pretty decent idea.  Then, when your player(s) log back in, the location is reconstructed during the player client enter script run.

I just whipped up a quick and dirty set of script blocks that should help you out.  Just copy/paste into your own scripts. :)

persistentlocation.txt

#17
Failed.Bard

Failed.Bard
  • Members
  • 774 messages
If it's a server vault environment, storing the location to the PC skin works as well, as long as you're using the location plus area tag method already mentioned.

#18
Pstemarie

Pstemarie
  • Members
  • 2 745 messages
Got it working! Many thanks for the help. I ended up using AD's scripts - one in the module OnHeartbeat and one in the Character Setup area's OnEnter event. It was more slim than the others. However, I do like the way Henesua's puts the location handler into its own function - something I'll do with this no doubt.

I did also relearn something - why I dumped NBDE originally. When I converted the write functions over to NBDE everything broke. For some reason the database was no longer being written too. I tried deleting the database and starting over, but to no avail. I'm thinking it has something to do with the subsystems I've included:

The Krit's Alternate Horse Scripts
Axe Murderer's Killer Death System
Scarface's Persistent Banking System
Screw Tape's Simple XP

I have no idea what's causing the conflict because everything compiles fine once NBDE is added. NBDE just won't write to the database or create it if/when needed.

#19
The Amethyst Dragon

The Amethyst Dragon
  • Members
  • 1 878 messages
Pstemarie,

Glad the scripting will work with your module's systems.

I too got rid of NBDE. It worked well for quite a while, but then started losing data on a too-often basis (like journal entries and quest states I was storing). Probably because I was using it to store a lot of variables. I ended up switching to a small PC inventory item for persistent data storage for almost everything (PC skins are frequently replaced in my PW, so that isn't a viable storage item).

Modifié par The Amethyst Dragon, 29 janvier 2013 - 12:32 .


#20
Pstemarie

Pstemarie
  • Members
  • 2 745 messages
Yeah, I've only got five players so its not like a lot is getting stored in the database. I have a journal item that I can store data on as well as the PC skin.

I still have reservations about using a Heartbeat like this, but I'm sure the quantity of data won't be too taxing. If I notice too much lag I'll just store the values on the PC Data Item instead.

EDIT - Just thought of a way I can reduce any drag that might be caused by this...I can skip the location storage if the PC is in combat.

Modifié par Pstemarie, 29 janvier 2013 - 01:00 .


#21
The Amethyst Dragon

The Amethyst Dragon
  • Members
  • 1 878 messages
That's a good idea. I include a bit to the storage chunk to see if a PC has moved since the last check. If not, no database storage. :)

#22
GhostOfGod

GhostOfGod
  • Members
  • 863 messages
Just to add 2 cents to the topic, I also do it as L8 suggests whether you do it with a DB or a DBItem. Something like so:

object oPlayer   = OBJECT_SELF;
object oDBItem   = GetItemPossessedBy(oPlayer, "item tag");
location lPlayer = GetLocation(oPlayer);
string sAreaTag  = GetTag(GetArea(oPlayer));

Store Location:

SetLocalLocation(oDBItem ,"MY_LOC" ,lPlayer);
SetLocalString(oDBItem ,"MY_AREA" ,sAreaTag);


Rebuild Location:

location lLoc   = GetLocalLocation(oDBItem ,"MY_LOC");
string sAreaTag = GetLocalString(oDBItem ,"MY_AREA");
lLoc = Location(GetObjectByTag(sAreaTag),
                GetPositionFromLocation(lLoc),
                GetFacingFromLocation(lLoc));


There's no need to store all the separate x,y,z and what not. I also dont use HB to store. Just multiple other events. OnPlayerRest, area OnEnter/OnExit, etc..

Glad to hear you got it worked out.

Modifié par GhostOfGod, 29 janvier 2013 - 03:05 .


#23
henesua

henesua
  • Members
  • 3 863 messages
*smacks head*

Of course. Because the only bad part of the data is the area so you can still use everything else.

I've got to edit some scripts.

Thanks.

#24
Pstemarie

Pstemarie
  • Members
  • 2 745 messages
Thanks for the input GoG, I've also got some adjustments to make. Seems that storing the location like I am has broken KDS1's ability to drop a "cheater" that logs out to avoid the Purgatory penalty back into Purgatory.

EDIT - Everythings working smoothly now....

I had to add some checks to the function KDS1OnClientEnter() to check if an entering PC had the PC Data Item and if so port them to their stored persistent location. If they don't have the PC Data Item then they are assumed to be a First Time Login and sent to the Character Setup area.

Next, I had to relocate the module's start location to the module's first area  - where they go when they exit the Character Setup area. KDS1OnClientEnter() was then moved from the module's OnClientEnter script to the OnEnter script for this area.

So now the module basically functions as so:

PC logs into server and is sent to the module's first area. The area's OnEnter event then runs them through the KDS1OnClientEnter() function and checks for:
  • If the character went to the Purgatory area then logged out to avoid the penalty, they are penalized and sent to the Church area - where all characters go when leaving Purgatory.
  • Else, If the character was a "dead" body when they logged out they are sent to Purgatory.
  • Else, if none of the above applies and the character is not a first time login, then they are sent to their stored location.
  • Else, if a first time login they are sent to the Character Setup area.

Modifié par Pstemarie, 29 janvier 2013 - 11:44 .


#25
Pstemarie

Pstemarie
  • Members
  • 2 745 messages
This is coming together quite nicely - thanks in no small part to Henesua and his allowing me to look over Arnheim. I've got tons of ideas just spinning in circles about my head. Anyway, I've added some more persistent functionality...

I've got persistence working now for remaining feat (thanks to HCR 2.0) and spell uses. I've also modified the rest event so that feats and spells can only be regained once every 24 hours. Furthermore, for every 8 hour cycle you rest, you regain 1 HP per Level.