extensions/clientprefs/extension.cpp
author Matt Woodrow <pred@alliedmods.net>
Tue Jul 15 00:24:08 2008 +0000 (2008-07-15)
changeset 2207 3a97a56ded62
parent 2135 2c56e1550aaa
child 2220 20b404790b3b
permissions -rw-r--r--
Fixed amb1810 - Clientprefs no longer blocks load.
pred@2009
     1
/**
pred@2009
     2
 * vim: set ts=4 :
pred@2009
     3
 * =============================================================================
pred@2009
     4
 * SourceMod Client Preferences Extension
pred@2009
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
pred@2009
     6
 * =============================================================================
pred@2009
     7
 *
pred@2009
     8
 * This program is free software; you can redistribute it and/or modify it under
pred@2009
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
pred@2009
    10
 * Free Software Foundation.
pred@2009
    11
 * 
pred@2009
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
pred@2009
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
pred@2009
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
pred@2009
    15
 * details.
pred@2009
    16
 *
pred@2009
    17
 * You should have received a copy of the GNU General Public License along with
pred@2009
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
pred@2009
    19
 *
pred@2009
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
pred@2009
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
pred@2009
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
pred@2009
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
pred@2009
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
pred@2009
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
pred@2009
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
pred@2009
    27
 * or <http://www.sourcemod.net/license.php>.
pred@2009
    28
 *
pred@2009
    29
 * Version: $Id$
pred@2009
    30
 */
pred@2009
    31
pred@2009
    32
#include "extension.h"
pred@2009
    33
pred@2009
    34
/**
pred@2009
    35
 * @file extension.cpp
pred@2009
    36
 * @brief Implement extension code here.
pred@2009
    37
 */
pred@2009
    38
pred@2009
    39
ClientPrefs g_ClientPrefs;		/**< Global singleton for extension's main interface */
pred@2009
    40
pred@2009
    41
SMEXT_LINK(&g_ClientPrefs);
pred@2009
    42
pred@2009
    43
HandleType_t g_CookieType = 0;
pred@2009
    44
CookieTypeHandler g_CookieTypeHandler;
pred@2009
    45
pred@2048
    46
HandleType_t g_CookieIterator = 0;
pred@2048
    47
CookieIteratorHandler g_CookieIteratorHandler;
pred@2048
    48
pred@2009
    49
pred@2009
    50
int driver = 0;
pred@2009
    51
pred@2009
    52
