core/systems/PluginSys.cpp
author David Anderson <dvander@alliedmods.net>
Fri Jan 19 08:22:44 2007 +0000 (2007-01-19)
changeset 327 e244337a9b38
parent 317 e5342c8a0cf4
child 333 b668cbd49c35
permissions -rw-r--r--
finished massive reorganization - IPluginFunction is now part of the VM, NOT the plugin system! This is how it should have been in the first place...
dvander@173
     1
#include <stdio.h>
dvander@173
     2
#include "PluginSys.h"
dvander@229
     3
#include "ShareSys.h"
dvander@190
     4
#include "LibrarySys.h"
dvander@220
     5
#include "HandleSys.h"
dvander@197
     6
#include "sourcemm_api.h"
dvander@209
     7
#include "sourcemod.h"
dvander@203
     8
#include "CTextParsers.h"
dvander@282
     9
#include "CLogger.h"
dvander@298
    10
#include "ExtensionSys.h"
dvander@309
    11
#include "sm_srvcmds.h"
dvander@309
    12
#include "sm_stringutil.h"
dvander@173
    13
dvander@211
    14
CPluginManager g_PluginSys;
dvander@220
    15
HandleType_t g_PluginType = 0;
dvander@229
    16
IdentityType_t g_PluginIdent = 0;
dvander@173
    17
dvander@209
    18
CPlugin::CPlugin(const char *file)
dvander@209
    19
{
dvander@209
    20
	static int MySerial = 0;
dvander@209
    21
dvander@209
    22
	m_type = PluginType_Private;
dvander@209
    23
	m_status = Plugin_Uncompiled;
dvander@209
    24
	m_serial = ++MySerial;
dvander@209
    25
	m_plugin = NULL;
dvander@209
    26
	m_errormsg[256] = '\0';
dvander@209
    27
	snprintf(m_filename, sizeof(m_filename), "%s", file);
dvander@229
    28
	m_handle = 0;
dvander@229
    29
	m_ident = NULL;
dvander@209
    30
}
dvander@209
    31
dvander@209
    32
CPlugin::~CPlugin()
dvander@173
    33
{
dvander@298
    34
	if (m_handle)
dvander@298
    35
	{
dvander@298
    36
		HandleSecurity sec;
dvander@298
    37
		sec.pOwner = g_PluginSys.GetIdentity();
dvander@298
    38
		sec.pIdentity = sec.pOwner;
dvander@298
    39
dvander@298
    40
		g_HandleSys.FreeHandle(m_handle, &sec);
dvander@298
    41
		g_ShareSys.DestroyIdentity(m_ident);
dvander@298
    42
	}
dvander@298
    43
dvander@209
    44
	if (m_ctx.base)
dvander@209
    45
	{
dvander@262
    46
		delete m_ctx.base;
dvander@209
    47
		m_ctx.base = NULL;
dvander@209
    48
	}
dvander@209
    49
	if (m_ctx.ctx)
dvander@209
    50
	{
dvander@209
    51
		m_ctx.vm->FreeContext(m_ctx.ctx);
dvander@209
    52
		m_ctx.ctx = NULL;
dvander@209
    53
	}
dvander@209
    54
	if (m_ctx.co)
dvander@209
    55
	{
dvander@209
    56
		m_ctx.vm->AbortCompilation(m_ctx.co);
dvander@209
    57
		m_ctx.co = NULL;
dvander@209
    58
	}
dvander@209
    59
dvander@222
    60
	if (m_plugin)
dvander@222
    61
	{
dvander@222
    62
		g_pSourcePawn->FreeFromMemory(m_plugin);
dvander@222
    63
		m_plugin = NULL;
dvander@222
    64
	}
dvander@229
    65
}
dvander@229
    66
dvander@229
    67
void CPlugin::InitIdentity()
dvander@229
    68
{
dvander@229
    69
	if (!m_handle)
dvander@229
    70
	{
dvander@229
    71
		m_ident = g_ShareSys.CreateIdentity(g_PluginIdent);
dvander@262
    72
		m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
dvander@262
    73
		m_ctx.base->SetIdentity(m_ident);
dvander@229
    74
	}
dvander@209
    75
}
dvander@209
    76
dvander@209
    77
CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
dvander@209
    78
{
dvander@209
    79
	char fullpath[PLATFORM_MAX_PATH+1];
dvander@317
    80
	g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file);
dvander@209
    81
	FILE *fp = fopen(fullpath, "rb");
dvander@173
    82
dvander@298
    83
	CPlugin *pPlugin = new CPlugin(file);
dvander@298
    84
dvander@173
    85
	if (!fp)
dvander@173
    86
	{
dvander@209
    87
		if (error)
dvander@209
    88
		{
dvander@209
    89
			snprintf(error, maxlength, "Unable to open file");
dvander@209
    90
		}
dvander@298
    91
		pPlugin->m_status = Plugin_BadLoad;
dvander@298
    92
		return pPlugin;
dvander@173
    93
	}
dvander@173
    94
dvander@173
    95
	int err;
dvander@173
    96
	sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err);
dvander@173
    97
	if (pl == NULL)
dvander@173
    98
	{
dvander@209
    99
		fclose(fp);
dvander@209
   100
		if (error)
dvander@209
   101
		{
dvander@209
   102
			snprintf(error, maxlength, "Error %d while parsing plugin", err);
dvander@209
   103
		}
dvander@298
   104
		pPlugin->m_status = Plugin_BadLoad;
dvander@298
   105
		return pPlugin;
dvander@173
   106
	}
dvander@173
   107
dvander@173
   108
	fclose(fp);
dvander@173
   109
dvander@209
   110
	pPlugin->m_plugin = pl;
dvander@298
   111
dvander@209
   112
	return pPlugin;
dvander@209
   113
}
dvander@209
   114
dvander@209
   115
ICompilation *CPlugin::StartMyCompile(IVirtualMachine *vm)
dvander@209
   116
{
dvander@209
   117
	if (!m_plugin)
dvander@173
   118
	{
dvander@209
   119
		return NULL;
dvander@173
   120
	}
dvander@173
   121
dvander@209
   122
	/* :NOTICE: We will eventually need to change these natives
dvander@209
   123
	 * for swapping in new contexts
dvander@209
   124
	 */
dvander@209
   125
	if (m_ctx.co || m_ctx.ctx)
dvander@173
   126
	{
dvander@173
   127
		return NULL;
dvander@173
   128
	}
dvander@173
   129
dvander@209
   130
	m_status = Plugin_Uncompiled;
dvander@173
   131
dvander@209
   132
	m_ctx.vm = vm;
dvander@209
   133
	m_ctx.co = vm->StartCompilation(m_plugin);
dvander@209
   134
dvander@209
   135
	return m_ctx.co;
dvander@209
   136
}
dvander@173
   137
dvander@209
   138
void CPlugin::CancelMyCompile()
dvander@209
   139
{
dvander@209
   140
	if (!m_ctx.co)
dvander@209
   141
	{
dvander@209
   142
		return;
dvander@209
   143
	}
dvander@176
   144
dvander@209
   145
	m_ctx.vm->AbortCompilation(m_ctx.co);
dvander@209
   146
	m_ctx.co = NULL;
dvander@209
   147
	m_ctx.vm = NULL;
dvander@209
   148
}
dvander@209
   149
dvander@209
   150
bool CPlugin::FinishMyCompile(char *error, size_t maxlength)
dvander@209
   151
{
dvander@209
   152
	if (!m_ctx.co)
dvander@176
   153
	{
dvander@209
   154
		return false;
dvander@176
   155
	}
dvander@176
   156
dvander@209
   157
	int err;
dvander@209
   158
	m_ctx.ctx = m_ctx.vm->CompileToContext(m_ctx.co, &err);
dvander@209
   159
	if (!m_ctx.ctx)
dvander@176
   160
	{
dvander@209
   161
		memset(&m_ctx, 0, sizeof(m_ctx));
dvander@298
   162
		if (error)
dvander@209
   163
		{
dvander@209
   164
			snprintf(error, maxlength, "Failed to compile (error %d)", err);
dvander@209
   165
		}
dvander@209
   166
		return false;
dvander@176
   167
	}
dvander@176
   168
dvander@262
   169
	m_ctx.base = new BaseContext(m_ctx.ctx);
dvander@327
   170
	m_ctx.base->SetRunnable(false);
dvander@209
   171
	m_ctx.ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)this;
dvander@209
   172
dvander@211
   173
	m_status = Plugin_Created;
faluco@216
   174
	m_ctx.co = NULL;
dvander@211
   175
dvander@209
   176
	UpdateInfo();
dvander@209
   177
dvander@209
   178
	return true;
dvander@209
   179
}
dvander@209
   180
dvander@209
   181
