Fixed amb1810 - Clientprefs no longer blocks load.

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%402418
This commit is contained in:
Matt Woodrow 2008-07-15 00:24:08 +00:00
parent 67e28d7475
commit e64e2534eb
11 changed files with 625 additions and 191 deletions

View File

@ -80,7 +80,17 @@ void CookieManager::Unload()
continue; continue;
} }
g_ClientPrefs.cookieMutex->Lock();
if (current->usedInQuery)
{
current->shouldDelete = true;
g_ClientPrefs.cookieMutex->Unlock();
}
else
{
g_ClientPrefs.cookieMutex->Unlock();
delete current; delete current;
}
_iter = cookieList.erase(_iter); _iter = cookieList.erase(_iter);
} }
@ -98,7 +108,6 @@ Cookie *CookieManager::FindCookie(const char *name)
return *pCookie; return *pCookie;
} }
Cookie *CookieManager::CreateCookie(const char *name, const char *description, CookieAccess access) Cookie *CookieManager::CreateCookie(const char *name, const char *description, CookieAccess access)
{ {
Cookie *pCookie = FindCookie(name); Cookie *pCookie = FindCookie(name);
@ -120,25 +129,16 @@ Cookie *CookieManager::CreateCookie(const char *name, const char *description, C
cookieTrie.insert(name, pCookie); cookieTrie.insert(name, pCookie);
cookieList.push_back(pCookie); cookieList.push_back(pCookie);
char quotedname[2 * MAX_NAME_LENGTH + 1];
char quoteddesc[2 * MAX_DESC_LENGTH + 1];
g_ClientPrefs.Database->QuoteString(pCookie->name, quotedname, sizeof(quotedname), NULL);
g_ClientPrefs.Database->QuoteString(pCookie->description, quoteddesc, sizeof(quoteddesc), NULL);
/* Attempt to insert cookie into the db and get its ID num */ /* Attempt to insert cookie into the db and get its ID num */
char query[300];
if (driver == DRIVER_SQLITE)
{
UTIL_Format(query, sizeof(query), "INSERT OR IGNORE INTO sm_cookies(name, description, access) VALUES('%s', '%s', %i)", quotedname, quoteddesc, access);
}
else
{
UTIL_Format(query, sizeof(query), "INSERT IGNORE INTO sm_cookies(name, description, access) VALUES('%s', '%s', %i)", quotedname, quoteddesc, access);
}
TQueryOp *op = new TQueryOp(g_ClientPrefs.Database, query, Query_InsertCookie, pCookie); TQueryOp *op = new TQueryOp(Query_InsertCookie, pCookie);
dbi->AddToThreadQueue(op, PrioQueue_Normal);
g_ClientPrefs.cookieMutex->Lock();
op->m_params.cookie = pCookie;
pCookie->usedInQuery++;
g_ClientPrefs.cookieMutex->Unlock();
g_ClientPrefs.AddQueryToQueue(op);
return pCookie; return pCookie;
} }
@ -198,11 +198,10 @@ void CookieManager::OnClientAuthorized(int client, const char *authstring)
{ {
connected[client] = true; connected[client] = true;
char query[300]; TQueryOp *op = new TQueryOp(Query_SelectData, client);
/* Assume that the authstring doesn't need to be quoted */ strcpy(op->m_params.steamId, authstring);
UTIL_Format(query, sizeof(query), "SELECT sm_cookies.name, sm_cookie_cache.value, sm_cookies.description, sm_cookies.access FROM sm_cookies JOIN sm_cookie_cache ON sm_cookies.id = sm_cookie_cache.cookie_id WHERE player = '%s'", authstring);
TQueryOp *op = new TQueryOp(g_ClientPrefs.Database, query, Query_SelectData, client); g_ClientPrefs.AddQueryToQueue(op);
dbi->AddToThreadQueue(op, PrioQueue_Normal);
} }
void CookieManager::OnClientDisconnecting(int client) void CookieManager::OnClientDisconnecting(int client)
@ -245,24 +244,18 @@ void CookieManager::OnClientDisconnecting(int client)
return; return;
} }
char quotedvalue[2 * MAX_VALUE_LENGTH + 1]; TQueryOp *op = new TQueryOp(Query_InsertData, client);
g_ClientPrefs.Database->QuoteString(current->value, quotedvalue, sizeof(quotedvalue), NULL);
char query[300]; strcpy(op->m_params.steamId, player->GetAuthString());
if (driver == DRIVER_SQLITE) op->m_params.cookieId = dbId;
{ op->m_params.data = current;
UTIL_Format(query, sizeof(query), "INSERT OR REPLACE INTO sm_cookie_cache(player,cookie_id, value, timestamp) VALUES('%s', %i, '%s', %i)", player->GetAuthString(), dbId, quotedvalue, time(NULL));
}
else
{
UTIL_Format(query, sizeof(query), "INSERT INTO sm_cookie_cache(player,cookie_id, value, timestamp) VALUES('%s', %i, '%s', %i) ON DUPLICATE KEY UPDATE value = '%s', timestamp = %i", player->GetAuthString(), dbId, quotedvalue, time(NULL), quotedvalue, time(NULL));
}
TQueryOp *op = new TQueryOp(g_ClientPrefs.Database, query, Query_InsertData, client); g_ClientPrefs.AddQueryToQueue(op);
dbi->AddToThreadQueue(op, PrioQueue_Normal);
current->parent->data[client] = NULL; current->parent->data[client] = NULL;
delete current;
/* We don't delete here, it will be removed when the query is completed */
_iter = clientData[client].erase(_iter); _iter = clientData[client].erase(_iter);
} }
@ -330,14 +323,10 @@ void CookieManager::InsertCookieCallback(Cookie *pCookie, int dbId)
return; return;
} }
char quotedname[2 * MAX_NAME_LENGTH + 1]; TQueryOp *op = new TQueryOp(Query_SelectId, pCookie);
g_ClientPrefs.Database->QuoteString(pCookie->name, quotedname, sizeof(quotedname), NULL); /* Put the cookie name into the steamId field to save space - Make sure we remember that it's there */
strcpy(op->m_params.steamId, pCookie->name);
char query[300]; g_ClientPrefs.AddQueryToQueue(op);
UTIL_Format(query, sizeof(query), "SELECT id FROM sm_cookies WHERE name='%s'", quotedname);
TQueryOp *op = new TQueryOp(g_ClientPrefs.Database, query, Query_SelectId, pCookie);
dbi->AddToThreadQueue(op, PrioQueue_Normal);
} }
void CookieManager::SelectIdCallback(Cookie *pCookie, IQuery *data) void CookieManager::SelectIdCallback(Cookie *pCookie, IQuery *data)

