306 lines
9.9 KiB
Plaintext
306 lines
9.9 KiB
Plaintext
|
/**
|
|||
|
* @file
|
|||
|
* All the functions dealing with the NWNx database. Based on the APS include
|
|||
|
* with optimisations, renamed to avoid naming clashes with the APS system.
|
|||
|
* @author Primogenitor, motu99, fluffyamoeba
|
|||
|
* @date created on 2009-01-25
|
|||
|
*/
|
|||
|
|
|||
|
const int PRC_SQL_ERROR = 0;
|
|||
|
const int PRC_SQL_SUCCESS = 1;
|
|||
|
const string XCHST_DB = "xchst_db";
|
|||
|
|
|||
|
//////////////////////////////////////////////////
|
|||
|
/* Function prototypes */
|
|||
|
//////////////////////////////////////////////////
|
|||
|
|
|||
|
// creates and stores a 1024 Byte string for database-fetches on the module
|
|||
|
void PRC_SQLInit();
|
|||
|
|
|||
|
// executes an SQL command direclty
|
|||
|
void PRC_SQLExecDirect(string sSQL);
|
|||
|
|
|||
|
// fetches data from a previous SQL fetch request; returns TRUE is fetch was successful
|
|||
|
int PRC_SQLFetch();
|
|||
|
|
|||
|
// gets the actual data from a fetch; if a table with more than 1 column was requested, return the table element at column iCol
|
|||
|
string PRC_SQLGetData(int iCol);
|
|||
|
|
|||
|
// SQLite and MYSQL use different syntaxes
|
|||
|
string PRC_SQLGetTick();
|
|||
|
|
|||
|
// Problems can arise with SQL commands if variables or values have single quotes
|
|||
|
// in their names. These functions are a replace these quote with the tilde character
|
|||
|
string ReplaceSingleChars(string sString, string sTarget, string sReplace);
|
|||
|
|
|||
|
// only needed for SQLite; commits (actually stores) the changed to the database
|
|||
|
void PRC_SQLiteCommit();
|
|||
|
// starts the pseudo heartbeat for committing SQLite DB
|
|||
|
void StartSQLiteCommitHB();
|
|||
|
// pseudo heartbeat for committing SQLite DB; interval given in switch PRC_DB_SQLITE_INTERVAL
|
|||
|
// default is 600 seconds (= 10 min)
|
|||
|
void SQLiteCommitHB();
|
|||
|
|
|||
|
// Set oObject's persistent object with sVarName to sValue
|
|||
|
// Optional parameters:
|
|||
|
// iExpiration: Number of days the persistent variable should be kept in database (default: 0=forever)
|
|||
|
// sTable: Name of the table where variable should be stored (default: pwobjdata)
|
|||
|
void PRC_SetPersistentObject(object oObject, string sVarName, object oObject2, int iExpiration = 0, string sTable = "pwobjdata");
|
|||
|
|
|||
|
// Get oObject's persistent object sVarName
|
|||
|
// Optional parameters:
|
|||
|
// sTable: Name of the table where object is stored (default: pwobjdata)
|
|||
|
// * Return value on error: 0
|
|||
|
object PRC_GetPersistentObject(object oObject, string sVarName, object oOwner = OBJECT_INVALID, string sTable = "pwobjdata");
|
|||
|
|
|||
|
// Portable presistent chest system (X-Chest) nwnx database support
|
|||
|
//
|
|||
|
void CreateXChestDB_SQLite();
|
|||
|
void CreateXChestDB_MySQL();
|
|||
|
void DeleteXChestDB();
|
|||
|
|
|||
|
//////////////////////////////////////////////////
|
|||
|
/* Function definitions */
|
|||
|
//////////////////////////////////////////////////
|
|||
|
|
|||
|
void PRC_SQLInit()
|
|||
|
{
|
|||
|
int i;
|
|||
|
|
|||
|
// Placeholder for ODBC persistence
|
|||
|
string sMemory;
|
|||
|
|
|||
|
for (i = 0; i < 8; i++) // reserve 8*128 bytes
|
|||
|
sMemory +=
|
|||
|
"................................................................................................................................";
|
|||
|
|
|||
|
SetLocalString(GetModule(), "NWNX!ODBC!SPACER", sMemory);
|
|||
|
}
|
|||
|
|
|||
|
void PRC_SQLExecDirect(string sSQL)
|
|||
|
{
|
|||
|
SetLocalString(GetModule(), "NWNX!ODBC!EXEC", sSQL);
|
|||
|
}
|
|||
|
|
|||
|
int PRC_SQLFetch()
|
|||
|
{
|
|||
|
string sRow;
|
|||
|
object oModule = GetModule();
|
|||
|
|
|||
|
// initialize the fetch string to a large 1024 Byte string; this will also trigger the fetch operation in NWNX
|
|||
|
SetLocalString(oModule, "NWNX!ODBC!FETCH", GetLocalString(oModule, "NWNX!ODBC!SPACER"));
|
|||
|
|
|||
|
// get the result from the fetch
|
|||
|
sRow = GetLocalString(oModule, "NWNX!ODBC!FETCH");
|
|||
|
if (GetStringLength(sRow) > 0)
|
|||
|
{
|
|||
|
// store the result on the module and signal success
|
|||
|
SetLocalString(oModule, "NWNX_ODBC_CurrentRow", sRow);
|
|||
|
return PRC_SQL_SUCCESS;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// set the result string on the module to empty and signal failure
|
|||
|
SetLocalString(oModule, "NWNX_ODBC_CurrentRow", "");
|
|||
|
return PRC_SQL_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/** @todo check mySQL manual, not sure if this is needed - fluffyamoeba */
|
|||
|
string PRC_SQLGetTick()
|
|||
|
{
|
|||
|
if(GetPRCSwitch(PRC_DB_SQLITE)) return "";
|
|||
|
else return "`";
|
|||
|
}
|
|||
|
|
|||
|
string PRC_SQLGetData(int iCol)
|
|||
|
{
|
|||
|
string sResultSet = GetLocalString(GetModule(), "NWNX_ODBC_CurrentRow");
|
|||
|
int iPos = FindSubString(sResultSet, "<22>");
|
|||
|
if (iCol == 1)
|
|||
|
{
|
|||
|
// only one column returned ? Then we are finished
|
|||
|
if (iPos == -1) return sResultSet;
|
|||
|
// more than one column returned ? Then return first column
|
|||
|
else return GetStringLeft(sResultSet, iPos);
|
|||
|
}
|
|||
|
// more than one column requested, but only one returned ? Something went wrong; return empty string
|
|||
|
else if (iPos == -1) return "";
|
|||
|
|
|||
|
// find column in current row
|
|||
|
int iCount = 0;
|
|||
|
int nLength = GetStringLength(sResultSet);
|
|||
|
|
|||
|
// loop through columns until found
|
|||
|
while (iCount++ != iCol)
|
|||
|
{
|
|||
|
if (iCount == iCol)
|
|||
|
return GetStringLeft(sResultSet, iPos);
|
|||
|
|
|||
|
// pull off the previous column and find new column marker
|
|||
|
nLength -= (iPos + 1);
|
|||
|
sResultSet = GetStringRight(sResultSet, nLength);
|
|||
|
iPos = FindSubString(sResultSet, "<22>");
|
|||
|
|
|||
|
// special case: last column in row
|
|||
|
if (iPos == -1) return sResultSet;
|
|||
|
}
|
|||
|
|
|||
|
return sResultSet;
|
|||
|
}
|
|||
|
|
|||
|
string ReplaceSingleChars(string sString, string sTarget, string sReplace)
|
|||
|
{
|
|||
|
if (FindSubString(sString, sTarget) == -1) // not found
|
|||
|
return sString;
|
|||
|
|
|||
|
int i;
|
|||
|
string sReturn = "";
|
|||
|
string sChar;
|
|||
|
|
|||
|
// Loop over every character and replace special characters
|
|||
|
for (i = 0; i < GetStringLength(sString); i++)
|
|||
|
{
|
|||
|
sChar = GetSubString(sString, i, 1);
|
|||
|
if (sChar == sTarget)
|
|||
|
sReturn += sReplace;
|
|||
|
else
|
|||
|
sReturn += sChar;
|
|||
|
}
|
|||
|
return sReturn;
|
|||
|
}
|
|||
|
|
|||
|
// only needed for SQLite; commits (actually stores) the changed to the database
|
|||
|
void PRC_SQLiteCommit()
|
|||
|
{
|
|||
|
PRC_SQLExecDirect("COMMIT");
|
|||
|
PRC_SQLExecDirect("BEGIN IMMEDIATE");
|
|||
|
}
|
|||
|
|
|||
|
// starts the pseudo heartbeat for committing SQLite DB
|
|||
|
void StartSQLiteCommitHB()
|
|||
|
{
|
|||
|
if (GetPRCSwitch(PRC_DB_SQLITE))
|
|||
|
{
|
|||
|
int nInterval = GetPRCSwitch(PRC_DB_SQLITE_INTERVAL);
|
|||
|
DelayCommand(nInterval ? IntToFloat(nInterval) : 600.0f, SQLiteCommitHB());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// pseudo heartbeat for committing SQLite DB; interval given in switch PRC_DB_SQLITE_INTERVAL
|
|||
|
// default is 600 seconds (= 10 min)
|
|||
|
void SQLiteCommitHB()
|
|||
|
{
|
|||
|
// check if we are still using SQLite
|
|||
|
if (GetPRCSwitch(PRC_DB_SQLITE))
|
|||
|
{
|
|||
|
// do the commit
|
|||
|
PRC_SQLExecDirect("COMMIT");
|
|||
|
PRC_SQLExecDirect("BEGIN IMMEDIATE");
|
|||
|
|
|||
|
// continue pseudo heartbeat
|
|||
|
int nInterval = GetPRCSwitch(PRC_DB_SQLITE_INTERVAL);
|
|||
|
DelayCommand(nInterval ? IntToFloat(nInterval) : 600.0f, SQLiteCommitHB());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void PRC_SetPersistentObject(object oOwner, string sVarName, object oObject, int iExpiration = 0, string sTable = "pwobjdata")
|
|||
|
{
|
|||
|
string sPlayer;
|
|||
|
string sTag;
|
|||
|
|
|||
|
if (GetIsPC(oOwner))
|
|||
|
{
|
|||
|
sPlayer = ReplaceSingleChars(GetPCPlayerName(oOwner), "'", "~");
|
|||
|
sTag = ReplaceSingleChars(GetName(oOwner), "'", "~");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
sPlayer = "~";
|
|||
|
sTag = GetTag(oOwner);
|
|||
|
}
|
|||
|
sVarName = ReplaceSingleChars(sVarName, "'", "~");
|
|||
|
|
|||
|
string sSQL = "SELECT player FROM " + sTable + " WHERE player='" + sPlayer +
|
|||
|
"' AND tag='" + sTag + "' AND name='" + sVarName + "'";
|
|||
|
PRC_SQLExecDirect(sSQL);
|
|||
|
|
|||
|
if (PRC_SQLFetch() == PRC_SQL_SUCCESS)
|
|||
|
{
|
|||
|
// row exists
|
|||
|
sSQL = "UPDATE " + sTable + " SET val=%s,expire=" + IntToString(iExpiration) +
|
|||
|
" WHERE player='" + sPlayer + "' AND tag='" + sTag + "' AND name='" + sVarName + "'";
|
|||
|
SetLocalString(GetModule(), "NWNX!ODBC!SETSCORCOSQL", sSQL);
|
|||
|
StoreCampaignObject ("NWNX", "-", oObject);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// row doesn't exist
|
|||
|
sSQL = "INSERT INTO " + sTable + " (player,tag,name,val,expire) VALUES" +
|
|||
|
"('" + sPlayer + "','" + sTag + "','" + sVarName + "',%s," + IntToString(iExpiration) + ")";
|
|||
|
SetLocalString(GetModule(), "NWNX!ODBC!SETSCORCOSQL", sSQL);
|
|||
|
StoreCampaignObject ("NWNX", "-", oObject);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
object PRC_GetPersistentObject(object oObject, string sVarName, object oOwner = OBJECT_INVALID, string sTable = "pwobjdata")
|
|||
|
{
|
|||
|
string sPlayer;
|
|||
|
string sTag;
|
|||
|
object oModule;
|
|||
|
|
|||
|
if (GetIsPC(oObject))
|
|||
|
{
|
|||
|
sPlayer = ReplaceSingleChars(GetPCPlayerName(oObject), "'", "~");
|
|||
|
sTag = ReplaceSingleChars(GetName(oObject), "'", "~");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
sPlayer = "~";
|
|||
|
sTag = GetTag(oObject);
|
|||
|
}
|
|||
|
sVarName = ReplaceSingleChars(sVarName, "'", "~");
|
|||
|
|
|||
|
string sSQL = "SELECT val FROM " + sTable + " WHERE player='" + sPlayer +
|
|||
|
"' AND tag='" + sTag + "' AND name='" + sVarName + "'";
|
|||
|
SetLocalString(GetModule(), "NWNX!ODBC!SETSCORCOSQL", sSQL);
|
|||
|
|
|||
|
if (!GetIsObjectValid(oOwner))
|
|||
|
oOwner = oObject;
|
|||
|
return RetrieveCampaignObject ("NWNX", "-", GetLocation(oOwner), oOwner);
|
|||
|
}
|
|||
|
|
|||
|
//////////////////////////////////////////////////
|
|||
|
/* Functions for portable persistent chest */
|
|||
|
//////////////////////////////////////////////////
|
|||
|
|
|||
|
void DeleteXChestDB()
|
|||
|
{
|
|||
|
PRC_SQLExecDirect("DROP TABLE "+XCHST_DB);
|
|||
|
}
|
|||
|
|
|||
|
void CreateXChestDB_SQLite()
|
|||
|
{
|
|||
|
PRC_SQLExecDirect("CREATE TABLE "+XCHST_DB+" (" +
|
|||
|
"player varchar(64) NOT NULL default '~'," +
|
|||
|
"tag varchar(64) NOT NULL default '~'," +
|
|||
|
"name varchar(64) NOT NULL default '~'," +
|
|||
|
"val blob," +
|
|||
|
"expire int(11) default NULL," +
|
|||
|
"last timestamp NOT NULL default current_timestamp," +
|
|||
|
"PRIMARY KEY (player,tag,name)" +
|
|||
|
")");
|
|||
|
}
|
|||
|
|
|||
|
void CreateXChestDB_MySQL()
|
|||
|
{
|
|||
|
PRC_SQLExecDirect("CREATE TABLE "+XCHST_DB+" (" +
|
|||
|
"player varchar(64) NOT NULL default '~'," +
|
|||
|
"tag varchar(64) NOT NULL default '~'," +
|
|||
|
"name varchar(64) NOT NULL default '~'," +
|
|||
|
"val blob," +
|
|||
|
"expire int(11) default NULL," +
|
|||
|
"last timestamp NOT NULL default CURRENT_TIMESTAMP," +
|
|||
|
"PRIMARY KEY (player,tag,name)" +
|
|||
|
") ENGINE=MyISAM DEFAULT CHARSET=latin1;");
|
|||
|
}
|