Author Topic: Persistent Inn System - W.I.P.  (Read 1430 times)

Legacy_wyldhunt1

  • Sr. Member
  • ****
  • Posts: 443
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #30 on: January 10, 2012, 08:25:34 pm »


               It would be interesting to see a test comparison, but here is my understanding of it:
The actual function call is nearly instant. If you were to copy/pasta all of your code 100 times and compare the time with a function call containing that code looped 100 times, they'd be almost exactly the same.
Larger include's create larger files which create memory bloat.

In fact, from a technical point of view, you are making a ton of repetative function calls all the time.
Stuff like GetEnteringObject() and ActionRandomWalk() are just functions that Bioware wrote for us. They are still making the function call and running the script in nearly the same way as a function that you code. I'm unsure how the compiler interprets it all, but I'm fairly sure that the finished compiled script should be about the same either way.
Aurorascript is essentially a C++ library of functions that Bioware made to enable us to interact with the engine.
Our functions should still be compiled in to C++ using their C++ library functions... In theory.

EDIT:
A quick note on the name parsing issue:
How many names have you ever seen that use such special characters? Dwarven names, elven names, none of them require those. Any player using such obscure symbols is most likely trying to make some type of L337 name. I'd suggest doing a check in the name field on any new characters made and marking their character as illegal if they are using such odd characters. That may be a bit much for an inn system... But, a lot of servers are already tracking PC's with similar methods.
The alternative would likely be to grab their name off of their character and get the string length and parse it that way, if you have access to the character when you are doing the parsing. Otherwise, save their name as another persistent variable that you can grab and get the length from that.
               
               

               


                     Modifié par wyldhunt1, 10 janvier 2012 - 08:58 .
                     
                  


            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #31 on: January 10, 2012, 08:59:04 pm »


               I'll consider this confirmation enough and proceed. I'm going to create a few utility functions out of the repetitive code in my include. Thanks.
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #32 on: January 10, 2012, 09:40:48 pm »


               Wyldhunt: regarding your comment on Name Parsing

I don't want to make any assumptions in my code about what lengths a user of my code is going through to prevent PCs with weird symbols in their name. I think its better to write the code so that it can't be broken. And then if a user of the scripts wants to improve efficiency they can remove my routine which replaces my delimiting characters with other characters.
               
               

               
            

Legacy_wyldhunt1

  • Sr. Member
  • ****
  • Posts: 443
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #33 on: January 10, 2012, 10:26:07 pm »


               Which script has the GetPCIdentifier and SetPCIdentifier code?
If it's aa_inc_util, I don't see a link to it here.

I had the beginings of an idea that may work for your parsing.
Find a character that you do not use for your parsing. Say... ©
In your function that saves the characters info, parse the name before you send it to the db.
Replace any characters that may conflict with your code with the new character.
IE: Replace all _ with ©.
That will allow you to parse their info without breaking it.
When you need their proper name again, after it has been parsed out, replace all © with _.
This runs the risk of re-building their proper name incorrectly if they used © in their name to begin with, but you could code a check for that also if you wanted to be really sure.

string s_CharacterReplacement = "©";
if (GetSubString(GetName(oPC), "s_CharacterReplacement"))
{
   s_CharacterReplacement = "§";
   SetPersistentString(oPC, "_replacement", "§");
}
               
               

               


                     Modifié par wyldhunt1, 10 janvier 2012 - 10:43 .
                     
                  


            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #34 on: January 11, 2012, 03:38:01 am »


               I only change the PCPlayer name if I store it in a data string- in this case the Inn.rooms string. Sorry that I didn't post the code. I'll post the function later. Tonight I'm just stealing a couple moments here on BSN before putting the kids to bed.

(EDIT here it is)
GetPCIdentifier()
               
               

               


                     Modifié par henesua, 11 janvier 2012 - 04:42 .
                     
                  


            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #35 on: January 11, 2012, 06:03:47 am »


               

henesua wrote...

Question:
Is it problematic in terms of efficiency to have many function calls? Or is it worth it if you have less lines of code in your include?
My goal is to cut down on repetitive code so it is easier to manage, but I don't want to reduce efficiency at run time.


I am not sure, I fully understand the question.  Perhaps the best answer for ne to make is to just comment on wildhunt's answer.