View File

@ -80,6 +80,9 @@ struct Cookie
{ {
data[i] = NULL; data[i] = NULL;
} }
shouldDelete = false;
usedInQuery = 0;
} }
~Cookie() ~Cookie()
@ -98,6 +101,10 @@ struct Cookie
int dbid; int dbid;
CookieData *data[MAXCLIENTS+1]; CookieData *data[MAXCLIENTS+1];
CookieAccess access; CookieAccess access;
/* Reference counting stuff */
bool shouldDelete;
int usedInQuery;
}; };
class CookieManager : public IClientListener, public IPluginsListener class CookieManager : public IClientListener, public IPluginsListener
@ -123,14 +130,13 @@ public:
bool AreClientCookiesCached(int client); bool AreClientCookiesCached(int client);
IForward *cookieDataLoadedForward;
SourceHook::List<Cookie *> cookieList;
IBaseMenu *clientMenu;
void OnPluginDestroyed(IPlugin *plugin); void OnPluginDestroyed(IPlugin *plugin);
public:
IForward *cookieDataLoadedForward;
SourceHook::List<Cookie *> cookieList;
IBaseMenu *clientMenu;
private: private:
KTrie<Cookie *> cookieTrie; KTrie<Cookie *> cookieTrie;
SourceHook::List<CookieData *> clientData[MAXCLIENTS]; SourceHook::List<CookieData *> clientData[MAXCLIENTS];

View File

