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