Jaysyn904 6ec137a24e Updated AMS marker feats
Updated AMS marker feats.  Removed arcane & divine marker feats.  Updated Dread Necromancer for epic progression. Updated weapon baseitem models.  Updated new weapons for crafting & npc equip.
 Updated prefix.  Updated release archive.
2024-02-11 14:01:05 -05:00

661 lines
25 KiB
HTML

<html>
<head>
<title> APS/NWNX_ODBC Documentation </title>
</head>
<body style="font-family:Arial,Verdana,sans-serif;">
<h2>Avlis Persistence System</h2>
<h3>Table of Contents:</h3>
<table>
<tr>
<td> <a href="#I">I.</a></td>
<td> <a href="#I">Introduction - What does APS do?</a></td>
</tr>
<tr>
<td><a href="#II">II.</a></td>
<td><a href="#II">Installing and updating APS</a></td>
</tr>
<tr>
<td><a href="#III">III.</a></td>
<td><a href="#III">Setting up a database</a></td>
</tr>
<tr>
<td><a href="#IV">IV.</a></td>
<td><a href="#IV">Customization</a></td>
</tr>
<tr>
<td><a href="#V">V.</a></td>
<td><a href="#V">Speed comparison</a></td>
</tr>
<tr>
<td><a href="#VI">VI.</a></td>
<td><a href="#VI">Troubleshooting</a></td>
</tr>
</table>
<a name="I"></a>
<h3>I. Introduction - What does APS/NWNX ODBC2 do?</h3>
<p>APS is a set of scripts written for Neverwinter Nights that work with NWNX2 to
produce reliable persistence in a module. At the heart of the APS is
an include file that lists a number of custom made functions for governing
persistence. These functions can be used as is in your module or changed to
suit your needs.
<p>Whenever a script work with persistent data, it calls our APS functions
and the Extender pulls the query out of the memory of the NWN server. It then
passes it to the database, and writes the result of the query back into the
memory of the server. The database has been tested with MySQL, MS-SQL,
PostgresSQL, Microsoft Access, and the internal SQL database engine so far. Conceivably, any database with a
decent ODBC driver will work.
<p>We have included a demo module that illustrates how to use APS/NWNX ODBC2 and makes
creating the database tables easy, and a second module demonstrating how
persistent containers could be implemented.
<p><b>Licence</b><br> APS and NWNX2 ODBC2 are distributed unter the terms of the GNU
GENERAL PUBLIC LICENSE included in <a href="../licence.txt">licence.txt</a>.
<a name="II"></a>
<h3>II. Installing and updating APS</h3>
<h4>A. Installing the plugin</h4>
<p>Copy the file nwnx_odbc.dll to you NWN folder, and aps_demo.mod
to your NWN module folder.
<h4>B. Importing the erf</h4>
<p>In order to be able to use the APS functions, you will need to import
the aps_include file into your module.
<ol>
<li>Place the file "aps2.erf" into the C:\Neverwinternights\NWN\erf directory.
<li>In the toolset, open up the module into which you wish to install the
scripts.
<li>Under the File Menu, click Import. A window will pop up.
<li>Make sure the contents of the C:\Neverwinternights\NWN\erf directory are
showing in the window
<li>Select "aps2.erf" from the list
<li>Click Import and ignore any messages about missing resources (click Yes).
</ol>
<p>The following scripts should now be imported: aps_onload, aps_include.
<h4>C. Updating from previous versions</h4>
<ul>
<li>from ODBC v2.5 to ODBC2: Copy the new DLL into your NWN folder.
Edit the database connection parameters in your nwnx.ini file (check the
supplied example INI file what parameters can be used). Import the updated
aps_include file (from ap2.erf) into your module.
</ul>
<a name="III"></a>
<h3>Setting up a database</h3>
<p>The first choice you have to make is what database you are going to use.
We strongly suggest starting out with the internal SQL database (SQLite), which
is one of the fastest and easiest options. It also comes with a prepared datase
that is ready to go. Read the section "Configuration for the internal database" on
how to set this up.
<p>If you are using MySQL, go to the section "Configuration for MySQL
database". If you are using any other database with an ODBC driver, go
to the section "Configuration for ODBC database".
<h4>3.a. Configuration for the internal database</h4>
The internal NWNX ODBC2 database is based on SQLite v3, which is a fast SQL
compliant database that developers can package with their applications - and
that is what we did ! The main benefits of SQLite for NWN users are twofold:
<ol>
<li>It is fast.
<li>It is easy to setup (zero configuration).
</ol>
<p>Note: We recommend this type of database for single instance servers,
meaning setups where only one NWN server is accessing the database.
We recommend against using it in setups where multiple computers access
the database simultaneously.
<p>Edit the nwnx.ini file and set the source= parameter to SQLite, and
the file= parameter to the path and file where you want your database file:
<pre>
[ODBC2]
source = sqlite
file = sqlite.db
</pre>
<p>Note: Omitting the path like in the example above will put the database
file into your NWN folder. If you want it somewhere else, set the file
parameter to e.g. c:\temp\nwn.db.
<p>Hint: One of the tools for working with the internal database is
<a href="http://home.student.uu.se/frax0795/">SQLiteCC</a>. It is not
needed to follow the setup instructions in this document, but will come in
handy later.
<p>Advanced note: After opening the database file, an implicit transaction
is started automatically, since SQLite is significantly faster when access to
the database is happening inside a transaction. If you want to access the
database concurrently, or if you want to handle transactions yourself, issue
a COMMIT right after the call to SQLInit to commit the implicit transaction.
<h4>3.b. Configuration for MySQL database</h4>
<p>Below the various parameters are listed which are required in the
configuration file nwnx.ini:
<ul>
<li>Server: Either the name or IP address of the database system which you are
using. Most of the time this will be your local machine: 'localhost'.
<li>User/pwd: This is the username and password of the user which will be
used to connect to the database. For security reasons it is best to
create a special user which has only access to the nwn database.
<li>DB: The database name.
</ul>
<p>An example configuration file is displayed below. In this configuration you
tell the plugin to use the direct mysql connection to the database nwn.
This database resides on the localhost (same machine as NWN runs)
and should be connected with user 'your_user' and password 'your_pwd'.
<pre>
[ODBC2]
source = mysql
server = localhost
user = your_user
pwd = your_pwd
db = nwn
</pre>
<p>Note: If your MySQL installation is not on the same machine you are
installing this plugin too, you will need to copy the library libmysql.dll to
your NWN folder as well, or ODBC2 will complain about a missing module.
<h4>3.c. Configuration for ODBC database</h4>
<p>If you have a different database system than MySQL you will use
the ODBC connection method. In this section we will describe
the steps needed to setup your system correctly. In the description
below we asume that you are using Windows XP or a similar system.
<ul>
<li>Make sure your database system (including ODBC support) is installed
and running correctly on your machine (with a remote database system you
only need the ODBC drivers on your machine).
<li>Create an ODBC datasource: in the Control Panel select
Administrative Tools and then Data Sources (ODBC). In the System Tab
click on Add, and fill in the required fields. When finished the
datasource should appear in the list (if not, consult your database
system manual).
</ul>
Edit the nwnx.ini file and set the dsn= parameter to the name you
have just entered, e.g.:
<pre>
[ODBC2]
source = odbc
dsn = nwn
</pre>
<h4>3.c. Creating the database tables</h4>
<p>Note: As there is an almost unlimited amount of different databases
out there, we can not give detailed instructions for all of them. If you want
to use a database server like MSSQL or PostgresSQL, try to follow the steps
described below accordingly. If you are using the internal database, all tables have already been
created for you (in the file sqlite.db).
<p>Make sure your database is up and running and that you have a
database that is accessible to the ODBC2 plugin. In order to store data in it,
you have to create some tables in the database. The included module
"aps_demo.mod" makes this easy for the internal database and MySQL.
By default, it creates tables for the internal database. If you want to use
MySQL instead, open the module with the toolset and edit the scripts
"demo_createtable" and "demo_obj_create" accordingly.
<p>Next connect to your server with the Neverwinter Nights client.
On the left side, you will see several different signs in front of you:
<ul>
<li>Create table: Issues a database command that creates a table
in the database
<li>Store variable in database: Tries to save a test variable named
"demoName" with the value "testValue" in the database
<li>Load variable from database: Tries to retrieve the variable
"demoName" from the database and prints the results in the
server message window.
</ul>
<p>Now click every sign once, starting with the one on the
left (Create Tables).
<p>If the last sign sends you the message "Retrieved variable from
database: testValue" your setup is ok and you're ready to start using APS.
Note: This is the most basic setup. We encourage you to use more
sophisticated databases and data structures if you feel confident
to do so (see below).
<p> On the right side, you will see several different signs that do almost
the same as the other signs, but they are dealing with object instead
of strings storage.
<p> If the variable is not retrieved correctly, check out the log file
nwnx_odbc.txt for errors. Also check your database if the table really
has been created. The SQL statement that is executed by default is suitable for MySQL. If you use a
different database server, you should adjust that statement accordingly:
<p>Create a table "pwdata" with the following fields
player, tag, name, val, expire, last. Here is an example for MySQL
(taken from aps_demo.mod, script demo_createtable):
<pre>
SQLExecDirect("CREATE TABLE pwdata (" +
"player varchar(64) default NULL," +
"tag varchar(64) default NULL," +
"name varchar(64) default NULL," +
"val text," +
"expire int(11) default NULL," +
"last timestamp(14) NOT NULL," +
"KEY idx (player,tag,name)" +
")" );
</pre>
<h4>E. Using the persistence functions in your module</h4>
<ol>
<li>After installing according to the instructions above, go to Module
Properties under the Edit menu.
<li> Select aps_onload for your module OnModuleLoad event.<br>
&nbsp;&nbsp;OR<br>
Open aps_onload in the script editor and paste the contents of it into your
pre-existing module's OnModuleLoad script. We only recommend doing this if you
are familiar with NWScript.
</ol>
The functions below are now implemented. Here is a lexicon containing
information on their purpose and use:
<p><b>void SQLInit()</b>
<p>Setup placeholders for ODBC requests and responses. This functions reserves memory APS and NWNX
use for communication. Call this function <b>once</b> in the module load event.
<p><b>SetPersistentString(object oObject, string sVarName, string sValue,
int iExpiration=0, string sTable="pwdata")</b>
<p>This sets a persistent string on an object. The object can be
any valid object in the game. The command works the same way the usual
SetLocalString function works, except you can optionally add a little more
information:
<ul>
<li>object oObject - The object which you wish to set the persistent variable
upon.
<li>string sVarName - The name of the persistent variable. Ex: "Quest Flag 1"
or "QuestCompleted_True_False"
<li>string sValue - The string you want to store.
<li>int Expiration - (optional) The number of days after which the variable should
expire, i.e. be deleted from the database. If you don't specify this parameter
or pass 0 here, the variable will never be purged from the database.
<li>string sTable - (optional) You can specify in which database table the value
should be stored. This parameter defaults to "pwdata".
</ul>
<p><b>SetPersistentInt(object oObject, string sVarName, int iValue,
int iExpiration=0, string sTable="pwdata")</b>
<p>This sets a persistent integer value on an object. The object can be
any valid object in the game. The command works the same way the usual
SetLocalInt function works, except you can optionally add a little more
information:
<ul>
<li>object oObject - The object which you wish to set the persistent variable
upon.
<li>string sVarName - The name of the persistent variable. Ex: "Quest Flag 1"
or "QuestCompleted_True_False"
<li>int iValue - The integer value of the variable. 1, 2, 3... etc.
<li>int Expiration - (optional) The number of days after which the variable should
expire, i.e. be deleted from the database. If you don't specify this parameter
or pass 0 here, the variable will never be purged from the database.
<li>string sTable - (optional) You can specify in which database table the value
should be stored. This parameter defaults to "pwdata".
</ul>
<p><b>SetPersistentFloat(object oObject, string sVarName, float fValue,
int iExpiration=0, string sTable="pwdata")</b>
<p>This sets a persistent float value on an object. The object can be
any valid object in the game. The command works the same way the usual
SetLocalFloat function works, except you can optionally add a little more
information:
<ul>
<li>object oObject - The object which you wish to set the persistent variable
upon.
<li>string sVarName - The name of the persistent variable. Ex: "Quest Flag 1"
or "QuestCompleted_True_False"
<li>float fValue - The float value of the variable. 1.6, 2.542, 3.0989... etc.
<li>int Expiration - (optional) The number of days after which the variable should
expire, i.e. be deleted from the database. If you don't specify this parameter
or pass 0 here, the variable will never be purged from the database.
<li>string sTable - (optional) You can specify in which database table the value
should be stored. This parameter defaults to "pwdata".
</ul>
<p><b>SetPersistentLocation(object oObject, string sVarName, location lLocation,
int iExpiration=0, string sTable="pwdata")</b>
<p>This sets a persistent location on an object. The object can be
any valid object in the game. The command works the same way the usual
SetLocalLocation function works, except you can optionally add a little more
information:
<ul>
<li>object oObject - The object which you wish to set the persistent variable
upon.
<li>string sVarName - The name of the persistent variable. Ex: "Quest Flag 1"
or "QuestCompleted_True_False"
<li>location lLocation - The location you want to store.
<li>int Expiration - (optional) The number of days after which the variable should
expire, i.e. be deleted from the database. If you don't specify this parameter
or pass 0 here, the variable will never be purged from the database.
<li>string sTable - (optional) You can specify in which database table the value
should be stored. This parameter defaults to "pwdata".
</ul>
<p><b>SetPersistentVector(object oObject, string sVarName, vector vVector,
int iExpiration=0, string sTable="pwdata")</b>
<p>This sets a persistent vector on an object. The object can be
any valid object in the game. The command works the same way the usual
Set local variable functions work, except you can optionally add a little more
information:
<ul>
<li>object oObject - The object which you wish to set the persistent variable
upon.
<li>string sVarName - The name of the persistent variable. Ex: "Quest Flag 1"
or "QuestCompleted_True_False"
<li>vector vVector - The vector you want to store.
<li>int Expiration - (optional) The number of days after which the variable should
expire, i.e. be deleted from the database. If you don't specify this parameter
or pass 0 here, the variable will never be purged from the database.
<li>string sTable - (optional) You can specify in which database table the value
should be stored. This parameter defaults to "pwdata".
</ul>
<p><b>GetPersistentString(object oObject, string sVarName, string sTable="pwdata")</b>
<p>This function works in the same manner as GetLocalString. It gets the
persistent string from object oObject.
<ul>
<li>object oObject - This is the object from which you are retrieving the
value of the variable.
<li>string sVarName - This is the name of the variable that will be retrieved
off of oObject. Ex: "Quest Flag 1"
<li>string sTable - (optional) You can specify in which database table the value
can be found. This parameter defaults to "pwdata".
</ul>
<p><b>GetPersistentInt(object oObject, string sVarName, string sTable="pwdata")</b>
<p>This function works in the same manner as GetLocalInt. It gets the
persistent integer value from object oObject.
<ul>
<li>object oObject - This is the object from which you are retrieving the
value of the variable.
<li>string sVarName - This is the name of the variable that will be retrieved
off of oObject. Ex: "Quest Flag 1"
<li>string sTable - (optional) You can specify in which database table the value
can be found. This parameter defaults to "pwdata".
</ul>
<p><b>GetPersistentFloat(object oObject, string sVarName, string sTable="pwdata")</b>
<p>This function works in the same manner as GetLocalFloat. It gets the
persistent float value from object oObject.
<ul>
<li>object oObject - This is the object from which you are retrieving the
value of the variable.
<li>string sVarName - This is the name of the variable that will be retrieved
off of oObject. Ex: "Quest Flag 1"
<li>string sTable - (optional) You can specify in which database table the value
can be found. This parameter defaults to "pwdata".
</ul>
<p><b>GetPersistentLocation(object oObject, string sVarName, string sTable="pwdata")</b>
<p>This function works in the same manner as GetLocalLocation. It gets the
persistent location value from object oObject.
<ul>
<li>object oObject - This is the object from which you are retrieving the
value of the variable.
<li>string sVarName - This is the name of the variable that will be retrieved
off of oObject. Ex: "Quest Flag 1"
<li>string sTable - (optional) You can specify in which database table the value
can be found. This parameter defaults to "pwdata".
</ul>
<p><b>GetPersistentVector(object oObject, string sVarName, string sTable="pwdata")</b>
<p>This function works in the same manner as the other get local variable functions. It gets the
persistent vector value from object oObject.
<ul>
<li>object oObject - This is the object from which you are retrieving the
value of the variable.
<li>string sVarName - This is the name of the variable that will be retrieved
off of oObject. Ex: "Quest Flag 1"
<li>string sTable - (optional) You can specify in which database table the value
can be found. This parameter defaults to "pwdata".
</ul>
<p><b>void DeletePersistentVariable(object oObject, string sVarName, string sTable="pwdata")</b>
<p>This function deletes a variable from the database.
<ul>
<li>object oObject - This is the object on which the variable has been stored.
<li>string sVarName - This is the name of the variable that will be deleted.
<li>string sTable - (optional) You can specify in which database table the value
can be found. This parameter defaults to "pwdata".
</ul>
<p><b>void SQLExecDirect(string sSQL)</b>
<p> Executes a SQL statement. If the statement returns a result set, you can read the data
with the next two functions.
<p><b>int SQLFetch()</b>
<p> Position cursor on next row of the resultset. Call this function before using SQLGetData().<br>
Returns
<ul>
<li>SQL_SUCCESS if there is a row
<li>SQL_ERROR if there are no more rows
</ul>
<p><b>int SQLFirstRow() (*deprecated*)</b>
<p> Function is deprecated but still there for backward compability. Simply calls SQLFetch().
<p><b>int SQLNextRow() (*deprecated*)</b>
<p> Function is deprecated but still there for backward compability. Simply calls SQLFetch().
<p><b>string SQLGetData(int iCol)</b>
<p> Return value of column iCol in the current row of result set sResultSetName.
<p><b>Comments</b>
<ul>
<li>Make sure you include aps_include in every script where you want to use these functions,
i.e. add the line
<pre>
#include "aps_include"
</pre>
at the top of your script.
</ul>
<a name="IV"></a>
<h3>IV. Customization</h3>
<p>The APS is merely a set of custom functions that were originally used for the Avlis
persistent world. The names of the functions and their parameters can be set to whatever
is convenient for your module. Below is a hypothetical example of how customization can be done.
<p>The "PowerG I33t" persistent world maintains their persistence with the Joe Bloe Persistence
system (JBPS). In that system, a persistent string is set with the following JBPS function:
<p>SetStickySring(string sVariableName, string sVariableValue, object oTarget)
<p>All 5000 scripts in PowerG's elite world are written with the SetStickyString function, and they
wish to retrofit their world to use NWNX. They would follow these steps:
<ol>
<li>Open up the file aps_include in the script editor.
<li>Change the name of the APS function called SetPersistentString to SetStickyString.
<li>Rearrange the parameters of:
<p>SetPersistentString(object oObject, string sVarName, string sVarValue, int iExpiration, string sTable = "pwdata")<br>
&nbsp;&nbsp;to:<br>
SetStickyString(string VarName, string VarValue, object oObject, int iExpiration = 0, string sTable = "pwdata")<br>
<li>Build the module, i.e. recompile all scripts.
</ol>
<p>Once the module is restarted, all of PowerG I33t's old persistent string scripts should be running the
new persistence system. All it took was changing one script.
<p>The above example is a simplified one, and the rest of the functions in the JBPS would need to be changed
in the same manner. In cases where the function parameters were completely not equivalent to those used
by the APS, they may have to be changed throughout every script in the module. Many but not all of the
persistence systems out there should be convertible by the above method. We encourage further modification
of aps_include to tailor the variable handling to your needs.
<p>For persistence systems that use tokens, conversion will not be as easy. In these systems, token items
are used to represent information on a player character. These tokens are not lost because they are
actually in the inventory of the character. Because these systems work with tokens and not actual variables
it will be hard to convert them into a database format. The module will most likely have to be completely
re-fitted.
<p>On possible idea to do this scriptomatically would be to write a module OnEnter script that strips the
character of their tokens and issues SetPersistent variable commands into the database before destroying
them. That would preserve the information that is there, but handling the actual scripts throughout the
module will have to be done separately.
<p>Alternatively, you can have a look at the GetPersistentString() function in
"aps_include". There are some comments in this functions that should give you
ideas on how to convert persistent data from your current system to APS.
<a name="V"></a>
<h3>V. Speed comparison </h3>
<p>To give you an idea what to expect from the various database options, we
conducted a small test involving 500 writes and reads. Note that this test is
very artificial, since many aspects like table fragmentation, concurrent access,
database size, and more realistic queries are not factored in. All tests were
done on a Athlon 64 3200+ with database server, NWServer, and NWClient
running local (NWClient with reduced process priority).
<p>Writes were done with the following code:
<pre>
for (i = 0; i < 500; i++)
{
SQLExecDirect("INSERT INTO pwdata (player, tag, name,val) values " +
"('~', '~', 'iter_" + IntToString(i) + "', 'value')");
}
</pre>
<p>Reads were done with the following code:
<pre>
SQLExecDirect("SELECT * from pwdata");
while (SQLFetch() == SQL_SUCCESS) {}
</pre>
<p>Bioware DB reads and write were done with the following code:
<pre>
for (i = 0; i < 500; i++)
{
SetCampaignString("test", "iter_" + IntToString(i), "value");
-- respecively --
s = GetCampaignString("test", "iter_" + IntToString(i));
}
</pre>
<p>Results:
<table border="1" width="300" style="margin-left:2em;">
<tr>
<td>Database</td>
<td>Write</td>
<td>Read</td>
</tr>
<tr>
<td>SQLite (1)</td>
<td>30 ms</td>
<td>20 ms</td>
</tr>
<tr>
<td>SQLite (2)</td>
<td>36 ms</td>
<td>20 ms</td>
</tr>
<tr>
<td>SQLite (3)</td>
<td>2800 ms</td>
<td>20 ms</td>
</tr>
<tr>
<td>MySQL via ODBC</td>
<td>71 ms</td>
<td>38 ms</td>
</tr>
<tr>
<td>MySQL direct</td>
<td>68 ms</td>
<td>22 ms</td>
</tr>
<tr>
<td>Bioware DB (4)</td>
<td>856 ms</td>
<td>10 ms</td>
</tr>
<table>
<p>Comments:
<ul>
<li>SQLITE (1): Using a transaction. No commit after the for loop.
<li>SQLITE (2): Using a transaction. Commit after the for loop.
<li>SQLITE (3): Not using a transaction. Terribly slow ! Note that
NWNX ODBC2 starts an implicit transaction automatically. If you want
to handle transactions yourself, issue a COMMIT right after SQLInit()
to end the implicit transaction.
<li>Bioware DB (4): This comparison is a bit unfair, since the call to the
Bioware database is significantly simpler and less flexible than its ODBC2
counterpart. Real world examples utilizing e.g. SQL resultsets would probably
favor ODBC2.
</ul>
<a name="VI"></a>
<h3>VI. Troubleshooting </h3>
<p> Starting out with NWNX ODBC2 can be a bit daunting at first, especially
if you are on your own. We highly encourage you to visit us at
<a href="http://www.nwnx.org">www.nwnx.org</a> to ask question and get
help with setting this system up.
</body>
</html>