@ -51,7 +51,10 @@ int driver = 0;
bool ClientPrefs::SDK_OnLoad(char *error, size_t maxlength, bool late) bool ClientPrefs::SDK_OnLoad(char *error, size_t maxlength, bool late)
{ {
const DatabaseInfo *DBInfo = dbi->FindDatabaseConf("clientprefs"); queryMutex = threader->MakeMutex();
cookieMutex = threader->MakeMutex();
DBInfo = dbi->FindDatabaseConf("clientprefs");
if (DBInfo == NULL) if (DBInfo == NULL)
{ {
@ -62,7 +65,6 @@ bool ClientPrefs::SDK_OnLoad(char *error, size_t maxlength, bool late)
snprintf(error, maxlength, "Could not find \"clientprefs\" or \"default\" database configs"); snprintf(error, maxlength, "Could not find \"clientprefs\" or \"default\" database configs");
return false; return false;
} }
} }
if (DBInfo->driver[0] != '\0') if (DBInfo->driver[0] != '\0')
@ -80,92 +82,13 @@ bool ClientPrefs::SDK_OnLoad(char *error, size_t maxlength, bool late)
return false; return false;
} }
Database = Driver->Connect(DBInfo, true, error, maxlength); Database = NULL;
databaseLoading = true;
if (Database == NULL) TQueryOp *op = new TQueryOp(Query_Connect, 0);
{ dbi->AddToThreadQueue(op, PrioQueue_High);
return false;
}
dbi->AddDependency(myself, Driver); dbi->AddDependency(myself, Driver);
const char *identifier = Driver->GetIdentifier();
if (strcmp(identifier, "sqlite") == 0)
{
driver = DRIVER_SQLITE;
TQueryOp *op = new TQueryOp(
Database,
"CREATE TABLE IF NOT EXISTS sm_cookies \
( \
id INTEGER PRIMARY KEY AUTOINCREMENT, \
name varchar(30) NOT NULL UNIQUE, \
description varchar(255), \
access INTEGER \
)",
Query_CreateTable,
0);
dbi->AddToThreadQueue(op, PrioQueue_Normal);
op = new TQueryOp(
Database,
"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
( \
player varchar(65) NOT NULL, \
cookie_id int(10) NOT NULL, \
value varchar(100), \
timestamp int, \
PRIMARY KEY (player, cookie_id) \
)",
Query_CreateTable,
0);
dbi->AddToThreadQueue(op, PrioQueue_Normal);
}
else if (strcmp(identifier, "mysql") == 0)
{
driver = DRIVER_MYSQL;
TQueryOp *op = new TQueryOp(
Database,
"CREATE TABLE IF NOT EXISTS sm_cookies \
( \
id INTEGER unsigned NOT NULL auto_increment, \
name varchar(30) NOT NULL UNIQUE, \
description varchar(255), \
access INTEGER, \
PRIMARY KEY (id) \
)",
Query_CreateTable,
0);
dbi->AddToThreadQueue(op, PrioQueue_Normal);
op = new TQueryOp(
Database,
"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
( \
player varchar(65) NOT NULL, \
cookie_id int(10) NOT NULL, \
value varchar(100), \
timestamp int NOT NULL, \
PRIMARY KEY (player, cookie_id) \
)",
Query_CreateTable,
0);
dbi->AddToThreadQueue(op, PrioQueue_Normal);
}
else
{
snprintf(error, maxlength, "Unsupported driver \"%s\"", identifier);
return false;
}
sharesys->AddNatives(myself, g_ClientPrefNatives); sharesys->AddNatives(myself, g_ClientPrefNatives);
sharesys->RegisterLibrary(myself, "clientprefs"); sharesys->RegisterLibrary(myself, "clientprefs");
g_CookieManager.cookieDataLoadedForward = forwards->CreateForward("OnClientCookiesCached", ET_Ignore, 1, NULL, Param_Cell); g_CookieManager.cookieDataLoadedForward = forwards->CreateForward("OnClientCookiesCached", ET_Ignore, 1, NULL, Param_Cell);
@ -218,6 +141,16 @@ void ClientPrefs::NotifyInterfaceDrop(SMInterface *pInterface)
{ {
if (Database != NULL && (void *)pInterface == (void *)(Database->GetDriver())) if (Database != NULL && (void *)pInterface == (void *)(Database->GetDriver()))
{ {
InsertCookieQuery->Destroy();
SelectDataQuery->Destroy();
SelectIdQuery->Destroy();
InsertDataQuery->Destroy();
InsertCookieQuery = NULL;
SelectDataQuery = NULL;
SelectIdQuery = NULL;
InsertDataQuery = NULL;
Database->Close(); Database->Close();
Database = NULL; Database = NULL;
} }
@ -243,6 +176,232 @@ void ClientPrefs::SDK_OnUnload()
plsys->RemovePluginsListener(&g_CookieManager); plsys->RemovePluginsListener(&g_CookieManager);
playerhelpers->RemoveClientListener(&g_CookieManager); playerhelpers->RemoveClientListener(&g_CookieManager);
/* Kill all our prepared queries - Queries are guaranteed to be flushed before this is called */
if (InsertCookieQuery != NULL)
{
InsertCookieQuery->Destroy();
}
if (SelectDataQuery != NULL)
{
SelectDataQuery->Destroy();
}
if (SelectIdQuery != NULL)
{
SelectIdQuery->Destroy();
}
if (InsertDataQuery != NULL)
{
InsertDataQuery->Destroy();
}
queryMutex->DestroyThis();
cookieMutex->DestroyThis();
}
void ClientPrefs::DatabaseConnect()
{
char error[256];
int errCode = 0;
Database = Driver->Connect(DBInfo, true, error, sizeof(error));
if (Database == NULL)
{
g_pSM->LogError(myself, error);
databaseLoading = false;
ProcessQueryCache();
return;
}
const char *identifier = Driver->GetIdentifier();
if (strcmp(identifier, "sqlite") == 0)
{
driver = DRIVER_SQLITE;
TQueryOp *op = new TQueryOp(Query_CreateTable, 0);
op->SetDatabase(Database);
op->SetCustomPreparedQuery
(Database->PrepareQuery(
"CREATE TABLE IF NOT EXISTS sm_cookies \
( \
id INTEGER PRIMARY KEY AUTOINCREMENT, \
name varchar(30) NOT NULL UNIQUE, \
description varchar(255), \
access INTEGER \
)",
error, sizeof(error), &errCode));
dbi->AddToThreadQueue(op, PrioQueue_High);
op = new TQueryOp(Query_CreateTable, 0);
op->SetDatabase(Database);
op->SetCustomPreparedQuery
(Database->PrepareQuery(
"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
( \
player varchar(65) NOT NULL, \
cookie_id int(10) NOT NULL, \
value varchar(100), \
timestamp int, \
PRIMARY KEY (player, cookie_id) \
)",
error, sizeof(error), &errCode));
dbi->AddToThreadQueue(op, PrioQueue_High);
}
else if (strcmp(identifier, "mysql") == 0)
{
driver = DRIVER_MYSQL;
TQueryOp *op = new TQueryOp(Query_CreateTable, 0);
op->SetDatabase(Database);
op->SetCustomPreparedQuery
(Database->PrepareQuery(
"CREATE TABLE IF NOT EXISTS sm_cookies \
( \
id INTEGER unsigned NOT NULL auto_increment, \
name varchar(30) NOT NULL UNIQUE, \
description varchar(255), \
access INTEGER, \
PRIMARY KEY (id) \
)",
error, sizeof(error), &errCode));
dbi->AddToThreadQueue(op, PrioQueue_High);
op = new TQueryOp(Query_CreateTable, 0);
op->SetDatabase(Database);
op->SetCustomPreparedQuery
(Database->PrepareQuery(
"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
( \
player varchar(65) NOT NULL, \
cookie_id int(10) NOT NULL, \
value varchar(100), \
timestamp int NOT NULL, \
PRIMARY KEY (player, cookie_id) \
)",
error, sizeof(error), &errCode));
dbi->AddToThreadQueue(op, PrioQueue_High);
}
else
{
g_pSM->LogError(myself, "Unsupported driver \"%s\"", identifier);
Database->Close();
Database = NULL;
databaseLoading = false;
ProcessQueryCache();
return;
}
if (driver == DRIVER_MYSQL)
{
InsertCookieQuery = Database->PrepareQuery(
"INSERT IGNORE INTO sm_cookies(name, description, access) \
VALUES(?, ?, ?)",
error, sizeof(error), &errCode);
InsertDataQuery = Database->PrepareQuery(
"INSERT INTO sm_cookie_cache(player, cookie_id, value, timestamp) \
VALUES(?, ?, ?, ?) \
ON DUPLICATE KEY UPDATE value = ?, timestamp = ?",
error, sizeof(error), &errCode);
}
else
{
InsertCookieQuery = Database->PrepareQuery(
"INSERT OR IGNORE INTO sm_cookies(name, description, access) \
VALUES(?, ?, ?)",
error, sizeof(error), &errCode);
InsertDataQuery = Database->PrepareQuery(
"INSERT OR REPLACE INTO sm_cookie_cache(player, cookie_id, value, timestamp) \
VALUES(?, ?, ?, ?)",
error, sizeof(error), &errCode);
}
SelectDataQuery = Database->PrepareQuery(
"SELECT sm_cookies.name, sm_cookie_cache.value, sm_cookies.description, sm_cookies.access \
FROM sm_cookies \
JOIN sm_cookie_cache \
ON sm_cookies.id = sm_cookie_cache.cookie_id \
WHERE player = ?",
error, sizeof(error), &errCode);
SelectIdQuery = Database->PrepareQuery(
"SELECT id \
FROM sm_cookies \
WHERE name=?",
error, sizeof(error), &errCode);
databaseLoading = false;
cell_t result = 0;
ProcessQueryCache();
return;
}
bool ClientPrefs::AddQueryToQueue( TQueryOp *query )
{
queryMutex->Lock();
if (Database == NULL && databaseLoading)
{
cachedQueries.push_back(query);
queryMutex->Unlock();
return true;
}
queryMutex->Unlock();
if (Database)
{
query->SetDatabase(Database);
query->SetPreparedQuery();
dbi->AddToThreadQueue(query, PrioQueue_Normal);
return true;
}
/* If Database is NULL and we're not in the loading phase it must have failed - Can't do much */
return false;
}
void ClientPrefs::ProcessQueryCache()
{
SourceHook::List<TQueryOp *>::iterator iter;
queryMutex->Lock();
iter = cachedQueries.begin();
while (iter != cachedQueries.end())
{
TQueryOp *op = (TQueryOp *)*iter;
if (Database != NULL)
{
op->SetDatabase(Database);
op->SetPreparedQuery();
dbi->AddToThreadQueue(op, PrioQueue_Normal);
}
else
{
delete op;
}
iter++;
}
cachedQueries.clear();
queryMutex->Unlock();
} }
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...) size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)

