PDA

View Full Version : Posessed DBNPC - Please help


scriptless
08-15-2014, 02:14 AM
Okay I got a DBNPC. I noticed something that I canno't explain. I did not know it was possible and have no idea how to fix this.

I used the following:

this.("option." @ player.account) = 0;

as well as

makevar("this.option." @ player.account) = 0;

Problem I am having is no matter which one I use. I get strange behavior. Either the string does get added but then magically disappears when the player logs off, or it appears and stays in the list but then the next time it just creates a second string, and so on. So then I'm left with 2 strings with 100% the same name.

Also,

temp.pl2 = findplayer("bloodpet");
this.("option." @ "bloodpet") = 1;
if ( this.option.bloodpet == null ) echo("null");

Outputs:

null

All I am trying to accomplish is add a "option.accountNAME = 0" if there is no string nammed option.accountNAME

PiX
08-15-2014, 03:10 AM
this.option.(@ player.account) will access a variable within the "option" var, but this.("option." @ player.account) treats "option.<account>" as a variable name itself. That is how you can have two separate data points with similar paths. It also can make the latter unwanted path impossible to access by makevar.

scriptless
08-15-2014, 05:11 AM
Thanks. That helped alot.

Now the only problem I am having with it is the getstringkeys not getting all the flags. Any idea why it might not? I made sure they are all added using the same method.

temp.choosefrom = getstringkeys("this.option.");
temp.options.desc = null;

// If player is not in the list go ahead and add them anyways
if ( this.option.(@player.account) == null ) {
this.option.(@player.account) = 0;
}

for ( temp.coption : temp.choosefrom ) {
// this tells it the option is a playerobject
// get there account name and head icon
if ( player.account != temp.coption ) {
temp.pl = findplayer(temp.coption);
temp.offline = temp.pl == NULL;
temp.pl = temp.offline ? new TServerPlayer(@temp.coption) : temp.pl;
temp.options.desc.add(temp.pl.account);
}
}

for ( temp.pl : allplayers ) {
if ( !(temp.pl.account in temp.options.desc) ) {
// they are RC
if ( temp.pl.level != null && temp.pl.account != player.account) {
temp.options.desc.add(temp.pl.account);
}
}
}

// now send temp.data to be shown in the list :)
temp.data = temp.options.savevarstoarray(false);
return temp.data;

I believe the problem is related to offline players. I could have sworn this was working before for loading head and hair of offline players.

PiX
08-15-2014, 01:16 PM
The function getstringkeys will not find a var path if it does not exist. If you are setting your vars to 0, "", {}, or null, the var name and path will not be created because it is equal to null. Try this.option.(@player.account) = 1; or any value not null.

scriptless
08-15-2014, 03:25 PM
The function getstringkeys will not find a var path if it does not exist. If you are setting your vars to 0, "", {}, or null, the var name and path will not be created because it is equal to null. Try this.option.(@player.account) = 1; or any value not null.

Thanks, I was wondering that. The DBNPC must be removing all flags = 0 when it is updated.

Restraint
08-21-2014, 02:33 AM
Have you considered simply using TStaticVars? It will likely simplify what you're trying to do.

You create a TStaticVar as follows:


temp.opt = this.options = new TStaticVar();

Then you can access, say, this.options.(@player.account) like:


opt.(@player.account) = ...;

You could get the "keystrings" via something like:


temp.vars = opt.getDynamicVarNames();

You could toss it to other scripts easily.

You could pass it from clientside to serverside via something like:


temp.info = opt.saveVarsToArray(false);
triggerServer("weapon", this.name, info);

With the serverside looking like:


function onActionServerside(info) {
temp.opts = this.options = new TStaticVar();
opts.loadVarsFromArray(info);
...
}

It'd generally allow for a lot more of what you're trying to do, since "this.options" is now a path like you want.

scriptless
08-21-2014, 07:27 PM
Kinda what I'm already doing. There's a problem tho. DBNPC seems to not always save flags and they dissapear. Not entirely sure under which circumstances tho. But occasionally I notice some just vanishing. Some people said it happens sometimes when restarting npc server even with the rc save npcs command

