core/logic/Database.cpp
author Ruben Gonzalez <drifter01620@gmail.com>
Thu May 22 09:49:27 2014 -0400 (2014-05-22)
changeset 4378 568f4497ef4a
parent 4349 4772045265f8
permissions -rw-r--r--
Update CS:GO team score gamedata.
dvander@667
     1
/**
dvander@4027
     2
 * vim: set ts=4 sw=4 tw=99 noet :
ds@1330
     3
 * =============================================================================
dvander@1231
     4
 * SourceMod
ds@1947
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
ds@1330
     6
 * =============================================================================
dvander@667
     7
 *
ds@1330
     8
 * This program is free software; you can redistribute it and/or modify it under
ds@1330
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
ds@1330
    10
 * Free Software Foundation.
dvander@1231
    11
 * 
ds@1330
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1330
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1330
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
ds@1330
    15
 * details.
dvander@1231
    16
 *
ds@1330
    17
 * You should have received a copy of the GNU General Public License along with
ds@1330
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@1231
    19
 *
ds@1330
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
ds@1330
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1330
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1330
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
ds@1330
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
ds@1330
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
ds@1330
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1330
    27
 * or <http://www.sourcemod.net/license.php>.
dvander@667
    28
 *
dvander@667
    29
 * Version: $Id$
dvander@667
    30
 */
dvander@667
    31
dvander@666
    32
#include "Database.h"
skamonkey@4138
    33
#include "ISourceMod.h"
skamonkey@4138
    34
#include "HandleSys.h"
skamonkey@4138
    35
#include "ExtensionSys.h"
skamonkey@4138
    36
#include "PluginSys.h"
dvander@868
    37
#include <stdlib.h>
dvander@2726
    38
#include <IThreader.h>
dvander@868
    39
dvander@868
    40
#define DBPARSE_LEVEL_NONE		0
dvander@868
    41
#define DBPARSE_LEVEL_MAIN		1
dvander@868
    42
#define DBPARSE_LEVEL_DATABASE	2
dvander@666
    43
dvander@666
    44
DBManager g_DBMan;
dvander@1096
    45
static bool s_OneTimeThreaderErrorMsg = false;
dvander@666
    46
dvander@868
    47
DBManager::DBManager() 
dvander@4027
    48
	: m_Terminate(false),
dvander@4027
    49
	  m_ParseLevel(0),
dvander@4027
    50
	  m_ParseState(0),
dvander@4027
    51
	  m_pDefault(NULL)
dvander@868
    52
{
dvander@868
    53
}
dvander@868
    54
skamonkey@4138
    55
static void FrameHook(bool simulating)
skamonkey@4138
    56
{
skamonkey@4138
    57
	g_DBMan.RunFrame();
skamonkey@4138
    58
}
skamonkey@4138
    59
dvander@666
    60
void DBManager::OnSourceModAllInitialized()
dvander@666
    61
{
dvander@666
    62
	HandleAccess sec;
dvander@666
    63
skamonkey@4138
    64
	g_HandleSys.InitAccessDefaults(NULL, &sec);
dvander@666
    65
	sec.access[HandleAccess_Delete] |= HANDLE_RESTRICT_IDENTITY;
dvander@666
    66
	sec.access[HandleAccess_Clone] |= HANDLE_RESTRICT_IDENTITY;
dvander@666
    67
	
skamonkey@4138
    68
	m_DriverType = g_HandleSys.CreateType("IDriver", this, 0, NULL, &sec, g_pCoreIdent, NULL);
skamonkey@4138
    69
	m_DatabaseType = g_HandleSys.CreateType("IDatabase", this, 0, NULL, NULL, g_pCoreIdent, NULL);
skamonkey@4138
    70
skamonkey@4138
    71
	g_ShareSys.AddInterface(NULL, this);
dvander@862
    72
skamonkey@4138
    73
	g_pSM->BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg");
dvander@868
    74
skamonkey@4138
    75
	g_PluginSys.AddPluginsListener(this);
skamonkey@4138
    76
	
skamonkey@4138
    77
	g_pSM->AddGameFrameHook(&FrameHook);
dvander@868
    78
}
dvander@868
    79
dvander@868
    80
