Graal Forums

Graal Forums (https://forums.graalonline.com/forums/index.php)
-   NPC Scripting (https://forums.graalonline.com/forums/forumdisplay.php?f=8)
-   -   Posessed DBNPC - Please help (https://forums.graalonline.com/forums/showthread.php?t=134269477)

scriptless 08-15-2014 02:14 AM

Posessed DBNPC - Please help
 
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:

PHP Code:

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

as well as

PHP Code:

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,

PHP Code:

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

Outputs:

PHP Code:

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.

PHP Code:

  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

Quote:

Originally Posted by PiX (Post 1730051)
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:

PHP Code:

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

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

PHP Code:

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

You could get the "keystrings" via something like:

PHP Code:

  temp.vars opt.getDynamicVarNames(); 

You could toss it to other scripts easily.

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

PHP Code:

  temp.info opt.saveVarsToArray(false);
  
triggerServer("weapon"this.nameinfo); 

With the serverside looking like:

PHP Code:

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

Quote:

Originally Posted by scriptless (Post 1730304)
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

Quote:

Originally Posted by cbk1994 (Post 1730310)
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

Quote:

Originally Posted by PiX (Post 1730308)
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

Quote:

Originally Posted by scriptless (Post 1730312)
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

Quote:

Originally Posted by Jakov_the_Jakovasaur (Post 1730317)
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

Quote:

Originally Posted by Restraint (Post 1730327)
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:

PHP Code:

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

Quote:

Originally Posted by scriptless (Post 1730342)
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.


All times are GMT +2. The time now is 07:00 PM.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2026, vBulletin Solutions Inc.
Copyright (C) 1998-2019 Toonslab All Rights Reserved.