void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
dvander@209
   182
{
dvander@209
   183
	m_status = status;
dvander@209
   184
dvander@209
   185
	va_list ap;
dvander@209
   186
	va_start(ap, error_fmt);
dvander@209
   187
	vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
dvander@209
   188
	va_end(ap);
dvander@173
   189
dvander@327
   190
	if (m_ctx.base)
dvander@176
   191
	{
dvander@327
   192
		m_ctx.base->SetRunnable(false);
dvander@176
   193
	}
dvander@176
   194
}
dvander@176
   195
dvander@173
   196
void CPlugin::UpdateInfo()
dvander@173
   197
{
dvander@173
   198
	/* Now grab the info */
dvander@173
   199
	uint32_t idx;
dvander@173
   200
	IPluginContext *base = GetBaseContext();
dvander@173
   201
	int err = base->FindPubvarByName("myinfo", &idx);
dvander@173
   202
dvander@173
   203
	if (err == SP_ERROR_NONE)
dvander@173
   204
	{
dvander@173
   205
		struct sm_plugininfo_c_t
dvander@173
   206
		{
dvander@173
   207
			cell_t name;
dvander@173
   208
			cell_t description;
dvander@173
   209
			cell_t author;
dvander@173
   210
			cell_t version;
dvander@173
   211
			cell_t url;
dvander@173
   212
		};
dvander@173
   213
		sm_plugininfo_c_t *cinfo;
dvander@173
   214
		cell_t local_addr;
dvander@173
   215
dvander@173
   216
		base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
dvander@173
   217
		base->LocalToString(cinfo->name, (char **)&m_info.name);
dvander@173
   218
		base->LocalToString(cinfo->description, (char **)&m_info.description);
dvander@173
   219
		base->LocalToString(cinfo->author, (char **)&m_info.author);
dvander@173
   220
		base->LocalToString(cinfo->url, (char **)&m_info.url);
dvander@173
   221
		base->LocalToString(cinfo->version, (char **)&m_info.version);
dvander@173
   222
	}
dvander@173
   223
dvander@173
   224
	m_info.author = m_info.author ? m_info.author : "";
dvander@173
   225
	m_info.description = m_info.description ? m_info.description : "";
dvander@173
   226
	m_info.name = m_info.name ? m_info.name : "";
dvander@173
   227
	m_info.url = m_info.url ? m_info.url : "";
dvander@173
   228
	m_info.version = m_info.version ? m_info.version : "";
dvander@173
   229
}
dvander@173
   230
dvander@211
   231
void CPlugin::Call_OnPluginInit()
dvander@211
   232
{
dvander@211
   233
	if (m_status != Plugin_Loaded)
dvander@211
   234
	{
dvander@211
   235
		return;
dvander@211
   236
	}
dvander@211
   237
dvander@211
   238
	m_status = Plugin_Running;
dvander@211
   239
dvander@211
   240
	cell_t result;
dvander@327
   241
	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginInit");
dvander@211
   242
	if (!pFunction)
dvander@211
   243
	{
dvander@211
   244
		return;
dvander@211
   245
	}
dvander@211
   246
dvander@222
   247
	pFunction->PushCell(m_handle);
dvander@282
   248
	pFunction->Execute(&result);
dvander@282
   249
}
dvander@282
   250
dvander@282
   251
void CPlugin::Call_OnPluginUnload()
dvander@282
   252
{
dvander@282
   253
	if (m_status < Plugin_Paused)
dvander@211
   254
	{
dvander@282
   255
		return;
dvander@211
   256
	}
dvander@282
   257
dvander@282
   258
	cell_t result;
dvander@327
   259
	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginUnload");
dvander@282
   260
	if (!pFunction)
dvander@282
   261
	{
dvander@282
   262
		return;
dvander@282
   263
	}
dvander@282
   264
dvander@282
   265
	pFunction->Execute(&result);
dvander@211
   266
}
dvander@211
   267
dvander@211
   268
bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
dvander@211
   269
{
dvander@211
   270
	if (m_status != Plugin_Created)
dvander@211
   271
	{
dvander@211
   272
		return false;
dvander@211
   273
	}
dvander@211
   274
faluco@216
   275
	m_status = Plugin_Loaded;
dvander@327
   276
	m_ctx.base->SetRunnable(true);
faluco@216
   277
dvander@211
   278
	int err;
dvander@211
   279
	cell_t result;
dvander@327
   280
	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("AskPluginLoad");
dvander@211
   281
dvander@211
   282
	if (!pFunction)
dvander@211
   283
	{
dvander@211
   284
		return true;
dvander@211
   285
	}
dvander@211
   286
dvander@222
   287
	pFunction->PushCell(m_handle);
dvander@211
   288
	pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
dvander@211
   289
	pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
dvander@211
   290
	pFunction->PushCell(maxlength);
dvander@211
   291
	if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
dvander@211
   292
	{
dvander@211
   293
		return false;
dvander@211
   294
	}
dvander@211
   295
dvander@282
   296
	if (!result || m_status != Plugin_Loaded)
dvander@211
   297
	{
dvander@211
   298
		return false;
dvander@211
   299
	}
dvander@211
   300
dvander@211
   301
	return true;
dvander@211
   302
}
dvander@211
   303
dvander@173
   304
const sp_plugin_t *CPlugin::GetPluginStructure() const
dvander@173
   305
{
dvander@173
   306
	return m_plugin;
dvander@173
   307
}
dvander@173
   308
dvander@173
   309
IPluginContext *CPlugin::GetBaseContext() const
dvander@173
   310
{
dvander@209
   311
	return m_ctx.base;
dvander@173
   312
}
dvander@173
   313
dvander@173
   314
sp_context_t *CPlugin::GetContext() const
dvander@173
   315
{
dvander@209
   316
	return m_ctx.ctx;
dvander@173
   317
}
dvander@173
   318
dvander@173
   319
const char *CPlugin::GetFilename() const
dvander@173
   320
{
dvander@173
   321
	return m_filename;
dvander@173
   322
}
dvander@173
   323
dvander@174
   324
PluginType CPlugin::GetType() const
dvander@173
   325
{
dvander@174
   326
	return m_type;
dvander@173
   327
}
dvander@173
   328
dvander@173
   329
const sm_plugininfo_t *CPlugin::GetPublicInfo() const
dvander@173
   330
{
dvander@173
   331
	return &m_info;
dvander@173
   332
}
dvander@173
   333
dvander@173
   334
unsigned int CPlugin::GetSerial() const
dvander@173
   335
{
dvander@173
   336
	return m_serial;
dvander@173
   337
}
dvander@173
   338
dvander@173
   339
PluginStatus CPlugin::GetStatus() const
dvander@173
   340
{
dvander@173
   341
	return m_status;
dvander@173
   342
}
dvander@173
   343
dvander@173
   344