wyldhunt1 wrote...

It would be interesting to see a test comparison, but here is my understanding of it:
The actual function call is nearly instant. If you were to copy/pasta all of your code 100 times and compare the time with a function call containing that code looped 100 times, they'd be almost exactly the same.


Lets just look at the loop real quick.   The way they are looked at has changed over the years.  In the early days, the stright line code was faster,  do to there being no overhead for making the loop.   Stright line code was faster,  Loops took up less memory but where slower.  It was a trade off for what you wanted.  Then something new came out, cache memory.    With cache memory the loop became not only smaller in memory  but also faster, if the entire loop could fit in the cache.  The reason is simply the memory access time to the faster cache memory, even with the extra code for the loop, out proformed the access time to the slower main memory.  Today with large cache memory, that is normally  8 way associative or better, there is no reason to fear loops.   It has now been years since i have looked at any of the new data for new inovations in computer science.   With CPU now doing  out-of-order execution and deep branch prediction  , I do not know where the ball bounces any more.


Larger include's create larger files which create memory bloat.

That is mainly false,  The only time it would be true is if you have a bunch of non-constant vars in the include, that where not used in the code.    There is really no differance between putting functions into an include and typing them directly into the script.   You will just use more hard drive space by typing the same function over ond over again into several source scripts.  The scripts themselves will compile the same.   To understand this, it is inportant to note, That just because a function is in a source script does not mean that the function will be compiled into the compiled code.   If the function Is used by the script it will be present in the compiled code.  If the function is not used by the script,  It will not be present in the compiled code, reguardless of its presence in the source code.   Constants are not present in compiled code at all,   The constant lables are simply replaced with the value for the constant before the script is compiled.  

In fact, from a technical point of view, you are making a ton of repetative function calls all the time.
Stuff like GetEnteringObject() and ActionRandomWalk() are just functions that Bioware wrote for us. They are still making the function call and running the script in nearly the same way as a function that you code. I'm unsure how the compiler interprets it all, but I'm fairly sure that the finished compiled script should be about the same either way.
Aurorascript is essentially a C++ library of functions that Bioware made to enable us to interact with the engine.
Our functions should still be compiled in to C++ using their C++ library functions... In theory.


There is a vast differance between the internal nwFunctions like GetEnteringObject() and functions that come from bioware includes or your own custom functions in nwscript.    The internal functions are compiled into machine instructions and are part of the VM (Virtual Machine) that run NW Compiled code.   The Functions that come from bioware includes and your own custom functions are compiled to NW VM instructions.   

I do not have time to go into great detail now.   but I will say that the overhead for a function in  not much.  The call to the function only has 31 instructions that gets excuted.  the return from the function is another 55 instructions. for a total of only 91 Instructions.   That is for a function that does not have any arguments.   

Just to put into context how small that number of instructions is.   a statment like  int nNum = 5;  be about  410 machine instructions that get excuted.    it is only  4 VM instructions that cause the 410 machine instructions to evecute.   
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #36 on: January 11, 2012, 06:33:52 pm »


               hmmm so the main benefit from abstracting repetitive code into functions is to reduce the likelihood of typos. Well it seems like good practice to structure your code into reusable functions, so I'll just continue with that practice even though there appears to be very little impact on efficiency.
               
               

               
            

Legacy_wyldhunt1

  • Sr. Member
  • ****
  • Posts: 443
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #37 on: January 11, 2012, 09:43:15 pm »


               Thanks for the clarification, Lightfoot8.
I taught myself how to code from studying other peoples example of code, so I tend to have to guess at the specifics of stuff like that.
Good to know that I was right about the functions being fast enough to not worry about though.

I had assumed that the pre-made functions were in a C++ library. The game had hooks in the code (The event hooks) which executed specific code (Our event scripts IE The ModOnAcquire script).
It would have to make sure that the custom code didn't execute any potentially harmful code to keep modders from programming viruses in to their mods, and then execute it.
The compiler is coded to only compile a very limited amount of code to help prevent damaging code from being written, and allows only the library functions and a minimal amount of C++ needed to make their functions work. Alternate compilers, such as NWNTX, can compile much more advanced code and have it execute properly (The engine still prevents any outright harmful code from executing).