View File

@ -47,6 +47,8 @@
#define MAX_TRANSLATE_PARAMS 32 #define MAX_TRANSLATE_PARAMS 32
class TQueryOp;
/** /**
* @brief Sample implementation of the SDK Extension. * @brief Sample implementation of the SDK Extension.
* Note: Uncomment one of the pre-defined virtual functions in order to use it. * Note: Uncomment one of the pre-defined virtual functions in order to use it.
@ -79,6 +81,11 @@ public:
virtual void NotifyInterfaceDrop(SMInterface *pInterface); virtual void NotifyInterfaceDrop(SMInterface *pInterface);
void DatabaseConnect();
bool AddQueryToQueue(TQueryOp *query);
void ProcessQueryCache();
/** /**
* @brief Called when the pause state is changed. * @brief Called when the pause state is changed.
*/ */
@ -128,7 +135,20 @@ public:
public: public:
IDBDriver *Driver; IDBDriver *Driver;
IDatabase *Database; IDatabase *Database;
bool databaseLoading;
IPhraseCollection *phrases; IPhraseCollection *phrases;
const DatabaseInfo *DBInfo;
IPreparedQuery *InsertCookieQuery;
IPreparedQuery *SelectDataQuery;
IPreparedQuery *InsertDataQuery;
IPreparedQuery *SelectIdQuery;
IMutex *cookieMutex;
private:
SourceHook::List<TQueryOp *> cachedQueries;
IMutex *queryMutex;
}; };
class CookieTypeHandler : public IHandleTypeDispatch class CookieTypeHandler : public IHandleTypeDispatch
@ -136,7 +156,7 @@ class CookieTypeHandler : public IHandleTypeDispatch
public: public:
void OnHandleDestroy(HandleType_t type, void *object) void OnHandleDestroy(HandleType_t type, void *object)
{ {
/* No delete needed since Cookies are persistant */ /* No delete needed since Cookies are persistent */
} }
}; };