void DBManager::OnSourceModLevelChange(const char *mapName)
dvander@868
    81
{
dvander@1656
    82
	SMCError err;
dvander@1656
    83
	SMCStates states = {0, 0};
dvander@1096
    84
dvander@1096
    85
	/* We lock and don't give up the lock until we're done.
dvander@1096
    86
	 * This way the thread's search won't be searching through a
dvander@1096
    87
	 * potentially empty/corrupt list, which would be very bad.
dvander@1096
    88
	 */
dvander@4027
    89
	ke::AutoLock lock(&m_ConfigLock);
dvander@1656
    90
	if ((err = textparsers->ParseFile_SMC(m_Filename, this, &states)) != SMCError_Okay)
dvander@868
    91
	{
skamonkey@4138
    92
		smcore.LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
dvander@1656
    93
		if (err != SMCError_Custom)
dvander@868
    94
		{
dvander@1406
    95
			const char *txt = textparsers->GetSMCErrorString(err);
skamonkey@4138
    96
			smcore.LogError("[SM] Line %d: %s", states.line, txt);
dvander@868
    97
		}
dvander@868
    98
	}
dvander@858
    99
}
dvander@858
   100
dvander@858
   101
void DBManager::OnSourceModShutdown()
dvander@858
   102
{
skamonkey@4138
   103
	g_pSM->RemoveGameFrameHook(&FrameHook);
dvander@1096
   104
	KillWorkerThread();
skamonkey@4138
   105
	g_PluginSys.RemovePluginsListener(this);
skamonkey@4138
   106
	g_HandleSys.RemoveType(m_DatabaseType, g_pCoreIdent);
skamonkey@4138
   107
	g_HandleSys.RemoveType(m_DriverType, g_pCoreIdent);
dvander@3140
   108
	ClearConfigs();
dvander@858
   109
}
dvander@858
   110
dvander@858
   111
unsigned int DBManager::GetInterfaceVersion()
dvander@858
   112
{
dvander@858
   113
	return SMINTERFACE_DBI_VERSION;
dvander@858
   114
}
dvander@858
   115
dvander@858
   116
const char *DBManager::GetInterfaceName()
dvander@858
   117
{
dvander@858
   118
	return SMINTERFACE_DBI_NAME;
dvander@666
   119
}
dvander@666
   120
dvander@666
   121
void DBManager::OnHandleDestroy(HandleType_t type, void *object)
dvander@666
   122
{
dvander@666
   123
	if (type == m_DriverType)
dvander@666
   124
	{
dvander@666
   125
		/* Ignore */
dvander@666
   126
		return;
dvander@666
   127
	}
dvander@666
   128
skamonkey@4138
   129
	if (g_HandleSys.TypeCheck(type, m_DatabaseType))
dvander@666
   130
	{
dvander@666
   131
		IDatabase *pdb = (IDatabase *)object;
dvander@666
   132
		pdb->Close();
dvander@666
   133
	}
dvander@666
   134
}
dvander@666
   135
dvander@868
   136
void DBManager::ReadSMC_ParseStart()
dvander@666
   137
{
dvander@3140
   138
	ClearConfigs();
dvander@868
   139
	m_ParseLevel = 0;
dvander@868
   140
	m_ParseState = DBPARSE_LEVEL_NONE;
dvander@868
   141
	m_DefDriver.clear();
dvander@868
   142
}
dvander@666
   143
dvander@3140
   144
void DBManager::ClearConfigs()
dvander@3140
   145
{
dvander@3140
   146
	List<ConfDbInfo *>::iterator iter;
dvander@3140
   147
	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
dvander@3140
   148
		delete (*iter);
dvander@3140
   149
	m_confs.clear();
dvander@3140
   150
}
dvander@3140
   151
dvander@868
   152
ConfDbInfo s_CurInfo;
dvander@1656
   153
SMCResult DBManager::ReadSMC_NewSection(const SMCStates *states, const char *name)
dvander@868
   154
{
dvander@868
   155
	if (m_ParseLevel)
dvander@868
   156
	{
dvander@868
   157
		m_ParseLevel++;
dvander@1656
   158
		return SMCResult_Continue;
dvander@868
   159
	}
dvander@868
   160
dvander@868
   161
	if (m_ParseState == DBPARSE_LEVEL_NONE)
dvander@666
   162
	{
dvander@868
   163
		if (strcmp(name, "Databases") == 0)
dvander@666
   164
		{
dvander@868
   165
			m_ParseState = DBPARSE_LEVEL_MAIN;
dvander@868
   166
		} else {
dvander@868
   167
			m_ParseLevel++;
dvander@868
   168
		}
dvander@868
   169
	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
dvander@868
   170
		s_CurInfo = ConfDbInfo();
fyrenmoo@3139
   171
		s_CurInfo.name = name;
dvander@868
   172
		m_ParseState = DBPARSE_LEVEL_DATABASE;
dvander@868
   173
	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
dvander@868
   174
		m_ParseLevel++;
dvander@868
   175
	}
dvander@868
   176
dvander@1656
   177
	return SMCResult_Continue;
dvander@868
   178
}
dvander@868
   179
dvander@1656
   180
