extensions/clientprefs/extension.cpp
author Matt Woodrow <pred@alliedmods.net>
Tue Mar 17 12:28:05 2009 +1300 (2009-03-17)
changeset 2666 18df531a8642
parent 2550 a2c8eec6dc22
child 2783 5f333027ef04
permissions -rw-r--r--
Clientprefs now loads cookies for clients on late load (bug 3735, r=dvander)
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;
dvander@2373
    48
DbDriver g_DriverType;
dvander@2550
    49
static const DatabaseInfo *storage_local = NULL;
pred@2009
    50
pred@2009
    51
bool ClientPrefs::SDK_OnLoad(char *error, size_t maxlength, bool late)
pred@2207
    52
{
pred@2207
    53
	queryMutex = threader->MakeMutex();
pred@2207
    54
	cookieMutex = threader->MakeMutex();
pred@2207
    55
pred@2207
    56
	DBInfo = dbi->FindDatabaseConf("clientprefs");
pred@2009
    57
pred@2009
    58
	if (DBInfo == NULL)
pred@2009
    59
	{
pred@2009
    60
		DBInfo = dbi->FindDatabaseConf("default");
pred@2009
    61
dvander@2550
    62
		if (DBInfo == NULL ||
dvander@2550
    63
			(strcmp(DBInfo->host, "localhost") == 0 &&
dvander@2550
    64
			 strcmp(DBInfo->database, "sourcemod") == 0 && 
dvander@2550
    65
			 strcmp(DBInfo->user, "root") == 0 &&
dvander@2550
    66
			 strcmp(DBInfo->pass, "") == 0 &&
dvander@2550
    67
			 strcmp(DBInfo->driver, "") == 0))
dvander@2550
    68
		{
dvander@2550
    69
			storage_local = dbi->FindDatabaseConf("storage-local");
dvander@2550
    70
			if (DBInfo == NULL)
dvander@2550
    71
			{
dvander@2550
    72
				DBInfo = storage_local;
dvander@2550
    73
			}
dvander@2550
    74
		}
dvander@2550
    75
pred@2009
    76
		if (DBInfo == NULL)
pred@2009
    77
		{
pred@2009
    78
			snprintf(error, maxlength, "Could not find \"clientprefs\" or \"default\" database configs");
pred@2009
    79
			return false;
pred@2009
    80
		}
pred@2009
    81
	}
pred@2009
    82
pred@2009
    83
	if (DBInfo->driver[0] != '\0')
pred@2009
    84
	{
pred@2009
    85
		Driver = dbi->FindOrLoadDriver(DBInfo->driver);
pred@2009
    86
	}
pred@2009
    87
	else
pred@2009
    88
	{
pred@2009
    89
		Driver = dbi->GetDefaultDriver();
pred@2009
    90
	}
pred@2009
    91
pred@2009
    92
	if (Driver == NULL)
pred@2009
    93
	{
pred@2009
    94
		snprintf(error, maxlength, "Could not load DB Driver \"%s\"", DBInfo->driver);
pred@2009
    95
		return false;
pred@2009
    96
	}
pred@2009
    97
pred@2207
    98
	Database = NULL;
pred@2207
    99
	databaseLoading = true;
pred@2207
   100
	TQueryOp *op = new TQueryOp(Query_Connect, 0);
pred@2207
   101
	dbi->AddToThreadQueue(op, PrioQueue_High);
pred@2009
   102
dvander@2135
   103
	dbi->AddDependency(myself, Driver);
dvander@2135
   104
pred@2009
   105
	sharesys->AddNatives(myself, g_ClientPrefNatives);
pred@2009
   106
	sharesys->RegisterLibrary(myself, "clientprefs");
pred@2048
   107
	g_CookieManager.cookieDataLoadedForward = forwards->CreateForward("OnClientCookiesCached", ET_Ignore, 1, NULL, Param_Cell);
pred@2009
   108
pred@2009
   109
	g_CookieType = handlesys->CreateType("Cookie", 
pred@2009
   110
		&g_CookieTypeHandler, 
pred@2009
   111
		0, 
pred@2009
   112
		NULL, 
pred@2009
   113
		NULL, 
pred@2009
   114
		myself->GetIdentity(), 
pred@2009
   115
		NULL);
pred@2009
   116
pred@2048
   117
	g_CookieIterator = handlesys->CreateType("CookieIterator", 
pred@2048
   118
		&g_CookieIteratorHandler, 
pred@2048
   119
		0, 
pred@2048
   120
		NULL, 
pred@2048
   121
		NULL, 
pred@2048
   122
		myself->GetIdentity(), 
pred@2048
   123
		NULL);
pred@2048
   124
pred@2048
   125
	IMenuStyle *style = menus->GetDefaultStyle();
pred@2048
   126
	g_CookieManager.clientMenu = style->CreateMenu(&g_Handler, NULL);
pred@2048
   127
	g_CookieManager.clientMenu->SetDefaultTitle("Client Settings:");
pred@2048
   128
pred@2048
   129
	plsys->AddPluginsListener(&g_CookieManager);
pred@2048
   130
pred@2048
   131
	phrases = translator->CreatePhraseCollection();
pred@2048
   132
	phrases->AddPhraseFile("clientprefs.phrases");
pred@2048
   133
	phrases->AddPhraseFile("common.phrases");
pred@2048
   134
pred@2666
   135
	if (late)
pred@2666
   136
	{
pred@2666
   137
		int maxclients = playerhelpers->GetMaxClients();
pred@2666
   138
pred@2666
   139
		for (int i = 1; i <= maxclients; i++)
pred@2666
   140
		{
pred@2666
   141
			IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(i);
pred@2666
   142
pred@2666
   143
			if (!pPlayer || !pPlayer->IsAuthorized())
pred@2666
   144
			{
pred@2666
   145
				continue;
pred@2666
   146
			}
pred@2666
   147
pred@2666
   148
			g_CookieManager.OnClientAuthorized(i, pPlayer->GetAuthString());
pred@2666
   149
		}
pred@2666
   150
	}
pred@2666
   151
pred@2009
   152
	return true;
pred@2009
   153
}
pred@2009
   154