bool ClientPrefs::SDK_OnLoad(char *error, size_t maxlength, bool late)
pred@2207
    53
{
pred@2207
    54
	queryMutex = threader->MakeMutex();
pred@2207
    55
	cookieMutex = threader->MakeMutex();
pred@2207
    56
pred@2207
    57
	DBInfo = dbi->FindDatabaseConf("clientprefs");
pred@2009
    58
pred@2009
    59
	if (DBInfo == NULL)
pred@2009
    60
	{
pred@2009
    61
		DBInfo = dbi->FindDatabaseConf("default");
pred@2009
    62
pred@2009
    63
		if (DBInfo == NULL)
pred@2009
    64
		{
pred@2009
    65
			snprintf(error, maxlength, "Could not find \"clientprefs\" or \"default\" database configs");
pred@2009
    66
			return false;
pred@2009
    67
		}
pred@2009
    68
	}
pred@2009
    69
pred@2009
    70
	if (DBInfo->driver[0] != '\0')
pred@2009
    71
	{
pred@2009
    72
		Driver = dbi->FindOrLoadDriver(DBInfo->driver);
pred@2009
    73
	}
pred@2009
    74
	else
pred@2009
    75
	{
pred@2009
    76
		Driver = dbi->GetDefaultDriver();
pred@2009
    77
	}
pred@2009
    78
pred@2009
    79
	if (Driver == NULL)
pred@2009
    80
	{
pred@2009
    81
		snprintf(error, maxlength, "Could not load DB Driver \"%s\"", DBInfo->driver);
pred@2009
    82
		return false;
pred@2009
    83
	}
pred@2009
    84
pred@2207
    85
	Database = NULL;
pred@2207
    86
	databaseLoading = true;
pred@2207
    87
	TQueryOp *op = new TQueryOp(Query_Connect, 0);
pred@2207
    88
	dbi->AddToThreadQueue(op, PrioQueue_High);
pred@2009
    89
dvander@2135
    90
	dbi->AddDependency(myself, Driver);
dvander@2135
    91
pred@2009
    92
	sharesys->AddNatives(myself, g_ClientPrefNatives);
pred@2009
    93
	sharesys->RegisterLibrary(myself, "clientprefs");
pred@2048
    94
	g_CookieManager.cookieDataLoadedForward = forwards->CreateForward("OnClientCookiesCached", ET_Ignore, 1, NULL, Param_Cell);
pred@2009
    95
pred@2009
    96
	g_CookieType = handlesys->CreateType("Cookie", 
pred@2009
    97
		&g_CookieTypeHandler, 
pred@2009
    98
		0, 
pred@2009
    99
		NULL, 
pred@2009
   100
		NULL, 
pred@2009
   101
		myself->GetIdentity(), 
pred@2009
   102
		NULL);
pred@2009
   103
pred@2048
   104
	g_CookieIterator = handlesys->CreateType("CookieIterator", 
pred@2048
   105
		&g_CookieIteratorHandler, 
pred@2048
   106
		0, 
pred@2048
   107
		NULL, 
pred@2048
   108
		NULL, 
pred@2048
   109
		myself->GetIdentity(), 
pred@2048
   110
		NULL);
pred@2048
   111
pred@2048
   112
	IMenuStyle *style = menus->GetDefaultStyle();
pred@2048
   113
	g_CookieManager.clientMenu = style->CreateMenu(&g_Handler, NULL);
pred@2048
   114
	g_CookieManager.clientMenu->SetDefaultTitle("Client Settings:");
pred@2048
   115
pred@2048
   116
	plsys->AddPluginsListener(&g_CookieManager);
pred@2048
   117
pred@2048
   118
	phrases = translator->CreatePhraseCollection();
pred@2048
   119
	phrases->AddPhraseFile("clientprefs.phrases");
pred@2048
   120
	phrases->AddPhraseFile("common.phrases");
pred@2048
   121
pred@2009
   122
	return true;
pred@2009
   123
}
pred@2009
   124
pred@2009
   125
void ClientPrefs::SDK_OnAllLoaded()
pred@2009
   126
{
pred@2009
   127
	playerhelpers->AddClientListener(&g_CookieManager);
pred@2009
   128
}
pred@2009
   129
dvander@2135
   130
bool ClientPrefs::QueryInterfaceDrop(SMInterface *pInterface)
dvander@2135
   131
{
dvander@2135
   132
	if ((void *)pInterface == (void *)(Database->GetDriver()))
dvander@2135
   133
	{
dvander@2135
   134
		return false;
dvander@2135
   135
	}
dvander@2135
   136
dvander@2135
   137
	return true;
dvander@2135
   138
}
dvander@2135
   139
dvander@2135
   140
void ClientPrefs::NotifyInterfaceDrop(SMInterface *pInterface)
dvander@2135
   141
{
dvander@2135
   142
	if (Database != NULL && (void *)pInterface == (void *)(Database->GetDriver()))
dvander@2135
   143
	{
pred@2207
   144
		InsertCookieQuery->Destroy();
pred@2207
   145
		SelectDataQuery->Destroy();
pred@2207
   146
		SelectIdQuery->Destroy();
pred@2207
   147
		InsertDataQuery->Destroy();
pred@2207
   148
pred@2207
   149
		InsertCookieQuery = NULL;
pred@2207
   150
		SelectDataQuery = NULL;
pred@2207
   151
		SelectIdQuery = NULL;
pred@2207
   152
		InsertDataQuery = NULL;
pred@2207
   153
dvander@2135
   154
		Database->Close();
dvander@2135
   155
		Database = NULL;
dvander@2135
   156
	}
dvander@2135
   157
}
dvander@2135
   158
