View previous topic :: View next topic |
Author |
Message |
PaithanQuindiniar
Joined: 12 Feb 2005 Posts: 4
|
Posted: Sat Feb 12, 2005 23:49 Post subject: NWN server crashes after writing to the DB |
|
|
We're running NWNX2 with a MySQL database and implemented KPB's banking system (for NWNX of course).
I added a bit of code to the banking script, that writes a string in the DB containing all items stored in the vault of a player (just for info, to see what players have stored in their vaults --> big brother ). The DB field is a TEXT type one. Now everything has been working okay, until suddenly the server starts crashing when using the vault. After some testing, I noticed, that it crashes when the string containing the items becomes rather long - 27 items (including name, stack size and tag) was my limit during testing. I runned the query in the DB control program with a text of over 1000 characters and that works. So it seems, that NWN (or NWNX?) has a problem with string of bigger size ...
Does anybody have any ideas?? I'd really appreciate some help here (it has cost me already about 5 hours, just to figure this bit out, so I thought, I'd ask the experts! ) |
|
Back to top |
|
|
Lanthar D'Alton
Joined: 10 Feb 2005 Posts: 100
|
Posted: Sun Feb 13, 2005 2:33 Post subject: Sure... |
|
|
NWNX was coded for 8*128 characters... 1024. That means you'll need to limit yourself to that many letters. I'd suggest you make it run in a loop that sends the data in each time the string hits just less than 1024... Just always call a DELETE FROM big_brother_table WHERE player='name' or whatever beforehand, then put all your entries in as a set of strings instead of one big one.
-Lanthar |
|
Back to top |
|
|
PaithanQuindiniar
Joined: 12 Feb 2005 Posts: 4
|
Posted: Sun Feb 13, 2005 3:11 Post subject: |
|
|
Okay ... I'll give that a try ...
One more question though. I tried the query
UPDATE big_brother_table SET val=CONCAT(val, 'new data') WHERE playerID=1 AND name='stored items'
for each item. I figured, that way, the DB access is always done with only one item information string (name, tag, stack size) and the "attaching" handled by the DB itself. Why isn't this approach successful? |
|
Back to top |
|
|
Lanthar D'Alton
Joined: 10 Feb 2005 Posts: 100
|
Posted: Sun Feb 13, 2005 6:48 Post subject: Well.... |
|
|
I'd say it failed before because your string was too long.
Now however, there will be multiple rows for a given player, and you will need to delete them all, then add new ones. Updates for that sort of thing would be pointless. You'd only repeatedly update the first row.
Egads. Actually, that's a TERRIBLE method (just reread it). You really should consider that you are doing one call per item for what may be hundreds of items... I'd advise that you do the string concatenation in the server and send a string in then instead... but then, I don't know how slow the server might be about concatenation. It depends on how the reallocation of the string is done when you add two together....
anyway, I'd try it the way I suggested. concatenate the string into something just less then 1024, then send to the db... concatenate the next string, send to db... but use inserts.
Code: |
//given resrefs are limited to 16 letters
//and you are storing a comma delineated list (1 per)
//and the rest of your query is say... 100 letters...
//and we add 1 for a null terminator...
//max_items= (1+100 + X * (16+1) ) < 1024
//and that means X = 54
object oItem=GetFirstItemInInventory(chest_or_whatever);
while(oItem!=OBJECT_INVALID)
{
string sItemInfoSet="";
int i=0;
for(i=0; i < X && oItem!=OBJECT_INVALID; i++)
{
string sItem=gettag(oItem);
sItemInfoSet=sItemInfoSet+sItem;
oItem = GetNextItemInInventory(chest_or_whatever);
}
string sQuery="INSERT INTO big_brother_info(playernumber, sItemset) VALUES( '"+sPlayerNum+"','"+sItem+"')";
SQLExecDirect(sQuery);
}
|
something like that... |
|
Back to top |
|
|
PaithanQuindiniar
Joined: 12 Feb 2005 Posts: 4
|
Posted: Sun Feb 13, 2005 12:15 Post subject: |
|
|
Hm okay ... still, I'm wondering why the next thing doesn't work (sorry for being such a pain in the *** about this! ). I'm gonna try with the exact example:
In the KPB system (as you might or might not know), all items you put in a chest are stored on an invisible creature when you walk away from the chest. This creature object then is stored in a NWN campaign database. So my problem is, that there's no way to see, what players got stored in the chest (i.e. the creature object in the NWN database).
To manage this, I wrote the following bit of code, which builds a string sItems. While the original banking code cycles through the player's items in his inventory, my code attaches a string with name, tag and stack size of the item, every time the original code finds an item and copies it to the invisible creature's.
The original code:
Code: |
if (GetIsObjectValid(oCreature))
{
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY), oCreature);
object oCopy;
object oItem = GetFirstItemInInventory(oStorage);
while(GetIsObjectValid(oItem))
{
oCopy = CopyItem(oItem, oCreature);
oItem = GetNextItemInInventory(oStorage);
}
int iRet = StoreCampaignObject(PPIS_DB_NAME, sID, oCreature); |
The original code with the big brother code included:
Code: |
if (GetIsObjectValid(oCreature))
{
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY), oCreature);
object oCopy;
object oItem = GetFirstItemInInventory(oStorage);
//added by Paithan
DeletePersistentVariable(oPC, "Stored Item (nr) (tag)", "big_brother_info");
string sItems = "(player has no items stored)";
SetPersistentString(oPC, "Stored Item (nr) (tag)", sItems, 0, "big_brother_info");
if(GetIsObjectValid(oItem)) {
sItems = GetName(oItem);
if(GetItemStackSize(oItem) > 1)
sItems += " (" + IntToString(GetItemStackSize(oItem)) + ")";
sItems += " (" + GetTag(oItem) + ")";
SetPersistentString(oPC, "Stored Item (nr) (tag)", sItems, 0, "big_brother_info");
}
while(GetIsObjectValid(oItem))
{
oCopy = CopyItem(oItem, oCreature);
oItem = GetNextItemInInventory(oStorage);
// added by Paithan
if(GetIsObjectValid(oItem)) {
sItems = "\n" + GetName(oItem);
if(GetItemStackSize(oItem) > 1)
sItems += " (" + IntToString(GetItemStackSize(oItem)) + ")";
sItems += " (" + GetTag(oItem) + ")";
AppendPersistentString(oPC, "Stored Item (nr) (tag)", sItems, 0, "big_brother_info");
}
}
int iRet = StoreCampaignObject(PPIS_DB_NAME, sID, oCreature); |
with AppendPersistentString() making the following query:
Code: |
sSQL = "UPDATE " + sTable + " SET val=CONCAT(val, '" + sValue +
"'),expire=" + IntToString(iExpiration) + " WHERE playerID=" + sPlayerID +
" AND name='" + sVarName + "'";
|
The entry in the DB looks thereafter like this:
Cloudkill (Cloudkill)
Finger of Death (FingerofDeath)
Leather Armor (NW_AARCL001)
Thieves~ Tools +1 (NW_IT_PICKS001)
Standard Rover Armor (StandardRoverArmor)
Torch (NW_IT_TORCH001)
Dagger (NW_WSWDG001)
Finger of Death (FingerofDeath)
Improved Elf Light Armor (ImprovedElfLightArmor)
Woodsman Outfit (NW_CLOTH001)
Minor Spike Trap Kit (NW_IT_TRAP001)
Border Legion Short Sword - Improved (BLShortSwordImproved)
Enhanced Borderlands Arrows (87) (EnhancedBorderlandsArrows)
Improved Elf Longbow (ImprovedElfLongbow)
Improved Elf Short Sword (ImprovedElfShortSword)
Improved Elf Arrows (93) (ImprovedElfArrows)
Rover Arrows - Fine (99) (RoverArrowsFine)
Cloudkill (Cloudkill)
Knowledge Book - The Gnome Language (LearningBookGnome)
Knowledge Book - The Troll Language (LanguageBookTroll)
Potion of Cure Light Wounds (3) (NW_IT_MPOTION001)
Torch (NW_IT_TORCH001)
Minor Spike Trap Kit (NW_IT_TRAP001)
Leather Armor (NW_AARCL001)
Woodsman Outfit (NW_CLOTH001)
This works for about 27 items ...
Okay, now I got the point, where you say, that queries have to be limited to 1024 characters, but what I don't get is, why doesn't the CONCAT function work? If I send a string in the DB, let's say
INSERT INTO big_brother_info(playernumber, sItemset) VALUES(1,'Knowledge Book - The Gnome Language (LearningBookGnome)')
and I then append another sItem string to it with CONCAT, isn't the appending supposed to be done in the MySQL DB itself, without even using the NWNX connection (except for the sending of the new string that has to be appended), meaning that the query will never have more than 1024 characters, unless the sItem variable itself is > 1024?
Thanks! |
|
Back to top |
|
|
Papillon x-man
Joined: 28 Dec 2004 Posts: 1060 Location: Germany
|
Posted: Sun Feb 13, 2005 18:05 Post subject: |
|
|
You can increase the amount of a data a single row can hold by using a bigger ODBC SPACER in aps_include. For Windows, the maximum should be 64KByte per Row.
Anyway, it should never crash, even if you go over the limit. Do you think you could create a small package that helps me reproducing the problem ?
Which OS btw ? _________________ Papillon |
|
Back to top |
|
|
PaithanQuindiniar
Joined: 12 Feb 2005 Posts: 4
|
Posted: Sun Feb 13, 2005 18:39 Post subject: |
|
|
Hi there! (Thanks for the replies by the way!)
I can arrange a sample mod alright - going to need until saturday, since next week I have a skiing trip planned I'll send it as soon as I can!
We're running Win XP (or Win2K on my computer, which results in the same problem) ... |
|
Back to top |
|
|
Papillon x-man
Joined: 28 Dec 2004 Posts: 1060 Location: Germany
|
Posted: Sun Feb 13, 2005 20:29 Post subject: |
|
|
Oh yes, forgot to ask which version and which plugins you are running. _________________ Papillon |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|