pred@2009
   155
void ClientPrefs::SDK_OnAllLoaded()
pred@2009
   156
{
pred@2009
   157
	playerhelpers->AddClientListener(&g_CookieManager);
pred@2009
   158
}
pred@2009
   159
dvander@2135
   160
bool ClientPrefs::QueryInterfaceDrop(SMInterface *pInterface)
dvander@2135
   161
{
dvander@2135
   162
	if ((void *)pInterface == (void *)(Database->GetDriver()))
dvander@2135
   163
	{
dvander@2135
   164
		return false;
dvander@2135
   165
	}
dvander@2135
   166
dvander@2135
   167
	return true;
dvander@2135
   168
}
dvander@2135
   169
dvander@2135
   170
void ClientPrefs::NotifyInterfaceDrop(SMInterface *pInterface)
dvander@2135
   171
{
dvander@2135
   172
	if (Database != NULL && (void *)pInterface == (void *)(Database->GetDriver()))
dvander@2135
   173
	{
dvander@2135
   174
		Database->Close();
dvander@2135
   175
		Database = NULL;
dvander@2135
   176
	}
dvander@2135
   177
}
dvander@2135
   178
pred@2009
   179
void ClientPrefs::SDK_OnUnload()
pred@2009
   180
{
pred@2009
   181
	handlesys->RemoveType(g_CookieType, myself->GetIdentity());
pred@2131
   182
	handlesys->RemoveType(g_CookieIterator, myself->GetIdentity());
pred@2009
   183
pred@2009
   184
	g_CookieManager.Unload();
pred@2009
   185
dvander@2135
   186
	if (Database != NULL)
dvander@2135
   187
	{
dvander@2135
   188
		Database->Close();
dvander@2135
   189
	}
pred@2009
   190
pred@2048
   191
	forwards->ReleaseForward(g_CookieManager.cookieDataLoadedForward);
pred@2048
   192
pred@2048
   193
	g_CookieManager.clientMenu->Destroy();
pred@2131
   194
pred@2131
   195
	phrases->Destroy();
pred@2131
   196
pred@2131
   197
	plsys->RemovePluginsListener(&g_CookieManager);
pred@2131
   198
	playerhelpers->RemoveClientListener(&g_CookieManager);
pred@2207
   199
pred@2207
   200
	queryMutex->DestroyThis();
pred@2207
   201
	cookieMutex->DestroyThis();
pred@2207
   202
}
pred@2207
   203
pred@2207
   204