pred@2009
   159
void ClientPrefs::SDK_OnUnload()
pred@2009
   160
{
pred@2009
   161
	handlesys->RemoveType(g_CookieType, myself->GetIdentity());
pred@2131
   162
	handlesys->RemoveType(g_CookieIterator, myself->GetIdentity());
pred@2009
   163
pred@2009
   164
	g_CookieManager.Unload();
pred@2009
   165
dvander@2135
   166
	if (Database != NULL)
dvander@2135
   167
	{
dvander@2135
   168
		Database->Close();
dvander@2135
   169
	}
pred@2009
   170
pred@2048
   171
	forwards->ReleaseForward(g_CookieManager.cookieDataLoadedForward);
pred@2048
   172
pred@2048
   173
	g_CookieManager.clientMenu->Destroy();
pred@2131
   174
pred@2131
   175
	phrases->Destroy();
pred@2131
   176
pred@2131
   177
	plsys->RemovePluginsListener(&g_CookieManager);
pred@2131
   178
	playerhelpers->RemoveClientListener(&g_CookieManager);
pred@2207
   179
pred@2207
   180
	/* Kill all our prepared queries - Queries are guaranteed to be flushed before this is called */
pred@2207
   181
pred@2207
   182
	if (InsertCookieQuery != NULL)
pred@2207
   183
	{
pred@2207
   184
		InsertCookieQuery->Destroy();
pred@2207
   185
	}
pred@2207
   186
pred@2207
   187
	if (SelectDataQuery != NULL)
pred@2207
   188
	{
pred@2207
   189
		SelectDataQuery->Destroy();
pred@2207
   190
	}
pred@2207
   191
pred@2207
   192
	if (SelectIdQuery != NULL)
pred@2207
   193
	{
pred@2207
   194
		SelectIdQuery->Destroy();
pred@2207
   195
	}
pred@2207
   196
pred@2207
   197
	if (InsertDataQuery != NULL)
pred@2207
   198
	{
pred@2207
   199
		InsertDataQuery->Destroy();
pred@2207
   200
	}
pred@2207
   201
pred@2207
   202
	queryMutex->DestroyThis();
pred@2207
   203
	cookieMutex->DestroyThis();
pred@2207
   204
}
pred@2207
   205
pred@2207
   206