bool CPlugin::IsDebugging() const
dvander@173
   345
{
dvander@209
   346
	if (!m_ctx.ctx)
dvander@209
   347
	{
dvander@209
   348
		return false;
dvander@209
   349
	}
dvander@209
   350
dvander@209
   351
	return ((m_ctx.ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
dvander@173
   352
}
dvander@173
   353
dvander@173
   354
bool CPlugin::SetPauseState(bool paused)
dvander@173
   355
{
dvander@173
   356
	if (paused && GetStatus() != Plugin_Paused)
dvander@173
   357
	{
dvander@173
   358
		return false;
dvander@173
   359
	} else if (!paused && GetStatus() != Plugin_Running) {
dvander@173
   360
		return false;
dvander@173
   361
	}
dvander@282
   362
faluco@288
   363
	m_status = (paused) ? Plugin_Paused : Plugin_Running;
faluco@288
   364
dvander@327
   365
	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginPauseChange");
dvander@282
   366
	if (pFunction)
dvander@282
   367
	{
dvander@282
   368
		cell_t result;
dvander@282
   369
		pFunction->PushCell(paused ? 1 : 0);
dvander@282
   370
		pFunction->Execute(&result);
dvander@282
   371
	}
dvander@173
   372
dvander@173
   373
	return true;
dvander@173
   374
}
dvander@174
   375
faluco@290
   376
IdentityToken_t *CPlugin::GetIdentity() const
dvander@220
   377
{
dvander@229
   378
	return m_ident;
dvander@220
   379
}
dvander@220
   380
dvander@309
   381
bool CPlugin::ToggleDebugMode(bool debug, char *error, size_t maxlength)
faluco@290
   382
{
faluco@290
   383
	int err;
faluco@290
   384
faluco@290
   385
	if (!IsRunnable())
faluco@290
   386
	{
dvander@309
   387
		if (error)
dvander@309
   388
		{
dvander@309
   389
			snprintf(error, maxlength, "Plugin is not runnable.");
dvander@309
   390
		}
faluco@290
   391
		return false;
faluco@290
   392
	}
faluco@290
   393
dvander@309
   394
	if (debug && IsDebugging())
faluco@290
   395
	{
dvander@309
   396
		if (error)
dvander@309
   397
		{
dvander@309
   398
			snprintf(error, maxlength, "Plugin is already in debug mode.");
dvander@309
   399
		}
dvander@309
   400
		return false;
dvander@309
   401
	} else if (!debug && !IsDebugging()) {
dvander@309
   402
		if (error)
dvander@309
   403
		{
dvander@309
   404
			snprintf(error, maxlength, "Plugins is already in production mode.");
dvander@309
   405
		}
faluco@290
   406
		return false;
faluco@290
   407
	}
dvander@309
   408
faluco@290
   409
	ICompilation *co = g_pVM->StartCompilation(m_ctx.ctx->plugin);
faluco@290
   410
	if (!g_pVM->SetCompilationOption(co, "debug", (debug) ? "1" : "0"))
faluco@290
   411
	{
dvander@309
   412
		if (error)
dvander@309
   413
		{
dvander@309
   414
			snprintf(error, maxlength, "Failed to change plugin mode (JIT failure).");
dvander@309
   415
		}
faluco@290
   416
		return false;
faluco@290
   417
	}
dvander@309
   418
faluco@290
   419
	sp_context_t *new_ctx = g_pVM->CompileToContext(co, &err);
faluco@290
   420
dvander@309
   421
	if (new_ctx)
dvander@309
   422
	{
dvander@309
   423
		memcpy(new_ctx->memory, m_ctx.ctx->memory, m_ctx.ctx->mem_size);
dvander@309
   424
		new_ctx->hp = m_ctx.ctx->hp;
dvander@309
   425
		new_ctx->sp = m_ctx.ctx->sp;
dvander@309
   426
		new_ctx->frm = m_ctx.ctx->frm;
dvander@309
   427
		new_ctx->dbreak = m_ctx.ctx->dbreak;
dvander@309
   428
		new_ctx->context = m_ctx.ctx->context;
dvander@309
   429
		memcpy(new_ctx->user, m_ctx.ctx->user, sizeof(m_ctx.ctx->user));
faluco@290
   430
dvander@309
   431
		g_pVM->FreeContext(m_ctx.ctx);
dvander@309
   432
		m_ctx.ctx = new_ctx;
dvander@309
   433
		m_ctx.base->SetContext(new_ctx);
faluco@290
   434
dvander@309
   435
		UpdateInfo();
dvander@309
   436
	} else {
dvander@309
   437
		if (error)
dvander@309
   438
		{
dvander@309
   439
			snprintf(error, maxlength, "Failed to recompile plugin (JIT error %d).", err);
dvander@309
   440
		}
dvander@309
   441
		return false;
dvander@309
   442
	}
faluco@290
   443
faluco@290
   444
	return true;
faluco@290
   445
}
faluco@290
   446
faluco@290
   447
bool CPlugin::IsRunnable() const
faluco@290
   448
{
faluco@290
   449
	return (m_status <= Plugin_Paused) ? true : false;
faluco@290
   450
}
faluco@290
   451
dvander@174
   452
/*******************
dvander@174
   453
 * PLUGIN ITERATOR *
dvander@174
   454
 *******************/
dvander@190
   455
dvander@213
   456
CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
dvander@174
   457
{
dvander@174
   458
	mylist = _mylist;
faluco@216
   459
	Reset();
dvander@174
   460
}
dvander@174
   461
dvander@174
   462
IPlugin *CPluginManager::CPluginIterator::GetPlugin()
dvander@174
   463
{
dvander@174
   464
	return (*current);
dvander@174
   465
}
dvander@174
   466
dvander@174
   467
bool CPluginManager::CPluginIterator::MorePlugins()
dvander@174
   468
{
dvander@174
   469
	return (current != mylist->end());
dvander@174
   470
}
dvander@174
   471
dvander@174
   472
void CPluginManager::CPluginIterator::NextPlugin()
dvander@174
   473
{
dvander@174
   474
	current++;
dvander@174
   475
}
dvander@174
   476
dvander@174
   477
void CPluginManager::CPluginIterator::Release()
dvander@174
   478
{
dvander@211
   479
	g_PluginSys.ReleaseIterator(this);
dvander@174
   480
}
dvander@174
   481
dvander@174
   482
CPluginManager::CPluginIterator::~CPluginIterator()
dvander@174
   483
{
dvander@174
   484
}
dvander@174
   485
dvander@174
   486
void CPluginManager::CPluginIterator::Reset()
dvander@174
   487
{
dvander@174
   488
	current = mylist->begin();
dvander@174
   489
}
dvander@174
   490
dvander@174
   491
/******************
dvander@174
   492
 * PLUGIN MANAGER *
dvander@174
   493
 ******************/
dvander@174
   494
dvander@211
   495
CPluginManager::CPluginManager()
dvander@211
   496
{
dvander@211
   497
	m_LoadLookup = sm_trie_create();
dvander@211
   498
	m_AllPluginsLoaded = false;
dvander@229
   499
	m_MyIdent = NULL;
dvander@211
   500
}
dvander@211
   501
dvander@211
   502
CPluginManager::~CPluginManager()
dvander@211
   503
{
dvander@282
   504
	/* :NOTICE: 
dvander@282
   505
	 * Ignore the fact that there might be plugins in the cache.
dvander@282
   506
	 * This usually means that Core is not being unloaded properly, and everything
dvander@282
   507
	 * will crash anyway.  YAY
dvander@282
   508
	 */
dvander@211
   509
	sm_trie_destroy(m_LoadLookup);
dvander@211
   510
}
dvander@211
   511
dvander@211
   512
dvander@213
   513
void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
dvander@190
   514
{
dvander@203
   515
	/* First read in the database of plugin settings */
dvander@203
   516
	SMCParseError err;
dvander@203
   517
	unsigned int line, col;
faluco@216
   518
	m_AllPluginsLoaded = false;
dvander@273
   519
	if ((err=g_TextParser.ParseFile_SMC(config, &m_PluginInfo, &line, &col)) != SMCParse_Okay)
dvander@203
   520
	{
dvander@305
   521
		g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
dvander@282
   522
		const char *err_msg = g_TextParser.GetSMCErrorString(err);
dvander@282
   523
		if (err_msg)
dvander@282
   524
		{
dvander@305
   525
			g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg);
dvander@282
   526
		}
dvander@203
   527
	}
dvander@203
   528
dvander@209
   529
	LoadPluginsFromDir(basedir, NULL);
dvander@209
   530
}
dvander@203
   531
dvander@209
   532
void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
dvander@209
   533
{
dvander@209
   534
	char base_path[PLATFORM_MAX_PATH+1];
dvander@209
   535
dvander@209
   536
	/* Form the current path to start reading from */
dvander@209
   537
	if (localpath == NULL)
dvander@209
   538
	{
dvander@209
   539
		g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
dvander@209
   540
	} else {
dvander@209
   541
		g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
dvander@209
   542
	}
dvander@209
   543
dvander@209
   544
	IDirectory *dir = g_LibSys.OpenDirectory(base_path);
dvander@209
   545
dvander@209
   546
	if (!dir)
dvander@209
   547
	{
dvander@282
   548
		char error[256];
dvander@282
   549
		g_LibSys.GetPlatformError(error, sizeof(error));
dvander@305
   550
		g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath);
dvander@305
   551
		g_Logger.LogError("[SM] Platform returned error: %s", error);
dvander@209
   552
		return;
dvander@209
   553
	}
dvander@209
   554
dvander@190
   555
	while (dir->MoreFiles())
dvander@190
   556
	{
dvander@209
   557
		if (dir->IsEntryDirectory() 
dvander@209
   558
			&& (strcmp(dir->GetEntryName(), ".") != 0)
dvander@209
   559
			&& (strcmp(dir->GetEntryName(), "..") != 0)
dvander@209
   560
			&& (strcmp(dir->GetEntryName(), "disabled") != 0)
dvander@209
   561
			&& (strcmp(dir->GetEntryName(), "optional") != 0))
dvander@190
   562
		{
dvander@209
   563
			char new_local[PLATFORM_MAX_PATH+1];
dvander@209
   564
			if (localpath == NULL)
dvander@209
   565
			{
dvander@209
   566
				/* If no path yet, don't add a former slash */
dvander@209
   567
				snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
dvander@209
   568
			} else {
dvander@209
   569
				g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
dvander@209
   570
			}
dvander@209
   571
			LoadPluginsFromDir(basedir, new_local);
dvander@209
   572
		} else if (dir->IsEntryFile()) {
dvander@209
   573
			const char *name = dir->GetEntryName();
dvander@209
   574
			size_t len = strlen(name);
dvander@211
   575
			if (len >= 4
dvander@211
   576
				&& strcmp(&name[len-4], ".smx") == 0)
dvander@209
   577
			{
dvander@211
   578
				/* If the filename matches, load the plugin */
dvander@211
   579
				char plugin[PLATFORM_MAX_PATH+1];
dvander@211
   580
				if (localpath == NULL)
dvander@211
   581
				{
dvander@211
   582
					snprintf(plugin, sizeof(plugin), "%s", name);
dvander@211
   583
				} else {
dvander@211
   584
					g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
dvander@211
   585
				}
dvander@211
   586
				LoadAutoPlugin(plugin);
dvander@209
   587
			}
dvander@209
   588
		}
dvander@209
   589
		dir->NextEntry();
dvander@209
   590
	}
dvander@209
   591
	g_LibSys.CloseDirectory(dir);
dvander@209
   592
}
dvander@209
   593