PiX
08-21-2014, 08:17 PM
TStaticVar is normally not used for database flag storage. It is the same as a TGraalVar (every base data type) except as a global reference. This means that the data would not be stored on the database npc, but actually on a global object, which may be deleted after time or server restart. A common technique for storing persistent dynamic vars to a database npc is using a prefix like this.("option_" @ player.account) . You can then find the full list of accounts by doing getstringkeys("this.option_") .

scriptless
08-21-2014, 09:31 PM
I would have to check my script but about the TStaticVar.. but I was meaning I'm using the save art array and loading also the same way. It shows in the flags. Also saves some flags but not every one. I would say I counted 77 saved strings and sadly no way to know for sure how many did not save. Not sure why it would work sometimes... but not all the times..

cbk1994
08-21-2014, 09:32 PM
DBNPC seems to not always save flags and they dissapear. Not entirely sure under which circumstances tho. But occasionally I notice some just vanishing. Some people said it happens sometimes when restarting npc server even with the rc save npcs command

I still have no idea why, but it seems that if you do this.trigger('update'); in a DBNPC after saving some value, it won't be lost. We had tons of trouble in the past with DBNPCs rolling back (especially on Era), and this seemed to fix it.

We also noticed that savenpcs seems not effective; we wrote our own that we called before restarting the NPC-server that just called trigger('update') on every DBNPC with much better results -shrug-

Maybe it's just a placebo but I kind of doubt it. I don't know why it works or if it has to be update.

scriptless
08-21-2014, 09:36 PM
I still have no idea why, but it seems that if you do this.trigger('update'); in a DBNPC after saving some value, it won't be lost. We had tons of trouble in the past with DBNPCs rolling back (especially on Era), and this seemed to fix it.

We also noticed that savenpcs seems not effective; we wrote our own that we called before restarting the NPC-server that just called trigger('update') on every DBNPC with much better results -shrug-

Maybe it's just a placebo but I kind of doubt it. I don't know why it works or if it has to be update.

Is that trigger available on all dbnpc or is that a custom trigger? Would like to see any documentation on that if possible. I will try it and see if we get any better results maybe. Would you recommend using that trigger every time a new value is added or no?

Restraint
08-21-2014, 09:44 PM
TStaticVar is normally not used for database flag storage. It is the same as a TGraalVar (every base data type) except as a global reference. This means that the data would not be stored on the database npc, but actually on a global object, which may be deleted after time or server restart. A common technique for storing persistent dynamic vars to a database npc is using a prefix like this.("option_" @ player.account) . You can then find the full list of accounts by doing getstringkeys("this.option_") .

This is a terribly ugly way of doing it, as you need to do (" " @ ) every time.

You can do what you're saying via:

this.options.(@player.account)

The advantages of a TStaticVar instead of that are:

1. Easier to send between server and client via savevarstoarray()
2. Easier to write to a file via savevars()
3. Easier to access with smaller variable names and thus cleaner
4?. I would imagine a guess that .getDynamicVarNames() is an O(c) operation, while .getStringKeys() is an O(n) operation.

Also, you claim it's a "global" reference but no more than a DB NPC's variables are.

The advantages outweigh the disadvantages.

However, I am well aware that TStaticVars do not persist for long periods of time. They seem to have some time limit until they are garbage collected. If you're creating a static variable, you can reload it whenever it's garbage collected. If this is a variable that changes rarely but needs to maintain long-term data, saving it to a file is more persistent than BOTH methods described (And a TStaticVar is ideal for that, via .saveVars()).

As Scriptless has stated, even this.'s can have troubles persisting in DB NPCs. If you want true persistence for long-term database usage, I would suggest either:

1. File I/O. This is best method if the files will not change often (since I/O can be laggy). The easiest method for this is, you guessed it, TStaticVars and .savevars

2. SQL DB. This is the best method if the options will be changing frequently, as reading/writing to SQL is less intense than files.

PiX
08-21-2014, 10:10 PM
Is that trigger available on all dbnpc or is that a custom trigger? Would like to see any documentation on that if possible. I will try it and see if we get any better results maybe. Would you recommend using that trigger every time a new value is added or no?