void ClientPrefs::DatabaseConnect()
pred@2207
   207
{
pred@2207
   208
	char error[256];
pred@2207
   209
	int errCode = 0;
pred@2207
   210
pred@2207
   211
	Database = Driver->Connect(DBInfo, true, error, sizeof(error));
pred@2207
   212
pred@2207
   213
	if (Database == NULL)
pred@2207
   214
	{
pred@2207
   215
		g_pSM->LogError(myself, error);
pred@2207
   216
		databaseLoading = false;
pred@2207
   217
		ProcessQueryCache();
pred@2207
   218
		return;
pred@2207
   219
	}
pred@2207
   220
pred@2207
   221
	const char *identifier = Driver->GetIdentifier();
pred@2207
   222
pred@2207
   223
	if (strcmp(identifier, "sqlite") == 0)
pred@2207
   224
	{
pred@2207
   225
		driver = DRIVER_SQLITE;
pred@2207
   226
pred@2207
   227
		TQueryOp *op = new TQueryOp(Query_CreateTable, 0);
pred@2207
   228
pred@2207
   229
		op->SetDatabase(Database);
pred@2207
   230
		op->SetCustomPreparedQuery
pred@2207
   231
				(Database->PrepareQuery(
pred@2207
   232
				"CREATE TABLE IF NOT EXISTS sm_cookies  \
pred@2207
   233
				( \
pred@2207
   234
					id INTEGER PRIMARY KEY AUTOINCREMENT, \
pred@2207
   235
					name varchar(30) NOT NULL UNIQUE, \
pred@2207
   236
					description varchar(255), \
pred@2207
   237
					access INTEGER \
pred@2207
   238
				)",
pred@2207
   239
				error, sizeof(error), &errCode));
pred@2207
   240
pred@2207
   241
		dbi->AddToThreadQueue(op, PrioQueue_High);
pred@2207
   242
pred@2207
   243
		op = new TQueryOp(Query_CreateTable, 0);
pred@2207
   244
		op->SetDatabase(Database);
pred@2207
   245
		op->SetCustomPreparedQuery
pred@2207
   246
				(Database->PrepareQuery(
pred@2207
   247
				"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
pred@2207
   248
				( \
pred@2207
   249
					player varchar(65) NOT NULL, \
pred@2207
   250
					cookie_id int(10) NOT NULL, \
pred@2207
   251
					value varchar(100), \
pred@2207
   252
					timestamp int, \
pred@2207
   253
					PRIMARY KEY (player, cookie_id) \
pred@2207
   254
				)",
pred@2207
   255
				error, sizeof(error), &errCode));
pred@2207
   256
pred@2207
   257
		dbi->AddToThreadQueue(op, PrioQueue_High);
pred@2207
   258
	}
pred@2207
   259
	else if (strcmp(identifier, "mysql") == 0)
pred@2207
   260
	{
pred@2207
   261
		driver = DRIVER_MYSQL;
pred@2207
   262
pred@2207
   263
		TQueryOp *op = new TQueryOp(Query_CreateTable, 0);
pred@2207
   264
		op->SetDatabase(Database);
pred@2207
   265
		op->SetCustomPreparedQuery
pred@2207
   266
				(Database->PrepareQuery(
pred@2207
   267
				"CREATE TABLE IF NOT EXISTS sm_cookies \
pred@2207
   268
				( \
pred@2207
   269
					id INTEGER unsigned NOT NULL auto_increment, \
pred@2207
   270
					name varchar(30) NOT NULL UNIQUE, \
pred@2207
   271
					description varchar(255), \
pred@2207
   272
					access INTEGER, \
pred@2207
   273
					PRIMARY KEY (id) \
pred@2207
   274
				)",
pred@2207
   275
				error, sizeof(error), &errCode));
pred@2207
   276
pred@2207
   277
		dbi->AddToThreadQueue(op, PrioQueue_High);
pred@2207
   278
pred@2207
   279
		op = new TQueryOp(Query_CreateTable, 0);
pred@2207
   280
		op->SetDatabase(Database);
pred@2207
   281
		op->SetCustomPreparedQuery
pred@2207
   282
				(Database->PrepareQuery(
pred@2207
   283
				"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
pred@2207
   284
				( \
pred@2207
   285
					player varchar(65) NOT NULL, \
pred@2207
   286
					cookie_id int(10) NOT NULL, \
pred@2207
   287
					value varchar(100), \
pred@2207
   288
					timestamp int NOT NULL, \
pred@2207
   289
					PRIMARY KEY (player, cookie_id) \
pred@2207
   290
				)",
pred@2207
   291
				error, sizeof(error), &errCode));
pred@2207
   292
pred@2207
   293
		dbi->AddToThreadQueue(op, PrioQueue_High);
pred@2207
   294
	}
pred@2207
   295
	else
pred@2207
   296
	{
pred@2207
   297
		g_pSM->LogError(myself, "Unsupported driver \"%s\"", identifier);
pred@2207
   298
		Database->Close();
pred@2207
   299
		Database = NULL;
pred@2207
   300
		databaseLoading = false;
pred@2207
   301
		ProcessQueryCache();
pred@2207
   302
		return;
pred@2207
   303
	}
pred@2207
   304
pred@2207
   305
	if (driver == DRIVER_MYSQL)
pred@2207
   306
	{
pred@2207
   307
		InsertCookieQuery = Database->PrepareQuery(
pred@2207
   308
				"INSERT IGNORE INTO sm_cookies(name, description, access) \
pred@2207
   309
				VALUES(?, ?, ?)",
pred@2207
   310
				error, sizeof(error), &errCode);
pred@2207
   311
		InsertDataQuery = Database->PrepareQuery(
pred@2207
   312
				"INSERT INTO sm_cookie_cache(player, cookie_id, value, timestamp) \
pred@2207
   313
				VALUES(?, ?, ?, ?) \
pred@2207
   314
				ON DUPLICATE KEY UPDATE value = ?, timestamp = ?",
pred@2207
   315
				error, sizeof(error), &errCode);
pred@2207
   316
	}
pred@2207
   317
	else
pred@2207
   318
	{
pred@2207
   319
		InsertCookieQuery = Database->PrepareQuery(
pred@2207
   320
				"INSERT OR IGNORE INTO sm_cookies(name, description, access) \
pred@2207
   321
				VALUES(?, ?, ?)", 
pred@2207
   322
				error, sizeof(error), &errCode);
pred@2207
   323
		InsertDataQuery = Database->PrepareQuery(
pred@2207
   324
				"INSERT OR REPLACE INTO sm_cookie_cache(player, cookie_id, value, timestamp) \
pred@2207
   325
				VALUES(?, ?, ?, ?)", 
pred@2207
   326
				error, sizeof(error), &errCode);
pred@2207
   327
	}
pred@2207
   328
pred@2207
   329
	SelectDataQuery = Database->PrepareQuery(
pred@2207
   330
			"SELECT sm_cookies.name, sm_cookie_cache.value, sm_cookies.description, sm_cookies.access \
pred@2207
   331
			FROM sm_cookies \
pred@2207
   332
			JOIN sm_cookie_cache \
pred@2207
   333
			ON sm_cookies.id = sm_cookie_cache.cookie_id \
pred@2207
   334
			WHERE player = ?",
pred@2207
   335
			error, sizeof(error), &errCode);
pred@2207
   336
pred@2207
   337
	SelectIdQuery = Database->PrepareQuery(
pred@2207
   338
			"SELECT id \
pred@2207
   339
			FROM sm_cookies \
pred@2207
   340
			WHERE name=?",
pred@2207
   341
			error, sizeof(error), &errCode);
pred@2207
   342
pred@2207
   343
	databaseLoading = false;
pred@2207
   344
	cell_t result = 0;
pred@2207
   345
pred@2207
   346
	ProcessQueryCache();
pred@2207
   347
pred@2207
   348
	return;
pred@2207
   349
}
pred@2207
   350
