logo logo

 Back to main page

The NWNX Community Forum

 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 
 
Database driven loot system?
Goto page 1, 2  Next
 
Post new topic   Reply to topic    nwnx.org Forum Index -> Database related
View previous topic :: View next topic  
Author Message
Kato



Joined: 05 Mar 2010
Posts: 47

PostPosted: Wed May 18, 2011 4:05    Post subject: Database driven loot system? Reply with quote

Hi all,

Anyone knows if it would make sense to build a loot system relying on an object table(MySQL) rather than using CreateItemOnObject() or CopyItem()?

Currently my loot system retrieves items from hidden chests and uses CopyItem() to create the item in object's inventory(just as many loot systems do). It works fine, but requires one chest for each item type(gems, weapons etc...), and the more items I add to the chests, the more ressources the system uses, since for each stored item(the module is growing bigger and bigger with each added item), an object variable + an int variable for indexing are stored on the chest for later retrieval.

I know that CopyItem() is faster than CreateItemOnObject(), but what about retrieving the object(randomly) from a table(BLOB type, if I recall) with MySQL? Would it be fast enough to prevent any lag?

Thanks!
Back to top
View user's profile Send private message Send e-mail
virusman



Joined: 30 Jan 2005
Posts: 1020
Location: Russia

PostPosted: Wed May 18, 2011 10:16    Post subject: Reply with quote

It won't be any faster than CopyItem(); the main advantage of DB is flexibility. In terms of performance, it will be the same or a bit slower than chests.
_________________
In Soviet Russia, NWN plays you!
Back to top
View user's profile Send private message Visit poster's website Yahoo Messenger
Kato



Joined: 05 Mar 2010
Posts: 47

PostPosted: Wed May 18, 2011 19:04    Post subject: Reply with quote

Thank you, virusman. When you say a bit slower, do you mean that it would be feasible nonetheless? I'm asking this because my goal is to get rid of those chests and items and variables, relying solely on the database for my loot system. Now, I know that some builders prefer to store the resref instead of the whole object, yet this implies using CreateItemOnObject().

So, wich option would create the less lag? Retrieving the resref + CreateItemOnObject() or simply retrieving the whole object from the DB, knowing that the items are created in the OnDeath event of monsters and the OnOpen event of some placeables?

Thank you very much for taking the time to answer this Smile
Back to top
View user's profile Send private message Send e-mail
virusman



Joined: 30 Jan 2005
Posts: 1020
Location: Russia

PostPosted: Wed May 18, 2011 19:52    Post subject: Reply with quote

Database latency is probably higher than object->GFF conversion, so I'd still use chests for caching (populate with items from database on module load, then reload cache when DB contents are changed).
The main question here is how flexible you want it to be. There is no point in switching from in-game to DB objects solely for performance.
_________________
In Soviet Russia, NWN plays you!
Back to top
View user's profile Send private message Visit poster's website Yahoo Messenger
Kato



Joined: 05 Mar 2010
Posts: 47

PostPosted: Wed May 18, 2011 22:10    Post subject: Reply with quote

Thank you, I'll stick to the chests for now, yet I still wonder what will happen when those will hold 500+ items... Question
Back to top
View user's profile Send private message Send e-mail
Skywing



Joined: 03 Jan 2008
Posts: 321

PostPosted: Thu May 19, 2011 9:27    Post subject: Reply with quote

Note that both the item creation routines and the database deserialization support involve unpacking an object from its saved GFF form - so you pay the cost for that work either way. As virusman notes, with the database, you just have the additional work of pulling things from MySQL.
Back to top
View user's profile Send private message
eeriegeek



Joined: 07 Jan 2008
Posts: 59

PostPosted: Fri Jun 10, 2011 0:08    Post subject: Reply with quote

I am currently generating all treasure drops via database look-ups on my (always under development) PW. There has not been a problem with performance with 6500 (2200 items in 3 grouping systems) items in the table, but the PW is admittedly lightly loaded. You have to be careful how you design your random look-ups so you don't end up doing full table scans for each drop.
Back to top
View user's profile Send private message
Kato



Joined: 05 Mar 2010
Posts: 47

PostPosted: Fri Jun 10, 2011 2:31    Post subject: Reply with quote

