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