A trigger("update"); can sometimes help with saving dbnpc data, but is normally not needed. It is also sometimes used on the serverside of npc scripts if something is changed on the serverside of it, but does not change on the clientside. Again, normally not needed.

Jakov_the_Jakovasaur
08-21-2014, 10:16 PM
hello!

tStaticVars should never be garbage collected for as long as they have an active reference, ive seen them remain for several months and only be destroyed whenever the npc-server is restarted

personally i prefer to store data within an sqlite database, however load the sql data into a tStaticVar cache upon database npc initialization for easier access

scriptless
08-21-2014, 11:30 PM
I definately want to move the system to sqlite. But it's currently live on the server and only going to be active another half a week. So it's kinda a bandaid to help make sure it works better till then. I'll try the trigger for now hopefully it buys me time to finish the sql part.

Thanks everyone for the info.

Restraint
08-22-2014, 12:21 AM
hello!

tStaticVars should never be garbage collected for as long as they have an active reference, ive seen them remain for several months and only be destroyed whenever the npc-server is restarted

personally i prefer to store data within an sqlite database, however load the sql data into a tStaticVar cache upon database npc initialization for easier access

In theory, you are correct. It *should* never be garbage collected. However, it often is.

I ran into this problem thrice: once for a jailing/punishments system, once for a Graphics Viewer system, and once for some system I don't recall.

They all had active references, namely this.'s that were initialized onCreated. 2 of them were in DB NPCs, and 1 was in a Weapon NPC. They DO appear to have some arbitrary time limit.

If you never ran into it -- and I'm not doubting you -- I'm sure we can chalk this up as one of those "random Graal glitches."

I agree with you entirely with regards to SQLite Database and caching. I have also done similar with file databases and caching. Such caches make it very easy to submit data to the clientside as well (Again, via .saveVarsToArray()).

Jakov_the_Jakovasaur
08-22-2014, 05:23 AM
In theory, you are correct. It *should* never be garbage collected. However, it often is.

I ran into this problem thrice: once for a jailing/punishments system, once for a Graphics Viewer system, and once for some system I don't recall.

They all had active references, namely this.'s that were initialized onCreated. 2 of them were in DB NPCs, and 1 was in a Weapon NPC. They DO appear to have some arbitrary time limit.

If you never ran into it -- and I'm not doubting you -- I'm sure we can chalk this up as one of those "random Graal glitches."

I agree with you entirely with regards to SQLite Database and caching. I have also done similar with file databases and caching. Such caches make it very easy to submit data to the clientside as well (Again, via .saveVarsToArray()).

are you using the default onInitialized() event within database npcs as well?

i have always set up database systems in the following way:


function onCreated()
this.onInitialized();

function onInitialized() {
if (this.cache == NULL) {
this.cache = new tStaticVar();
this.populateCache();
}
}


even prior to the introduction of tStaticVar() you needed to use onInitialize() within a database npc for it to be reliable

scriptless
08-22-2014, 11:03 AM
Someone explain what you guys mean by garbage collecting? And the initialization making it more reliable? How so?

Jakov_the_Jakovasaur
08-22-2014, 04:31 PM
Someone explain what you guys mean by garbage collecting? And the initialization making it more reliable? How so?

iirc the onCreated() event is not invoked when the npc-server is started, whereas onInitialized() is

garbage collection runs destroy objects that have no active reference, for example if you do this twice:

this.obj = new tStaticVar();

then the first object you created will no longer have an active reference, as that reference is now being used for the second object

Restraint
08-22-2014, 04:56 PM
It isn't an issue of not using onInitialized. I am talking about TStaticVars going AWOL hours after their creation without an NPC server shutdown or downtime in between.

In addition, yes, both of the DB NPCs I ran this in had onCreated() called in their onInitialized().

Like I said, I'm not doubting you that you've had success with TStaticVars not poofing. However, I've got several years of experience and troubleshooting this exact situation that say otherwise.

@Scriptless: Garbage collecting is a term used to mean that no-longer-references objects/variables are simply deleted after their references are gone.

The most common example of garbage collection is when a function terminates, its temp.variables are garbage collected unless stored elsewhere. That's what makes 'em temp.