Campaign Database and updating module in toolset
How does the campaign database persistense work if I choose to script a database, and then make updates into the module in toolset?
Will it be erased, or is there any way to keep player information?
And is the campaign database still "slow" as people claimed it was in 1.69?
Asking here because search engines didnt produce satisfactory results or I just didnt know what to look for
Thanks in advance.
Will it be erased, or is there any way to keep player information?
And is the campaign database still "slow" as people claimed it was in 1.69?
Asking here because search engines didnt produce satisfactory results or I just didnt know what to look for
Thanks in advance.
0
Comments
The Campaign Database is given a name. There are two lots of functions I think and one lot auto-names it the same as the module, where-as the other asks for the database name. This database is saved in the /database folder, and so long as it's not deleted (By script or manually) it remains there.
Editing a module will only break this continuation if it changes the module name (in the auto-named case) or if you change the name which you gave to the database in the calling scripts.
I've been using a system made by Knat called Natural Bioware Database Extension (NBDE). Think I found it in the vault.
Anyway, what it does is it stores all the variables that would be written to the database on objects. Then when called to, it saves those objects to the database. Essentially it can write thousands of entries with one entry. There are a bunch of other benefits it claims. Anyway it doesn't seems to work with locations, or vectors. And it also would not work with any SQL queries.
Just sharing to give you an idea about it.
If an old module uses it, it's fine to keep using - but highly recommended to avoid in new setups.
If variable name length really ends up being unsolvable by just using smaller names my recommendation is to make use of the full sqlite implementation. It does require acquiring a familiarity with sqlite commands but it can fill in far more gaps/additional functionality than NBDE
So I should Get persistence even on a virtual server currently?
Shutting down the host, and rehosting will preserve player locations etc if I create functions for these?
This is new for me and im trying to get into this because its very interesting, but I havent found a good tool to learn about neverwinter's database so im asking these dumb questions here
Calling SetCampaignInt("Dude", "Thing", 1); in a script, will create a file in that folder named 'dude.sqlite3'.
It is that file that stores the data. So long as that file is being persisted, (You did say virtual, throwing doubt into my understanding of how you are doing things...) your data—like locations—should be in it.
Im currently trying to get the module to save player location into a database OnClientExit but having no luck:
I made an include file:
//Database Include file:
//Variables:
const string DB_NAME = "THIS_DATABASE";//Test Database
const string DB_PC_L_LEAVE = "DB_PC_L_PLAYER_LEAVE"; //String into database for last place of exit
//Interface:
location GetPCStartLocation(object oPC, string sLocationID);
void SetPCStartLocation(object oPC, string sLocationID);
//Implementation:
location GetPCStartLocation(object oPC, string sLocationID){
return GetCampaignLocation(DB_NAME, sLocationID, oPC);
}
void SetPCStartLocation(object oPC, string sLocation){
{
SetCampaignLocation(DB_NAME, sLocationID , GetLocation(oPC), oPC);
}
}
And then OnClientLeave:
#include "dbmodule_test"
void main()
{
object oPC = GetExitingObject();
SetPCStartLocation(oPC, DB_PC_L_LEAVE);
}
Getting errors here while compiling.
Could you lend me a hand what I am doing wrong here.
I am not new to neverscript, but I am new to these databases.
Change to
Is this "resetting" the server ? The players start from the editor set start location after this.
Shouldnt variables placed into a campaign database persist and save "quest status" even after driving server down?
Alrighty. So, I was able to save and get locations through server resets using Get/SetCampaignLocation();
The only thing I did different was I did not use oPC as the last parameter: I set DB_NAME to be a database that only stores locations. I use sLocation as the unique identifier for the player. So in this database each player gets one, and only one, location.
This worked calling SetCampaignLocation() in the OnClientLeave script, and it also worked using test module from the toolset.
(I use GetTag(); to get the unique identifier, but this only works if you have a tagging system setup. Check out this post for details of how to set that up.)
This code of yours, from that playerhouse thread. Is it an include file you wrote?
// Give the player a unique tag. // sIsNew — The name of the database string MakePlayerTag(object oPC, string sIsNew); string MakePlayerTag(object oPC, string sIsNew) { string sTag = "tag"; // Get the last number that was used to make a tag. int iTag = GetCampaignInt(sIsNew, sTag); iTag = iTag + 1; // Mark that number has been used now. SetCampaignInt(sIsNew, sTag, iTag); string sParsed = IntToString(iTag); // Pad the tag with some zeros. int iLen; for(iLen = GetStringLength(sParsed); iLen <= 6; iLen++) { sParsed = "0" + sParsed; } sParsed = "ZZ" + sParsed; SetTag(oPC, sParsed); return sParsed; }I mean that a player wont get Tagged everytime he enters?
And then yes, you need to add the if statement from that same post in your OnClientEnter script.
By default GetTag(oPC); will return "". That will have a length of 0.
So its possible for a module to have more databases than one?
I thought that ppl generally made an #include file where they implemented the database name as a string constant, and implemented all database functions into there.
When I made all my database functions, that wasn't really an option. (And I still don't love SQL)
I made one database for each variable I wanted to keep track of. For example, I made a kill tracker that tracks kills by challenge rating. That means I have 7 databases just for all the different challenge ratings, and it works fine.
Now, I could have made just one database, and added a suffix or prefix to the unique identifier for each rating, but I didn't learn that until much later.
Posting my OnClientEnter, OnClientLeave, and Include files from my test module
OnClientEnter:
#include "x3_inc_horse" #include "dbmodule_test" string MakePlayerTag(object oPC, string sIsNew); void main() { object oPC=GetEnteringObject(); ExecuteScript("x3_mod_pre_enter",OBJECT_SELF); // Override for other skin systems if ((GetIsPC(oPC)||GetIsDM(oPC))&&!GetHasFeat(FEAT_HORSE_MENU,oPC)) { // add horse menu HorseAddHorseMenu(oPC); if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")) { // restore PC horse status from database DelayCommand(2.0,HorseReloadFromDatabase(oPC,X3_HORSE_DATABASE)); } // restore PC horse status from database } // add horse menu if (GetIsPC(oPC)) { // more details // restore appearance in case you export your character in mounted form, etc. if (!GetSkinInt(oPC,"bX3_IS_MOUNTED")) HorseIfNotDefaultAppearanceChange(oPC); // pre-cache horse animations for player as attaching a tail to the model HorsePreloadAnimations(oPC); DelayCommand(3.0,HorseRestoreHenchmenLocations(oPC)); } // more details string sName = GetTag(oPC); if(GetStringLength(sName) <= 0 || GetSubString(sName, 0, 2) != "ZZ") { MakePlayerTag(oPC, "THIS_DATABASE"); } AssignCommand(oPC, ActionJumpToLocation(GetCampaignLocation(PW_DB_NAME, DB_PC_L_LEAVE, oPC))); } string MakePlayerTag(object oPC, string sIsNew) { string sTag = "tag"; // Get the last number that was used to make a tag. int iTag = GetCampaignInt(sIsNew, sTag); iTag = iTag + 1; // Mark that number has been used now. SetCampaignInt(sIsNew, sTag, iTag); string sParsed = IntToString(iTag); // Pad the tag with some zeros. int iLen; for(iLen = GetStringLength(sParsed); iLen <= 6; iLen++) { sParsed = "0" + sParsed; } sParsed = "ZZ" + sParsed; SetTag(oPC, sParsed); return sParsed; }OnClientLeave:
#include "dbmodule_test" #include "pctag" void main() { object oPC = GetExitingObject(); SetCampaignLocation(PW_DB_NAME, DB_PC_L_LEAVE, GetLocation(oPC)); }Include Files:
//Database Include file //Variables: const string PW_DB_NAME = "THIS_DATABASE"; const string DB_PC_L_LEAVE = "DB_PC_L_PLAYER_LEAVE"; //Interface: location GetPCStartLocation(object oPC, string sLocation); void SetPCStartLocation(object oPC, string sLocation); //Implementation: location GetPCStartLocation(object oPC, string sLocation){ return GetCampaignLocation(PW_DB_NAME, sLocation, oPC); } void SetPCStartLocation(object oPC, string sLocation){ { SetCampaignLocation(PW_DB_NAME, sLocation, GetLocation(oPC)); } }// Give the player a unique tag. // sIsNew — The name of the database string MakePlayerTag(object oPC, string sIsNew); string MakePlayerTag(object oPC, string sIsNew) { string sTag = "tag"; // Get the last number that was used to make a tag. int iTag = GetCampaignInt(sIsNew, sTag); iTag = iTag + 1; // Mark that number has been used now. SetCampaignInt(sIsNew, sTag, iTag); string sParsed = IntToString(iTag); // Pad the tag with some zeros. int iLen; for(iLen = GetStringLength(sParsed); iLen <= 6; iLen++) { sParsed = "0" + sParsed; } sParsed = "ZZ" + sParsed; SetTag(oPC, sParsed); return sParsed; }Here I'll add loads of comments.
The database include file. I joined the two include files:
//Database Include file //Variables: const string PW_DB_NAME = "THIS_DATABASE"; const string DB_PC_L_LEAVE_PREFIX = "PLAYER_LEAVE_"; //Interface: // Gets the last saved location of oPC from the database. location GetPCStartLocation(object oPC); // Saves the current location of oPC to the database. void SetPCStartLocation(object oPC); // Give the player a unique tag. // sIsNew — The name of the database string MakePlayerTag(object oPC, string sIsNew = PW_DB_NAME); //Implementation: location GetPCStartLocation(object oPC) { // The prefix plus the tag of oPC is the unique identifier for the location stored in the database. return GetCampaignLocation(PW_DB_NAME, (DB_PC_L_LEAVE_PREFIX + GetTag(oPC))); } void SetPCStartLocation(object oPC) { SetCampaignLocation(PW_DB_NAME, (DB_PC_L_LEAVE_PREFIX + GetTag(oPC)), GetLocation(oPC)); } string MakePlayerTag(object oPC, string sIsNew) { string sTag = "tag"; // Get the last number that was used to make a tag. int iTag = GetCampaignInt(sIsNew, sTag); iTag = iTag + 1; // Mark that number has been used now. SetCampaignInt(sIsNew, sTag, iTag); string sParsed = IntToString(iTag); // Pad the tag with some zeros. int iLen; for(iLen = GetStringLength(sParsed); iLen <= 6; iLen++) { sParsed = "0" + sParsed; } // Add a prefix to the zeros sParsed = "ZZ" + sParsed; SetTag(oPC, sParsed); return sParsed; }The OnClientEnter script:
#include "x3_inc_horse" #include "dbmodule_test" void main() { // Define the entering object. object oPC = GetEnteringObject(); // Check if oPC is a player and not a DM if(GetIsPC(oPC) && GetIsDM(oPC) == FALSE) { // Get the tag of oPC. string sName = GetTag(oPC); // Check if the player already has a tag. // A string length less than or equal to zero means they do not. // A string starting with something that is not 'ZZ' means they got a tag from some other server. That's sus. if(GetStringLength(sName) <= 0 || GetSubString(sName, 0, 2) != "ZZ") { // Give the player a new tag and replace sName with it. sName = MakePlayerTag(oPC, PW_DB_NAME); } // Get the saved location from the database. location lLoc = GetPCStartLocation(oPC); // Check if the area from the location is valid. object oCheck = GetAreaFromLocation(lLoc); // A new character will not have a saved location. if(GetIsObjectValid(oCheck)) { // Send them on their way. AssignCommand(oPC, ClearAllActions()); AssignCommand(oPC, JumpToLocation(lLoc)); } // All the default horse stuff ExecuteScript("x3_mod_pre_enter",OBJECT_SELF); // Override for other skin systems if ((GetIsPC(oPC)||GetIsDM(oPC))&&!GetHasFeat(FEAT_HORSE_MENU,oPC)) { // add horse menu HorseAddHorseMenu(oPC); if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")) { // restore PC horse status from database DelayCommand(2.0,HorseReloadFromDatabase(oPC,X3_HORSE_DATABASE)); } // restore PC horse status from database // restore appearance in case you export your character in mounted form, etc. if (!GetSkinInt(oPC,"bX3_IS_MOUNTED")) { HorseIfNotDefaultAppearanceChange(oPC); } // pre-cache horse animations for player as attaching a tail to the model HorsePreloadAnimations(oPC); DelayCommand(3.0,HorseRestoreHenchmenLocations(oPC)); } // add horse menu } }OnClientLeave:
#include "dbmodule_test" void main() { // Call the function made in the include file. SetPCStartLocation(GetExitingObject()); }Good to know that you can create a database for each of the identifiers you want to store in that certain database itself.
I think im still doing something wrong here.
Are you running this on a dedicated server?
The difference is I do not teleport the player until they choose to leave the server lobby area.
You can try putting the teleport lines in delay commands.