Indeed, thank you for this precison.
Back to top
View user's profile Send private message Send e-mail
elven



Joined: 28 Jul 2006
Posts: 259
Location: Germany

PostPosted: Mon Jun 13, 2011 12:03    Post subject: Reply with quote

Querying a good database doesn't really involve that much overhead, latency-wise. Most things will be in RAM and the worst to happen is some indirection and hitting the query planner. The overhead for de/serializing gff data will be much much bigger than doing a simple query on a properly indexed table. Either way, it doesn't matter - it's fast enough in any case even under heavy load.

I as well run a loot system based on wildcard-matched resrefs, with random chance for various items and lots of flexibility. No problems there, and it really cut down on maintenance regarding utc templates.
Back to top
View user's profile Send private message
Kato



Joined: 05 Mar 2010
Posts: 47

PostPosted: Tue Jun 14, 2011 20:46    Post subject: Reply with quote

Thank you for the infos, Elven. By the way, would you agree to give me some clues regarding the formula used to randomly retrieve the "weighted" items, please? (My table would hold the fields "resref", "category", "weight" and "rarity", the last one being used to update weights upon adding/removing items from any category)

Thank you!

Kato
Back to top
View user's profile Send private message Send e-mail
elven



Joined: 28 Jul 2006
Posts: 259
Location: Germany

PostPosted: Tue Jun 14, 2011 21:00    Post subject: Reply with quote

Code:

p=# \d loot_aggregator
        View "public.loot_aggregator"
   Column    |       Type        | Modifiers
-------------+-------------------+-----------
 loot        | text              |
 replace     | integer           |
 racial_type | integer           |
 resref      | character varying |
 tag         | character varying |
 name        | character varying |
 uniq        | boolean           |
 log         | boolean           |
View definition:
 SELECT loot_chains.loot, loot_chains.replace, loot_chains.racial_type, loot_chains.resref, loot_chains.tag, loot_chains.name, loot_chains.uniq, loot_chains.log
   FROM loot_chains
  WHERE (random() - loot_chains.chance) <= 0::double precision
  ORDER BY loot_chains.racial_type, loot_chains.tag, loot_chains.resref, loot_chains.name, loot_chains."order";

p=# select * from loot_chains limit 2;
 id | racial_type |   resref    | tag | name | lvar | order | replace |      loot      | chance | log | uniq | last_update_by
----+-------------+-------------+-----+------+------+-------+---------+----------------+--------+-----+------+----------------
 35 |          12 | fk_krieger  | %   | %    | %    |     1 |       0 | kurzschwert001 |    0.1 | f   | f    |               
 41 |          12 | fk_schuetze | %   | %    | %    |     1 |       0 | kurzbogen      |   0.15 | f   | f    |               
(2 rows)

p=# select * from loot_aggregator where 'fk_krieger' like resref  limit 2;
     loot     | replace | racial_type | resref | tag | name | uniq | log
--------------+---------+-------------+--------+-----+------+------+-----
 food_raw_002 |       0 |           8 | %      | %   | %    | f    | f
 goblinkopf   |       0 |          12 | %      | %   | %    | f    | f
(2 rows)



Code:

void GenerateNPCTreasure(object oNPC = OBJECT_SELF) {
   _DEBUG("lootchain", 1, "getfor: " + GetName(oNPC) + " -> " + GetTag(oNPC) + ":" + GetResRef(oNPC));

   object oPlayer = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oNPC);

   // First, clean the inventory.
   if (gvGetInt("treasure_clean_npcs")) {
      object o = GetFirstItemInInventory(oNPC);
      while (GetIsObjectValid(o)) {
         if (!GetPlotFlag(o)) {
            DestroyObject(o);
         }
         o = GetNextItemInInventory(oNPC);
      }
   }

   // Select all loot chains.
   pQ(
      "select replace, loot, uniq, log from loot_aggregator where " +
         "(racial_type = -1 or racial_type = " + pSi(GetRacialType(oNPC)) + ") and " +
         pSs(GetResRef(oNPC)) + " like resref and " +
         pSs(GetTag(oNPC)) + " like tag and " +
         pSs(GetName(oNPC)) + " like name;"
   );

   string loot;
   int replace, uniq, log;

   string tocreate = "";

   while (pF()) {
      replace = pGi(1);
      loot = pGs(2);
      uniq = pGb(3);
      log  = pGb(4);

      // it is unique. Do not spawn, do not do the replacing, because the player already has one.
      if (uniq && GetIsObjectValid(GetItemResRefPossessedBy(oPlayer, loot)))
         continue;

      _DEBUG("lootchain", 1, "result[]: " + loot + " << " + itoa(replace) + " -> '" + tocreate + "'");

      // Skim off replaced loots before this one gets added.
      while (replace > 0) {
         int idx = strpos(tocreate, "#");
         if (-1 == idx)
            break;

         _DEBUG("lootchain", 1, "replace <" + itoa(replace) + ">: " + substr(tocreate, 0, idx));
         tocreate = substr(tocreate, idx + 1, strlen(tocreate));
         _DEBUG("lootchain", 1, "isnow: " + tocreate);

         replace--;
      }

      if (log) {
         audit("loot", oPlayer, audit_fields("resref", loot), "loot_chains");
         WriteTimestampedLogEntry("lootchain: Spawning '" + loot + "' for " + GetName(oPlayer));
      }

      tocreate = loot + "#" + tocreate;
   }
   
   // Skim off the last hash that gets invariably added.
   tocreate = substr(tocreate, 0, strlen(tocreate) - 1);
   
   _DEBUG("lootchain", 1, "isnow at end: " + tocreate);

   DelayCommand(1.0f, CreateChainedOnObjectByResRefString(tocreate, oNPC));
}

Back to top
View user's profile Send private message
Kato



Joined: 05 Mar 2010
Posts: 47

PostPosted: Tue Jun 14, 2011 23:05    Post subject: Reply with quote

Thank you very much!!!
Back to top
View user's profile Send private message Send e-mail
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Thu Jun 16, 2011 16:28    Post subject: I wouldnt Reply with quote

I wouldnt replace the chest system completely, at least, not at first. I instead chose to supliment it.

http://rhun.wikia.com/wiki/Development_:_Dynamic_Loot_Generator

This is a walk through of how our Dynamic Loot generator works.


It works on the simple premise of

1 - Select a random item type/resref from the stored item details database table.

( select * from item_templates order by rand() limit 1 )

This gets us the resref of the template.

We create it.
We can then get its item type, and then we can further query the database for the item properties we desire for that item type.
eg - Select the itemproperty int value for Damage Bonus 2d12 Divine for item type Long Sword etc....


Our system works well, and doesnt have noticable lag.


Note - The sql Queries are blocking, so its important to remember that the further away your db is, eg- off site, then the longer the lag/delay between query start, and query end will be.
Back to top
View user's profile Send private message
Kato



Joined: 05 Mar 2010
Posts: 47

PostPosted: Thu Jun 16, 2011 20:26    Post subject: Reply with quote

Thank you, Baaleos. Since you mention retrieving resrefs with RAND(), I seize the opportunity to share/evaluate a formula/query I found after a lot of researches:

SELECT resref FROM items WHERE category = (SELECT category FROM items ORDER BY -LOG(1.0 - RAND()) / avail limit 1) ORDER BY -LOG(1.0 - RAND()) / weight LIMIT 1

Now the goal is to retrieve a random weighted item resref from a random weighted category. The higher the weight, the higher the chance for the resref to be picked. Same for category, this time the "weight" being the field "avail".

The query is made for a table with fields: resref, category, weight, avail. For weight and avail, I currently use values between 1 and 99. The query works, and it takes 0,0472 seconds to execute, I don't know if it's considered fast or not...

However, for categories, the subquery is not flawless, it will tend to pick categories containing the most resrefs more often. Any idea on how to improve the subquery? (or the whole query if it's wrong)

Thank you!

Kato
Back to top
View user's profile Send private message Send e-mail
Kato



Joined: 05 Mar 2010
Posts: 47

PostPosted: Fri Jun 17, 2011 6:03    Post subject: Reply with quote

I should be ok, thank you. A MySQL expert advised me to create 2 tables, simplifying the queries a lot. Thank you all for reading and answering this. Smile
Back to top
View user's profile Send private message Send e-mail
Display posts from previous:   
Post new topic   Reply to topic    nwnx.org Forum Index -> Database related All times are GMT + 2 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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