SMCResult DBManager::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
dvander@868
   181
{
dvander@868
   182
	if (m_ParseLevel)
dvander@868
   183
	{
dvander@1656
   184
		return SMCResult_Continue;
dvander@868
   185
	}
dvander@868
   186
dvander@868
   187
	if (m_ParseState == DBPARSE_LEVEL_MAIN)
dvander@868
   188
	{
dvander@868
   189
		if (strcmp(key, "driver_default") == 0)
dvander@868
   190
		{
dvander@868
   191
			m_DefDriver.assign(value);
dvander@868
   192
		}
dvander@868
   193
	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
dvander@868
   194
		if (strcmp(key, "driver") == 0)
dvander@868
   195
		{
dvander@868
   196
			if (strcmp(value, "default") != 0)
dvander@868
   197
			{
fyrenmoo@3139
   198
				s_CurInfo.driver = value;
dvander@868
   199
			}
dvander@868
   200
		} else if (strcmp(key, "database") == 0) {
fyrenmoo@3139
   201
			s_CurInfo.database = value;
dvander@868
   202
		} else if (strcmp(key, "host") == 0) {
fyrenmoo@3139
   203
			s_CurInfo.host = value;
dvander@868
   204
		} else if (strcmp(key, "user") == 0) {
fyrenmoo@3139
   205
			s_CurInfo.user = value;
dvander@868
   206
		} else if (strcmp(key, "pass") == 0) {
fyrenmoo@3139
   207
			s_CurInfo.pass = value;
dvander@868
   208
		} else if (strcmp(key, "timeout") == 0) {
dvander@868
   209
			s_CurInfo.info.maxTimeout = atoi(value);
dvander@868
   210
		} else if (strcmp(key, "port") == 0) {
dvander@868
   211
			s_CurInfo.info.port = atoi(value);
dvander@666
   212
		}
dvander@666
   213
	}
dvander@666
   214
dvander@1656
   215
	return SMCResult_Continue;
dvander@868
   216
}
dvander@868
   217
dvander@1656
   218
SMCResult DBManager::ReadSMC_LeavingSection(const SMCStates *states)
dvander@868
   219
{
dvander@868
   220
	if (m_ParseLevel)
dvander@868
   221
	{
dvander@868
   222
		m_ParseLevel--;
dvander@1656
   223
		return SMCResult_Continue;
dvander@868
   224
	}
dvander@868
   225
dvander@868
   226
	if (m_ParseState == DBPARSE_LEVEL_DATABASE)
dvander@868
   227
	{
dvander@3140
   228
		ConfDbInfo *cdb = new ConfDbInfo();
dvander@3140
   229
dvander@3140
   230
		cdb->name = s_CurInfo.name;
dvander@3140
   231
		cdb->driver = s_CurInfo.driver;
dvander@3140
   232
		cdb->host = s_CurInfo.host;
dvander@3140
   233
		cdb->user = s_CurInfo.user;
dvander@3140
   234
		cdb->pass = s_CurInfo.pass;
dvander@3140
   235
		cdb->database = s_CurInfo.database;
dvander@3140
   236
		cdb->realDriver = s_CurInfo.realDriver;
dvander@3140
   237
		cdb->info.maxTimeout = s_CurInfo.info.maxTimeout;
dvander@3140
   238
		cdb->info.port = s_CurInfo.info.port;
dvander@3140
   239
dvander@3140
   240
		cdb->info.driver = cdb->driver.c_str();
dvander@3140
   241
		cdb->info.database = cdb->database.c_str();
dvander@3140
   242
		cdb->info.host = cdb->host.c_str();
dvander@3140
   243
		cdb->info.user = cdb->user.c_str();
dvander@3140
   244
		cdb->info.pass = cdb->pass.c_str();
dvander@868
   245
		
dvander@868
   246
		/* Save it.. */
dvander@3140
   247
		m_confs.push_back(cdb);
dvander@868
   248
dvander@868
   249
		/* Go up one level */
dvander@868
   250
		m_ParseState = DBPARSE_LEVEL_MAIN;
dvander@868
   251
	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
dvander@868
   252
		m_ParseState = DBPARSE_LEVEL_NONE;
dvander@1656
   253
		return SMCResult_Halt;
dvander@868
   254
	}
dvander@868
   255
dvander@1656
   256
	return SMCResult_Continue;
dvander@868
   257
}
dvander@868
   258
dvander@868
   259
void DBManager::ReadSMC_ParseEnd(bool halted, bool failed)
dvander@868
   260
{
dvander@868
   261
}
dvander@868
   262
dvander@868
   263
bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
dvander@868
   264
{
dvander@868
   265
	ConfDbInfo *pInfo = GetDatabaseConf(name);
dvander@868
   266
dvander@868
   267
	if (!pInfo)
dvander@868
   268
	{
dvander@868
   269
		if (pdr)
dvander@868
   270
		{
dvander@868
   271
			*pdr = NULL;
dvander@868
   272
		}
dvander@868
   273
		*pdb = NULL;
skamonkey@4138
   274
		g_pSM->Format(error, maxlength, "Configuration \"%s\" not found", name);
dvander@868
   275
		return false;
dvander@868
   276
	}
dvander@868
   277
dvander@869
   278
	const char *dname = pInfo->info.driver;
dvander@868
   279
	if (!pInfo->realDriver)
dvander@868
   280
	{
dvander@868
   281
		/* Try to assign a real driver pointer */
dvander@868
   282
		if (pInfo->info.driver[0] == '\0')
dvander@868
   283
		{
dvander@868
   284
			if (!m_pDefault && m_DefDriver.size() > 0)
dvander@868
   285
			{
dvander@868
   286
				m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
dvander@868
   287
			}
dvander@869
   288
			dname = m_DefDriver.size() ? m_DefDriver.c_str() : "default";
dvander@868
   289
			pInfo->realDriver = m_pDefault;
dvander@868
   290
		} else {
dvander@868
   291
			pInfo->realDriver = FindOrLoadDriver(pInfo->info.driver);
dvander@868
   292
		}
dvander@868
   293
	}
dvander@868
   294
dvander@868
   295
	if (pInfo->realDriver)
dvander@868
   296
	{
dvander@868
   297
		if (pdr)
dvander@868
   298
		{
dvander@868
   299
			*pdr = pInfo->realDriver;
dvander@868
   300
		}
dvander@868
   301
		*pdb = pInfo->realDriver->Connect(&pInfo->info, persistent, error, maxlength);
dvander@868
   302
		return (*pdb != NULL);
dvander@868
   303
	}
dvander@868
   304
dvander@868
   305
	if (pdr)
dvander@868
   306
	{
dvander@868
   307
		*pdr = NULL;
dvander@868
   308
	}
dvander@666
   309
	*pdb = NULL;
dvander@666
   310
skamonkey@4138
   311
	g_pSM->Format(error, maxlength, "Driver \"%s\" not found", dname);
dvander@868
   312
dvander@666
   313
	return false;
dvander@666
   314
}
dvander@858
   315
dvander@858
   316
void DBManager::AddDriver(IDBDriver *pDriver)
dvander@858
   317
{
dvander@1096
   318
	 /* Let's kill the worker.  Join the thread and let the queries flush.
dvander@1096
   319
	  * This is kind of stupid but we just want to unload safely.
dvander@1096
   320
	  * Rather than recreate the worker, we'll wait until someone throws 
dvander@1096
   321
	  * another query through.
dvander@1096
   322
	  */
dvander@1096
   323
	KillWorkerThread();
dvander@1096
   324
dvander@858
   325
	 m_drivers.push_back(pDriver);
dvander@858
   326
}
dvander@858
   327
dvander@858
   328
void DBManager::RemoveDriver(IDBDriver *pDriver)
dvander@858
   329
{
dvander@1096
   330
	/* Again, we're forced to kill the worker.  How rude!
dvander@1096
   331
	 * Doing this flushes the queue, and thus we don't need to 
dvander@1096
   332
	 * clean anything else.  
dvander@1096
   333
	 */
dvander@1096
   334
	KillWorkerThread();
dvander@1096
   335
dvander@858
   336
	for (size_t i=0; i<m_drivers.size(); i++)
dvander@858
   337
	{
dvander@858
   338
		if (m_drivers[i] == pDriver)
dvander@858
   339
		{
dvander@858
   340
			m_drivers.erase(m_drivers.iterAt(i));
dvander@1201
   341
			break;
dvander@858
   342
		}
dvander@858
   343
	}
dvander@868
   344
dvander@868
   345
	/* Make sure NOTHING references this! */
dvander@3140
   346
	List<ConfDbInfo *>::iterator iter;
dvander@868
   347
	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
dvander@868
   348
	{
dvander@3140
   349
		ConfDbInfo &db = *(*iter);
dvander@868
   350
		if (db.realDriver == pDriver)
dvander@868
   351
		{
dvander@868
   352
			db.realDriver = NULL;
dvander@868
   353
		}
dvander@868
   354
	}
dvander@1096
   355
januk@4349
   356
	/* Someone unloaded the default driver? Silly.. */
januk@4349
   357
	if (pDriver == m_pDefault)
januk@4349
   358
	{
januk@4349
   359
		m_pDefault = NULL;
januk@4349
   360
	}
januk@4349
   361
dvander@1096
   362
	/* Now that the driver is gone, we have to test the think queue.
dvander@1096
   363
	 * Whatever happens therein is up to the db op!
dvander@1096
   364
	 */
dvander@1096
   365
	Queue<IDBThreadOperation *>::iterator qiter = m_ThinkQueue.begin();
dvander@1096
   366
	Queue<IDBThreadOperation *> templist;
dvander@1096
   367
	while (qiter != m_ThinkQueue.end())
dvander@1096
   368
	{
dvander@1096
   369
		IDBThreadOperation *op = (*qiter);
dvander@1096
   370
		if (op->GetDriver() == pDriver)
dvander@1096
   371
		{
dvander@1096
   372
			templist.push(op);
dvander@1096
   373
			qiter = m_ThinkQueue.erase(qiter);
dvander@1096
   374
		} else {
dvander@1096
   375
			qiter++;
dvander@1096
   376
		}
dvander@1096
   377
	}
dvander@1096
   378
dvander@1096
   379
	for (qiter = templist.begin();
dvander@1096
   380
		 qiter != templist.end();
dvander@1096
   381
		 qiter++)
dvander@1096
   382
	{
dvander@1096
   383
		IDBThreadOperation *op = (*qiter);
dvander@1096
   384
		op->CancelThinkPart();
dvander@1157
   385
		op->Destroy();
dvander@1096
   386
	}
dvander@868
   387
}
dvander@868
   388