In fact, I just noticed that the nested structs example that I posted compiles and runs fine if you compile it with NWNTX, but it fails to compile with the default compiler.

Anyway, I don't want to throw the thread off topic and I'm sure Lightfoot8 is correct. He's a lot more knowlegeable about the tech side of things than I am.

<ontopic>
Henesua, if you need nested structs or other more advanced things that the normal compiler doesn't like, you can nab NWNTX from here:
http://social.biowar...index/7645830/5
               
               

               


                     Modifié par wyldhunt1, 11 janvier 2012 - 09:43 .
                     
                  


            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #38 on: January 11, 2012, 09:58:59 pm »


               I'm using NWNTX. Its very nice.
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #39 on: January 12, 2012, 08:30:27 pm »


                [Cross posting from my Module's development thread so that people have an idea what this system will do]

FEATURES:
  • Inns have an hour of check out (different for each inn), (and may have an hour of check in depending on the inn), but your room is not necessarily immediately forfeit during check out after your paid period of stay expires. Unpaid rent however at those places that DON'T kick you out is added to your debt to the inn keeper who will try to collect it. You can imagine what some inns might do with a patron's items in persistent storage should a patron accumulate a large debt.
  • Thats right... Inns can track how much money you owe them. this is separated into damages and backrent (unpaid rent if you hold on to your key after checkout time, and the value of the furniture/doors you've destroyed) I am still working on a system to forgive damages accrued during a specific event: a gang of pirates enters the inn to kidnap the innkeeper's daughter, and although you broke a dozen tables and chairs in the process you manage to save the day.
  • Inn Rooms have names and show up on the mini map
  • Each Inn can have five types of Inn Rooms - each of which has a different daily cost, services offered, number of occupants accommodated, etc... So this means that you can have Common Rooms where you can all sleep on the floor for cheap, Private Rooms, Elite Luxury Suites etc...
  • Thats right... Inn Rooms can be shared by characters. The old "you are all meeting in your room when ..." is totally possible. The Inn will actually track characters sharing a room. This will be more relevant when I have expanded the number of services on offer. Free meals for everyone etc... This also means - if you aren't in a room after a certain hour... what the hell are you doing in the inn? In other words the Innkeeper (and staff) will know who is who, who is with who, and who is paying. Of course you can always sneak a few extra friends into a room and hope you don't get caught.
  • Inn Room Doors can be picked, or bashed open. But the system tracks this stuff... so all I have to do is write an InnKeeper staff AI and tell them what to do if they catch you picking a lock. There is also a personal reputation system I am using that already works so...this could be interesting. (The AI phase will probably happen after we've played a bit, but consider yourself warned. I've already written a few AIs now, and got the hang of it.)
  • Private Inn Rooms have actual keys that can be stolen, lost, lent out, copied etc...
  • The character given the Room Key is the responsible member of the Inn Room. Responsible for rent, returning the key etc... But anyone staying at the Inn can return the key.
  • Implemented in my module but not necessarily in this system: A key dropped on the ground, appears indistinguishable to any other room key to other characters. But once you learn which lock the key fits, you will be able to recognize and identify the key when you pick it up. (This is tacked on to my system which unidentifies dropped items, but tracks what a character knows - so knowledge of the item can be reapplied when it is picked up)
  • Inn Keeper can also hire a locksmith to change a lock. To be implemented: If you lose a key, the cost of the smith will go on your tab.
Thats what I have done so far. Still have a little more work to do, and then I have to implement it all in game. So far I have only tested and implemented the basic room functions: selecting a room, tracking who is in what room, and the use of keys. Also to be implemented: how to handle PC debts. Suffice it to say, characters with outstanding debts will have a harder time renting rooms, and maintaining a good reputation.Version two may have persistency of objects across server resets. Meaning that you can decorate your own rooms, drop items on the ground and expect to see them again after the reset, and broken furniture, doors, scorch marks, NPC deaths etc.. would be remembered. But I'm not there yet, and not sure how much of it all I'l implement. First plan is to wrap up Version One and then get back to designing areas, quests, and adventure.
               
               

               


                     Modifié par henesua, 12 janvier 2012 - 09:26 .
                     
                  


            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #40 on: January 13, 2012, 07:30:33 pm »


                I had a hit a wall recently trying to work out this system in code, and then I realized that my problem was that I had no real plan, no idea how I was going to wrap this up. So I took another tack and started figuring out what questions I want the system to be able to answer. Here's the list I sketched out on the bus this morning:

Questions to Answer:

After Checkout Hour
  • How many days late in rent is room(x)?
  • Who has the key to room(x)?
  • Is it time to empty the room, and change the lock?
In Conversation with Inn Staff/Innkeeper
  • Am I (NPC) in my Inn?
  • Is it checkout time?
  • Is the PC late in rent?
  • Is the PC responsible for the room?
  • Is the PC indebted to me?
  • What room is the PC in? (also answers: Does the PC have a room?)
NPC AI: HB, OnPerception etc...
  • Am I busy?
  • What is my next task? (think in terms of if I am not busy, should I be busy?)
  • Is property being damaged?
  • Is anyone disturbing the peace?
  • Is the Inn closed?
  • Is the PC a patron?
  • -- Have I already dealt with this non-patron?
  • Is the PC late in rent?
  • -- Have I already bothered the PC about rent?
  • Is the PC indebted to the Inn?
  • -- Have I already bothered the PC about debt?
(to speed up AI, the goal is to answer AI pertinent questions with local variables already set on self or PC. Variables set on PC in onEnter script)
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #41 on: January 15, 2012, 02:07:11 am »


               After trying a number of options for updating the Inn's state, I've settled on a hybrid system between a module heartbeat and area onenter scripts. The functions I am using to accomplish this are on pastebin.

Synopsis:
An hourly update cycle runs on the module's heartbeat, processing each Inn on the INN_LIST. However when the module loads, the INN_LIST  is blank, and so no inns are processed until one is added to the list. An Inn is added to the list from the Inn's OnAreaEnter script.

Procedural example:
Player enters Inn.
OnAreaEnter launches the function OnEnterInn which
  • processes the player, tagging them with 1 - 3 local variables (debt, inn room, days overdue)
  • then processes the Inn. If the Inn has not been added to the 

    INN_LIST it does so. then if the checkout hour has passed, it processes checkoout.
Henceforth every hour on the hour, UpdateInnTopOfHour executes looking for Inns which have just completed checkout, and if so, checkout is processed for that Inn.

***
Make sense? Anyone see a flaw in the system?
               
               

               


                     Modifié par henesua, 15 janvier 2012 - 02:12 .
                     
                  


            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #42 on: January 15, 2012, 06:09:32 pm »


                I implemented the changes discussed above.
aa_inc_housing has been updated.

Next I need to make modifications to the conversation and the conversation script. Once that is done I will test. And then after bug fixes this should be finished. I think. I'll get to a final set of documentation at that point.
               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #43 on: January 16, 2012, 02:31:25 pm »


               

Larger include's create larger files which create memory bloat.

That is mainly false,  The only time it would be true is if you have a bunch of non-constant vars in the include, that where not used in the code.    There is really no differance between putting functions into an include and typing them directly into the script.   You will just use more hard drive space by typing the same function over ond over again into several source scripts.  The scripts themselves will compile the same.   To understand this, it is inportant to note, That just because a function is in a source script does not mean that the function will be compiled into the compiled code.   If the function Is used by the script it will be present in the compiled code.  If the function is not used by the script,  It will not be present in the compiled code, reguardless of its presence in the source code.   Constants are not present in compiled code at all,   The constant lables are simply replaced with the value for the constant before the script is compiled.  


Someone correct me if I am wrong, but when someone uses a function in code, where that function is located in an include file.
The compiled script does NOT contain the whole include file, but rather function that was being used.
The include statement is more a less like a pointer, or directory location, telling the compiler where to look for the relevant functions.


at least from my own observations, I noticed that ncs files would be hefty in size, if I used hefty functions from include files, but the sizes remained small, if I used the smaller functions from the include file.



in effect,


##include "my_include"

void main()
{

MyWonderfulFunction();

}





 ** My_Include   <-- my example include file

##include "aps_include"  <-- Which also includes a file of its own

void MyWonderfulFunction()
{

WriteTimeStampedLogEntry(SQLEncodeSpecialCharacters("Hello World"));

}



Inside my void main using script, it calls
'MyWonderfulFunction()'

Which in turn calls
SQLEncodeSpecialCharacters() inside
'WriteTimeStampedLogEntry()'

In the compiled ncs file, it will contain the binary code for at least these 3 functions, and any sub functions they use.

So,
The code from my_include  - for MyWonderfulFunction() Will be added, which will also include within it, the code for the sub functions it uses.


All in all, while the scripting language loots neat and tidy, being a single call to a function, it gets complicated post compiler, because it embeds the code from the include files into the final articles.
Infact, you could delete the ncs and nss include files from your module, since they are not accessed post compiling.
All used functions from your includes, are accessed from the ncs of the scripts that use them, the includes are kept only for future modifications if desired. (Which is why you need to compile the void main scripts again, compiling the include files will not update the include using scripts.)


Anyway -  I kinda went off on a tangent there - I just wanted to post my own observations on that topic matter.
               
               

               
            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Persistent Inn System - W.I.P.
« Reply #44 on: January 16, 2012, 10:10:23 pm »


               

Baaleos wrote...

First, let me state that, you are correct on every point that matters.

Someone correct me if I am wrong, but when someone uses a function in code, where that function is located in an include file.

That is correct.  It however does not matter if that function in in an include or not.  Even if the function was typed directly into the script it would be handled the same way.

The compiled script does NOT contain the whole include file, but rather function that was being used.
The include statement is more a less like a pointer, or directory location, telling the compiler where to look for the relevant functions.

Even simpler.  The #Include is a Compiler Directive that tells the compiler to find that file and copy and paste it right here.   Hence the reason that it does not matter if the Function is in an include ot typed directly into the script.  

at least from my own observations, I noticed that ncs files would be hefty in size, if I used hefty functions from include files, but the sizes remained small, if I used the smaller functions from the include file.

That is 100% correct.



in effect,


##include "my_include"

void main()
{

MyWonderfulFunction();

}





** My_Include <-- my example include file

##include "aps_include" <-- Which also includes a file of its own

void MyWonderfulFunction()
{

WriteTimeStampedLogEntry(SQLEncodeSpecialCharacters("Hello World"));

}



Inside my void main using script, it calls
'MyWonderfulFunction()'

Which in turn calls
SQLEncodeSpecialCharacters() inside
'WriteTimeStampedLogEntry()'

In the compiled ncs file, it will contain the binary code for at least these 3 functions, and any sub functions they use.

So,
The code from my_include - for MyWonderfulFunction() Will be added, which will also include within it, the code for the sub functions it uses.


Correct, the compiler after a few preProcessing pass, Would see the script to be compiled as 

** all the code in aps_include strung out here. **

void MyWonderfulFunction()
{

WriteTimeStampedLogEntry(SQLEncodeSpecialCharacters("Hello World"));

}


void main()
{

MyWonderfulFunction();

}
  
Because the compiler would first expand the ##include "my_include"  to the actual text in the include file.   Then it would do the same thing for the  aps_include file.  This is why circular includes caused such a problem in the early days before the ##Include_Once Type directives came out.

All in all, while the scripting language loots neat and tidy, being a single call to a function, it gets complicated post compiler, because it embeds the code from the include files into the final articles.


Includes are not dynamic Link Libirarys or Object Code.   They are simply inserted into the source as uncompiled test before the script is compiled. 
 

Infact, you could delete the ncs and nss include files from your module, since they are not accessed post compiling.

Correct, source code is not needed to run compiled code, only to build it.  Include files however do not have .ncs files that go along with them.  In order for a .ncs file to be built you have to have either a void main() entry point for the code. Or an int StartingCondition() entry point for the code.   code can not be compiled in NWScript without an Entry point.  The Two functions above are the only two Entry points that the  NWScript Compiler excepts.

All used functions from your includes, are accessed from the ncs of the scripts that use them, the includes are kept only for future modifications if desired. (Which is why you need to compile the void main scripts again, compiling the include files will not update the include using scripts.)

Correct, except for the fact that you can not compile an include by itself,   It would have to have a void main(), at that point it is no longer an include it is its own script.

Anyway - I kinda went off on a tangent there - I just wanted to post my own observations on that topic matter.

  I think it is always good to post your observations.   I just hope the OP does not mind me cluttering there thread also. 

*Ducks as the Inn Room Key goes sailing ouver head*