View File

@ -35,6 +35,11 @@
cell_t RegClientPrefCookie(IPluginContext *pContext, const cell_t *params) cell_t RegClientPrefCookie(IPluginContext *pContext, const cell_t *params)
{ {
if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
{
return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
}
char *name; char *name;
pContext->LocalToString(params[1], &name); pContext->LocalToString(params[1], &name);
@ -62,6 +67,11 @@ cell_t RegClientPrefCookie(IPluginContext *pContext, const cell_t *params)
cell_t FindClientPrefCookie(IPluginContext *pContext, const cell_t *params) cell_t FindClientPrefCookie(IPluginContext *pContext, const cell_t *params)
{ {
if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
{
return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
}
char *name; char *name;
pContext->LocalToString(params[1], &name); pContext->LocalToString(params[1], &name);
@ -81,6 +91,11 @@ cell_t FindClientPrefCookie(IPluginContext *pContext, const cell_t *params)
cell_t SetClientPrefCookie(IPluginContext *pContext, const cell_t *params) cell_t SetClientPrefCookie(IPluginContext *pContext, const cell_t *params)
{ {
if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
{
return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
}
int client = params[1]; int client = params[1];
if ((client < 1) || (client > playerhelpers->GetMaxClients())) if ((client < 1) || (client > playerhelpers->GetMaxClients()))
@ -111,6 +126,11 @@ cell_t SetClientPrefCookie(IPluginContext *pContext, const cell_t *params)
cell_t GetClientPrefCookie(IPluginContext *pContext, const cell_t *params) cell_t GetClientPrefCookie(IPluginContext *pContext, const cell_t *params)
{ {
if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
{
return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
}
int client = params[1]; int client = params[1];
if ((client < 1) || (client > playerhelpers->GetMaxClients())) if ((client < 1) || (client > playerhelpers->GetMaxClients()))
@ -144,6 +164,11 @@ cell_t GetClientPrefCookie(IPluginContext *pContext, const cell_t *params)
cell_t AreClientCookiesCached(IPluginContext *pContext, const cell_t *params) cell_t AreClientCookiesCached(IPluginContext *pContext, const cell_t *params)
{ {
if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
{
return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
}
int client = params[1]; int client = params[1];
if ((client < 1) || (client > playerhelpers->GetMaxClients())) if ((client < 1) || (client > playerhelpers->GetMaxClients()))
@ -156,6 +181,11 @@ cell_t AreClientCookiesCached(IPluginContext *pContext, const cell_t *params)
cell_t GetCookieAccess(IPluginContext *pContext, const cell_t *params) cell_t GetCookieAccess(IPluginContext *pContext, const cell_t *params)
{ {
if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
{
return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
}
Handle_t hndl = static_cast<Handle_t>(params[1]); Handle_t hndl = static_cast<Handle_t>(params[1]);
HandleError err; HandleError err;
HandleSecurity sec; HandleSecurity sec;
@ -176,6 +206,11 @@ cell_t GetCookieAccess(IPluginContext *pContext, const cell_t *params)
static cell_t GetCookieIterator(IPluginContext *pContext, const cell_t *params) static cell_t GetCookieIterator(IPluginContext *pContext, const cell_t *params)
{ {
if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
{
return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
}
SourceHook::List<Cookie *>::iterator *iter = new SourceHook::List<Cookie *>::iterator; SourceHook::List<Cookie *>::iterator *iter = new SourceHook::List<Cookie *>::iterator;
*iter = g_CookieManager.cookieList.begin(); *iter = g_CookieManager.cookieList.begin();
@ -190,6 +225,11 @@ static cell_t GetCookieIterator(IPluginContext *pContext, const cell_t *params)
static cell_t ReadCookieIterator(IPluginContext *pContext, const cell_t *params) static cell_t ReadCookieIterator(IPluginContext *pContext, const cell_t *params)
{ {
if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
{
return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
}
SourceHook::List<Cookie *>::iterator *iter; SourceHook::List<Cookie *>::iterator *iter;
Handle_t hndl = static_cast<Handle_t>(params[1]); Handle_t hndl = static_cast<Handle_t>(params[1]);
@ -226,6 +266,11 @@ static cell_t ReadCookieIterator(IPluginContext *pContext, const cell_t *params)
cell_t ShowSettingsMenu(IPluginContext *pContext, const cell_t *params) cell_t ShowSettingsMenu(IPluginContext *pContext, const cell_t *params)
{ {
if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
{
return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
}
char message[256]; char message[256];
Translate(message, sizeof(message), "%T:", 2, NULL, "Client Settings", &params[1]); Translate(message, sizeof(message), "%T:", 2, NULL, "Client Settings", &params[1]);
@ -237,6 +282,11 @@ cell_t ShowSettingsMenu(IPluginContext *pContext, const cell_t *params)
cell_t AddSettingsMenuItem(IPluginContext *pContext, const cell_t *params) cell_t AddSettingsMenuItem(IPluginContext *pContext, const cell_t *params)
{ {
if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
{
return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
}
char *display; char *display;
pContext->LocalToString(params[3], &display); pContext->LocalToString(params[3], &display);
@ -278,6 +328,11 @@ cell_t AddSettingsMenuItem(IPluginContext *pContext, const cell_t *params)
cell_t AddSettingsPrefabMenuItem(IPluginContext *pContext, const cell_t *params) cell_t AddSettingsPrefabMenuItem(IPluginContext *pContext, const cell_t *params)
{ {
if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
{
return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
}
Handle_t hndl = static_cast<Handle_t>(params[1]); Handle_t hndl = static_cast<Handle_t>(params[1]);
HandleError err; HandleError err;
HandleSecurity sec; HandleSecurity sec;

View File

@ -36,52 +36,83 @@ void TQueryOp::RunThinkPart()
{ {
//handler for threaded sql queries //handler for threaded sql queries
if (m_type == Query_Connect)
{
return;
}
if (m_pQuery) if (m_pQuery)
{ {
switch (m_type) switch (m_type)
{ {
case Query_InsertCookie: case Query_InsertCookie:
g_CookieManager.InsertCookieCallback(pCookie, m_insertId); {
break; g_CookieManager.InsertCookieCallback(m_pCookie, m_insertId);
case Query_SelectData:
g_CookieManager.ClientConnectCallback(m_client, m_pQuery);
break;
case Query_InsertData:
//No specific handling
break;
case Query_SelectId:
g_CookieManager.SelectIdCallback(pCookie, m_pQuery);
break;
default:
break; break;
} }
m_pQuery->Destroy(); case Query_SelectData:
}
else
{ {
g_pSM->LogError(myself,"Failed SQL Query, Error: \"%s\" (Query id %i - client %i)", error, m_type, m_client); g_CookieManager.ClientConnectCallback(m_client, m_pQuery);
break;
}
case Query_SelectId:
{
g_CookieManager.SelectIdCallback(m_pCookie, m_pQuery);
break;
}
case Query_CreateTable:
{
m_pQuery->Destroy();
delete m_pQuery;
m_pQuery = NULL;
break;
}
default:
{
break;
}
}
} }
} }
void TQueryOp::RunThreadPart() void TQueryOp::RunThreadPart()
{ {
m_pDatabase->LockForFullAtomicOperation(); if (m_type == Query_Connect)
m_pQuery = m_pDatabase->DoQuery(m_Query.c_str());
if (!m_pQuery)
{ {
g_pSM->LogError(myself, "Failed SQL Query, Error: \"%s\" (Query id %i - client %i)", m_pDatabase->GetError(), m_type, m_client); g_ClientPrefs.DatabaseConnect();
}
else
{
if (m_database == NULL)
{
return;
} }
m_insertId = g_ClientPrefs.Database->GetInsertID(); m_database->LockForFullAtomicOperation();
m_pDatabase->UnlockFromFullAtomicOperation(); if (!BindParamsAndRun())
{
g_pSM->LogError(myself, "Failed SQL Query, Error: \"%s\" (Query id %i - client %i)", m_database->GetError(), m_type, m_client);
}
m_insertId = m_database->GetInsertID();
m_database->UnlockFromFullAtomicOperation();
}
} }
IDBDriver *TQueryOp::GetDriver() IDBDriver *TQueryOp::GetDriver()
{ {
return m_pDatabase->GetDriver(); if (m_database == NULL)
{
return NULL;
}
return m_database->GetDriver();
} }
IdentityToken_t *TQueryOp::GetOwner() IdentityToken_t *TQueryOp::GetOwner()
{ {
@ -92,24 +123,157 @@ void TQueryOp::Destroy()
delete this; delete this;
} }
TQueryOp::TQueryOp(IDatabase *db, const char *query, enum querytype type, int client) TQueryOp::TQueryOp(enum querytype type, int client)
{ {
m_pDatabase = db;
m_Query = query;
m_type = type; m_type = type;
m_client = client; m_client = client;
m_pQuery = NULL; m_pQuery = NULL;
m_database = NULL;
m_pDatabase->IncReferenceCount();
} }
TQueryOp::TQueryOp(IDatabase *db, const char *query, enum querytype type, Cookie *cookie) TQueryOp::TQueryOp(enum querytype type, Cookie *cookie)
{ {
m_pDatabase = db;
m_Query = query;
m_type = type; m_type = type;
pCookie = cookie; m_pCookie = cookie;
m_pQuery = NULL; m_pQuery = NULL;
m_database = NULL;
m_pDatabase->IncReferenceCount(); }
void TQueryOp::SetDatabase( IDatabase *db )
{
m_database = db;
m_database->IncReferenceCount();
}
bool TQueryOp::BindParamsAndRun()
{
if (m_pQuery == NULL)
{
return false;
}
switch (m_type)
{
case Query_InsertCookie:
{
m_pQuery->BindParamString(0, m_params.cookie->name, false);
m_pQuery->BindParamString(1, m_params.cookie->description, false);
m_pQuery->BindParamInt(2, m_params.cookie->access);
break;
}
case Query_SelectData:
{
m_pQuery->BindParamString(0, m_params.steamId, false);
break;
}
case Query_InsertData:
{
m_pQuery->BindParamString(0, m_params.steamId, false);
m_pQuery->BindParamInt(1, m_params.cookieId);
m_pQuery->BindParamString(2, m_params.data->value, false);
m_pQuery->BindParamInt(3, (unsigned int)time(NULL), false);
if (driver == DRIVER_MYSQL)
{
m_pQuery->BindParamString(4, m_params.data->value, false);
m_pQuery->BindParamInt(5, (unsigned int)time(NULL), false);
}
break;
}
case Query_SelectId:
{
/* the steamId var was actually used to store the name of the cookie - Save duplicating vars */
m_pQuery->BindParamString(0, m_params.steamId, false);
break;
}
default:
{
break;
}
}
return m_pQuery->Execute();
}
void TQueryOp::SetPreparedQuery()
{
switch (m_type)
{
case Query_InsertCookie:
{
m_pQuery = g_ClientPrefs.InsertCookieQuery;
break;
}
case Query_SelectData:
{
m_pQuery = g_ClientPrefs.SelectDataQuery;
break;
}
case Query_InsertData:
{
m_pQuery = g_ClientPrefs.InsertDataQuery;
break;
}
case Query_SelectId:
{
m_pQuery = g_ClientPrefs.SelectIdQuery;
break;
}
default:
{
break;
}
}
}
void TQueryOp::SetCustomPreparedQuery(IPreparedQuery *query)
{
m_pQuery = query;
}
ParamData::~ParamData()
{
if (cookie)
{
g_ClientPrefs.cookieMutex->Lock();
cookie->usedInQuery--;
if (cookie->shouldDelete && cookie->usedInQuery <= 0)
{
g_ClientPrefs.cookieMutex->Unlock();
delete cookie;
cookie = NULL;
}
g_ClientPrefs.cookieMutex->Unlock();
}
if (data)
{
/* Data is only ever passed in a client disconnect query and always needs to be deleted */
delete data;
data = NULL;
}
}
ParamData::ParamData()
{
cookie = NULL;
data = NULL;
steamId[0] = '\0';
cookieId = 0;
} }

View File

@ -43,36 +43,67 @@ enum querytype
Query_InsertData, Query_InsertData,
Query_SelectId, Query_SelectId,
Query_CreateTable, Query_CreateTable,
Query_Connect,
};
struct PreparedQueryWrapper;
struct Cookie;
struct CookieData;
#define MAX_NAME_LENGTH 30
/* This stores all the info required for our param binding until the thread is executed */
struct ParamData
{
ParamData();
~ParamData();
/* Contains a name, description and access for InsertCookie queries */
Cookie *cookie;
/* A clients steamid - Used for most queries - Doubles as storage for the cookie name*/
char steamId[MAX_NAME_LENGTH];
int cookieId;
CookieData *data;
}; };
class TQueryOp : public IDBThreadOperation class TQueryOp : public IDBThreadOperation
{ {
public: public:
TQueryOp(IDatabase *db, const char *query, enum querytype type, int client); TQueryOp(enum querytype type, int client);
TQueryOp(IDatabase *db, const char *query, enum querytype type, Cookie *cookie); TQueryOp(enum querytype type, Cookie *cookie);
~TQueryOp() {} ~TQueryOp() {}
IDBDriver *GetDriver(); IDBDriver *GetDriver();
IdentityToken_t *GetOwner(); IdentityToken_t *GetOwner();
void SetDatabase(IDatabase *db);
void SetPreparedQuery();
void SetCustomPreparedQuery(IPreparedQuery *wrapper);
void Destroy(); void Destroy();
void RunThreadPart(); void RunThreadPart();
/* Thread has been cancelled due to driver unloading. Nothing else to do? */ /* Thread has been cancelled due to driver unloading. Nothing else to do? */
void CancelThinkPart() void CancelThinkPart() {}
{
}
void RunThinkPart(); void RunThinkPart();
bool BindParamsAndRun();
/* Params to be bound */
ParamData m_params;
private: private:
IDatabase *m_pDatabase; IPreparedQuery *m_pQuery;
IQuery *m_pQuery; IDatabase *m_database;
SourceHook::String m_Query;
char error[255]; /* Query type */
enum querytype m_type; enum querytype m_type;
/* Data to be passed to the callback */
int m_client; int m_client;
int m_insertId; int m_insertId;
Cookie *pCookie; Cookie *m_pCookie;
}; };

View File

@ -69,7 +69,7 @@
//#define SMEXT_ENABLE_MEMUTILS //#define SMEXT_ENABLE_MEMUTILS
#define SMEXT_ENABLE_GAMEHELPERS #define SMEXT_ENABLE_GAMEHELPERS
//#define SMEXT_ENABLE_TIMERSYS //#define SMEXT_ENABLE_TIMERSYS
//#define SMEXT_ENABLE_THREADER #define SMEXT_ENABLE_THREADER
//#define SMEXT_ENABLE_LIBSYS //#define SMEXT_ENABLE_LIBSYS
#define SMEXT_ENABLE_MENUS #define SMEXT_ENABLE_MENUS
//#define SMEXT_ENABLE_ADTFACTORY //#define SMEXT_ENABLE_ADTFACTORY

View File

@ -58,7 +58,7 @@ public Action:Command_Cookie(client, args)
{ {
if (args == 0) if (args == 0)
{ {
ReplyToCommand(client, "[SM] Usage: sm_cookie <name> [value]"); ReplyToCommand(client, "[SM] Usage: sm_cookies <name> [value]");
ReplyToCommand(client, "[SM] %t", "Printing Cookie List"); ReplyToCommand(client, "[SM] %t", "Printing Cookie List");
/* Show list of cookies */ /* Show list of cookies */

View File

@ -74,6 +74,14 @@ enum CookieMenuAction
CookieMenuAction_SelectOption = 1, CookieMenuAction_SelectOption = 1,
}; };
/**
* Note:
*
* A successful return value/result on any client prefs native only guarantees that the local cache has been updated.
* Database connection problems can still prevent the data from being permanently saved. Connection problems will be logged as
* errors by the clientprefs extension.
*/
/** /**
* Creates a new Client preference cookie. * Creates a new Client preference cookie.
* *

View File

@ -44,6 +44,8 @@ public CookieSelected(client, CookieMenuAction:action, any:info, String:buffer[]
public bool:OnClientConnect(client, String:rejectmsg[], maxlen) public bool:OnClientConnect(client, String:rejectmsg[], maxlen)
{ {
LogMessage("Connect Cookie state: %s", AreClientCookiesCached(client) ? "YES" : "NO"); LogMessage("Connect Cookie state: %s", AreClientCookiesCached(client) ? "YES" : "NO");
return true;
} }
public OnClientCookiesCached(client) public OnClientCookiesCached(client)