pred@2207
   351
bool ClientPrefs::AddQueryToQueue( TQueryOp *query )
pred@2207
   352
{
pred@2207
   353
	queryMutex->Lock();
pred@2207
   354
pred@2207
   355
	if (Database == NULL && databaseLoading)
pred@2207
   356
	{
pred@2207
   357
		cachedQueries.push_back(query);
pred@2207
   358
		queryMutex->Unlock();
pred@2207
   359
		return true;
pred@2207
   360
	}
pred@2207
   361
pred@2207
   362
	queryMutex->Unlock();
pred@2207
   363
pred@2207
   364
	if (Database)
pred@2207
   365
	{
pred@2207
   366
		query->SetDatabase(Database);
pred@2207
   367
		query->SetPreparedQuery();
pred@2207
   368
		dbi->AddToThreadQueue(query, PrioQueue_Normal);
pred@2207
   369
		return true;
pred@2207
   370
	}
pred@2207
   371
pred@2207
   372
	/* If Database is NULL and we're not in the loading phase it must have failed - Can't do much */
pred@2207
   373
	return false;
pred@2207
   374
}
pred@2207
   375
pred@2207
   376
void ClientPrefs::ProcessQueryCache()
pred@2207
   377
{
pred@2207
   378
	SourceHook::List<TQueryOp *>::iterator iter;
pred@2207
   379
pred@2207
   380
	queryMutex->Lock();
pred@2207
   381
pred@2207
   382
	iter = cachedQueries.begin();
pred@2207
   383
	
pred@2207
   384
	while (iter != cachedQueries.end())
pred@2207
   385
	{
pred@2207
   386
		TQueryOp *op = (TQueryOp *)*iter;
pred@2207
   387
pred@2207
   388
		if (Database != NULL)
pred@2207
   389
		{
pred@2207
   390
			op->SetDatabase(Database);
pred@2207
   391
			op->SetPreparedQuery();
pred@2207
   392
			dbi->AddToThreadQueue(op, PrioQueue_Normal);
pred@2207
   393
		}
pred@2207
   394
		else
pred@2207
   395
		{
pred@2207
   396
			delete op;
pred@2207
   397
		}
pred@2207
   398
pred@2207
   399
		iter++;
pred@2207
   400
	}
pred@2207
   401
pred@2207
   402
	cachedQueries.clear();
pred@2207
   403
pred@2207
   404
	queryMutex->Unlock();
pred@2048
   405
}
pred@2048
   406