void ClientPrefs::DatabaseConnect()
pred@2207
   205
{
pred@2207
   206
	char error[256];
pred@2207
   207
	int errCode = 0;
pred@2207
   208
pred@2207
   209
	Database = Driver->Connect(DBInfo, true, error, sizeof(error));
pred@2207
   210
dvander@2550
   211
	if (Database == NULL &&
dvander@2550
   212
		DBInfo != storage_local &&
dvander@2550
   213
		storage_local != NULL)
dvander@2550
   214
	{
dvander@2550
   215
		DBInfo = storage_local;
dvander@2550
   216
		Database = Driver->Connect(DBInfo, true, error, sizeof(error));
dvander@2550
   217
	}
dvander@2550
   218
pred@2207
   219
	if (Database == NULL)
pred@2207
   220
	{
pred@2207
   221
		g_pSM->LogError(myself, error);
pred@2207
   222
		databaseLoading = false;
pred@2207
   223
		ProcessQueryCache();
pred@2207
   224
		return;
pred@2207
   225
	}
pred@2207
   226
pred@2207
   227
	const char *identifier = Driver->GetIdentifier();
pred@2207
   228
pred@2207
   229
	if (strcmp(identifier, "sqlite") == 0)
pred@2207
   230
	{
dvander@2373
   231
		g_DriverType = Driver_SQLite;
pred@2207
   232
dvander@2338
   233
		if (!Database->DoSimpleQuery(
pred@2207
   234
				"CREATE TABLE IF NOT EXISTS sm_cookies  \
pred@2207
   235
				( \
pred@2207
   236
					id INTEGER PRIMARY KEY AUTOINCREMENT, \
pred@2207
   237
					name varchar(30) NOT NULL UNIQUE, \
pred@2207
   238
					description varchar(255), \
pred@2207
   239
					access INTEGER \
dvander@2338
   240
				)"))
pred@2220
   241
		{
dvander@2338
   242
			g_pSM->LogMessage(myself, "Failed to CreateTable sm_cookies: %s", Database->GetError());
dvander@2338
   243
			goto fatal_fail;
pred@2220
   244
		}
pred@2220
   245
dvander@2338
   246
		if (!Database->DoSimpleQuery(
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) \
dvander@2338
   254
				)"))
pred@2220
   255
		{
dvander@2338
   256
			g_pSM->LogMessage(myself, "Failed to CreateTable sm_cookie_cache: %s", Database->GetError());
dvander@2338
   257
			goto fatal_fail;
pred@2220
   258
		}
pred@2207
   259
	}
pred@2207
   260
	else if (strcmp(identifier, "mysql") == 0)
pred@2207
   261
	{
dvander@2373
   262
		g_DriverType = Driver_MySQL;
pred@2207
   263
dvander@2338
   264
		if (!Database->DoSimpleQuery(
pred@2207
   265
				"CREATE TABLE IF NOT EXISTS sm_cookies \
pred@2207
   266
				( \
pred@2207
   267
					id INTEGER unsigned NOT NULL auto_increment, \
pred@2207
   268
					name varchar(30) NOT NULL UNIQUE, \
pred@2207
   269
					description varchar(255), \
pred@2207
   270
					access INTEGER, \
pred@2207
   271
					PRIMARY KEY (id) \
dvander@2338
   272
				)"))
pred@2220
   273
		{
dvander@2338
   274
			g_pSM->LogMessage(myself, "Failed to CreateTable sm_cookies: %s", Database->GetError());
dvander@2338
   275
			goto fatal_fail;
pred@2220
   276
		}
pred@2220
   277
dvander@2338
   278
		if (!Database->DoSimpleQuery(
pred@2207
   279
				"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
pred@2207
   280
				( \
pred@2207
   281
					player varchar(65) NOT NULL, \
pred@2207
   282
					cookie_id int(10) NOT NULL, \
pred@2207
   283
					value varchar(100), \
pred@2207
   284
					timestamp int NOT NULL, \
pred@2207
   285
					PRIMARY KEY (player, cookie_id) \
dvander@2338
   286
				)"))
pred@2220
   287
		{
dvander@2338
   288
			g_pSM->LogMessage(myself, "Failed to CreateTable sm_cookie_cache: %s", Database->GetError());
dvander@2338
   289
			goto fatal_fail;
pred@2220
   290
		}
pred@2207
   291
	}
pred@2207
   292
	else
pred@2207
   293
	{
pred@2207
   294
		g_pSM->LogError(myself, "Unsupported driver \"%s\"", identifier);
dvander@2338
   295
		goto fatal_fail;
pred@2207
   296
	}
pred@2207
   297
pred@2207
   298
	databaseLoading = false;
pred@2207
   299
pred@2207
   300
	ProcessQueryCache();
pred@2207
   301
pred@2207
   302
	return;
dvander@2338
   303
dvander@2338
   304
fatal_fail:
dvander@2338
   305
	Database->Close();
dvander@2338
   306
	Database = NULL;
dvander@2338
   307
	databaseLoading = false;
dvander@2338
   308
	ProcessQueryCache();
pred@2207
   309
}
pred@2207
   310
pred@2207
   311
bool ClientPrefs::AddQueryToQueue( TQueryOp *query )
pred@2207
   312
{
pred@2207
   313
	queryMutex->Lock();
pred@2207
   314
pred@2207
   315
	if (Database == NULL && databaseLoading)
pred@2207
   316
	{
pred@2207
   317
		cachedQueries.push_back(query);
pred@2207
   318
		queryMutex->Unlock();
pred@2207
   319
		return true;
pred@2207
   320
	}
pred@2207
   321
pred@2207
   322
	queryMutex->Unlock();
pred@2207
   323
pred@2207
   324
	if (Database)
pred@2207
   325
	{
pred@2207
   326
		query->SetDatabase(Database);
dvander@2373
   327
		dbi->AddToThreadQueue(query, PrioQueue_Normal);
pred@2207
   328
		return true;
pred@2207
   329
	}
pred@2207
   330
pred@2220
   331
	query->Destroy();
pred@2220
   332
pred@2207
   333
	/* If Database is NULL and we're not in the loading phase it must have failed - Can't do much */
pred@2207
   334
	return false;
pred@2207
   335
}
pred@2207
   336
pred@2207
   337
void ClientPrefs::ProcessQueryCache()
pred@2207
   338
{
pred@2207
   339
	SourceHook::List<TQueryOp *>::iterator iter;
pred@2207
   340
pred@2207
   341
	queryMutex->Lock();
pred@2207
   342
pred@2207
   343
	iter = cachedQueries.begin();
pred@2207
   344
	
pred@2207
   345
	while (iter != cachedQueries.end())
pred@2207
   346
	{
dvander@2359
   347
		TQueryOp *op = *iter;
pred@2207
   348
pred@2207
   349
		if (Database != NULL)
pred@2207
   350
		{
pred@2207
   351
			op->SetDatabase(Database);
dvander@2373
   352
			dbi->AddToThreadQueue(op, PrioQueue_Normal);
pred@2207
   353
		}
pred@2207
   354
		else
pred@2207
   355
		{
dvander@2359
   356
			op->Destroy();
pred@2207
   357
		}
pred@2207
   358
pred@2207
   359
		iter++;
pred@2207
   360
	}
pred@2207
   361
pred@2207
   362
	cachedQueries.clear();
pred@2207
   363
pred@2207
   364
	queryMutex->Unlock();
pred@2048
   365
}
pred@2048
   366
pred@2048
   367
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
pred@2048
   368
{
pred@2048
   369
	va_list ap;
pred@2048
   370
	va_start(ap, fmt);
pred@2048
   371
	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
pred@2048
   372
	va_end(ap);
pred@2048
   373
pred@2048
   374
	if (len >= maxlength)
pred@2048
   375
	{
pred@2048
   376
		buffer[maxlength - 1] = '\0';
pred@2048
   377
		return (maxlength - 1);
pred@2048
   378
	}
pred@2048
   379
	else
pred@2048
   380
	{
pred@2048
   381
		return len;
pred@2048
   382
	}
pred@2048
   383
}
pred@2048
   384
pred@2048
   385
bool Translate(char *buffer, 
pred@2048
   386
				   size_t maxlength,
pred@2048
   387
				   const char *format,
pred@2048
   388
				   unsigned int numparams,
pred@2048
   389
				   size_t *pOutLength,
pred@2048
   390
				   ...)
pred@2048
   391
{
pred@2048
   392
	va_list ap;
pred@2048
   393
	unsigned int i;
pred@2048
   394
	const char *fail_phrase;
pred@2048
   395
	void *params[MAX_TRANSLATE_PARAMS];
pred@2048
   396
pred@2048
   397
	if (numparams > MAX_TRANSLATE_PARAMS)
pred@2048
   398
	{
pred@2048
   399
		assert(false);
pred@2048
   400
		return false;
pred@2048
   401
	}
pred@2048
   402
pred@2048
   403
	va_start(ap, pOutLength);
pred@2048
   404
	for (i = 0; i < numparams; i++)
pred@2048
   405
	{
pred@2048
   406
		params[i] = va_arg(ap, void *);
pred@2048
   407
	}
pred@2048
   408
	va_end(ap);
pred@2048
   409
pred@2048
   410
	if (!g_ClientPrefs.phrases->FormatString(buffer,
pred@2048
   411
		maxlength, 
pred@2048
   412
		format, 
pred@2048
   413
		params,
pred@2048
   414
		numparams,
pred@2048
   415
		pOutLength,
pred@2048
   416
		&fail_phrase))
pred@2048
   417
	{
pred@2048
   418
		if (fail_phrase != NULL)
pred@2048
   419
		{
pred@2048
   420
			g_pSM->LogError(myself, "[SM] Could not find core phrase: %s", fail_phrase);
pred@2048
   421
		}
pred@2048
   422
		else
pred@2048
   423
		{
pred@2048
   424
			g_pSM->LogError(myself, "[SM] Unknown fatal error while translating a core phrase.");
pred@2048
   425
		}
pred@2048
   426
pred@2048
   427
		return false;
pred@2048
   428
	}
pred@2048
   429
pred@2048
   430
	return true;
pred@2009
   431
}
pred@2009
   432
pred@2009
   433
pred@2048
   434