dvander@868
   389
IDBDriver *DBManager::GetDefaultDriver()
dvander@868
   390
{
dvander@868
   391
	if (!m_pDefault && m_DefDriver.size() > 0)
dvander@868
   392
	{
dvander@868
   393
		m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
dvander@868
   394
	}
dvander@868
   395
dvander@868
   396
	return m_pDefault;
dvander@858
   397
}
dvander@858
   398
dvander@858
   399
Handle_t DBManager::CreateHandle(DBHandleType dtype, void *ptr, IdentityToken_t *pToken)
dvander@858
   400
{
dvander@858
   401
	HandleType_t type = 0;
dvander@858
   402
dvander@858
   403
	if (dtype == DBHandle_Driver)
dvander@858
   404
	{
dvander@858
   405
		type = m_DriverType;
dvander@858
   406
	} else if (dtype == DBHandle_Database) {
dvander@858
   407
		type = m_DatabaseType;
dvander@858
   408
	} else {
dvander@858
   409
		return BAD_HANDLE;
dvander@858
   410
	}
dvander@858
   411
skamonkey@4138
   412
	return g_HandleSys.CreateHandle(type, ptr, pToken, g_pCoreIdent, NULL);
dvander@858
   413
}
dvander@858
   414
dvander@858
   415
HandleError DBManager::ReadHandle(Handle_t hndl, DBHandleType dtype, void **ptr)
dvander@858
   416
{
dvander@858
   417
	HandleType_t type = 0;
dvander@858
   418
dvander@858
   419
	if (dtype == DBHandle_Driver)
dvander@858
   420
	{
dvander@858
   421
		type = m_DriverType;
dvander@858
   422
	} else if (dtype == DBHandle_Database) {
dvander@858
   423
		type = m_DatabaseType;
dvander@858
   424
	} else {
dvander@858
   425
		return HandleError_Type;
dvander@858
   426
	}
dvander@858
   427
dvander@858
   428
	HandleSecurity sec(NULL, g_pCoreIdent);
dvander@858
   429
skamonkey@4138
   430
	return g_HandleSys.ReadHandle(hndl, type, &sec, ptr);
dvander@858
   431
}
dvander@858
   432
dvander@858
   433
HandleError DBManager::ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token)
dvander@858
   434
{
dvander@858
   435
	HandleSecurity sec(token, g_pCoreIdent);
skamonkey@4138
   436
	return g_HandleSys.FreeHandle(hndl, &sec);
dvander@858
   437
}
dvander@858
   438
dvander@858
   439
unsigned int DBManager::GetDriverCount()
dvander@858
   440
{
dvander@858
   441
	return (unsigned int)m_drivers.size();
dvander@858
   442
}
dvander@858
   443
dvander@858
   444
IDBDriver *DBManager::GetDriver(unsigned int index)
dvander@858
   445
{
dvander@858
   446
	if (index >= m_drivers.size())
dvander@858
   447
	{
dvander@858
   448
		return NULL;
dvander@858
   449
	}
dvander@858
   450
dvander@858
   451
	return m_drivers[index];
dvander@858
   452
}
dvander@858
   453
dvander@858
   454