dvander@211
   594
//well i have discovered that gabe newell is very fat, so i wrote this comment now
dvander@298
   595
bool CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t err_max)
dvander@209
   596
{
dvander@211
   597
	/**
dvander@211
   598
	 * Does this plugin already exist?
dvander@211
   599
	 */
dvander@211
   600
	CPlugin *pPlugin;
dvander@298
   601
	if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin))
dvander@211
   602
	{
dvander@211
   603
		/* First check the type */
dvander@211
   604
		PluginType type = pPlugin->GetType();
dvander@211
   605
		if (type == PluginType_Private
dvander@211
   606
			|| type == PluginType_Global)
dvander@211
   607
		{
dvander@298
   608
			return true;
dvander@211
   609
		}
dvander@211
   610
		/* Check to see if we should try reloading it */
dvander@211
   611
		if (pPlugin->GetStatus() == Plugin_BadLoad
dvander@282
   612
			|| pPlugin->GetStatus() == Plugin_Error
dvander@282
   613
			|| pPlugin->GetStatus() == Plugin_Failed)
dvander@211
   614
		{
dvander@211
   615
			UnloadPlugin(pPlugin);
dvander@211
   616
		}
dvander@211
   617
	}
dvander@211
   618
dvander@298
   619
	pPlugin = CPlugin::CreatePlugin(path, error, err_max);
dvander@209
   620
dvander@209
   621
	assert(pPlugin != NULL);
dvander@209
   622
dvander@209
   623
	pPlugin->m_type = PluginType_MapUpdated;
dvander@209
   624
dvander@209
   625
	ICompilation *co = NULL;
dvander@209
   626
dvander@209
   627
	if (pPlugin->m_status == Plugin_Uncompiled)
dvander@209
   628
	{
dvander@209
   629
		co = pPlugin->StartMyCompile(g_pVM);
dvander@209
   630
	}
dvander@209
   631
dvander@209
   632
	PluginSettings *pset;
dvander@209
   633
	unsigned int setcount = m_PluginInfo.GetSettingsNum();
dvander@209
   634
	for (unsigned int i=0; i<setcount; i++)
dvander@209
   635
	{
dvander@298
   636
		if ((pset=m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
dvander@209
   637
		{
dvander@209
   638
			continue;
dvander@209
   639
		}
dvander@209
   640
		pPlugin->m_type = pset->type_val;
dvander@209
   641
		if (co)
dvander@209
   642
		{
dvander@209
   643
			for (unsigned int j=0; j<pset->opts_num; j++)
dvander@209
   644
			{
dvander@209
   645
				const char *key, *val;
dvander@209
   646
				m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
dvander@209
   647
				if (!key || !val)
dvander@209
   648
				{
dvander@209
   649
					continue;
dvander@209
   650
				}
dvander@209
   651
				if (!g_pVM->SetCompilationOption(co, key, val))
dvander@209
   652
				{
dvander@298
   653
					if (error)
dvander@298
   654
					{
dvander@298
   655
						snprintf(error, err_max, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val);
dvander@298
   656
					}
dvander@209
   657
					pPlugin->CancelMyCompile();
dvander@209
   658
					co = NULL;
dvander@209
   659
					break;
dvander@209
   660
				}
dvander@209
   661
			}
dvander@190
   662
		}
dvander@190
   663
	}
dvander@209
   664
dvander@298
   665
	/* Do the actual compiling */
dvander@209
   666
	if (co)
dvander@209
   667
	{
dvander@209
   668
		pPlugin->FinishMyCompile(NULL, 0);
dvander@209
   669
		co = NULL;
dvander@209
   670
	}
dvander@209
   671
dvander@298
   672
	/* Get the status */
dvander@211
   673
	if (pPlugin->GetStatus() == Plugin_Created)
dvander@211
   674
	{
dvander@211
   675
		AddCoreNativesToPlugin(pPlugin);
dvander@229
   676
		pPlugin->InitIdentity();
dvander@315
   677
		if (pPlugin->Call_AskPluginLoad(error, err_max))
dvander@315
   678
		{
dvander@315
   679
			/* Autoload any modules */
dvander@315
   680
			LoadOrRequireExtensions(pPlugin, 1, error, err_max);
dvander@315
   681
		}
dvander@211
   682
	}
dvander@211
   683
dvander@298
   684
	if (_plugin)
dvander@298
   685
	{
dvander@298
   686
		*_plugin = pPlugin;
dvander@298
   687
	}
dvander@298
   688
dvander@298
   689
	return (pPlugin->GetStatus() == Plugin_Loaded);
dvander@190
   690
}
dvander@190
   691
dvander@174
   692
IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t err_max)
dvander@174
   693
{
dvander@298
   694
	CPlugin *pl;
dvander@211
   695
dvander@298
   696
	if (!_LoadPlugin(&pl, path, debug, type, error, err_max))
dvander@211
   697
	{
dvander@298
   698
		delete pl;
dvander@174
   699
		return NULL;
dvander@174
   700
	}
dvander@174
   701
dvander@298
   702
	AddPlugin(pl);
dvander@298
   703
dvander@315
   704
	/* Run second pass if we need to */
dvander@315
   705
	if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
dvander@315
   706
	{
dvander@315
   707
		if (!RunSecondPass(pl, error, err_max))
dvander@315
   708
		{
dvander@315
   709
			UnloadPlugin(pl);
dvander@315
   710
			return NULL;
dvander@315
   711
		}
dvander@315
   712
	}
dvander@315
   713
dvander@298
   714
	return pl;
dvander@298
   715
}
dvander@298
   716
dvander@298
   717
void CPluginManager::LoadAutoPlugin(const char *plugin)
dvander@298
   718
{
dvander@298
   719
	CPlugin *pl;
dvander@298
   720
	char error[255] = "Unknown error";
dvander@298
   721
dvander@298
   722
	if (!_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error)))
dvander@209
   723
	{
dvander@305
   724
		g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error);
dvander@298
   725
		pl->SetErrorState(Plugin_Failed, "%s", error);
dvander@209
   726
	}
dvander@209
   727
dvander@298
   728
	AddPlugin(pl);
dvander@209
   729
}
dvander@209
   730
dvander@211
   731
void CPluginManager::AddPlugin(CPlugin *pPlugin)
dvander@209
   732
{
dvander@174
   733
	List<IPluginsListener *>::iterator iter;
dvander@174
   734
	IPluginsListener *pListener;
dvander@298
   735
dvander@174
   736
	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
dvander@174
   737
	{
dvander@174
   738
		pListener = (*iter);
dvander@174
   739
		pListener->OnPluginCreated(pPlugin);
dvander@174
   740
	}
dvander@174
   741
dvander@298
   742
	m_plugins.push_back(pPlugin);
dvander@298
   743
	sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
dvander@211
   744
}
dvander@211
   745
dvander@213
   746
void CPluginManager::LoadAll_SecondPass()
dvander@213
   747
{
dvander@213
   748
	List<CPlugin *>::iterator iter;
dvander@213
   749
	CPlugin *pPlugin;
dvander@213
   750
dvander@315
   751
	char error[256];
dvander@213
   752
	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
dvander@213
   753
	{
dvander@213
   754
		pPlugin = (*iter);
dvander@213
   755
		if (pPlugin->GetStatus() == Plugin_Loaded)
dvander@213
   756
		{
dvander@315
   757
			error[0] = '\0';
dvander@315
   758
			if (!RunSecondPass(pPlugin, error, sizeof(error)))
dvander@315
   759
			{
dvander@315
   760
				g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
dvander@315
   761
				pPlugin->SetErrorState(Plugin_Failed, "%s", error);
dvander@315
   762
			}
dvander@213
   763
		}
dvander@213
   764
	}
dvander@298
   765
faluco@216
   766
	m_AllPluginsLoaded = true;
dvander@213
   767
}
dvander@213
   768
dvander@315
   769
bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
dvander@211
   770
{
dvander@298
   771
	/* Find any extensions this plugin needs */
dvander@298
   772
	struct _ext
dvander@298
   773
	{
dvander@298
   774
		cell_t name;
dvander@298
   775
		cell_t file;
dvander@298
   776
		cell_t autoload;
dvander@298
   777
		cell_t required;
dvander@298
   778
	} *ext;
dvander@298
   779
dvander@298
   780
	IPluginContext *pBase = pPlugin->GetBaseContext();
dvander@298
   781
	uint32_t num = pBase->GetPubVarsNum();
dvander@298
   782
	sp_pubvar_t *pubvar;
dvander@298
   783
	IExtension *pExt;
dvander@298
   784
	char path[PLATFORM_MAX_PATH+1];
dvander@298
   785
	char *file, *name;
dvander@298
   786
	for (uint32_t i=0; i<num; i++)
dvander@298
   787
	{
dvander@298
   788
		if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
dvander@298
   789
		{
dvander@298
   790
			continue;
dvander@298
   791
		}
dvander@298
   792
		if (strncmp(pubvar->name, "__ext_", 6) == 0)
dvander@298
   793
		{
dvander@298
   794
			ext = (_ext *)pubvar->offs;
dvander@298
   795
			if (!ext->required && !ext->autoload)
dvander@298
   796
			{
dvander@298
   797
				continue;
dvander@298
   798
			}
dvander@298
   799
			if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
dvander@298
   800
			{
dvander@298
   801
				continue;
dvander@298
   802
			}
dvander@298
   803
			if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
dvander@298
   804
			{
dvander@298
   805
				continue;
dvander@298
   806
			}
dvander@315
   807
			if (pass == 1)
dvander@298
   808
			{
dvander@315
   809
				/* Attempt to auto-load if necessary */
dvander@315
   810
				if (ext->autoload)
dvander@315
   811
				{
dvander@315
   812
					g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
dvander@315
   813
					g_Extensions.LoadAutoExtension(path);
dvander@315
   814
				}
dvander@315
   815
			} else if (pass == 2) {
dvander@315
   816
				/* Is this required? */
dvander@315
   817
				if (ext->required)
dvander@298
   818
				{
dvander@315
   819
					g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
dvander@315
   820
					if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
dvander@315
   821
					{
dvander@315
   822
						pExt = g_Extensions.FindExtensionByName(name);
dvander@315
   823
					}
dvander@315
   824
					/* :TODO: should we bind to unloaded extensions?
dvander@315
   825
					 * Currently the extension manager will ignore this.
dvander@315
   826
					 */
dvander@315
   827
					if (!pExt || !pExt->IsRunning(NULL, 0))
dvander@315
   828
					{
dvander@315
   829
						if (error)
dvander@315
   830
						{
dvander@315
   831
							snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
dvander@315
   832
						}
dvander@315
   833
						return false;
dvander@315
   834
					} else {
dvander@315
   835
						g_Extensions.BindChildPlugin(pExt, pPlugin);
dvander@315
   836
					}
dvander@298
   837
				}
dvander@298
   838
			}
dvander@315
   839
		}
dvander@315
   840
	}
dvander@315
   841
dvander@315
   842
	return true;
dvander@315
   843
}
dvander@315
   844
dvander@315
   845
bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
dvander@315
   846
{
dvander@315
   847
	/* Second pass for extension requirements */
dvander@315
   848
	if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
dvander@315
   849
	{
dvander@315
   850
		return false;
dvander@315
   851
	}
dvander@315
   852
dvander@315
   853
	/* Bind all extra natives */
dvander@315
   854
	g_Extensions.BindAllNativesToPlugin(pPlugin);
dvander@315
   855
dvander@315
   856
	/* Find any unbound natives
dvander@315
   857
	 * Right now, these are not allowed
dvander@315
   858
	 */
dvander@315
   859
	IPluginContext *pContext = pPlugin->GetBaseContext();
dvander@315
   860
	uint32_t num = pContext->GetNativesNum();
dvander@315
   861
	sp_native_t *native;
dvander@315
   862
	for (unsigned int i=0; i<num; i++)
dvander@315
   863
	{
dvander@315
   864
		if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
dvander@315
   865
		{
dvander@315
   866
			break;
dvander@315
   867
		}
dvander@315
   868
		if (native->status == SP_NATIVE_UNBOUND)
dvander@315
   869
		{
dvander@315
   870
			if (error)
dvander@298
   871
			{
dvander@315
   872
				snprintf(error, maxlength, "Native \"%s\" was not found.", native->name);
dvander@298
   873
			}
dvander@315
   874
			return false;
dvander@298
   875
		}
dvander@298
   876
	}
dvander@211
   877
dvander@211
   878
	/* Finish by telling all listeners */
dvander@211
   879
	List<IPluginsListener *>::iterator iter;
dvander@211
   880
	IPluginsListener *pListener;
dvander@211
   881
	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
dvander@211
   882
	{
dvander@211
   883
		pListener = (*iter);
dvander@211
   884
		pListener->OnPluginLoaded(pPlugin);
dvander@211
   885
	}
dvander@298
   886
	
dvander@298
   887
	/* Tell this plugin to finish initializing itself */
dvander@298
   888
	pPlugin->Call_OnPluginInit();
dvander@298
   889
dvander@298
   890
	return true;
dvander@211
   891
}
dvander@211
   892
dvander@211
   893
void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin)
dvander@211
   894
{
dvander@211
   895
	List<sp_nativeinfo_t *>::iterator iter;
dvander@211
   896
dvander@211
   897
	for (iter=m_natives.begin(); iter!=m_natives.end(); iter++)
dvander@211
   898
	{
dvander@211
   899
		sp_nativeinfo_t *natives = (*iter);
dvander@211
   900
		IPluginContext *ctx = pPlugin->GetBaseContext();
dvander@211
   901
		unsigned int i=0;
dvander@211
   902
		/* Attempt to bind every native! */
dvander@211
   903
		while (natives[i].func != NULL)
dvander@211
   904
		{
dvander@211
   905
			ctx->BindNative(&natives[i++]);
dvander@211
   906
		}
dvander@211
   907
	}
dvander@174
   908
}
dvander@174
   909
dvander@174
   910
bool CPluginManager::UnloadPlugin(IPlugin *plugin)
dvander@174
   911
{
dvander@174
   912
	CPlugin *pPlugin = (CPlugin *)plugin;
dvander@298
   913
dvander@298
   914
	/* This prevents removal during insertion or anything else weird */
dvander@298
   915
	if (m_plugins.find(pPlugin) == m_plugins.end())
dvander@298
   916
	{
dvander@298
   917
		return false;
dvander@298
   918
	}
dvander@298
   919
dvander@174
   920
	List<IPluginsListener *>::iterator iter;
dvander@174
   921
	IPluginsListener *pListener;
dvander@282
   922
dvander@311
   923
	if (pPlugin->GetStatus() <= Plugin_Error)
dvander@282
   924
	{
dvander@282
   925
		/* Notify listeners of unloading */
dvander@282
   926
		for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
dvander@282
   927
		{
dvander@282
   928
			pListener = (*iter);
dvander@282
   929
			pListener->OnPluginUnloaded(pPlugin);
dvander@282
   930
		}
dvander@282
   931
		/* Notify plugin */
dvander@282
   932
		pPlugin->Call_OnPluginUnload();
dvander@282
   933
	}
dvander@282
   934
dvander@174
   935
	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
dvander@174
   936
	{
dvander@282
   937
		/* Notify listeners of destruction */
dvander@174
   938
		pListener = (*iter);
dvander@174
   939
		pListener->OnPluginDestroyed(pPlugin);
dvander@174
   940
	}
dvander@211
   941
dvander@282
   942
	/* Remove us from the lookup table and linked list */
dvander@213
   943
	m_plugins.remove(pPlugin);
dvander@211
   944
	sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
dvander@176
   945
	
dvander@282
   946
	/* Tell the plugin to delete itself */
dvander@174
   947
	delete pPlugin;
dvander@174
   948
dvander@174
   949
	return true;
dvander@174
   950
}
dvander@174
   951
dvander@174
   952
IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
dvander@174
   953
{
dvander@174
   954
	IPlugin *pl = (IPlugin *)ctx->user[SM_CONTEXTVAR_MYSELF];
dvander@174
   955
	return pl;
dvander@174
   956
}
dvander@174
   957
dvander@174
   958
unsigned int CPluginManager::GetPluginCount()
dvander@174
   959
{
dvander@174
   960
	return m_plugins.size();
dvander@174
   961
}
dvander@174
   962
dvander@174
   963
void CPluginManager::AddPluginsListener(IPluginsListener *listener)
dvander@174
   964
{
dvander@174
   965
	m_listeners.push_back(listener);
dvander@174
   966
}
dvander@174
   967
dvander@174
   968
void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
dvander@174
   969
{
dvander@174
   970
	m_listeners.remove(listener);
dvander@174
   971
}
dvander@174
   972
dvander@174
   973
IPluginIterator *CPluginManager::GetPluginIterator()
dvander@174
   974
{
dvander@174
   975
	if (m_iters.empty())
dvander@174
   976
	{
dvander@174
   977
		return new CPluginIterator(&m_plugins);
dvander@174
   978
	} else {
dvander@174
   979
		CPluginIterator *iter = m_iters.front();
dvander@174
   980
		m_iters.pop();
faluco@216
   981
		iter->Reset();
dvander@174
   982
		return iter;
dvander@174
   983
	}
dvander@174
   984
}
dvander@174
   985
dvander@174
   986
void CPluginManager::ReleaseIterator(CPluginIterator *iter)
dvander@174
   987
{
dvander@174
   988
	m_iters.push(iter);
dvander@174
   989
}
dvander@176
   990
