Fixed amb1810 - Clientprefs no longer blocks load.
authorMatt Woodrow <pred@alliedmods.net>
Tue Jul 15 00:24:08 2008 +0000 (2008-07-15)
changeset 22073a97a56ded62
parent 2206 6b946d8524d8
child 2208 babfbc636787
Fixed amb1810 - Clientprefs no longer blocks load.
extensions/clientprefs/cookie.cpp
extensions/clientprefs/cookie.h
extensions/clientprefs/extension.cpp
extensions/clientprefs/extension.h
extensions/clientprefs/natives.cpp
extensions/clientprefs/query.cpp
extensions/clientprefs/query.h
extensions/clientprefs/sdk/smsdk_config.h
plugins/clientprefs.sp
plugins/include/clientprefs.inc
plugins/testsuite/clientprefstest.sp
     1.1 --- a/extensions/clientprefs/cookie.cpp	Sun Jul 13 07:01:54 2008 +0000
     1.2 +++ b/extensions/clientprefs/cookie.cpp	Tue Jul 15 00:24:08 2008 +0000
     1.3 @@ -80,7 +80,17 @@
     1.4  			continue;
     1.5  		}
     1.6  
     1.7 -		delete current;
     1.8 +		g_ClientPrefs.cookieMutex->Lock();
     1.9 +		if (current->usedInQuery)
    1.10 +		{
    1.11 +			current->shouldDelete = true;
    1.12 +			g_ClientPrefs.cookieMutex->Unlock();
    1.13 +		}
    1.14 +		else
    1.15 +		{
    1.16 +			g_ClientPrefs.cookieMutex->Unlock();
    1.17 +			delete current;
    1.18 +		}
    1.19  
    1.20  		_iter = cookieList.erase(_iter);
    1.21  	}
    1.22 @@ -98,7 +108,6 @@
    1.23  	return *pCookie;
    1.24  }
    1.25  
    1.26 -
    1.27  Cookie *CookieManager::CreateCookie(const char *name, const char *description, CookieAccess access)
    1.28  {
    1.29  	Cookie *pCookie = FindCookie(name);
    1.30 @@ -120,25 +129,16 @@
    1.31  	cookieTrie.insert(name, pCookie);
    1.32  	cookieList.push_back(pCookie);
    1.33  
    1.34 -	char quotedname[2 * MAX_NAME_LENGTH + 1];
    1.35 -	char quoteddesc[2 * MAX_DESC_LENGTH + 1];
    1.36 +	/* Attempt to insert cookie into the db and get its ID num */
    1.37 +
    1.38 +	TQueryOp *op = new TQueryOp(Query_InsertCookie, pCookie);
    1.39  
    1.40 -	g_ClientPrefs.Database->QuoteString(pCookie->name, quotedname, sizeof(quotedname), NULL);
    1.41 -	g_ClientPrefs.Database->QuoteString(pCookie->description, quoteddesc, sizeof(quoteddesc), NULL);
    1.42 -	
    1.43 -	/* Attempt to insert cookie into the db and get its ID num */
    1.44 -	char query[300];
    1.45 -	if (driver == DRIVER_SQLITE)
    1.46 -	{
    1.47 -		UTIL_Format(query, sizeof(query), "INSERT OR IGNORE INTO sm_cookies(name, description, access) VALUES('%s', '%s', %i)", quotedname, quoteddesc, access);
    1.48 -	}
    1.49 -	else
    1.50 -	{
    1.51 -		UTIL_Format(query, sizeof(query), "INSERT IGNORE INTO sm_cookies(name, description, access) VALUES('%s', '%s', %i)", quotedname, quoteddesc, access);
    1.52 -	}
    1.53 +	g_ClientPrefs.cookieMutex->Lock();
    1.54 +	op->m_params.cookie = pCookie;
    1.55 +	pCookie->usedInQuery++;
    1.56 +	g_ClientPrefs.cookieMutex->Unlock();
    1.57  
    1.58 -	TQueryOp *op = new TQueryOp(g_ClientPrefs.Database, query, Query_InsertCookie, pCookie);
    1.59 -	dbi->AddToThreadQueue(op, PrioQueue_Normal);
    1.60 +	g_ClientPrefs.AddQueryToQueue(op);
    1.61  
    1.62  	return pCookie;
    1.63  }
    1.64 @@ -198,11 +198,10 @@
    1.65  {
    1.66  	connected[client] = true;
    1.67  
    1.68 -	char query[300];
    1.69 -	/* Assume that the authstring doesn't need to be quoted */
    1.70 -	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);
    1.71 -	TQueryOp *op = new TQueryOp(g_ClientPrefs.Database, query, Query_SelectData, client);
    1.72 -	dbi->AddToThreadQueue(op, PrioQueue_Normal);
    1.73 +	TQueryOp *op = new TQueryOp(Query_SelectData, client);
    1.74 +	strcpy(op->m_params.steamId, authstring);
    1.75 +
    1.76 +	g_ClientPrefs.AddQueryToQueue(op);
    1.77  }
    1.78  
    1.79  void CookieManager::OnClientDisconnecting(int client)
    1.80 @@ -245,24 +244,18 @@
    1.81  			return;
    1.82  		}
    1.83  
    1.84 -		char quotedvalue[2 * MAX_VALUE_LENGTH + 1];
    1.85 -		g_ClientPrefs.Database->QuoteString(current->value, quotedvalue, sizeof(quotedvalue), NULL);
    1.86 +		TQueryOp *op = new TQueryOp(Query_InsertData, client);
    1.87  
    1.88 -		char query[300];
    1.89 -		if (driver == DRIVER_SQLITE)
    1.90 -		{
    1.91 -			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));
    1.92 -		}
    1.93 -		else
    1.94 -		{
    1.95 -			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));
    1.96 -		}
    1.97 -	
    1.98 -		TQueryOp *op = new TQueryOp(g_ClientPrefs.Database, query, Query_InsertData, client);
    1.99 -		dbi->AddToThreadQueue(op, PrioQueue_Normal);
   1.100 +		strcpy(op->m_params.steamId, player->GetAuthString());
   1.101 +		op->m_params.cookieId = dbId;
   1.102 +		op->m_params.data = current;
   1.103 +
   1.104 +		g_ClientPrefs.AddQueryToQueue(op);
   1.105  
   1.106  		current->parent->data[client] = NULL;
   1.107 -		delete current;
   1.108 +		
   1.109 +
   1.110 +		/* We don't delete here, it will be removed when the query is completed */
   1.111  
   1.112  		_iter = clientData[client].erase(_iter);
   1.113  	}
   1.114 @@ -330,14 +323,10 @@
   1.115  		return;
   1.116  	}
   1.117  
   1.118 -	char quotedname[2 * MAX_NAME_LENGTH + 1];
   1.119 -	g_ClientPrefs.Database->QuoteString(pCookie->name, quotedname, sizeof(quotedname), NULL);
   1.120 -
   1.121 -	char query[300];
   1.122 -	UTIL_Format(query, sizeof(query), "SELECT id FROM sm_cookies WHERE name='%s'", quotedname);
   1.123 -
   1.124 -	TQueryOp *op = new TQueryOp(g_ClientPrefs.Database, query, Query_SelectId, pCookie);
   1.125 -	dbi->AddToThreadQueue(op, PrioQueue_Normal);
   1.126 +	TQueryOp *op = new TQueryOp(Query_SelectId, pCookie);
   1.127 +	/* Put the cookie name into the steamId field to save space - Make sure we remember that it's there */
   1.128 +	strcpy(op->m_params.steamId, pCookie->name);
   1.129 +	g_ClientPrefs.AddQueryToQueue(op);
   1.130  }
   1.131  
   1.132  void CookieManager::SelectIdCallback(Cookie *pCookie, IQuery *data)
     2.1 --- a/extensions/clientprefs/cookie.h	Sun Jul 13 07:01:54 2008 +0000
     2.2 +++ b/extensions/clientprefs/cookie.h	Tue Jul 15 00:24:08 2008 +0000
     2.3 @@ -80,6 +80,9 @@
     2.4  		{
     2.5  			data[i] = NULL;
     2.6  		}
     2.7 +
     2.8 +		shouldDelete = false;
     2.9 +		usedInQuery = 0;
    2.10  	}
    2.11  
    2.12  	~Cookie()
    2.13 @@ -98,6 +101,10 @@
    2.14  	int dbid;
    2.15  	CookieData *data[MAXCLIENTS+1];
    2.16  	CookieAccess access;
    2.17 +
    2.18 +	/* Reference counting stuff */
    2.19 +	bool shouldDelete;
    2.20 +	int usedInQuery;
    2.21  };
    2.22  
    2.23  class CookieManager : public IClientListener, public IPluginsListener
    2.24 @@ -123,14 +130,13 @@
    2.25  
    2.26  	bool AreClientCookiesCached(int client);
    2.27  
    2.28 -	IForward *cookieDataLoadedForward;
    2.29 -
    2.30 -	SourceHook::List<Cookie *> cookieList;
    2.31 +	void OnPluginDestroyed(IPlugin *plugin);
    2.32  
    2.33 +public:
    2.34 +	IForward *cookieDataLoadedForward;
    2.35 +	SourceHook::List<Cookie *> cookieList;
    2.36  	IBaseMenu *clientMenu;
    2.37  
    2.38 -	void OnPluginDestroyed(IPlugin *plugin);
    2.39 -
    2.40  private:
    2.41  	KTrie<Cookie *> cookieTrie;
    2.42  	SourceHook::List<CookieData *> clientData[MAXCLIENTS];
     3.1 --- a/extensions/clientprefs/extension.cpp	Sun Jul 13 07:01:54 2008 +0000
     3.2 +++ b/extensions/clientprefs/extension.cpp	Tue Jul 15 00:24:08 2008 +0000
     3.3 @@ -50,8 +50,11 @@
     3.4  int driver = 0;
     3.5  
     3.6  bool ClientPrefs::SDK_OnLoad(char *error, size_t maxlength, bool late)
     3.7 -{	
     3.8 -	const DatabaseInfo *DBInfo = dbi->FindDatabaseConf("clientprefs");
     3.9 +{
    3.10 +	queryMutex = threader->MakeMutex();
    3.11 +	cookieMutex = threader->MakeMutex();
    3.12 +
    3.13 +	DBInfo = dbi->FindDatabaseConf("clientprefs");
    3.14  
    3.15  	if (DBInfo == NULL)
    3.16  	{
    3.17 @@ -62,7 +65,6 @@
    3.18  			snprintf(error, maxlength, "Could not find \"clientprefs\" or \"default\" database configs");
    3.19  			return false;
    3.20  		}
    3.21 -
    3.22  	}
    3.23  
    3.24  	if (DBInfo->driver[0] != '\0')
    3.25 @@ -80,92 +82,13 @@
    3.26  		return false;
    3.27  	}
    3.28  
    3.29 -	Database = Driver->Connect(DBInfo, true, error, maxlength);
    3.30 -
    3.31 -	if (Database == NULL)
    3.32 -	{
    3.33 -		return false;
    3.34 -	}
    3.35 +	Database = NULL;
    3.36 +	databaseLoading = true;
    3.37 +	TQueryOp *op = new TQueryOp(Query_Connect, 0);
    3.38 +	dbi->AddToThreadQueue(op, PrioQueue_High);
    3.39  
    3.40  	dbi->AddDependency(myself, Driver);
    3.41  
    3.42 -	const char *identifier = Driver->GetIdentifier();
    3.43 -
    3.44 -	if (strcmp(identifier, "sqlite") == 0)
    3.45 -	{
    3.46 -		driver = DRIVER_SQLITE;
    3.47 -
    3.48 -		TQueryOp *op = new TQueryOp(
    3.49 -						Database, 
    3.50 -						"CREATE TABLE IF NOT EXISTS sm_cookies  \
    3.51 -						( \
    3.52 -							id INTEGER PRIMARY KEY AUTOINCREMENT, \
    3.53 -							name varchar(30) NOT NULL UNIQUE, \
    3.54 -							description varchar(255), \
    3.55 -							access INTEGER \
    3.56 -						)", 
    3.57 -						Query_CreateTable, 
    3.58 -						0);
    3.59 -
    3.60 -		dbi->AddToThreadQueue(op, PrioQueue_Normal);
    3.61 -
    3.62 -		op = new TQueryOp(
    3.63 -						Database, 
    3.64 -						"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
    3.65 -						( \
    3.66 -							player varchar(65) NOT NULL, \
    3.67 -							cookie_id int(10) NOT NULL, \
    3.68 -							value varchar(100), \
    3.69 -							timestamp int, \
    3.70 -							PRIMARY KEY (player, cookie_id) \
    3.71 -						)", 
    3.72 -						Query_CreateTable, 
    3.73 -						0);
    3.74 -
    3.75 -		dbi->AddToThreadQueue(op, PrioQueue_Normal);
    3.76 -	}
    3.77 -	else if (strcmp(identifier, "mysql") == 0)
    3.78 -	{
    3.79 -		driver = DRIVER_MYSQL;
    3.80 -
    3.81 -		TQueryOp *op = new TQueryOp(
    3.82 -						Database, 
    3.83 -						"CREATE TABLE IF NOT EXISTS sm_cookies \
    3.84 -						( \
    3.85 -							id INTEGER unsigned NOT NULL auto_increment, \
    3.86 -							name varchar(30) NOT NULL UNIQUE, \
    3.87 -							description varchar(255), \
    3.88 -							access INTEGER, \
    3.89 -							PRIMARY KEY (id) \
    3.90 -						)", 
    3.91 -						Query_CreateTable, 
    3.92 -						0);
    3.93 -
    3.94 -		dbi->AddToThreadQueue(op, PrioQueue_Normal);
    3.95 -
    3.96 -		op = new TQueryOp(
    3.97 -						Database, 
    3.98 -						"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
    3.99 -						( \
   3.100 -							player varchar(65) NOT NULL, \
   3.101 -							cookie_id int(10) NOT NULL, \
   3.102 -							value varchar(100), \
   3.103 -							timestamp int NOT NULL, \
   3.104 -							PRIMARY KEY (player, cookie_id) \
   3.105 -						)", 
   3.106 -						Query_CreateTable, 
   3.107 -						0);
   3.108 -
   3.109 -		dbi->AddToThreadQueue(op, PrioQueue_Normal);
   3.110 -	}
   3.111 -	else
   3.112 -	{
   3.113 -		snprintf(error, maxlength, "Unsupported driver \"%s\"", identifier);
   3.114 -		return false;
   3.115 -	}
   3.116 -
   3.117 -
   3.118 -
   3.119  	sharesys->AddNatives(myself, g_ClientPrefNatives);
   3.120  	sharesys->RegisterLibrary(myself, "clientprefs");
   3.121  	g_CookieManager.cookieDataLoadedForward = forwards->CreateForward("OnClientCookiesCached", ET_Ignore, 1, NULL, Param_Cell);
   3.122 @@ -218,6 +141,16 @@
   3.123  {
   3.124  	if (Database != NULL && (void *)pInterface == (void *)(Database->GetDriver()))
   3.125  	{
   3.126 +		InsertCookieQuery->Destroy();
   3.127 +		SelectDataQuery->Destroy();
   3.128 +		SelectIdQuery->Destroy();
   3.129 +		InsertDataQuery->Destroy();
   3.130 +
   3.131 +		InsertCookieQuery = NULL;
   3.132 +		SelectDataQuery = NULL;
   3.133 +		SelectIdQuery = NULL;
   3.134 +		InsertDataQuery = NULL;
   3.135 +
   3.136  		Database->Close();
   3.137  		Database = NULL;
   3.138  	}
   3.139 @@ -243,6 +176,232 @@
   3.140  
   3.141  	plsys->RemovePluginsListener(&g_CookieManager);
   3.142  	playerhelpers->RemoveClientListener(&g_CookieManager);
   3.143 +
   3.144 +	/* Kill all our prepared queries - Queries are guaranteed to be flushed before this is called */
   3.145 +
   3.146 +	if (InsertCookieQuery != NULL)
   3.147 +	{
   3.148 +		InsertCookieQuery->Destroy();
   3.149 +	}
   3.150 +
   3.151 +	if (SelectDataQuery != NULL)
   3.152 +	{
   3.153 +		SelectDataQuery->Destroy();
   3.154 +	}
   3.155 +
   3.156 +	if (SelectIdQuery != NULL)
   3.157 +	{
   3.158 +		SelectIdQuery->Destroy();
   3.159 +	}
   3.160 +
   3.161 +	if (InsertDataQuery != NULL)
   3.162 +	{
   3.163 +		InsertDataQuery->Destroy();
   3.164 +	}
   3.165 +
   3.166 +	queryMutex->DestroyThis();
   3.167 +	cookieMutex->DestroyThis();
   3.168 +}
   3.169 +
   3.170 +void ClientPrefs::DatabaseConnect()
   3.171 +{
   3.172 +	char error[256];
   3.173 +	int errCode = 0;
   3.174 +
   3.175 +	Database = Driver->Connect(DBInfo, true, error, sizeof(error));
   3.176 +
   3.177 +	if (Database == NULL)
   3.178 +	{
   3.179 +		g_pSM->LogError(myself, error);
   3.180 +		databaseLoading = false;
   3.181 +		ProcessQueryCache();
   3.182 +		return;
   3.183 +	}
   3.184 +
   3.185 +	const char *identifier = Driver->GetIdentifier();
   3.186 +
   3.187 +	if (strcmp(identifier, "sqlite") == 0)
   3.188 +	{
   3.189 +		driver = DRIVER_SQLITE;
   3.190 +
   3.191 +		TQueryOp *op = new TQueryOp(Query_CreateTable, 0);
   3.192 +
   3.193 +		op->SetDatabase(Database);
   3.194 +		op->SetCustomPreparedQuery
   3.195 +				(Database->PrepareQuery(
   3.196 +				"CREATE TABLE IF NOT EXISTS sm_cookies  \
   3.197 +				( \
   3.198 +					id INTEGER PRIMARY KEY AUTOINCREMENT, \
   3.199 +					name varchar(30) NOT NULL UNIQUE, \
   3.200 +					description varchar(255), \
   3.201 +					access INTEGER \
   3.202 +				)",
   3.203 +				error, sizeof(error), &errCode));
   3.204 +
   3.205 +		dbi->AddToThreadQueue(op, PrioQueue_High);
   3.206 +
   3.207 +		op = new TQueryOp(Query_CreateTable, 0);
   3.208 +		op->SetDatabase(Database);
   3.209 +		op->SetCustomPreparedQuery
   3.210 +				(Database->PrepareQuery(
   3.211 +				"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
   3.212 +				( \
   3.213 +					player varchar(65) NOT NULL, \
   3.214 +					cookie_id int(10) NOT NULL, \
   3.215 +					value varchar(100), \
   3.216 +					timestamp int, \
   3.217 +					PRIMARY KEY (player, cookie_id) \
   3.218 +				)",
   3.219 +				error, sizeof(error), &errCode));
   3.220 +
   3.221 +		dbi->AddToThreadQueue(op, PrioQueue_High);
   3.222 +	}
   3.223 +	else if (strcmp(identifier, "mysql") == 0)
   3.224 +	{
   3.225 +		driver = DRIVER_MYSQL;
   3.226 +
   3.227 +		TQueryOp *op = new TQueryOp(Query_CreateTable, 0);
   3.228 +		op->SetDatabase(Database);
   3.229 +		op->SetCustomPreparedQuery
   3.230 +				(Database->PrepareQuery(
   3.231 +				"CREATE TABLE IF NOT EXISTS sm_cookies \
   3.232 +				( \
   3.233 +					id INTEGER unsigned NOT NULL auto_increment, \
   3.234 +					name varchar(30) NOT NULL UNIQUE, \
   3.235 +					description varchar(255), \
   3.236 +					access INTEGER, \
   3.237 +					PRIMARY KEY (id) \
   3.238 +				)",
   3.239 +				error, sizeof(error), &errCode));
   3.240 +
   3.241 +		dbi->AddToThreadQueue(op, PrioQueue_High);
   3.242 +
   3.243 +		op = new TQueryOp(Query_CreateTable, 0);
   3.244 +		op->SetDatabase(Database);
   3.245 +		op->SetCustomPreparedQuery
   3.246 +				(Database->PrepareQuery(
   3.247 +				"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
   3.248 +				( \
   3.249 +					player varchar(65) NOT NULL, \
   3.250 +					cookie_id int(10) NOT NULL, \
   3.251 +					value varchar(100), \
   3.252 +					timestamp int NOT NULL, \
   3.253 +					PRIMARY KEY (player, cookie_id) \
   3.254 +				)",
   3.255 +				error, sizeof(error), &errCode));
   3.256 +
   3.257 +		dbi->AddToThreadQueue(op, PrioQueue_High);
   3.258 +	}
   3.259 +	else
   3.260 +	{
   3.261 +		g_pSM->LogError(myself, "Unsupported driver \"%s\"", identifier);
   3.262 +		Database->Close();
   3.263 +		Database = NULL;
   3.264 +		databaseLoading = false;
   3.265 +		ProcessQueryCache();
   3.266 +		return;
   3.267 +	}
   3.268 +
   3.269 +	if (driver == DRIVER_MYSQL)
   3.270 +	{
   3.271 +		InsertCookieQuery = Database->PrepareQuery(
   3.272 +				"INSERT IGNORE INTO sm_cookies(name, description, access) \
   3.273 +				VALUES(?, ?, ?)",
   3.274 +				error, sizeof(error), &errCode);
   3.275 +		InsertDataQuery = Database->PrepareQuery(
   3.276 +				"INSERT INTO sm_cookie_cache(player, cookie_id, value, timestamp) \
   3.277 +				VALUES(?, ?, ?, ?) \
   3.278 +				ON DUPLICATE KEY UPDATE value = ?, timestamp = ?",
   3.279 +				error, sizeof(error), &errCode);
   3.280 +	}
   3.281 +	else
   3.282 +	{
   3.283 +		InsertCookieQuery = Database->PrepareQuery(
   3.284 +				"INSERT OR IGNORE INTO sm_cookies(name, description, access) \
   3.285 +				VALUES(?, ?, ?)", 
   3.286 +				error, sizeof(error), &errCode);
   3.287 +		InsertDataQuery = Database->PrepareQuery(
   3.288 +				"INSERT OR REPLACE INTO sm_cookie_cache(player, cookie_id, value, timestamp) \
   3.289 +				VALUES(?, ?, ?, ?)", 
   3.290 +				error, sizeof(error), &errCode);
   3.291 +	}
   3.292 +
   3.293 +	SelectDataQuery = Database->PrepareQuery(
   3.294 +			"SELECT sm_cookies.name, sm_cookie_cache.value, sm_cookies.description, sm_cookies.access \
   3.295 +			FROM sm_cookies \
   3.296 +			JOIN sm_cookie_cache \
   3.297 +			ON sm_cookies.id = sm_cookie_cache.cookie_id \
   3.298 +			WHERE player = ?",
   3.299 +			error, sizeof(error), &errCode);
   3.300 +
   3.301 +	SelectIdQuery = Database->PrepareQuery(
   3.302 +			"SELECT id \
   3.303 +			FROM sm_cookies \
   3.304 +			WHERE name=?",
   3.305 +			error, sizeof(error), &errCode);
   3.306 +
   3.307 +	databaseLoading = false;
   3.308 +	cell_t result = 0;
   3.309 +
   3.310 +	ProcessQueryCache();
   3.311 +
   3.312 +	return;
   3.313 +}
   3.314 +
   3.315 +bool ClientPrefs::AddQueryToQueue( TQueryOp *query )
   3.316 +{
   3.317 +	queryMutex->Lock();
   3.318 +
   3.319 +	if (Database == NULL && databaseLoading)
   3.320 +	{
   3.321 +		cachedQueries.push_back(query);
   3.322 +		queryMutex->Unlock();
   3.323 +		return true;
   3.324 +	}
   3.325 +
   3.326 +	queryMutex->Unlock();
   3.327 +
   3.328 +	if (Database)
   3.329 +	{
   3.330 +		query->SetDatabase(Database);
   3.331 +		query->SetPreparedQuery();
   3.332 +		dbi->AddToThreadQueue(query, PrioQueue_Normal);
   3.333 +		return true;
   3.334 +	}
   3.335 +
   3.336 +	/* If Database is NULL and we're not in the loading phase it must have failed - Can't do much */
   3.337 +	return false;
   3.338 +}
   3.339 +
   3.340 +void ClientPrefs::ProcessQueryCache()
   3.341 +{
   3.342 +	SourceHook::List<TQueryOp *>::iterator iter;
   3.343 +
   3.344 +	queryMutex->Lock();
   3.345 +
   3.346 +	iter = cachedQueries.begin();
   3.347 +	
   3.348 +	while (iter != cachedQueries.end())
   3.349 +	{
   3.350 +		TQueryOp *op = (TQueryOp *)*iter;
   3.351 +
   3.352 +		if (Database != NULL)
   3.353 +		{
   3.354 +			op->SetDatabase(Database);
   3.355 +			op->SetPreparedQuery();
   3.356 +			dbi->AddToThreadQueue(op, PrioQueue_Normal);
   3.357 +		}
   3.358 +		else
   3.359 +		{
   3.360 +			delete op;
   3.361 +		}
   3.362 +
   3.363 +		iter++;
   3.364 +	}
   3.365 +
   3.366 +	cachedQueries.clear();
   3.367 +
   3.368 +	queryMutex->Unlock();
   3.369  }
   3.370  
   3.371  size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
     4.1 --- a/extensions/clientprefs/extension.h	Sun Jul 13 07:01:54 2008 +0000
     4.2 +++ b/extensions/clientprefs/extension.h	Tue Jul 15 00:24:08 2008 +0000
     4.3 @@ -47,6 +47,8 @@
     4.4  
     4.5  #define MAX_TRANSLATE_PARAMS		32
     4.6  
     4.7 +class TQueryOp;
     4.8 +
     4.9  /**
    4.10   * @brief Sample implementation of the SDK Extension.
    4.11   * Note: Uncomment one of the pre-defined virtual functions in order to use it.
    4.12 @@ -79,6 +81,11 @@
    4.13  
    4.14  	virtual void NotifyInterfaceDrop(SMInterface *pInterface);
    4.15  
    4.16 +	void DatabaseConnect();
    4.17 +
    4.18 +	bool AddQueryToQueue(TQueryOp *query);
    4.19 +	void ProcessQueryCache();
    4.20 +
    4.21  	/**
    4.22  	 * @brief Called when the pause state is changed.
    4.23  	 */
    4.24 @@ -128,7 +135,20 @@
    4.25  public:
    4.26  	IDBDriver *Driver;
    4.27  	IDatabase *Database;
    4.28 +	bool databaseLoading;
    4.29  	IPhraseCollection *phrases;
    4.30 +	const DatabaseInfo *DBInfo;
    4.31 +
    4.32 +	IPreparedQuery *InsertCookieQuery;
    4.33 +	IPreparedQuery *SelectDataQuery;
    4.34 +	IPreparedQuery *InsertDataQuery;
    4.35 +	IPreparedQuery *SelectIdQuery;
    4.36 +
    4.37 +	IMutex *cookieMutex;
    4.38 +
    4.39 +private:
    4.40 +	SourceHook::List<TQueryOp *> cachedQueries;
    4.41 +	IMutex *queryMutex;
    4.42  };
    4.43  
    4.44  class CookieTypeHandler : public IHandleTypeDispatch
    4.45 @@ -136,7 +156,7 @@
    4.46  public:
    4.47  	void OnHandleDestroy(HandleType_t type, void *object)
    4.48  	{
    4.49 -		/* No delete needed since Cookies are persistant */
    4.50 +		/* No delete needed since Cookies are persistent */
    4.51  	}
    4.52  };
    4.53  
     5.1 --- a/extensions/clientprefs/natives.cpp	Sun Jul 13 07:01:54 2008 +0000
     5.2 +++ b/extensions/clientprefs/natives.cpp	Tue Jul 15 00:24:08 2008 +0000
     5.3 @@ -35,6 +35,11 @@
     5.4  
     5.5  cell_t RegClientPrefCookie(IPluginContext *pContext, const cell_t *params)
     5.6  {
     5.7 +	if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
     5.8 +	{
     5.9 +		return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
    5.10 +	}
    5.11 +
    5.12  	char *name;
    5.13  	pContext->LocalToString(params[1], &name);
    5.14  
    5.15 @@ -62,6 +67,11 @@
    5.16  
    5.17  cell_t FindClientPrefCookie(IPluginContext *pContext, const cell_t *params)
    5.18  {
    5.19 +	if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
    5.20 +	{
    5.21 +		return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
    5.22 +	}
    5.23 +
    5.24  	char *name;
    5.25  	pContext->LocalToString(params[1], &name);
    5.26  
    5.27 @@ -81,6 +91,11 @@
    5.28  
    5.29  cell_t SetClientPrefCookie(IPluginContext *pContext, const cell_t *params)
    5.30  {
    5.31 +	if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
    5.32 +	{
    5.33 +		return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
    5.34 +	}
    5.35 +
    5.36  	int client = params[1];
    5.37  
    5.38  	if ((client < 1) || (client > playerhelpers->GetMaxClients()))
    5.39 @@ -111,6 +126,11 @@
    5.40  
    5.41  cell_t GetClientPrefCookie(IPluginContext *pContext, const cell_t *params)
    5.42  {
    5.43 +	if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
    5.44 +	{
    5.45 +		return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
    5.46 +	}
    5.47 +
    5.48  	int client = params[1];
    5.49  
    5.50  	if ((client < 1) || (client > playerhelpers->GetMaxClients()))
    5.51 @@ -144,6 +164,11 @@
    5.52  
    5.53  cell_t AreClientCookiesCached(IPluginContext *pContext, const cell_t *params)
    5.54  {
    5.55 +	if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
    5.56 +	{
    5.57 +		return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
    5.58 +	}
    5.59 +
    5.60  	int client = params[1];
    5.61  
    5.62  	if ((client < 1) || (client > playerhelpers->GetMaxClients()))
    5.63 @@ -156,6 +181,11 @@
    5.64  
    5.65  cell_t GetCookieAccess(IPluginContext *pContext, const cell_t *params)
    5.66  {
    5.67 +	if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
    5.68 +	{
    5.69 +		return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
    5.70 +	}
    5.71 +
    5.72  	Handle_t hndl = static_cast<Handle_t>(params[1]);
    5.73  	HandleError err;
    5.74  	HandleSecurity sec;
    5.75 @@ -176,6 +206,11 @@
    5.76  
    5.77  static cell_t GetCookieIterator(IPluginContext *pContext, const cell_t *params)
    5.78  {
    5.79 +	if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
    5.80 +	{
    5.81 +		return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
    5.82 +	}
    5.83 +
    5.84  	SourceHook::List<Cookie *>::iterator *iter = new SourceHook::List<Cookie *>::iterator;
    5.85  	*iter = g_CookieManager.cookieList.begin();
    5.86  
    5.87 @@ -190,6 +225,11 @@
    5.88  
    5.89  static cell_t ReadCookieIterator(IPluginContext *pContext, const cell_t *params)
    5.90  {
    5.91 +	if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
    5.92 +	{
    5.93 +		return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
    5.94 +	}
    5.95 +
    5.96  	SourceHook::List<Cookie *>::iterator *iter;
    5.97  
    5.98  	Handle_t hndl = static_cast<Handle_t>(params[1]);
    5.99 @@ -226,6 +266,11 @@
   5.100  
   5.101  cell_t ShowSettingsMenu(IPluginContext *pContext, const cell_t *params)
   5.102  {
   5.103 +	if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
   5.104 +	{
   5.105 +		return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
   5.106 +	}
   5.107 +
   5.108  	char message[256];
   5.109  	Translate(message, sizeof(message), "%T:", 2, NULL, "Client Settings", &params[1]);
   5.110  
   5.111 @@ -237,6 +282,11 @@
   5.112  
   5.113  cell_t AddSettingsMenuItem(IPluginContext *pContext, const cell_t *params)
   5.114  {
   5.115 +	if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
   5.116 +	{
   5.117 +		return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
   5.118 +	}
   5.119 +
   5.120  	char *display;
   5.121  	pContext->LocalToString(params[3], &display);
   5.122  
   5.123 @@ -278,6 +328,11 @@
   5.124  
   5.125  cell_t AddSettingsPrefabMenuItem(IPluginContext *pContext, const cell_t *params)
   5.126  {
   5.127 +	if (g_ClientPrefs.Database == NULL && !g_ClientPrefs.databaseLoading)
   5.128 +	{
   5.129 +		return pContext->ThrowNativeError("Clientprefs is disabled due to a failed database connection");
   5.130 +	}
   5.131 +
   5.132  	Handle_t hndl = static_cast<Handle_t>(params[1]);
   5.133  	HandleError err;
   5.134  	HandleSecurity sec;
     6.1 --- a/extensions/clientprefs/query.cpp	Sun Jul 13 07:01:54 2008 +0000
     6.2 +++ b/extensions/clientprefs/query.cpp	Tue Jul 15 00:24:08 2008 +0000
     6.3 @@ -36,52 +36,83 @@
     6.4  {
     6.5  	//handler for threaded sql queries
     6.6  
     6.7 +	if (m_type == Query_Connect)
     6.8 +	{
     6.9 +		return;
    6.10 +	}
    6.11 +
    6.12  	if (m_pQuery)
    6.13  	{
    6.14  		switch (m_type)
    6.15  		{
    6.16 -		case Query_InsertCookie:
    6.17 -			g_CookieManager.InsertCookieCallback(pCookie, m_insertId);
    6.18 -			break;
    6.19 -		case Query_SelectData:
    6.20 -			g_CookieManager.ClientConnectCallback(m_client, m_pQuery);
    6.21 -			break;
    6.22 -		case Query_InsertData:
    6.23 -			//No specific handling
    6.24 -			break;
    6.25 -		case Query_SelectId:
    6.26 -			g_CookieManager.SelectIdCallback(pCookie, m_pQuery);
    6.27 -			break;
    6.28 -		default:
    6.29 -			break;
    6.30 +			case Query_InsertCookie:
    6.31 +			{
    6.32 +				g_CookieManager.InsertCookieCallback(m_pCookie, m_insertId);
    6.33 +				break;
    6.34 +			}
    6.35 +
    6.36 +			case Query_SelectData:
    6.37 +			{
    6.38 +				g_CookieManager.ClientConnectCallback(m_client, m_pQuery);
    6.39 +				break;
    6.40 +			}
    6.41 +
    6.42 +			case Query_SelectId:
    6.43 +			{
    6.44 +				g_CookieManager.SelectIdCallback(m_pCookie, m_pQuery);
    6.45 +				break;
    6.46 +			}
    6.47 +
    6.48 +			case Query_CreateTable:
    6.49 +			{
    6.50 +				m_pQuery->Destroy();
    6.51 +				delete m_pQuery;
    6.52 +				m_pQuery = NULL;
    6.53 +				break;
    6.54 +			}
    6.55 +
    6.56 +			default:
    6.57 +			{
    6.58 +				break;
    6.59 +			}
    6.60  		}
    6.61 -
    6.62 -		m_pQuery->Destroy();
    6.63 -	}
    6.64 -	else
    6.65 -	{
    6.66 -		g_pSM->LogError(myself,"Failed SQL Query, Error: \"%s\" (Query id %i - client %i)", error, m_type, m_client);
    6.67  	}
    6.68  }
    6.69  
    6.70  void TQueryOp::RunThreadPart()
    6.71  {
    6.72 -	m_pDatabase->LockForFullAtomicOperation();
    6.73 -	m_pQuery = m_pDatabase->DoQuery(m_Query.c_str());
    6.74 -
    6.75 -	if (!m_pQuery)
    6.76 +	if (m_type == Query_Connect)
    6.77 +	{
    6.78 +		g_ClientPrefs.DatabaseConnect();
    6.79 +	}
    6.80 +	else
    6.81  	{
    6.82 -		g_pSM->LogError(myself, "Failed SQL Query, Error: \"%s\" (Query id %i - client %i)", m_pDatabase->GetError(), m_type, m_client);
    6.83 -	}
    6.84 +		if (m_database == NULL)
    6.85 +		{
    6.86 +			return;
    6.87 +		}
    6.88 +
    6.89 +		m_database->LockForFullAtomicOperation();
    6.90  
    6.91 -	m_insertId = g_ClientPrefs.Database->GetInsertID();
    6.92 +		if (!BindParamsAndRun())
    6.93 +		{
    6.94 +			g_pSM->LogError(myself, "Failed SQL Query, Error: \"%s\" (Query id %i - client %i)", m_database->GetError(), m_type, m_client);
    6.95 +		}
    6.96  
    6.97 -	m_pDatabase->UnlockFromFullAtomicOperation();
    6.98 +		m_insertId = m_database->GetInsertID();
    6.99 +
   6.100 +		m_database->UnlockFromFullAtomicOperation();
   6.101 +	}
   6.102  }
   6.103  
   6.104  IDBDriver *TQueryOp::GetDriver()
   6.105  {
   6.106 -	return m_pDatabase->GetDriver();
   6.107 +	if (m_database == NULL)
   6.108 +	{
   6.109 +		return NULL;
   6.110 +	}
   6.111 +
   6.112 +	return m_database->GetDriver();
   6.113  }
   6.114  IdentityToken_t *TQueryOp::GetOwner()
   6.115  {
   6.116 @@ -92,24 +123,157 @@
   6.117  	delete this;
   6.118  }
   6.119  
   6.120 -TQueryOp::TQueryOp(IDatabase *db, const char *query, enum querytype type, int client)
   6.121 +TQueryOp::TQueryOp(enum querytype type, int client)
   6.122  {
   6.123 -	m_pDatabase = db;
   6.124 -	m_Query = query;
   6.125  	m_type = type;
   6.126  	m_client = client;
   6.127  	m_pQuery = NULL;
   6.128 +	m_database = NULL;
   6.129 +}
   6.130  
   6.131 -	m_pDatabase->IncReferenceCount();
   6.132 +TQueryOp::TQueryOp(enum querytype type, Cookie *cookie)
   6.133 +{
   6.134 +	m_type = type;
   6.135 +	m_pCookie = cookie;
   6.136 +	m_pQuery = NULL;
   6.137 +	m_database = NULL;
   6.138 +}
   6.139 +
   6.140 +void TQueryOp::SetDatabase( IDatabase *db )
   6.141 +{
   6.142 +	m_database = db;
   6.143 +	m_database->IncReferenceCount();
   6.144  }
   6.145  
   6.146 -TQueryOp::TQueryOp(IDatabase *db, const char *query, enum querytype type, Cookie *cookie)
   6.147 +bool TQueryOp::BindParamsAndRun()
   6.148 +{
   6.149 +	if (m_pQuery == NULL)
   6.150 +	{
   6.151 +		return false;
   6.152 +	}
   6.153 +
   6.154 +	switch (m_type)
   6.155 +	{
   6.156 +		case Query_InsertCookie:
   6.157 +		{
   6.158 +			m_pQuery->BindParamString(0, m_params.cookie->name, false);
   6.159 +			m_pQuery->BindParamString(1, m_params.cookie->description, false);
   6.160 +			m_pQuery->BindParamInt(2, m_params.cookie->access);
   6.161 +
   6.162 +			break;
   6.163 +		}
   6.164 +
   6.165 +		case Query_SelectData:
   6.166 +		{
   6.167 +			m_pQuery->BindParamString(0, m_params.steamId, false);
   6.168 +
   6.169 +			break;
   6.170 +		}
   6.171 +
   6.172 +		case Query_InsertData:
   6.173 +		{
   6.174 +			m_pQuery->BindParamString(0, m_params.steamId, false);
   6.175 +			m_pQuery->BindParamInt(1, m_params.cookieId);
   6.176 +			m_pQuery->BindParamString(2, m_params.data->value, false);
   6.177 +			m_pQuery->BindParamInt(3, (unsigned int)time(NULL), false);
   6.178 +
   6.179 +			if (driver == DRIVER_MYSQL)
   6.180 +			{
   6.181 +				m_pQuery->BindParamString(4, m_params.data->value, false);
   6.182 +				m_pQuery->BindParamInt(5, (unsigned int)time(NULL), false);
   6.183 +			}
   6.184 +
   6.185 +			break;
   6.186 +		}
   6.187 +
   6.188 +		case Query_SelectId:
   6.189 +		{
   6.190 +			/* the steamId var was actually used to store the name of the cookie - Save duplicating vars */
   6.191 +			m_pQuery->BindParamString(0, m_params.steamId, false);
   6.192 +
   6.193 +			break;
   6.194 +		}
   6.195 +
   6.196 +		default:
   6.197 +		{
   6.198 +			break;
   6.199 +		}
   6.200 +			
   6.201 +	}
   6.202 +
   6.203 +	return m_pQuery->Execute();
   6.204 +}
   6.205 +
   6.206 +void TQueryOp::SetPreparedQuery()
   6.207  {
   6.208 -	m_pDatabase = db;
   6.209 -	m_Query = query;
   6.210 -	m_type = type;
   6.211 -	pCookie = cookie;
   6.212 -	m_pQuery = NULL;
   6.213 +	switch (m_type)
   6.214 +	{
   6.215 +		case Query_InsertCookie:
   6.216 +		{
   6.217 +			m_pQuery = g_ClientPrefs.InsertCookieQuery;
   6.218 +			break;
   6.219 +		}
   6.220 +
   6.221 +		case Query_SelectData:
   6.222 +		{
   6.223 +			m_pQuery = g_ClientPrefs.SelectDataQuery;
   6.224 +			break;
   6.225 +		}
   6.226 +
   6.227 +		case Query_InsertData:
   6.228 +		{
   6.229 +			m_pQuery = g_ClientPrefs.InsertDataQuery;
   6.230 +			break;
   6.231 +		}
   6.232 +
   6.233 +		case Query_SelectId:
   6.234 +		{
   6.235 +			m_pQuery = g_ClientPrefs.SelectIdQuery;
   6.236 +			break;
   6.237 +		}
   6.238 +
   6.239 +		default:
   6.240 +		{
   6.241 +			break;
   6.242 +		}
   6.243 +
   6.244 +	}
   6.245 +}
   6.246  
   6.247 -	m_pDatabase->IncReferenceCount();
   6.248 +void TQueryOp::SetCustomPreparedQuery(IPreparedQuery *query)
   6.249 +{
   6.250 +	m_pQuery = query;
   6.251  }
   6.252 +
   6.253 +ParamData::~ParamData()
   6.254 +{
   6.255 +	if (cookie)
   6.256 +	{
   6.257 +		g_ClientPrefs.cookieMutex->Lock();
   6.258 +		cookie->usedInQuery--;
   6.259 +
   6.260 +		if (cookie->shouldDelete && cookie->usedInQuery <= 0)
   6.261 +		{
   6.262 +			g_ClientPrefs.cookieMutex->Unlock();
   6.263 +			delete cookie;
   6.264 +			cookie = NULL;
   6.265 +		}
   6.266 +
   6.267 +		g_ClientPrefs.cookieMutex->Unlock();	
   6.268 +	}
   6.269 +
   6.270 +	if (data)
   6.271 +	{
   6.272 +		/* Data is only ever passed in a client disconnect query and always needs to be deleted */
   6.273 +		delete data;
   6.274 +		data = NULL;
   6.275 +	}
   6.276 +}
   6.277 +
   6.278 +ParamData::ParamData()
   6.279 +{
   6.280 +	cookie = NULL;
   6.281 +	data = NULL;
   6.282 +	steamId[0] = '\0';
   6.283 +	cookieId = 0;
   6.284 +}
   6.285 \ No newline at end of file
     7.1 --- a/extensions/clientprefs/query.h	Sun Jul 13 07:01:54 2008 +0000
     7.2 +++ b/extensions/clientprefs/query.h	Tue Jul 15 00:24:08 2008 +0000
     7.3 @@ -43,36 +43,67 @@
     7.4  	Query_InsertData,
     7.5  	Query_SelectId,
     7.6  	Query_CreateTable,
     7.7 +	Query_Connect,
     7.8 +};
     7.9 +
    7.10 +struct PreparedQueryWrapper;
    7.11 +struct Cookie;
    7.12 +struct CookieData;
    7.13 +#define MAX_NAME_LENGTH 30
    7.14 +
    7.15 +/* This stores all the info required for our param binding until the thread is executed */
    7.16 +struct ParamData
    7.17 +{
    7.18 +	ParamData();
    7.19 +
    7.20 +	~ParamData();
    7.21 +
    7.22 +	/* Contains a name, description and access for InsertCookie queries */
    7.23 +	Cookie *cookie;
    7.24 +	/* A clients steamid - Used for most queries - Doubles as storage for the cookie name*/
    7.25 +	char steamId[MAX_NAME_LENGTH];
    7.26 +
    7.27 +	int cookieId;
    7.28 +	CookieData *data;
    7.29  };
    7.30  
    7.31  class TQueryOp : public IDBThreadOperation
    7.32  {
    7.33  public:
    7.34 -	TQueryOp(IDatabase *db, const char *query, enum querytype type, int client);
    7.35 -	TQueryOp(IDatabase *db, const char *query, enum querytype type, Cookie *cookie);
    7.36 +	TQueryOp(enum querytype type, int client);
    7.37 +	TQueryOp(enum querytype type, Cookie *cookie);
    7.38  	~TQueryOp() {}
    7.39  
    7.40  	IDBDriver *GetDriver();
    7.41  	IdentityToken_t *GetOwner();
    7.42  
    7.43 +	void SetDatabase(IDatabase *db);
    7.44 +	void SetPreparedQuery();
    7.45 +	void SetCustomPreparedQuery(IPreparedQuery *wrapper);
    7.46 +
    7.47  	void Destroy();
    7.48  
    7.49  	void RunThreadPart();
    7.50  	/* Thread has been cancelled due to driver unloading. Nothing else to do? */
    7.51 -	void CancelThinkPart()
    7.52 -	{
    7.53 -	}
    7.54 +	void CancelThinkPart()	{}
    7.55  	void RunThinkPart();
    7.56  
    7.57 +	bool BindParamsAndRun();
    7.58 +
    7.59 +	/* Params to be bound */
    7.60 +	ParamData m_params;
    7.61 +
    7.62  private:
    7.63 -	IDatabase *m_pDatabase;
    7.64 -	IQuery *m_pQuery;
    7.65 -	SourceHook::String m_Query;
    7.66 -	char error[255];
    7.67 +	IPreparedQuery *m_pQuery;
    7.68 +	IDatabase *m_database;
    7.69 +
    7.70 +	/* Query type */
    7.71  	enum querytype m_type;
    7.72 +
    7.73 +	/* Data to be passed to the callback */
    7.74  	int m_client;
    7.75  	int m_insertId;
    7.76 -	Cookie *pCookie;
    7.77 +	Cookie *m_pCookie;
    7.78  };
    7.79  
    7.80  
     8.1 --- a/extensions/clientprefs/sdk/smsdk_config.h	Sun Jul 13 07:01:54 2008 +0000
     8.2 +++ b/extensions/clientprefs/sdk/smsdk_config.h	Tue Jul 15 00:24:08 2008 +0000
     8.3 @@ -69,7 +69,7 @@
     8.4  //#define SMEXT_ENABLE_MEMUTILS
     8.5  #define SMEXT_ENABLE_GAMEHELPERS
     8.6  //#define SMEXT_ENABLE_TIMERSYS
     8.7 -//#define SMEXT_ENABLE_THREADER
     8.8 +#define SMEXT_ENABLE_THREADER
     8.9  //#define SMEXT_ENABLE_LIBSYS
    8.10  #define SMEXT_ENABLE_MENUS
    8.11  //#define SMEXT_ENABLE_ADTFACTORY
     9.1 --- a/plugins/clientprefs.sp	Sun Jul 13 07:01:54 2008 +0000
     9.2 +++ b/plugins/clientprefs.sp	Tue Jul 15 00:24:08 2008 +0000
     9.3 @@ -58,7 +58,7 @@
     9.4  {
     9.5  	if (args == 0)
     9.6  	{
     9.7 -		ReplyToCommand(client, "[SM] Usage: sm_cookie <name> [value]");
     9.8 +		ReplyToCommand(client, "[SM] Usage: sm_cookies <name> [value]");
     9.9  		ReplyToCommand(client, "[SM] %t", "Printing Cookie List");
    9.10  		
    9.11  		/* Show list of cookies */
    10.1 --- a/plugins/include/clientprefs.inc	Sun Jul 13 07:01:54 2008 +0000
    10.2 +++ b/plugins/include/clientprefs.inc	Tue Jul 15 00:24:08 2008 +0000
    10.3 @@ -75,6 +75,14 @@
    10.4  };
    10.5  
    10.6  /**
    10.7 + * Note:
    10.8 + * 
    10.9 + * A successful return value/result on any client prefs native only guarantees that the local cache has been updated.
   10.10 + * Database connection problems can still prevent the data from being permanently saved. Connection problems will be logged as
   10.11 + * errors by the clientprefs extension.
   10.12 + */
   10.13 +
   10.14 +/**
   10.15   * Creates a new Client preference cookie.
   10.16   *
   10.17   * @param name			Name of the new preference cookie.
    11.1 --- a/plugins/testsuite/clientprefstest.sp	Sun Jul 13 07:01:54 2008 +0000
    11.2 +++ b/plugins/testsuite/clientprefstest.sp	Tue Jul 15 00:24:08 2008 +0000
    11.3 @@ -44,6 +44,8 @@
    11.4  public bool:OnClientConnect(client, String:rejectmsg[], maxlen)
    11.5  {
    11.6  	LogMessage("Connect Cookie state: %s", AreClientCookiesCached(client) ? "YES" : "NO");
    11.7 +	
    11.8 +	return true;
    11.9  }
   11.10  
   11.11  public OnClientCookiesCached(client)