const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
dvander@858
   455
{
dvander@868
   456
	ConfDbInfo *info = GetDatabaseConf(name);
dvander@868
   457
	if (!info)
dvander@868
   458
	{
dvander@868
   459
		return NULL;
dvander@868
   460
	}
dvander@868
   461
dvander@868
   462
	return &info->info;
dvander@868
   463
}
dvander@868
   464
dvander@868
   465
ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
dvander@868
   466
{
dvander@3140
   467
	List<ConfDbInfo *>::iterator iter;
dvander@868
   468
dvander@868
   469
	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
dvander@868
   470
	{
dvander@3140
   471
		ConfDbInfo &conf = *(*iter);
fyrenmoo@3139
   472
		if (conf.name == name)
dvander@868
   473
		{
dvander@868
   474
			return &conf;
dvander@868
   475
		}
dvander@868
   476
	}
dvander@868
   477
dvander@858
   478
	return NULL;
dvander@858
   479
}
dvander@868
   480
dvander@868
   481
IDBDriver *DBManager::FindOrLoadDriver(const char *name)
dvander@868
   482
{
dvander@868
   483
	size_t last_size = m_drivers.size();
dvander@868
   484
	for (size_t i=0; i<last_size; i++)
dvander@868
   485
	{
dvander@868
   486
		if (strcmp(m_drivers[i]->GetIdentifier(), name) == 0)
dvander@868
   487
		{
dvander@868
   488
			return m_drivers[i];
dvander@868
   489
		}
dvander@868
   490
	}
dvander@868
   491
dvander@868
   492
	char filename[PLATFORM_MAX_PATH];
skamonkey@4138
   493
	g_pSM->Format(filename, sizeof(filename), "dbi.%s.ext", name);
dvander@868
   494
skamonkey@4138
   495
	IExtension *pExt = g_Extensions.LoadAutoExtension(filename);
dvander@868
   496
	if (!pExt || !pExt->IsLoaded() || m_drivers.size() <= last_size)
dvander@868
   497
	{
dvander@868
   498
		return NULL;
dvander@868
   499
	}
dvander@868
   500
dvander@868
   501
	/* last_size is now gauranteed to be a valid index.
dvander@868
   502
	 * The identifier must match the name.
dvander@868
   503
	 */
dvander@868
   504
	if (strcmp(m_drivers[last_size]->GetIdentifier(), name) == 0)
dvander@868
   505
	{
dvander@868
   506
		return m_drivers[last_size];
dvander@868
   507
	}
dvander@868
   508
dvander@868
   509
	return NULL;
dvander@868
   510
}
dvander@1096
   511
dvander@1096
   512
void DBManager::KillWorkerThread()
dvander@1096
   513
{
dvander@4027
   514
	if (m_Worker)
dvander@1096
   515
	{
dvander@4027
   516
		{
dvander@4027
   517
			ke::AutoLock lock(&m_QueueEvent);
dvander@4027
   518
			m_Terminate = true;
dvander@4027
   519
			m_QueueEvent.Notify();
dvander@4027
   520
		}
dvander@4027
   521
		m_Worker->Join();
dvander@4027
   522
		m_Worker = NULL;
dvander@1096
   523
		s_OneTimeThreaderErrorMsg = false;
dvander@4027
   524
		m_Terminate = false;
dvander@1096
   525
	}
dvander@1096
   526
}
dvander@1096
   527
dvander@1157
   528
static IdentityToken_t *s_pAddBlock = NULL;
dvander@1157
   529
dvander@1096
   530
bool DBManager::AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio)
dvander@1096
   531
{
dvander@1157
   532
	if (s_pAddBlock && op->GetOwner() == s_pAddBlock)
dvander@1157
   533
	{
dvander@1157
   534
		return false;
dvander@1157
   535
	}
dvander@1157
   536
dvander@4027
   537
	if (!m_Worker)
dvander@1096
   538
	{
dvander@4027
   539
		m_Worker = new ke::Thread(this, "SM SQL Worker");
dvander@4027
   540
		if (!m_Worker->Succeeded())
dvander@1096
   541
		{
dvander@1096
   542
			if (!s_OneTimeThreaderErrorMsg)
dvander@1096
   543
			{
skamonkey@4138
   544
				smcore.LogError("[SM] Unable to create db threader (error unknown)");
dvander@1096
   545
				s_OneTimeThreaderErrorMsg = true;
dvander@1096
   546
			}
dvander@4027
   547
			m_Worker = NULL;
dvander@1096
   548
			return false;
dvander@1096
   549
		}
dvander@1096
   550
	}
dvander@1096
   551
dvander@1096
   552
	/* Add to the queue */
dvander@1096
   553
	{
dvander@4027
   554
		ke::AutoLock lock(&m_QueueEvent);
dvander@1096
   555
		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio);
dvander@1096
   556
		queue.push(op);
dvander@4027
   557
		m_QueueEvent.Notify();
dvander@1096
   558
	}