dvander@203
   991
bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
dvander@203
   992
{
dvander@203
   993
	/* As an optimization, we do not call strlen, but compute the length in the first pass */
dvander@203
   994
	size_t alias_len = 0;
dvander@203
   995
	size_t local_len = 0;
dvander@203
   996
dvander@203
   997
	const char *ptr = alias;
dvander@203
   998
	unsigned int alias_explicit_paths = 0;
dvander@203
   999
	unsigned int alias_path_end = 0;
dvander@203
  1000
	while (*ptr != '\0')
dvander@203
  1001
	{
dvander@203
  1002
		if (*ptr == '\\' || *ptr == '/')
dvander@203
  1003
		{
dvander@203
  1004
			alias_explicit_paths++;
dvander@203
  1005
			alias_path_end = alias_len;
dvander@203
  1006
		}
dvander@203
  1007
		alias_len++;
dvander@203
  1008
		ptr++;
dvander@203
  1009
	}
dvander@203
  1010
dvander@209
  1011
	if (alias_explicit_paths && alias_path_end == alias_len - 1)
dvander@203
  1012
	{
dvander@203
  1013
		/* Trailing slash is totally invalid here */
dvander@203
  1014
		return false;
dvander@203
  1015
	}
dvander@203
  1016
dvander@203
  1017
	ptr = localpath;
dvander@203
  1018
	unsigned int local_explicit_paths = 0;
dvander@203
  1019
	unsigned int local_path_end = 0;
dvander@203
  1020
	while (*ptr != '\0')
dvander@203
  1021
	{
dvander@203
  1022
		if (*ptr == '\\' || *ptr == '/')
dvander@203
  1023
		{
dvander@203
  1024
			local_explicit_paths++;
dvander@203
  1025
			local_path_end = local_len;
dvander@203
  1026
		}
dvander@203
  1027
		local_len++;
dvander@203
  1028
		ptr++;
dvander@203
  1029
	}
dvander@203
  1030
dvander@203
  1031
	/* If the alias has more explicit paths than the real path,
dvander@203
  1032
	 * no match will be possible.
dvander@203
  1033
	 */
dvander@203
  1034
	if (alias_explicit_paths > local_explicit_paths)
dvander@203
  1035
	{
dvander@203
  1036
		return false;
dvander@203
  1037
	}
dvander@203
  1038
dvander@203
  1039
	if (alias_explicit_paths)
dvander@203
  1040
	{
dvander@203
  1041
		/* We need to find if the paths match now.  For example, these should all match:
dvander@203
  1042
		 * csdm     csdm
dvander@203
  1043
		 * csdm     optional/csdm
dvander@203
  1044
		 * csdm/ban optional/crab/csdm/ban
dvander@203
  1045
		*/
dvander@203
  1046
		const char *aliasptr = alias;
dvander@203
  1047
		const char *localptr = localpath;
dvander@203
  1048
		bool match = true;
dvander@203
  1049
		do
dvander@203
  1050
		{
dvander@203
  1051
			if (*aliasptr != *localptr)
dvander@203
  1052
			{
dvander@203
  1053
				/* We have to knock one path off */
dvander@203
  1054
				local_explicit_paths--;
dvander@203
  1055
				if (alias_explicit_paths > local_explicit_paths)
dvander@203
  1056
				{
dvander@203
  1057
					/* Skip out if we're gonna have an impossible match */
dvander@203
  1058
					return false;
dvander@203
  1059
				}
dvander@203
  1060
				/* Eat up localptr tokens until we get a result */
dvander@203
  1061
				while (((localptr - localpath) < (int)local_path_end)
dvander@203
  1062
					&& *localptr != '/'
dvander@203
  1063
					&& *localptr != '\\')
dvander@203
  1064
				{
dvander@203
  1065
					localptr++;
dvander@203
  1066
				}
dvander@203
  1067
				/* Check if we hit the end of our searchable area.
dvander@203
  1068
				 * This probably isn't possible because of the path 
dvander@203
  1069
				 * count check, but it's a good idea anyway.
dvander@203
  1070
				 */
dvander@203
  1071
				if ((localptr - localpath) >= (int)local_path_end)
dvander@203
  1072
				{
dvander@203
  1073
					return false;
dvander@203
  1074
				} else {
dvander@203
  1075
					/* Consume the slash token */
dvander@203
  1076
					localptr++;
dvander@203
  1077
				}
dvander@203
  1078
				/* Reset the alias pointer so we can continue consuming */
dvander@203
  1079
				aliasptr = alias;
dvander@203
  1080
				match = false;
dvander@203
  1081
				continue;
dvander@203
  1082
			}
dvander@203
  1083
			/* Note:
dvander@203
  1084
			 * This is safe because if localptr terminates early, aliasptr will too
dvander@203
  1085
			 */
dvander@203
  1086
			do
dvander@203
  1087
			{
dvander@203
  1088
				/* We should never reach the end of the string because of this check. */
dvander@203
  1089
				bool aliasend = (aliasptr - alias) > (int)alias_path_end;
dvander@203
  1090
				bool localend = (localptr - localpath) > (int)local_path_end;
dvander@203
  1091
				if (aliasend || localend)
dvander@203
  1092
				{
dvander@203
  1093
					if (aliasend && localend)
dvander@203
  1094
					{
dvander@203
  1095
						/* we matched, and we can break out now */
dvander@203
  1096
						match = true;
dvander@203
  1097
						break;
dvander@203
  1098
					}
dvander@203
  1099
					/* Otherwise, we've hit the end somehow and rest won't match up.  Break out. */
dvander@203
  1100
					match = false;
dvander@203
  1101
					break;
dvander@203
  1102
				}
dvander@203
  1103
dvander@203
  1104
				/* If we got here, it's safe to compare the next two tokens */
dvander@203
  1105
				if (*localptr != *aliasptr)
dvander@203
  1106
				{
dvander@203
  1107
					match = false;
dvander@203
  1108
					break;
dvander@203
  1109
				}
dvander@203
  1110
				localptr++;
dvander@203
  1111
				aliasptr++;
dvander@203
  1112
			} while (true);
dvander@203
  1113
		} while (!match);
dvander@203
  1114
	}
dvander@203
  1115
dvander@203
  1116
	/* If we got here, it's time to compare filenames */
dvander@203
  1117
	const char *aliasptr = alias;
dvander@203
  1118
	const char *localptr = localpath;
dvander@203
  1119
dvander@203
  1120
	if (alias_explicit_paths)
dvander@203
  1121
	{
dvander@203
  1122
		aliasptr = &alias[alias_path_end + 1];
dvander@203
  1123
	}
dvander@203
  1124
dvander@203
  1125
	if (local_explicit_paths)
dvander@203
  1126
	{
dvander@203
  1127
		localptr = &localpath[local_path_end + 1];
dvander@203
  1128
	}
dvander@203
  1129
dvander@203
  1130
	while (true)
dvander@203
  1131
	{
dvander@203
  1132
		if (*aliasptr == '*')
dvander@203
  1133
		{
dvander@203
  1134
			/* First, see if this is the last character */
dvander@203
  1135
			if (aliasptr - alias == alias_len - 1)
dvander@203
  1136
			{
dvander@203
  1137
				/* If so, there's no need to match anything else */
dvander@203
  1138
				return true;
dvander@203
  1139
			}
dvander@203
  1140
			/* Otherwise, we need to search for an appropriate matching sequence in local.
dvander@203
  1141
			 * Note that we only need to search up to the next asterisk.
dvander@203
  1142
			 */
dvander@203
  1143
			aliasptr++;
dvander@203
  1144
			bool match = true;
dvander@203
  1145
			const char *local_orig = localptr;
dvander@203
  1146
			do
dvander@203
  1147
			{
dvander@203
  1148
				match = true;
dvander@203
  1149
				while (*aliasptr != '\0' && *aliasptr != '*')
dvander@203
  1150
				{
dvander@203
  1151
					/* Since aliasptr is never '\0', localptr hitting the end will fail */
dvander@203
  1152
					if (*aliasptr != *localptr)
dvander@203
  1153
					{
dvander@203
  1154
						match = false;
dvander@203
  1155
						break;
dvander@203
  1156
					}
dvander@203
  1157
					aliasptr++;
dvander@203
  1158
					localptr++;
dvander@203
  1159
				}
dvander@203
  1160
				if (!match)
dvander@203
  1161
				{
dvander@203
  1162
					/* If we didn't get a match, we need to advance the search stream.
dvander@203
  1163
					 * This will let us skip tokens while still searching for another match.
dvander@203
  1164
					 */
dvander@203
  1165
					localptr = ++local_orig;
dvander@203
  1166
					/* Make sure we don't go out of bounds */
dvander@203
  1167
					if (*localptr == '\0')
dvander@203
  1168
					{
dvander@203
  1169
						break;
dvander@203
  1170
					}
dvander@203
  1171
				}
dvander@203
  1172
			} while (!match);
dvander@203
  1173
dvander@203
  1174
			if (!match)
dvander@203
  1175
			{
dvander@203
  1176
				return false;
dvander@203
  1177
			} else {
dvander@203
  1178
				/* If we got a match, move on to the next token */
dvander@203
  1179
				continue;
dvander@203
  1180
			}
dvander@203
  1181
		} else if (*aliasptr == '\0') {
dvander@203
  1182
			if (*localptr == '\0'
dvander@203
  1183
				||
dvander@203
  1184
				strcmp(localptr, ".smx") == 0)
dvander@203
  1185
			{
dvander@203
  1186
				return true;
dvander@203
  1187
			} else {
dvander@203
  1188
				return false;
dvander@203
  1189
			}
dvander@203
  1190
		} else if (*aliasptr != *localptr) {
dvander@203
  1191
			return false;
dvander@203
  1192
		}
dvander@203
  1193
		aliasptr++;
dvander@203
  1194
		localptr++;
dvander@203
  1195
	}
dvander@203
  1196
dvander@203
  1197
	return true;
dvander@203
  1198
}
dvander@203
  1199