pred@2048
   407
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
pred@2048
   408
{
pred@2048
   409
	va_list ap;
pred@2048
   410
	va_start(ap, fmt);
pred@2048
   411
	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
pred@2048
   412
	va_end(ap);
pred@2048
   413
pred@2048
   414
	if (len >= maxlength)
pred@2048
   415
	{
pred@2048
   416
		buffer[maxlength - 1] = '\0';
pred@2048
   417
		return (maxlength - 1);
pred@2048
   418
	}
pred@2048
   419
	else
pred@2048
   420
	{
pred@2048
   421
		return len;
pred@2048
   422
	}
pred@2048
   423
}
pred@2048
   424
pred@2048
   425
bool Translate(char *buffer, 
pred@2048
   426
				   size_t maxlength,
pred@2048
   427
				   const char *format,
pred@2048
   428
				   unsigned int numparams,
pred@2048
   429
				   size_t *pOutLength,
pred@2048
   430
				   ...)
pred@2048
   431
{
pred@2048
   432
	va_list ap;
pred@2048
   433
	unsigned int i;
pred@2048
   434
	const char *fail_phrase;
pred@2048
   435
	void *params[MAX_TRANSLATE_PARAMS];
pred@2048
   436
pred@2048
   437
	if (numparams > MAX_TRANSLATE_PARAMS)
pred@2048
   438
	{
pred@2048
   439
		assert(false);
pred@2048
   440
		return false;
pred@2048
   441
	}
pred@2048
   442
pred@2048
   443
	va_start(ap, pOutLength);
pred@2048
   444
	for (i = 0; i < numparams; i++)
pred@2048
   445
	{
pred@2048
   446
		params[i] = va_arg(ap, void *);
pred@2048
   447
	}
pred@2048
   448
	va_end(ap);
pred@2048
   449
pred@2048
   450
	if (!g_ClientPrefs.phrases->FormatString(buffer,
pred@2048
   451
		maxlength, 
pred@2048
   452
		format, 
pred@2048
   453
		params,
pred@2048
   454
		numparams,
pred@2048
   455
		pOutLength,
pred@2048
   456
		&fail_phrase))
pred@2048
   457
	{
pred@2048
   458
		if (fail_phrase != NULL)
pred@2048
   459
		{
pred@2048
   460
			g_pSM->LogError(myself, "[SM] Could not find core phrase: %s", fail_phrase);
pred@2048
   461
		}
pred@2048
   462
		else
pred@2048
   463
		{
pred@2048
   464
			g_pSM->LogError(myself, "[SM] Unknown fatal error while translating a core phrase.");
pred@2048
   465
		}
pred@2048
   466
pred@2048
   467
		return false;
pred@2048
   468
	}
pred@2048
   469
pred@2048
   470
	return true;
pred@2009
   471
}
pred@2009
   472
pred@2009
   473
pred@2048
   474