dvander@1096
   559
dvander@1096
   560
	return true;
dvander@1096
   561
}
dvander@1096
   562
dvander@4027
   563
void DBManager::Run()
dvander@1096
   564
{
dvander@4027
   565
	// Initialize DB threadsafety.
dvander@4027
   566
	for (size_t i=0; i < m_drivers.size(); i++)
dvander@1096
   567
	{
dvander@1096
   568
		if (m_drivers[i]->IsThreadSafe())
dvander@1096
   569
			m_drSafety.push_back(m_drivers[i]->InitializeThreadSafety());
dvander@4027
   570
		else
dvander@1096
   571
			m_drSafety.push_back(false);
dvander@1096
   572
	}
dvander@1096
   573
dvander@4027
   574
	// Run actual worker thread logic.
dvander@4027
   575
	ThreadMain();
dvander@4027
   576
dvander@4027
   577
	// Shutdown DB threadsafety.
dvander@1096
   578
	for (size_t i=0; i<m_drivers.size(); i++)
dvander@1096
   579
	{
dvander@1096
   580
		if (m_drSafety[i])
dvander@1096
   581
			m_drivers[i]->ShutdownThreadSafety();
dvander@1096
   582
	}
dvander@1096
   583
	m_drSafety.clear();
dvander@1096
   584
}
dvander@1096
   585
dvander@4027
   586
void DBManager::ThreadMain()
dvander@1096
   587
{
dvander@4027
   588
	ke::AutoLock lock(&m_QueueEvent);
dvander@4027
   589
dvander@4027
   590
	while (true) {
dvander@4027
   591
		// The lock has been acquired. Grab everything we can out of the
dvander@4027
   592
		// queue. Since we want to flush the queue even if we're terminated,
dvander@4027
   593
		// we process all operations we can before checking to terminate.
dvander@4027
   594
		// There's no risk of starvation since the main thread blocks on us
dvander@4027
   595
		// terminating.
dvander@4027
   596
		while (true)
dvander@4027
   597
		{
dvander@4027
   598
			Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
dvander@4027
   599
			if (queue.empty())
dvander@4027
   600
				break;
dvander@1096
   601
dvander@4027
   602
			IDBThreadOperation *op = queue.first();
dvander@1096
   603
			queue.pop();
dvander@4027
   604
dvander@4027
   605
			// Unlock the queue when we run the query, so the main thread can
dvander@4027
   606
			// keep pumping events. We re-acquire the lock to check for more
dvander@4027
   607
			// items. It's okay if we terminate while unlocked; the main
dvander@4027
   608
			// thread would be blocked and we'd need to flush the queue
dvander@4027
   609
			// anyway, so after we've depleted the queue here, we'll just
dvander@4027
   610
			// reach the terminate at the top of the loop.
dvander@4027
   611
			{
dvander@4027
   612
				ke::AutoUnlock unlock(&m_QueueEvent);
dvander@4027
   613
				op->RunThreadPart();
dvander@4027
   614
dvander@4027
   615
				ke::AutoLock lock(&m_ThinkLock);
dvander@4027
   616
				m_ThinkQueue.push(op);
dvander@4027
   617
			}
dvander@1096
   618
		}
dvander@4027
   619
dvander@4027
   620
		if (m_Terminate)
dvander@4027
   621
			return;
dvander@1096
   622
dvander@4027
   623
		// Release the lock and wait for a signal.
dvander@4027
   624
		m_QueueEvent.Wait();
dvander@1096
   625
	}
dvander@1096
   626
}
dvander@1096
   627
dvander@1096
   628
void DBManager::RunFrame()
dvander@1096
   629
{
dvander@1096
   630
	/* Don't bother if we're empty */
dvander@1096
   631
	if (!m_ThinkQueue.size())
dvander@1096
   632
	{
dvander@1096
   633
		return;
dvander@1096
   634
	}
dvander@1096
   635
dvander@1096
   636
	/* Dump one thing per-frame so the server stays sane. */
dvander@4027
   637
	IDBThreadOperation *op;
dvander@4027
   638
	{
dvander@4027
   639
		ke::AutoLock lock(&m_ThinkLock);
dvander@4027
   640
		op = m_ThinkQueue.first();
dvander@4027
   641
		m_ThinkQueue.pop();
dvander@4027
   642
	}
dvander@1096
   643
	op->RunThinkPart();
dvander@1157
   644
	op->Destroy();
dvander@1096
   645
}
dvander@1096
   646
dvander@1157
   647