faluco@290
  1200
bool CPluginManager::IsLateLoadTime() const
dvander@176
  1201
{
dvander@315
  1202
	return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading());
dvander@176
  1203
}
dvander@220
  1204
dvander@220
  1205
void CPluginManager::OnSourceModAllInitialized()
dvander@220
  1206
{
dvander@229
  1207
	m_MyIdent = g_ShareSys.CreateCoreIdentity();
dvander@229
  1208
dvander@262
  1209
	HandleAccess sec;
dvander@262
  1210
	g_HandleSys.InitAccessDefaults(NULL, &sec);
dvander@220
  1211
dvander@262
  1212
	sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
dvander@262
  1213
	sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
dvander@262
  1214
dvander@262
  1215
	g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
dvander@229
  1216
	g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
dvander@309
  1217
dvander@309
  1218
	g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this);
dvander@220
  1219
}
dvander@220
  1220
dvander@220
  1221
void CPluginManager::OnSourceModShutdown()
dvander@220
  1222
{
dvander@309
  1223
	g_RootMenu.RemoveRootConsoleCommand("plugins", this);
dvander@309
  1224
dvander@298
  1225
	List<CPlugin *>::iterator iter;
dvander@298
  1226
	while ( (iter = m_plugins.begin()) != m_plugins.end() )
dvander@298
  1227
	{
dvander@298
  1228
		UnloadPlugin((*iter));
dvander@298
  1229
	}
dvander@298
  1230
dvander@229
  1231
	g_HandleSys.RemoveType(g_PluginType, m_MyIdent);
dvander@229
  1232
	g_ShareSys.DestroyIdentType(g_PluginIdent);
dvander@229
  1233
	g_ShareSys.DestroyIdentity(m_MyIdent);
dvander@220
  1234
}
dvander@220
  1235
dvander@220
  1236
void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
dvander@220
  1237
{
dvander@220
  1238
	/* We don't care about the internal object, actually */
dvander@220
  1239
}
dvander@233
  1240
dvander@233
  1241
void CPluginManager::RegisterNativesFromCore(sp_nativeinfo_t *natives)
dvander@233
  1242
{
dvander@233
  1243
	m_natives.push_back(natives);
dvander@233
  1244
}
dvander@247
  1245
dvander@247
  1246
IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
dvander@247
  1247
{
dvander@247
  1248
	IPlugin *pPlugin;
dvander@247
  1249
	HandleError _err;
dvander@247
  1250
dvander@262
  1251
	HandleSecurity sec;
dvander@262
  1252
dvander@262
  1253
	sec.pOwner = NULL;
dvander@262
  1254
	sec.pIdentity = m_MyIdent;
dvander@262
  1255
dvander@262
  1256
	if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
dvander@247
  1257
	{
dvander@247
  1258
		pPlugin = NULL;
dvander@247
  1259
	}
dvander@247
  1260
dvander@247
  1261
	if (err)
dvander@247
  1262
	{
dvander@247
  1263
		*err = _err;
dvander@247
  1264
	}
dvander@247
  1265
dvander@247
  1266
	return pPlugin;
dvander@247
  1267
}
faluco@290
  1268
faluco@290
  1269
CPlugin *CPluginManager::GetPluginByOrder(int num)
faluco@290
  1270
{
faluco@290
  1271
	if (num < 1 || num > (int)GetPluginCount())
faluco@290
  1272
	{
faluco@290
  1273
		return NULL;
faluco@290
  1274
	}
faluco@290
  1275
faluco@290
  1276
	CPlugin *pl;
faluco@290
  1277
	int id = 1;
faluco@290
  1278
faluco@290
  1279
	IPluginIterator *iter = GetPluginIterator();
faluco@290
  1280
	for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++) {}
faluco@290
  1281
faluco@290
  1282
	pl = (CPlugin *)(iter->GetPlugin());
faluco@290
  1283
	iter->Release();
faluco@290
  1284
faluco@290
  1285
	return pl;
dvander@298
  1286
}
dvander@309
  1287
dvander@309
  1288
const char *CPluginManager::GetStatusText(PluginStatus st)
dvander@309
  1289
{
dvander@309
  1290
	switch (st)
dvander@309
  1291
	{
dvander@309
  1292
	case Plugin_Running:
dvander@309
  1293
		return "Running";
dvander@309
  1294
	case Plugin_Paused:
dvander@309
  1295
		return "Paused";
dvander@309
  1296
	case Plugin_Error:
dvander@309
  1297
		return "Error";
dvander@309
  1298
	case Plugin_Uncompiled:
dvander@309
  1299
		return "Uncompiled";
dvander@309
  1300
	case Plugin_BadLoad:
dvander@309
  1301
		return "Bad Load";
dvander@309
  1302
	case Plugin_Failed:
dvander@309
  1303
		return "Failed";
dvander@309
  1304
	default:
dvander@309
  1305
		assert(false);
dvander@309
  1306
		return "-";
dvander@309
  1307
	}
dvander@309
  1308
}
dvander@309
  1309
dvander@309
  1310
#define IS_STR_FILLED(var) (var[0] != '\0')
dvander@309
  1311
void CPluginManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
dvander@309
  1312
{
dvander@309
  1313
	if (argcount >= 3)
dvander@309
  1314
	{
dvander@309
  1315
		const char *cmd = g_RootMenu.GetArgument(2);
dvander@309
  1316
		if (strcmp(cmd, "list") == 0)
dvander@309
  1317
		{
dvander@309
  1318
			char buffer[256];
dvander@309
  1319
			unsigned int id = 1;
dvander@309
  1320
			int plnum = GetPluginCount();
dvander@309
  1321
dvander@309
  1322
			if (!plnum)
dvander@309
  1323
			{
dvander@309
  1324
				g_RootMenu.ConsolePrint("[SM] No plugins loaded");
dvander@309
  1325
				return;
dvander@309
  1326
			} else {
dvander@309
  1327
				g_RootMenu.ConsolePrint("[SM] Displaying %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
dvander@309
  1328
			}
dvander@309
  1329
dvander@309
  1330
			IPluginIterator *iter = GetPluginIterator();
dvander@309
  1331
			for (; iter->MorePlugins(); iter->NextPlugin(), id++)
dvander@309
  1332
			{
dvander@309
  1333
				IPlugin *pl = iter->GetPlugin();
dvander@309
  1334
				assert(pl->GetStatus() != Plugin_Created);
dvander@309
  1335
				int len = 0;
dvander@309
  1336
				const sm_plugininfo_t *info = pl->GetPublicInfo();
dvander@309
  1337
dvander@309
  1338
				len += UTIL_Format(buffer, sizeof(buffer), "  %02d <%s>", id, GetStatusText(pl->GetStatus()));
dvander@309
  1339
				len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
dvander@309
  1340
				if (IS_STR_FILLED(info->version))
dvander@309
  1341
				{
dvander@309
  1342
					len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
dvander@309
  1343
				}
dvander@309
  1344
				if (IS_STR_FILLED(info->author))
dvander@309
  1345
				{
dvander@309
  1346
					UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
dvander@309
  1347
				}
dvander@309
  1348
				g_RootMenu.ConsolePrint("%s", buffer);
dvander@309
  1349
			}
dvander@309
  1350
dvander@309
  1351
			iter->Release();
dvander@309
  1352
			return;
dvander@309
  1353
		} else if (strcmp(cmd, "load") == 0) {
dvander@309
  1354
			if (argcount < 4)
dvander@309
  1355
			{
dvander@310
  1356
				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load <file>");
dvander@309
  1357
				return;
dvander@309
  1358
			}
dvander@309
  1359
dvander@309
  1360
			char error[128];
dvander@309
  1361
			const char *filename = g_RootMenu.GetArgument(3);
dvander@309
  1362
			IPlugin *pl = LoadPlugin(filename, false, PluginType_MapUpdated, error, sizeof(error));
dvander@309
  1363
dvander@309
  1364
			if (pl)
dvander@309
  1365
			{
dvander@309
  1366
				g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", filename);
dvander@309
  1367
			} else {
dvander@309
  1368
				g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", filename, error);
dvander@309
  1369
			}
dvander@309
  1370
dvander@309
  1371
			return;
dvander@309
  1372
		} else if (strcmp(cmd, "unload") == 0) {
dvander@309
  1373
			if (argcount < 4)
dvander@309
  1374
			{
dvander@310
  1375
				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#>");
dvander@309
  1376
				return;
dvander@309
  1377
			}
dvander@309
  1378
dvander@309
  1379
			int id = 1;
dvander@309
  1380
			int num = atoi(g_RootMenu.GetArgument(3));
dvander@309
  1381
			if (num < 1 || num > (int)GetPluginCount())
dvander@309
  1382
			{
dvander@310
  1383
				g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", num);
dvander@309
  1384
				return;
dvander@309
  1385
			}
dvander@309
  1386
dvander@309
  1387
			IPluginIterator *iter = GetPluginIterator();
dvander@309
  1388
			for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++)
dvander@309
  1389
				;
dvander@309
  1390
			IPlugin *pl = iter->GetPlugin();
dvander@309
  1391
dvander@309
  1392
			char name[PLATFORM_MAX_PATH+1];
dvander@309
  1393
			const sm_plugininfo_t *info = pl->GetPublicInfo();
dvander@309
  1394
			strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
dvander@309
  1395
dvander@309
  1396
			if (UnloadPlugin(pl))
dvander@309
  1397
			{
dvander@310
  1398
				g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
dvander@309
  1399
			} else {
dvander@310
  1400
				g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name);
dvander@309
  1401
			}
dvander@309
  1402
dvander@309
  1403
			iter->Release();
dvander@309
  1404
			return;
dvander@309
  1405
		} else if (strcmp(cmd, "info") == 0) {
dvander@309
  1406
			if (argcount < 4)
dvander@309
  1407
			{
dvander@310
  1408
				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>");
dvander@309
  1409
				return;
dvander@309
  1410
			}
dvander@309
  1411
dvander@309
  1412
			int id = 1;
dvander@309
  1413
			int num = atoi(g_RootMenu.GetArgument(3));
dvander@309
  1414
			if (num < 1 || num > (int)GetPluginCount())
dvander@309
  1415
			{
dvander@310
  1416
				g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
dvander@309
  1417
				return;
dvander@309
  1418
			}
dvander@309
  1419
dvander@315
  1420
			CPlugin *pl = GetPluginByOrder(num);
dvander@309
  1421
			const sm_plugininfo_t *info = pl->GetPublicInfo();
dvander@309
  1422
dvander@309
  1423
			g_RootMenu.ConsolePrint("  Filename: %s", pl->GetFilename());
dvander@315
  1424
			if (pl->GetStatus() <= Plugin_Error)
dvander@309
  1425
			{
dvander@315
  1426
				if (IS_STR_FILLED(info->name))
dvander@315
  1427
				{
dvander@315
  1428
					g_RootMenu.ConsolePrint("  Title: %s", info->name);
dvander@315
  1429
				}
dvander@315
  1430
				if (IS_STR_FILLED(info->version))
dvander@315
  1431
				{
dvander@315
  1432
					g_RootMenu.ConsolePrint("  Version: %s", info->version);
dvander@315
  1433
				}
dvander@315
  1434
				if (IS_STR_FILLED(info->url))
dvander@315
  1435
				{
dvander@315
  1436
					g_RootMenu.ConsolePrint("  URL: %s", info->url);
dvander@315
  1437
				}
dvander@315
  1438
				if (IS_STR_FILLED(info->author))
dvander@315
  1439
				{
dvander@315
  1440
					g_RootMenu.ConsolePrint("  Author: %s", info->author);
dvander@315
  1441
				}
dvander@315
  1442
				if (IS_STR_FILLED(info->version))
dvander@315
  1443
				{
dvander@315
  1444
					g_RootMenu.ConsolePrint("  Version: %s", info->version);
dvander@315
  1445
				}
dvander@315
  1446
				if (IS_STR_FILLED(info->description))
dvander@315
  1447
				{
dvander@315
  1448
					g_RootMenu.ConsolePrint("  Description: %s", info->description);
dvander@315
  1449
				}	
dvander@315
  1450
				g_RootMenu.ConsolePrint("  Debugging: %s", pl->IsDebugging() ? "yes" : "no");
dvander@315
  1451
				g_RootMenu.ConsolePrint("  Paused: %s", pl->GetStatus() == Plugin_Running ? "yes" : "no");
dvander@315
  1452
			} else {
dvander@315
  1453
				g_RootMenu.ConsolePrint("  Load error: %s", pl->m_errormsg);
dvander@315
  1454
				g_RootMenu.ConsolePrint("  File info: (title \"%s\") (version \"%s\")", 
dvander@315
  1455
										info->name ? info->name : "<none>",
dvander@315
  1456
										info->version ? info->version : "<none>");
dvander@315
  1457
				if (IS_STR_FILLED(info->url))
dvander@315
  1458
				{
dvander@315
  1459
					g_RootMenu.ConsolePrint("  File URL: %s", info->url);
dvander@315
  1460
				}
dvander@309
  1461
			}
dvander@309
  1462
dvander@309
  1463
			return;
dvander@309
  1464
		} else if (strcmp(cmd, "debug") == 0) {
dvander@309
  1465
			if (argcount < 5)
dvander@309
  1466
			{
dvander@310
  1467
				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins debug <#> [on|off]");
dvander@309
  1468
				return;
dvander@309
  1469
			}
dvander@309
  1470
dvander@309
  1471
			int num = atoi(g_RootMenu.GetArgument(3));
dvander@309
  1472
			if (num < 1 || num > (int)GetPluginCount())
dvander@309
  1473
			{
dvander@310
  1474
				g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
dvander@309
  1475
				return;
dvander@309
  1476
			}
dvander@309
  1477
dvander@309
  1478
			int res;
dvander@309
  1479
			const char *mode = g_RootMenu.GetArgument(4);
dvander@309
  1480
			if ((res=strcmp("on", mode)) && strcmp("off", mode))
dvander@309
  1481
			{
dvander@310
  1482
				g_RootMenu.ConsolePrint("[SM] The only possible options are \"on\" and \"off.\"");
dvander@309
  1483
				return;
dvander@309
  1484
			}
dvander@309
  1485
dvander@309
  1486
			bool debug;
dvander@309
  1487
			if (!res)
dvander@309
  1488
			{
dvander@309
  1489
				debug = true;
dvander@309
  1490
			} else {
dvander@309
  1491
				debug = false;
dvander@309
  1492
			}
dvander@309
  1493
dvander@309
  1494
			CPlugin *pl = GetPluginByOrder(num);
dvander@309
  1495
			if (debug && pl->IsDebugging())
dvander@309
  1496
			{
dvander@309
  1497
				g_RootMenu.ConsolePrint("[SM] This plugin is already in debug mode.");
dvander@309
  1498
				return;
dvander@309
  1499
			} else if (!debug && !pl->IsDebugging()) {
dvander@309
  1500
				g_RootMenu.ConsolePrint("[SM] Debug mode is already disabled in this plugin.");
dvander@309
  1501
				return;
dvander@309
  1502
			}
dvander@309
  1503
dvander@309
  1504
			char error[256];
dvander@309
  1505
			if (pl->ToggleDebugMode(debug, error, sizeof(error)))
dvander@309
  1506
			{
dvander@310
  1507
				g_RootMenu.ConsolePrint("[SM] Successfully toggled debug mode on plugin %s.", pl->GetFilename());
dvander@309
  1508
				return;
dvander@309
  1509
			} else {
dvander@309
  1510
				g_RootMenu.ConsolePrint("[SM] Could not toggle debug mode on plugin %s.", pl->GetFilename());
dvander@309
  1511
				g_RootMenu.ConsolePrint("[SM] Plugin returned error: %s", error);
dvander@309
  1512
				return;
dvander@309
  1513
			}
dvander@309
  1514
		}
dvander@309
  1515
	}
dvander@309
  1516
dvander@309
  1517
	/* Draw the main menu */
dvander@309
  1518
	g_RootMenu.ConsolePrint("SourceMod Plugins Menu:");
dvander@309
  1519
	g_RootMenu.DrawGenericOption("list", "Show loaded plugins");
dvander@309
  1520
	g_RootMenu.DrawGenericOption("load", "Load a plugin");
dvander@309
  1521
	g_RootMenu.DrawGenericOption("unload", "Unload a plugin");
dvander@309
  1522
	g_RootMenu.DrawGenericOption("info", "Information about a plugin");
dvander@309
  1523
	g_RootMenu.DrawGenericOption("debug", "Toggle debug mode on a plugin");
dvander@309
  1524
}