void DBManager::OnSourceModIdentityDropped(IdentityToken_t *pToken)
dvander@1157
   648
{
dvander@1157
   649
	s_pAddBlock = pToken;
dvander@1157
   650
dvander@1157
   651
	/* Kill the thread so we can flush everything into the think queue... */
dvander@1157
   652
	KillWorkerThread();
dvander@1157
   653
dvander@1157
   654
	/* Run all of the think operations.
dvander@1157
   655
 	 * Unlike the driver unloading example, we'll let these calls go through, 
dvander@1157
   656
	 * since a plugin unloading is far more normal.
dvander@1157
   657
	 */
dvander@1157
   658
	Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
dvander@1157
   659
	Queue<IDBThreadOperation *> templist;
dvander@1157
   660
	while (iter != m_ThinkQueue.end())
dvander@1157
   661
	{
dvander@1157
   662
		IDBThreadOperation *op = (*iter);
dvander@1157
   663
		if (op->GetOwner() == pToken)
dvander@1157
   664
		{
dvander@1157
   665
			templist.push(op);
dvander@1157
   666
			iter = m_ThinkQueue.erase(iter);
dvander@1157
   667
		} else {
dvander@1157
   668
			iter++;
dvander@1157
   669
		}
dvander@1157
   670
	}
dvander@1157
   671
dvander@1157
   672
	for (iter = templist.begin();
dvander@1157
   673
		iter != templist.end();
dvander@1157
   674
		iter++)
dvander@1157
   675
	{
dvander@1157
   676
		IDBThreadOperation *op = (*iter);
dvander@1157
   677
		op->RunThinkPart();
dvander@1157
   678
		op->Destroy();
dvander@1157
   679
	}
dvander@1157
   680
dvander@1157
   681
	s_pAddBlock = NULL;
dvander@1157
   682
}
dvander@1157
   683
dvander@1096
   684
void DBManager::OnPluginUnloaded(IPlugin *plugin)
dvander@1096
   685
{
dvander@1096
   686
	/* Kill the thread so we can flush everything into the think queue... */
dvander@1096
   687
	KillWorkerThread();
dvander@1096
   688
dvander@1096
   689
	/* Mark the plugin as being unloaded so future database calls will ignore threading... */
dvander@1096
   690
	plugin->SetProperty("DisallowDBThreads", NULL);
dvander@1096
   691
dvander@1096
   692
	/* Run all of the think operations.
dvander@1096
   693
	 * Unlike the driver unloading example, we'll let these calls go through, 
dvander@1096
   694
	 * since a plugin unloading is far more normal.
dvander@1096
   695
	 */
dvander@1096
   696
	Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
dvander@1096
   697
	Queue<IDBThreadOperation *> templist;
dvander@1096
   698
	while (iter != m_ThinkQueue.end())
dvander@1096
   699
	{
dvander@1096
   700
		IDBThreadOperation *op = (*iter);
dvander@1157
   701
		if (op->GetOwner() == plugin->GetIdentity())
dvander@1096
   702
		{
dvander@1096
   703
			templist.push(op);
dvander@1096
   704
			iter = m_ThinkQueue.erase(iter);
dvander@1096
   705
		} else {
dvander@1096
   706
			iter++;
dvander@1096
   707
		}
dvander@1096
   708
	}
dvander@1096
   709
dvander@1096
   710
	for (iter = templist.begin();
dvander@1096
   711
		 iter != templist.end();
dvander@1096
   712
		 iter++)
dvander@1096
   713
	{
dvander@1096
   714
		IDBThreadOperation *op = (*iter);
dvander@1096
   715
		op->RunThinkPart();
dvander@1157
   716
		op->Destroy();
dvander@1096
   717
	}
dvander@1096
   718
}
dvander@1096
   719
dvander@1096
   720
void DBManager::LockConfig()
dvander@1096
   721
{
dvander@4027
   722
	m_ConfigLock.Lock();
dvander@1096
   723
}
dvander@1096
   724
dvander@1096
   725
void DBManager::UnlockConfig()
dvander@1096
   726
{
dvander@4027
   727
	m_ConfigLock.Unlock();
dvander@1096
   728
}
dvander@1096
   729
dvander@1096
   730
const char *DBManager::GetDefaultDriverName()
dvander@1096
   731
{
dvander@1096
   732
	return m_DefDriver.c_str();
dvander@1096
   733
}
dvander@2134
   734
dvander@2134
   735
void DBManager::AddDependency(IExtension *myself, IDBDriver *driver)
dvander@2134
   736
{
skamonkey@4138
   737
	g_Extensions.AddRawDependency(myself, driver->GetIdentity(), driver);
dvander@2134
   738
}
dvander@2134
   739