core/systems/PluginSys.cpp
author David Anderson <dvander@alliedmods.net>
Mon May 26 07:51:11 2008 +0000 (2008-05-26)
changeset 2086 debb2ab2c782
parent 2042 4a2faafca477
child 2105 ad190b9bb5af
permissions -rw-r--r--
rewrite of the native and dependency binding/interaction code. this will ease the transition for native overrides, and greatly simplifies most of the logic. all native binding code now takes place almost entirely in ShareSys, and PluginSys supplements this logic where appropriate. extensionsys has been cleaned up
ds@1952
     1
/**
ds@1952
     2
 * vim: set ts=4 :
ds@1952
     3
 * =============================================================================
ds@1952
     4
 * SourceMod
ds@1952
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
ds@1952
     6
 * =============================================================================
ds@1952
     7
 *
ds@1952
     8
 * This program is free software; you can redistribute it and/or modify it under
ds@1952
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
ds@1952
    10
 * Free Software Foundation.
ds@1952
    11
 * 
ds@1952
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1952
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1952
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
ds@1952
    15
 * details.
ds@1952
    16
 *
ds@1952
    17
 * You should have received a copy of the GNU General Public License along with
ds@1952
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
ds@1952
    19
 *
ds@1952
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
ds@1952
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1952
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1952
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
ds@1952
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
ds@1952
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
ds@1952
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1952
    27
 * or <http://www.sourcemod.net/license.php>.
ds@1952
    28
 *
ds@1952
    29
 * Version: $Id$
ds@1952
    30
 */
ds@1952
    31
ds@1952
    32
#include <stdio.h>
ds@1952
    33
#include "PluginSys.h"
ds@1952
    34
#include "ShareSys.h"
ds@1952
    35
#include "LibrarySys.h"
ds@1952
    36
#include "HandleSys.h"
ds@1952
    37
#include "ForwardSys.h"
ds@1952
    38
#include "sourcemm_api.h"
ds@1952
    39
#include "sourcemod.h"
ds@1952
    40
#include "TextParsers.h"
ds@1952
    41
#include "Logger.h"
ds@1952
    42
#include "ExtensionSys.h"
ds@1952
    43
#include "sm_srvcmds.h"
ds@1952
    44
#include "sm_stringutil.h"
ds@1952
    45
#include "ConCmdManager.h"
ds@1952
    46
#include "PlayerManager.h"
ds@1952
    47
#include "CoreConfig.h"
ds@1952
    48
#include "Translator.h"
ds@1952
    49
ds@1952
    50
CPluginManager g_PluginSys;
ds@1952
    51
HandleType_t g_PluginType = 0;
ds@1952
    52
IdentityType_t g_PluginIdent = 0;
ds@1952
    53
ds@1952
    54
CPlugin::CPlugin(const char *file)
ds@1952
    55
{
ds@1952
    56
	static int MySerial = 0;
ds@1952
    57
ds@1952
    58
	m_type = PluginType_Private;
ds@1952
    59
	m_status = Plugin_Uncompiled;
ds@1952
    60
	m_serial = ++MySerial;
ds@1952
    61
	m_plugin = NULL;
ds@1952
    62
	m_errormsg[256] = '\0';
ds@1952
    63
	snprintf(m_filename, sizeof(m_filename), "%s", file);
ds@1952
    64
	m_handle = 0;
ds@1952
    65
	m_ident = NULL;
ds@1952
    66
	m_pProps = sm_trie_create();
ds@1952
    67
	m_FakeNativesMissing = false;
ds@1952
    68
	m_LibraryMissing = false;
ds@1952
    69
	m_bGotAllLoaded = false;
dvander@2042
    70
	m_pPhrases = g_Translator.CreatePhraseCollection();
ds@1952
    71
}
ds@1952
    72
ds@1952
    73
CPlugin::~CPlugin()
ds@1952
    74
{
ds@1952
    75
	if (m_handle)
ds@1952
    76
	{
ds@1952
    77
		HandleSecurity sec;
ds@1952
    78
		sec.pOwner = g_PluginSys.GetIdentity();
ds@1952
    79
		sec.pIdentity = sec.pOwner;
ds@1952
    80
ds@1952
    81
		g_HandleSys.FreeHandle(m_handle, &sec);
ds@1952
    82
		g_ShareSys.DestroyIdentity(m_ident);
ds@1952
    83
	}
ds@1952
    84
ds@1952
    85
	if (m_ctx.base)
ds@1952
    86
	{
ds@1952
    87
		delete m_ctx.base;
ds@1952
    88
		m_ctx.base = NULL;
ds@1952
    89
	}
ds@1952
    90
	if (m_ctx.ctx)
ds@1952
    91
	{
ds@1952
    92
		m_ctx.vm->FreeContext(m_ctx.ctx);
ds@1952
    93
		m_ctx.ctx = NULL;
ds@1952
    94
	}
ds@1952
    95
	if (m_ctx.co)
ds@1952
    96
	{
ds@1952
    97
		m_ctx.vm->AbortCompilation(m_ctx.co);
ds@1952
    98
		m_ctx.co = NULL;
ds@1952
    99
	}
ds@1952
   100
ds@1952
   101
	if (m_plugin)
ds@1952
   102
	{
ds@1952
   103
		g_pSourcePawn->FreeFromMemory(m_plugin);
ds@1952
   104
		m_plugin = NULL;
ds@1952
   105
	}
ds@1952
   106
	if (m_pProps)
ds@1952
   107
	{
ds@1952
   108
		sm_trie_destroy(m_pProps);
ds@1952
   109
	}
ds@1952
   110
	for (size_t i=0; i<m_configs.size(); i++)
ds@1952
   111
	{
ds@1952
   112
		delete m_configs[i];
ds@1952
   113
	}
ds@1952
   114
	m_configs.clear();
dvander@2042
   115
	if (m_pPhrases != NULL)
dvander@2042
   116
	{
dvander@2042
   117
		m_pPhrases->Destroy();
dvander@2042
   118
		m_pPhrases = NULL;
dvander@2042
   119
	}
ds@1952
   120
}
ds@1952
   121
ds@1952
   122
void CPlugin::InitIdentity()
ds@1952
   123
{
ds@1952
   124
	if (!m_handle)
ds@1952
   125
	{
ds@1952
   126
		m_ident = g_ShareSys.CreateIdentity(g_PluginIdent, this);
ds@1952
   127
		m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
ds@1952
   128
		m_ctx.base->SetIdentity(m_ident);
ds@1952
   129
	}
ds@1952
   130
}
ds@1952
   131
ds@1952
   132
unsigned int CPlugin::CalcMemUsage()
ds@1952
   133
{
ds@1952
   134
	unsigned int base_size = 
ds@1952
   135
		sizeof(CPlugin) 
ds@1952
   136
		+ sizeof(IdentityToken_t)
ds@1952
   137
		+ (m_configs.size() * (sizeof(AutoConfig *) + sizeof(AutoConfig)))
ds@1952
   138
		+ sm_trie_mem_usage(m_pProps);
ds@1952
   139
ds@1952
   140
	for (unsigned int i = 0; i < m_configs.size(); i++)
ds@1952
   141
	{
ds@1952
   142
		base_size += m_configs[i]->autocfg.size();
ds@1952
   143
		base_size += m_configs[i]->folder.size();
ds@1952
   144
	}
ds@1952
   145
ds@1952
   146
	for (List<String>::iterator i = m_Libraries.begin();
ds@1952
   147
		 i != m_Libraries.end();
ds@1952
   148
		 i++)
ds@1952
   149
	{
ds@1952
   150
		base_size += (*i).size();
ds@1952
   151
	}
ds@1952
   152
ds@1952
   153
	for (List<String>::iterator i = m_RequiredLibs.begin();
ds@1952
   154
		 i != m_RequiredLibs.end();
ds@1952
   155
		 i++)
ds@1952
   156
	{
ds@1952
   157
		base_size += (*i).size();
ds@1952
   158
	}
ds@1952
   159
ds@1952
   160
	if (m_plugin != NULL)
ds@1952
   161
	{
ds@1952
   162
		base_size += sizeof(sp_plugin_t);
ds@1952
   163
		base_size += m_plugin->data_size;
ds@1952
   164
		base_size += m_plugin->pcode_size;
ds@1952
   165
		base_size += (m_plugin->info.natives_num * sizeof(sp_file_natives_t));
ds@1952
   166
		base_size += (m_plugin->info.publics_num * sizeof(sp_file_publics_t));
ds@1952
   167
		base_size += (m_plugin->info.pubvars_num * sizeof(sp_file_pubvars_t));
ds@1952
   168
		base_size += (m_plugin->debug.files_num * sizeof(sp_fdbg_file_t));
ds@1952
   169
		base_size += (m_plugin->debug.lines_num * sizeof(sp_fdbg_line_t));
ds@1952
   170
		base_size += (m_plugin->debug.syms_num * sizeof(sp_fdbg_symbol_t));
ds@1952
   171
		/* We can't get strtab size, oh well. */
ds@1952
   172
	}
ds@1952
   173
ds@1952
   174
	if (m_ctx.base != NULL)
ds@1952
   175
	{
ds@1952
   176
		base_size += sizeof(BaseContext);
ds@1952
   177
		base_size += m_ctx.base->GetPublicsNum() * sizeof(CFunction);
ds@1952
   178
	}
ds@1952
   179
	if (m_ctx.ctx != NULL)
ds@1952
   180
	{
ds@1952
   181
		base_size += m_ctx.ctx->mem_size;
ds@1952
   182
		base_size += (m_plugin->debug.files_num * sizeof(sp_debug_file_t));
ds@1952
   183
		base_size += (m_plugin->debug.lines_num * sizeof(sp_debug_line_t));
ds@1952
   184
		base_size += (m_plugin->debug.syms_num * sizeof(sp_debug_symbol_t));
ds@1952
   185
		base_size += (m_plugin->info.pubvars_num * sizeof(sp_pubvar_t));
ds@1952
   186
		base_size += (m_plugin->info.publics_num * sizeof(sp_public_t));
ds@1952
   187
		base_size += (m_plugin->info.natives_num * sizeof(sp_native_t));
ds@1952
   188
		/* We also don't know the JIT code size, oh well. */
ds@1952
   189
	}
ds@1952
   190
ds@1952
   191
	return base_size;
ds@1952
   192
}
ds@1952
   193
ds@1952
   194
Handle_t CPlugin::GetMyHandle()
ds@1952
   195
{
ds@1952
   196
	return m_handle;
ds@1952
   197
}
ds@1952
   198
ds@1952
   199
CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
ds@1952
   200
{
ds@1952
   201
	char fullpath[PLATFORM_MAX_PATH];
ds@1952
   202
	g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file);
ds@1952
   203
	FILE *fp = fopen(fullpath, "rb");
ds@1952
   204
ds@1952
   205
	CPlugin *pPlugin = new CPlugin(file);
ds@1952
   206
ds@1952
   207
	if (!fp)
ds@1952
   208
	{
ds@1952
   209
		if (error)
ds@1952
   210
		{
ds@1952
   211
			snprintf(error, maxlength, "Unable to open file");
ds@1952
   212
		}
ds@1952
   213
		pPlugin->m_status = Plugin_BadLoad;
ds@1952
   214
		return pPlugin;
ds@1952
   215
	}
ds@1952
   216
ds@1952
   217
	int err;
ds@1952
   218
	sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err);
ds@1952
   219
	if (pl == NULL)
ds@1952
   220
	{
ds@1952
   221
		fclose(fp);
ds@1952
   222
		if (error)
ds@1952
   223
		{
ds@1952
   224
			snprintf(error, maxlength, "Error %d while parsing plugin", err);
ds@1952
   225
		}
ds@1952
   226
		pPlugin->m_status = Plugin_BadLoad;
ds@1952
   227
		return pPlugin;
ds@1952
   228
	}
ds@1952
   229
ds@1952
   230
	fclose(fp);
ds@1952
   231
ds@1952
   232
	pPlugin->m_plugin = pl;
ds@1952
   233
ds@1952
   234
	return pPlugin;
ds@1952
   235
}
ds@1952
   236
ds@1952
   237
bool CPlugin::GetProperty(const char *prop, void **ptr, bool remove/* =false */)
ds@1952
   238
{
ds@1952
   239
	bool exists = sm_trie_retrieve(m_pProps, prop, ptr);
ds@1952
   240
ds@1952
   241
	if (exists && remove)
ds@1952
   242
	{
ds@1952
   243
		sm_trie_delete(m_pProps, prop);
ds@1952
   244
	}
ds@1952
   245
ds@1952
   246
	return exists;
ds@1952
   247
}
ds@1952
   248
ds@1952
   249
bool CPlugin::SetProperty(const char *prop, void *ptr)
ds@1952
   250
{
ds@1952
   251
	return sm_trie_insert(m_pProps, prop, ptr);
ds@1952
   252
}
ds@1952
   253
ds@1952
   254
ICompilation *CPlugin::StartMyCompile(IVirtualMachine *vm)
ds@1952
   255
{
ds@1952
   256
	if (!m_plugin)
ds@1952
   257
	{
ds@1952
   258
		return NULL;
ds@1952
   259
	}
ds@1952
   260
ds@1952
   261
	/* :NOTICE: We will eventually need to change these natives
ds@1952
   262
	 * for swapping in new contexts
ds@1952
   263
	 */
ds@1952
   264
	if (m_ctx.co || m_ctx.ctx)
ds@1952
   265
	{
ds@1952
   266
		return NULL;
ds@1952
   267
	}
ds@1952
   268
ds@1952
   269
	m_status = Plugin_Uncompiled;
ds@1952
   270
ds@1952
   271
	m_ctx.vm = vm;
ds@1952
   272
	m_ctx.co = vm->StartCompilation(m_plugin);
ds@1952
   273
ds@1952
   274
	return m_ctx.co;
ds@1952
   275
}
ds@1952
   276
ds@1952
   277
void CPlugin::CancelMyCompile()
ds@1952
   278
{
ds@1952
   279
	if (!m_ctx.co)
ds@1952
   280
	{
ds@1952
   281
		return;
ds@1952
   282
	}
ds@1952
   283
ds@1952
   284
	m_ctx.vm->AbortCompilation(m_ctx.co);
ds@1952
   285
	m_ctx.co = NULL;
ds@1952
   286
	m_ctx.vm = NULL;
ds@1952
   287
}
ds@1952
   288
ds@1952
   289
bool CPlugin::FinishMyCompile(char *error, size_t maxlength)
ds@1952
   290
{
ds@1952
   291
	if (!m_ctx.co)
ds@1952
   292
	{
ds@1952
   293
		return false;
ds@1952
   294
	}
ds@1952
   295
ds@1952
   296
	int err;
ds@1952
   297
	m_ctx.ctx = m_ctx.vm->CompileToContext(m_ctx.co, &err);
ds@1952
   298
	if (!m_ctx.ctx)
ds@1952
   299
	{
ds@1952
   300
		memset(&m_ctx, 0, sizeof(m_ctx));
ds@1952
   301
		if (error)
ds@1952
   302
		{
ds@1952
   303
			snprintf(error, maxlength, "JIT failed to compile (error %d)", err);
ds@1952
   304
		}
ds@1952
   305
		return false;
ds@1952
   306
	}
ds@1952
   307
ds@1952
   308
	m_ctx.base = new BaseContext(m_ctx.ctx);
ds@1952
   309
	m_ctx.ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)this;
ds@1952
   310
ds@1952
   311
	m_status = Plugin_Created;
ds@1952
   312
	m_ctx.co = NULL;
ds@1952
   313
ds@1952
   314
	UpdateInfo();
ds@1952
   315
ds@1952
   316
	return true;
ds@1952
   317
}
ds@1952
   318
ds@1952
   319
void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
ds@1952
   320
{
ds@1952
   321
	PluginStatus old_status = m_status;
ds@1952
   322
	m_status = status;
ds@1952
   323
ds@1952
   324
	if (old_status == Plugin_Running)
ds@1952
   325
	{
ds@1952
   326
		/* Tell everyone we're now paused */
ds@1952
   327
		g_PluginSys._SetPauseState(this, true);
ds@1952
   328
	}
ds@1952
   329
ds@1952
   330
	va_list ap;
ds@1952
   331
	va_start(ap, error_fmt);
ds@1952
   332
	vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
ds@1952
   333
	va_end(ap);
ds@1952
   334
ds@1952
   335
	if (m_ctx.ctx)
ds@1952
   336
	{
ds@1952
   337
		m_ctx.ctx->flags |= SPFLAG_PLUGIN_PAUSED;
ds@1952
   338
	}
ds@1952
   339
}
ds@1952
   340
ds@1952
   341
void CPlugin::UpdateInfo()
ds@1952
   342
{
ds@1952
   343
	/* Now grab the info */
ds@1952
   344
	uint32_t idx;
ds@1952
   345
	IPluginContext *base = GetBaseContext();
ds@1952
   346
	int err = base->FindPubvarByName("myinfo", &idx);
ds@1952
   347
ds@1952
   348
	memset(&m_info, 0, sizeof(m_info));
ds@1952
   349
ds@1952
   350
	if (err == SP_ERROR_NONE)
ds@1952
   351
	{
ds@1952
   352
		struct sm_plugininfo_c_t
ds@1952
   353
		{
ds@1952
   354
			cell_t name;
ds@1952
   355
			cell_t description;
ds@1952
   356
			cell_t author;
ds@1952
   357
			cell_t version;
ds@1952
   358
			cell_t url;
ds@1952
   359
		};
ds@1952
   360
		sm_plugininfo_c_t *cinfo;
ds@1952
   361
		cell_t local_addr;
ds@1952
   362
ds@1952
   363
		base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
ds@1952
   364
		base->LocalToString(cinfo->name, (char **)&m_info.name);
ds@1952
   365
		base->LocalToString(cinfo->description, (char **)&m_info.description);
ds@1952
   366
		base->LocalToString(cinfo->author, (char **)&m_info.author);
ds@1952
   367
		base->LocalToString(cinfo->url, (char **)&m_info.url);
ds@1952
   368
		base->LocalToString(cinfo->version, (char **)&m_info.version);
ds@1952
   369
	}
ds@1952
   370
ds@1952
   371
	m_info.author = m_info.author ? m_info.author : "";
ds@1952
   372
	m_info.description = m_info.description ? m_info.description : "";
ds@1952
   373
	m_info.name = m_info.name ? m_info.name : "";
ds@1952
   374
	m_info.url = m_info.url ? m_info.url : "";
ds@1952
   375
	m_info.version = m_info.version ? m_info.version : "";
ds@1952
   376
}
ds@1952
   377
ds@1952
   378
void CPlugin::Call_OnPluginStart()
ds@1952
   379
{
ds@1952
   380
	if (m_status != Plugin_Loaded)
ds@1952
   381
	{
ds@1952
   382
		return;
ds@1952
   383
	}
ds@1952
   384
ds@1952
   385
	m_status = Plugin_Running;
ds@1952
   386
ds@1952
   387
	cell_t result;
ds@1952
   388
	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginStart");
ds@1952
   389
	if (!pFunction)
ds@1952
   390
	{
ds@1952
   391
		return;
ds@1952
   392
	}
ds@1952
   393
ds@1952
   394
	int err;
ds@1952
   395
	if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
ds@1952
   396
	{
ds@1952
   397
		SetErrorState(Plugin_Error, "Error detected in plugin startup (see error logs)");
ds@1952
   398
	}
ds@1952
   399
}
ds@1952
   400
ds@1952
   401
void CPlugin::Call_OnPluginEnd()
ds@1952
   402
{
ds@1952
   403
	if (m_status > Plugin_Paused)
ds@1952
   404
	{
ds@1952
   405
		return;
ds@1952
   406
	}
ds@1952
   407
ds@1952
   408
	cell_t result;
ds@1952
   409
	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginEnd");
ds@1952
   410
	if (!pFunction)
ds@1952
   411
	{
ds@1952
   412
		return;
ds@1952
   413
	}
ds@1952
   414
ds@1952
   415
	pFunction->Execute(&result);
ds@1952
   416
}
ds@1952
   417
ds@1952
   418
void CPlugin::Call_OnAllPluginsLoaded()
ds@1952
   419
{
ds@1952
   420
	if (m_status > Plugin_Paused)
ds@1952
   421
	{
ds@1952
   422
		return;
ds@1952
   423
	}
ds@1952
   424
ds@1952
   425
	if (m_bGotAllLoaded)
ds@1952
   426
	{
ds@1952
   427
		return;
ds@1952
   428
	}
ds@1952
   429
ds@1952
   430
	m_bGotAllLoaded = true;
ds@1952
   431
ds@1952
   432
	cell_t result;
ds@1952
   433
	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnAllPluginsLoaded");
ds@1952
   434
	if (pFunction != NULL)
ds@1952
   435
	{
ds@1952
   436
		pFunction->Execute(&result);
ds@1952
   437
	}
ds@1952
   438
ds@1952
   439
	if (g_OnMapStarted)
ds@1952
   440
	{
ds@1952
   441
		if ((pFunction = m_ctx.base->GetFunctionByName("OnMapStart")) != NULL)
ds@1952
   442
		{
ds@1952
   443
			pFunction->Execute(NULL);
ds@1952
   444
		}
ds@1952
   445
	}
ds@1952
   446
ds@1952
   447
	if (SM_AreConfigsExecuted())
ds@1952
   448
	{
ds@1952
   449
		SM_ExecuteForPlugin(GetBaseContext());
ds@1952
   450
	}
ds@1952
   451
}
ds@1952
   452
ds@1952
   453
bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
ds@1952
   454
{
ds@1952
   455
	if (m_status != Plugin_Created)
ds@1952
   456
	{
ds@1952
   457
		return false;
ds@1952
   458
	}
ds@1952
   459
ds@1952
   460
	m_status = Plugin_Loaded;
ds@1952
   461
ds@1952
   462
	int err;
ds@1952
   463
	cell_t result;
ds@1952
   464
	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("AskPluginLoad");
ds@1952
   465
ds@1952
   466
	if (!pFunction)
ds@1952
   467
	{
ds@1952
   468
		return true;
ds@1952
   469
	}
ds@1952
   470
ds@1952
   471
	pFunction->PushCell(m_handle);
ds@1952
   472
	pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
ds@1952
   473
	pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
ds@1952
   474
	pFunction->PushCell(maxlength);
ds@1952
   475
	if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
ds@1952
   476
	{
ds@1952
   477
		return false;
ds@1952
   478
	}
ds@1952
   479
ds@1952
   480
	if (!result || m_status != Plugin_Loaded)
ds@1952
   481
	{
ds@1952
   482
		return false;
ds@1952
   483
	}
ds@1952
   484
ds@1952
   485
	return true;
ds@1952
   486
}
ds@1952
   487
ds@1952
   488
const sp_plugin_t *CPlugin::GetPluginStructure()
ds@1952
   489
{
ds@1952
   490
	return m_plugin;
ds@1952
   491
}
ds@1952
   492
ds@1952
   493
IPluginContext *CPlugin::GetBaseContext()
ds@1952
   494
{
ds@1952
   495
	return m_ctx.base;
ds@1952
   496
}
ds@1952
   497
ds@1952
   498
sp_context_t *CPlugin::GetContext()
ds@1952
   499
{
ds@1952
   500
	return m_ctx.ctx;
ds@1952
   501
}
ds@1952
   502
ds@1952
   503
const char *CPlugin::GetFilename()
ds@1952
   504
{
ds@1952
   505
	return m_filename;
ds@1952
   506
}
ds@1952
   507
ds@1952
   508
PluginType CPlugin::GetType()
ds@1952
   509
{
ds@1952
   510
	return m_type;
ds@1952
   511
}
ds@1952
   512
ds@1952
   513
const sm_plugininfo_t *CPlugin::GetPublicInfo()
ds@1952
   514
{
ds@1952
   515
	return &m_info;
ds@1952
   516
}
ds@1952
   517
ds@1952
   518
unsigned int CPlugin::GetSerial()
ds@1952
   519
{
ds@1952
   520
	return m_serial;
ds@1952
   521
}
ds@1952
   522
ds@1952
   523
PluginStatus CPlugin::GetStatus()
ds@1952
   524
{
ds@1952
   525
	return m_status;
ds@1952
   526
}
ds@1952
   527
ds@1952
   528
bool CPlugin::IsDebugging()
ds@1952
   529
{
ds@1952
   530
	if (!m_ctx.ctx)
ds@1952
   531
	{
ds@1952
   532
		return false;
ds@1952
   533
	}
ds@1952
   534
ds@1952
   535
	return ((m_ctx.ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
ds@1952
   536
}
ds@1952
   537
ds@1952
   538
void CPlugin::LibraryActions(bool dropping)
ds@1952
   539
{
ds@1952
   540
	List<String>::iterator iter;
ds@1952
   541
	for (iter = m_Libraries.begin();
ds@1952
   542
		iter != m_Libraries.end();
ds@1952
   543
		iter++)
ds@1952
   544
	{
ds@1952
   545
		g_PluginSys.OnLibraryAction((*iter).c_str(), true, dropping);
ds@1952
   546
	}
ds@1952
   547
}
ds@1952
   548
ds@1952
   549
bool CPlugin::SetPauseState(bool paused)
ds@1952
   550
{
ds@1952
   551
	if (paused && GetStatus() != Plugin_Running)
ds@1952
   552
	{
ds@1952
   553
		return false;
ds@1952
   554
	} else if (!paused && GetStatus() != Plugin_Paused) {
ds@1952
   555
		return false;
ds@1952
   556
	}
ds@1952
   557
ds@1952
   558
	if (paused)
ds@1952
   559
	{
ds@1952
   560
		LibraryActions(true);
ds@1952
   561
	}
ds@1952
   562
ds@1952
   563
	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginPauseChange");
ds@1952
   564
	if (pFunction)
ds@1952
   565
	{
ds@1952
   566
		cell_t result;
ds@1952
   567
		pFunction->PushCell(paused ? 1 : 0);
ds@1952
   568
		pFunction->Execute(&result);
ds@1952
   569
	}
ds@1952
   570
ds@1952
   571
	if (paused)
ds@1952
   572
	{
ds@1952
   573
		m_status = Plugin_Paused;
ds@1952
   574
		m_ctx.ctx->flags |= SPFLAG_PLUGIN_PAUSED;
ds@1952
   575
	} else {
ds@1952
   576
		m_status = Plugin_Running;
ds@1952
   577
		m_ctx.ctx->flags &= ~SPFLAG_PLUGIN_PAUSED;
ds@1952
   578
	}
ds@1952
   579
ds@1952
   580
	g_PluginSys._SetPauseState(this, paused);
ds@1952
   581
ds@1952
   582
	if (!paused)
ds@1952
   583
	{
ds@1952
   584
		LibraryActions(false);
ds@1952
   585
	}
ds@1952
   586
ds@1952
   587
	return true;
ds@1952
   588
}
ds@1952
   589
ds@1952
   590
IdentityToken_t *CPlugin::GetIdentity()
ds@1952
   591
{
ds@1952
   592
	return m_ident;
ds@1952
   593
}
ds@1952
   594
ds@1952
   595
bool CPlugin::ToggleDebugMode(bool debug, char *error, size_t maxlength)
ds@1952
   596
{
ds@1952
   597
	int err;
ds@1952
   598
ds@1952
   599
	if (!IsRunnable())
ds@1952
   600
	{
ds@1952
   601
		if (error)
ds@1952
   602
		{
ds@1952
   603
			snprintf(error, maxlength, "Plugin is not runnable.");
ds@1952
   604
		}
ds@1952
   605
		return false;
ds@1952
   606
	}
ds@1952
   607
ds@1952
   608
	if (debug && IsDebugging())
ds@1952
   609
	{
ds@1952
   610
		if (error)
ds@1952
   611
		{
ds@1952
   612
			snprintf(error, maxlength, "Plugin is already in debug mode.");
ds@1952
   613
		}
ds@1952
   614
		return false;
ds@1952
   615
	}
ds@1952
   616
	else if (!debug && !IsDebugging())
ds@1952
   617
	{
ds@1952
   618
		if (error)
ds@1952
   619
		{
ds@1952
   620
			snprintf(error, maxlength, "Plugins is already in production mode.");
ds@1952
   621
		}
ds@1952
   622
		return false;
ds@1952
   623
	}
ds@1952
   624
ds@1952
   625
	ICompilation *co = g_pVM->StartCompilation(m_ctx.ctx->plugin);
ds@1952
   626
	if (!g_pVM->SetCompilationOption(co, "debug", (debug) ? "1" : "0"))
ds@1952
   627
	{
ds@1952
   628
		if (error)
ds@1952
   629
		{
ds@1952
   630
			snprintf(error, maxlength, "Failed to change plugin mode (JIT failure).");
ds@1952
   631
		}
ds@1952
   632
		return false;
ds@1952
   633
	}
ds@1952
   634
ds@1952
   635
	sp_context_t *new_ctx = g_pVM->CompileToContext(co, &err);
ds@1952
   636
ds@1952
   637
	if (new_ctx)
ds@1952
   638
	{
ds@1952
   639
		memcpy(new_ctx->memory, m_ctx.ctx->memory, m_ctx.ctx->mem_size);
ds@1952
   640
		new_ctx->hp = m_ctx.ctx->hp;
ds@1952
   641
		new_ctx->sp = m_ctx.ctx->sp;
ds@1952
   642
		new_ctx->frm = m_ctx.ctx->frm;
ds@1952
   643
		new_ctx->dbreak = m_ctx.ctx->dbreak;
ds@1952
   644
		new_ctx->context = m_ctx.ctx->context;
ds@1952
   645
		memcpy(new_ctx->user, m_ctx.ctx->user, sizeof(m_ctx.ctx->user));
ds@1952
   646
ds@1952
   647
		uint32_t nativeCount = m_plugin->info.natives_num;
ds@1952
   648
		for (uint32_t i=0; i<nativeCount; i++)
ds@1952
   649
		{
ds@1952
   650
			new_ctx->natives[i].pfn = m_ctx.ctx->natives[i].pfn;
ds@1952
   651
			new_ctx->natives[i].status = m_ctx.ctx->natives[i].status;
ds@1952
   652
		}
ds@1952
   653
ds@1952
   654
		g_pVM->FreeContext(m_ctx.ctx);
ds@1952
   655
		m_ctx.ctx = new_ctx;
ds@1952
   656
		m_ctx.base->SetContext(new_ctx);
ds@1952
   657
ds@1952
   658
		UpdateInfo();
ds@1952
   659
	}
ds@1952
   660
	else
ds@1952
   661
	{
ds@1952
   662
		if (error)
ds@1952
   663
		{
ds@1952
   664
			snprintf(error, maxlength, "Failed to recompile plugin (JIT error %d).", err);
ds@1952
   665
		}
ds@1952
   666
		return false;
ds@1952
   667
	}
ds@1952
   668
ds@1952
   669
	return true;
ds@1952
   670
}
ds@1952
   671
ds@1952
   672
bool CPlugin::IsRunnable()
ds@1952
   673
{
ds@1952
   674
	return (m_status <= Plugin_Paused) ? true : false;
ds@1952
   675
}
ds@1952
   676
ds@1952
   677
time_t CPlugin::GetFileTimeStamp()
ds@1952
   678
{
ds@1952
   679
	char path[PLATFORM_MAX_PATH];
ds@1952
   680
	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename);
ds@1952
   681
#ifdef PLATFORM_WINDOWS
ds@1952
   682
	struct _stat s;
ds@1952
   683
	if (_stat(path, &s) != 0)
ds@1952
   684
#elif defined PLATFORM_POSIX
ds@1952
   685
	struct stat s;
ds@1952
   686
	if (stat(path, &s) != 0)
ds@1952
   687
#endif
ds@1952
   688
	{
ds@1952
   689
		return 0;
ds@1952
   690
	}
ds@1952
   691
	else
ds@1952
   692
	{
ds@1952
   693
		return s.st_mtime;
ds@1952
   694
	}
ds@1952
   695
}
ds@1952
   696
ds@1952
   697
time_t CPlugin::GetTimeStamp()
ds@1952
   698
{
ds@1952
   699
	return m_LastAccess;
ds@1952
   700
}
ds@1952
   701
ds@1952
   702
void CPlugin::SetTimeStamp(time_t t)
ds@1952
   703
{
ds@1952
   704
	m_LastAccess = t;
ds@1952
   705
}
ds@1952
   706
dvander@2042
   707
IPhraseCollection *CPlugin::GetPhrases()
ds@1952
   708
{
dvander@2042
   709
	return m_pPhrases;
ds@1952
   710
}
ds@1952
   711
ds@1952
   712
void CPlugin::DependencyDropped(CPlugin *pOwner)
ds@1952
   713
{
ds@1952
   714
	if (!m_ctx.ctx)
ds@1952
   715
	{
ds@1952
   716
		return;
ds@1952
   717
	}
ds@1952
   718
ds@1952
   719
	List<String>::iterator reqlib_iter;
ds@1952
   720
	List<String>::iterator lib_iter;
ds@1952
   721
	for (lib_iter=pOwner->m_Libraries.begin(); lib_iter!=pOwner->m_Libraries.end(); lib_iter++)
ds@1952
   722
	{
ds@1952
   723
		for (reqlib_iter=m_RequiredLibs.begin(); reqlib_iter!=m_RequiredLibs.end(); reqlib_iter++)
ds@1952
   724
		{
ds@1952
   725
			if ((*reqlib_iter) == (*lib_iter))
ds@1952
   726
			{
ds@1952
   727
				m_LibraryMissing = true;
ds@1952
   728
			}
ds@1952
   729
		}	
ds@1952
   730
	}
ds@1952
   731
dvander@2086
   732
	List<NativeEntry *>::iterator iter;
dvander@2086
   733
	NativeEntry *pNative;
ds@1952
   734
	sp_native_t *native;
ds@1952
   735
	uint32_t idx;
ds@1952
   736
	unsigned int unbound = 0;
ds@1952
   737
dvander@2086
   738
	for (iter = pOwner->m_Natives.begin();
dvander@2086
   739
		 iter != pOwner->m_Natives.end();
ds@1952
   740
		 iter++)
ds@1952
   741
	{
ds@1952
   742
		pNative = (*iter);
ds@1952
   743
		/* Find this native! */
dvander@2086
   744
		if (m_ctx.base->FindNativeByName(pNative->name, &idx) != SP_ERROR_NONE)
ds@1952
   745
		{
ds@1952
   746
			continue;
ds@1952
   747
		}
ds@1952
   748
		/* Unbind it */
ds@1952
   749
		m_ctx.base->GetNativeByIndex(idx, &native);
ds@1952
   750
		native->pfn = NULL;
ds@1952
   751
		native->status = SP_NATIVE_UNBOUND;
ds@1952
   752
		unbound++;
ds@1952
   753
	}
ds@1952
   754
ds@1952
   755
	if (unbound)
ds@1952
   756
	{
ds@1952
   757
		m_FakeNativesMissing = true;
ds@1952
   758
	}
ds@1952
   759
ds@1952
   760
	/* :IDEA: in the future, add native trapping? */
ds@1952
   761
	if (m_FakeNativesMissing || m_LibraryMissing)
ds@1952
   762
	{
ds@1952
   763
		SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename());
ds@1952
   764
	}
ds@1952
   765
}
ds@1952
   766
ds@1952
   767
unsigned int CPlugin::GetConfigCount()
ds@1952
   768
{
ds@1952
   769
	return (unsigned int)m_configs.size();
ds@1952
   770
}
ds@1952
   771
ds@1952
   772
AutoConfig *CPlugin::GetConfig(unsigned int i)
ds@1952
   773
{
ds@1952
   774
	if (i >= GetConfigCount())
ds@1952
   775
	{
ds@1952
   776
		return NULL;
ds@1952
   777
	}
ds@1952
   778
ds@1952
   779
	return m_configs[i];
ds@1952
   780
}
ds@1952
   781
ds@1952
   782
void CPlugin::AddConfig(bool autoCreate, const char *cfg, const char *folder)
ds@1952
   783
{
ds@1952
   784
	/* Do a check for duplicates to prevent double-execution */
ds@1952
   785
	for (size_t i = 0; i < m_configs.size(); i++)
ds@1952
   786
	{
ds@1952
   787
		if (m_configs[i]->autocfg.compare(cfg) == 0
ds@1952
   788
			&& m_configs[i]->folder.compare(folder) == 0
ds@1952
   789
			&& m_configs[i]->create == autoCreate)
ds@1952
   790
		{
ds@1952
   791
			return;
ds@1952
   792
		}
ds@1952
   793
	}
ds@1952
   794
ds@1952
   795
	AutoConfig *c = new AutoConfig;
ds@1952
   796
ds@1952
   797
	c->autocfg = cfg;
ds@1952
   798
	c->folder = folder;
ds@1952
   799
	c->create = autoCreate;
ds@1952
   800
ds@1952
   801
	m_configs.push_back(c);
ds@1952
   802
}
ds@1952
   803
dvander@2086
   804
void CPlugin::DropEverything()
dvander@2086
   805
{
dvander@2086
   806
	CPlugin *pOther;
dvander@2086
   807
	List<CPlugin *>::iterator iter;
dvander@2086
   808
	List<WeakNative>::iterator wk_iter;
dvander@2086
   809
dvander@2086
   810
	/* Tell everyone that depends on us that we're about to drop */
dvander@2086
   811
	for (iter = m_Dependents.begin();
dvander@2086
   812
		 iter != m_Dependents.end(); 
dvander@2086
   813
		 iter++)
dvander@2086
   814
	{
dvander@2086
   815
		pOther = (*iter);
dvander@2086
   816
		pOther->DependencyDropped(this);
dvander@2086
   817
	}
dvander@2086
   818
dvander@2086
   819
	/* Note: we don't care about things we depend on. 
dvander@2086
   820
	 * The reason is that extensions have their own cleanup 
dvander@2086
   821
	 * code for plugins.  Although the "right" design would be 
dvander@2086
   822
	 * to centralize that here, i'm omitting it for now.  Thus, 
dvander@2086
   823
	 * the code below to walk the plugins list will suffice.
dvander@2086
   824
	 */
dvander@2086
   825
	
dvander@2086
   826
	/* Other plugins could be holding weak references that were 
dvander@2086
   827
	 * added by us.  We need to clean all of those up now.
dvander@2086
   828
	 */
dvander@2086
   829
	for (iter = g_PluginSys.m_plugins.begin();
dvander@2086
   830
		 iter != g_PluginSys.m_plugins.end();
dvander@2086
   831
		 iter++)
dvander@2086
   832
	{
dvander@2086
   833
		(*iter)->DropRefsTo(this);
dvander@2086
   834
	}
dvander@2086
   835
dvander@2086
   836
	/* Proceed with the rest of the necessities. */
dvander@2086
   837
	CNativeOwner::DropEverything();
dvander@2086
   838
}
dvander@2086
   839
dvander@2086
   840
bool CPlugin::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func)
dvander@2086
   841
{
dvander@2086
   842
	NativeEntry *pEntry;
dvander@2086
   843
dvander@2086
   844
	if ((pEntry = g_ShareSys.AddFakeNative(pFunc, name, func)) == NULL)
dvander@2086
   845
	{
dvander@2086
   846
		return false;
dvander@2086
   847
	}
dvander@2086
   848
dvander@2086
   849
	m_Natives.push_back(pEntry);
dvander@2086
   850
dvander@2086
   851
	return true;
dvander@2086
   852
}
dvander@2086
   853
ds@1952
   854
/*******************
ds@1952
   855
 * PLUGIN ITERATOR *
ds@1952
   856
 *******************/
ds@1952
   857
ds@1952
   858
CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
ds@1952
   859
{
ds@1952
   860
	mylist = _mylist;
ds@1952
   861
	Reset();
ds@1952
   862
}
ds@1952
   863
ds@1952
   864
IPlugin *CPluginManager::CPluginIterator::GetPlugin()
ds@1952
   865
{
ds@1952
   866
	return (*current);
ds@1952
   867
}
ds@1952
   868
ds@1952
   869
bool CPluginManager::CPluginIterator::MorePlugins()
ds@1952
   870
{
ds@1952
   871
	return (current != mylist->end());
ds@1952
   872
}
ds@1952
   873
ds@1952
   874
void CPluginManager::CPluginIterator::NextPlugin()
ds@1952
   875
{
ds@1952
   876
	current++;
ds@1952
   877
}
ds@1952
   878
ds@1952
   879
void CPluginManager::CPluginIterator::Release()
ds@1952
   880
{
ds@1952
   881
	g_PluginSys.ReleaseIterator(this);
ds@1952
   882
}
ds@1952
   883
ds@1952
   884
CPluginManager::CPluginIterator::~CPluginIterator()
ds@1952
   885
{
ds@1952
   886
}
ds@1952
   887
ds@1952
   888
void CPluginManager::CPluginIterator::Reset()
ds@1952
   889
{
ds@1952
   890
	current = mylist->begin();
ds@1952
   891
}
ds@1952
   892
ds@1952
   893
/******************
ds@1952
   894
 * PLUGIN MANAGER *
ds@1952
   895
 ******************/
ds@1952
   896
ds@1952
   897
CPluginManager::CPluginManager()
ds@1952
   898
{
ds@1952
   899
	m_LoadLookup = sm_trie_create();
ds@1952
   900
	m_AllPluginsLoaded = false;
ds@1952
   901
	m_MyIdent = NULL;
ds@1952
   902
	m_LoadingLocked = false;
ds@1952
   903
}
ds@1952
   904
ds@1952
   905
CPluginManager::~CPluginManager()
ds@1952
   906
{
ds@1952
   907
	/* :NOTICE: 
ds@1952
   908
	 * Ignore the fact that there might be plugins in the cache.
ds@1952
   909
	 * This usually means that Core is not being unloaded properly, and everything
ds@1952
   910
	 * will crash anyway.  YAY
ds@1952
   911
	 */
ds@1952
   912
	sm_trie_destroy(m_LoadLookup);
ds@1952
   913
ds@1952
   914
	CStack<CPluginManager::CPluginIterator *>::iterator iter;
ds@1952
   915
	for (iter=m_iters.begin(); iter!=m_iters.end(); iter++)
ds@1952
   916
	{
ds@1952
   917
		delete (*iter);
ds@1952
   918
	}
ds@1952
   919
	m_iters.popall();
ds@1952
   920
}
ds@1952
   921
ds@1952
   922
void CPluginManager::Shutdown()
ds@1952
   923
{
ds@1952
   924
	List<CPlugin *>::iterator iter;
ds@1952
   925
ds@1952
   926
	while ((iter = m_plugins.begin()) != m_plugins.end())
ds@1952
   927
	{
ds@1952
   928
		UnloadPlugin(*iter);
ds@1952
   929
	}
ds@1952
   930
}
ds@1952
   931
ds@1952
   932
void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
ds@1952
   933
{
ds@1952
   934
	/* First read in the database of plugin settings */
ds@1952
   935
	SMCError err;
ds@1952
   936
	SMCStates states;
ds@1952
   937
	m_AllPluginsLoaded = false;
ds@1952
   938
	if ((err=textparsers->ParseFile_SMC(config, &m_PluginInfo, &states)) != SMCError_Okay)
ds@1952
   939
	{
ds@1952
   940
		g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
ds@1952
   941
		const char *err_msg = textparsers->GetSMCErrorString(err);
ds@1952
   942
		if (err_msg)
ds@1952
   943
		{
ds@1952
   944
			g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg);
ds@1952
   945
		}
ds@1952
   946
	}
ds@1952
   947
ds@1952
   948
	LoadPluginsFromDir(basedir, NULL);
ds@1952
   949
}
ds@1952
   950
ds@1952
   951
void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
ds@1952
   952
{
ds@1952
   953
	char base_path[PLATFORM_MAX_PATH];
ds@1952
   954
ds@1952
   955
	/* Form the current path to start reading from */
ds@1952
   956
	if (localpath == NULL)
ds@1952
   957
	{
ds@1952
   958
		g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
ds@1952
   959
	} else {
ds@1952
   960
		g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
ds@1952
   961
	}
ds@1952
   962
ds@1952
   963
	IDirectory *dir = g_LibSys.OpenDirectory(base_path);
ds@1952
   964
ds@1952
   965
	if (!dir)
ds@1952
   966
	{
ds@1952
   967
		char error[256];
ds@1952
   968
		g_LibSys.GetPlatformError(error, sizeof(error));
ds@1952
   969
		g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath);
ds@1952
   970
		g_Logger.LogError("[SM] Platform returned error: %s", error);
ds@1952
   971
		return;
ds@1952
   972
	}
ds@1952
   973
ds@1952
   974
	while (dir->MoreFiles())
ds@1952
   975
	{
ds@1952
   976
		if (dir->IsEntryDirectory() 
ds@1952
   977
			&& (strcmp(dir->GetEntryName(), ".") != 0)
ds@1952
   978
			&& (strcmp(dir->GetEntryName(), "..") != 0)
ds@1952
   979
			&& (strcmp(dir->GetEntryName(), "disabled") != 0)
ds@1952
   980
			&& (strcmp(dir->GetEntryName(), "optional") != 0))
ds@1952
   981
		{
ds@1952
   982
			char new_local[PLATFORM_MAX_PATH];
ds@1952
   983
			if (localpath == NULL)
ds@1952
   984
			{
ds@1952
   985
				/* If no path yet, don't add a former slash */
ds@1952
   986
				snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
ds@1952
   987
			} else {
ds@1952
   988
				g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
ds@1952
   989
			}
ds@1952
   990
			LoadPluginsFromDir(basedir, new_local);
ds@1952
   991
		} else if (dir->IsEntryFile()) {
ds@1952
   992
			const char *name = dir->GetEntryName();
ds@1952
   993
			size_t len = strlen(name);
ds@1952
   994
			if (len >= 4
ds@1952
   995
				&& strcmp(&name[len-4], ".smx") == 0)
ds@1952
   996
			{
ds@1952
   997
				/* If the filename matches, load the plugin */
ds@1952
   998
				char plugin[PLATFORM_MAX_PATH];
ds@1952
   999
				if (localpath == NULL)
ds@1952
  1000
				{
ds@1952
  1001
					snprintf(plugin, sizeof(plugin), "%s", name);
ds@1952
  1002
				} else {
ds@1952
  1003
					g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
ds@1952
  1004
				}
ds@1952
  1005
				LoadAutoPlugin(plugin);
ds@1952
  1006
			}
ds@1952
  1007
		}
ds@1952
  1008
		dir->NextEntry();
ds@1952
  1009
	}
ds@1952
  1010
	g_LibSys.CloseDirectory(dir);
ds@1952
  1011
}
ds@1952
  1012
ds@1952
  1013
LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength)
ds@1952
  1014
{
ds@1952
  1015
	if (m_LoadingLocked)
ds@1952
  1016
	{
ds@1952
  1017
		return LoadRes_NeverLoad;
ds@1952
  1018
	}
ds@1952
  1019
ds@1952
  1020
	bool no_load = false;
ds@1952
  1021
	PluginSettings *pset;
ds@1952
  1022
	unsigned int setcount = m_PluginInfo.GetSettingsNum();
ds@1952
  1023
	for (unsigned int i=0; i<setcount; i++)
ds@1952
  1024
	{
ds@1952
  1025
		if ((pset = m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
ds@1952
  1026
		{
ds@1952
  1027
			continue;
ds@1952
  1028
		}
ds@1952
  1029
		if (pset->blockload_val)
ds@1952
  1030
		{
ds@1952
  1031
			no_load = true;
ds@1952
  1032
			break;
ds@1952
  1033
		}
ds@1952
  1034
	}
ds@1952
  1035
ds@1952
  1036
	/**
ds@1952
  1037
	 * Does this plugin already exist?
ds@1952
  1038
	 */
ds@1952
  1039
	CPlugin *pPlugin;
ds@1952
  1040
	if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin))
ds@1952
  1041
	{
ds@1952
  1042
		/* Check to see if we should try reloading it */
ds@1952
  1043
		if (pPlugin->GetStatus() == Plugin_BadLoad
ds@1952
  1044
			|| pPlugin->GetStatus() == Plugin_Error
ds@1952
  1045
			|| pPlugin->GetStatus() == Plugin_Failed
ds@1952
  1046
			|| no_load)
ds@1952
  1047
		{
ds@1952
  1048
			UnloadPlugin(pPlugin);
ds@1952
  1049
		}
ds@1952
  1050
		else
ds@1952
  1051
		{
ds@1952
  1052
			if (_plugin)
ds@1952
  1053
			{
ds@1952
  1054
				*_plugin = pPlugin;
ds@1952
  1055
			}
ds@1952
  1056
			return LoadRes_AlreadyLoaded;
ds@1952
  1057
		}
ds@1952
  1058
	}
ds@1952
  1059
ds@1952
  1060
	if (no_load)
ds@1952
  1061
	{
ds@1952
  1062
		return LoadRes_NeverLoad;
ds@1952
  1063
	}
ds@1952
  1064
ds@1952
  1065
	pPlugin = CPlugin::CreatePlugin(path, error, maxlength);
ds@1952
  1066
ds@1952
  1067
	assert(pPlugin != NULL);
ds@1952
  1068
ds@1952
  1069
	pPlugin->m_type = PluginType_MapUpdated;
ds@1952
  1070
ds@1952
  1071
	ICompilation *co = NULL;
ds@1952
  1072
ds@1952
  1073
	if (pPlugin->m_status == Plugin_Uncompiled)
ds@1952
  1074
	{
ds@1952
  1075
		co = pPlugin->StartMyCompile(g_pVM);
ds@1952
  1076
	}
ds@1952
  1077
ds@1952
  1078
	for (unsigned int i=0; i<setcount; i++)
ds@1952
  1079
	{
ds@1952
  1080
		if ((pset=m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
ds@1952
  1081
		{
ds@1952
  1082
			continue;
ds@1952
  1083
		}
ds@1952
  1084
		pPlugin->m_type = pset->type_val;
ds@1952
  1085
		if (co)
ds@1952
  1086
		{
ds@1952
  1087
			for (unsigned int j=0; j<pset->opts_num; j++)
ds@1952
  1088
			{
ds@1952
  1089
				const char *key, *val;
ds@1952
  1090
				m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
ds@1952
  1091
				if (!key || !val)
ds@1952
  1092
				{
ds@1952
  1093
					continue;
ds@1952
  1094
				}
ds@1952
  1095
				if (!g_pVM->SetCompilationOption(co, key, val))
ds@1952
  1096
				{
ds@1952
  1097
					if (error)
ds@1952
  1098
					{
ds@1952
  1099
						snprintf(error, maxlength, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val);
ds@1952
  1100
					}
ds@1952
  1101
					pPlugin->CancelMyCompile();
ds@1952
  1102
					co = NULL;
ds@1952
  1103
					break;
ds@1952
  1104
				}
ds@1952
  1105
			}
ds@1952
  1106
		}
ds@1952
  1107
	}
ds@1952
  1108
ds@1952
  1109
	/* Do the actual compiling */
ds@1952
  1110
	if (co)
ds@1952
  1111
	{
ds@1952
  1112
		pPlugin->FinishMyCompile(error, maxlength);
ds@1952
  1113
		co = NULL;
ds@1952
  1114
	}
ds@1952
  1115
ds@1952
  1116
	/* Get the status */
ds@1952
  1117
	if (pPlugin->GetStatus() == Plugin_Created)
ds@1952
  1118
	{
dvander@2086
  1119
		/* First native pass - add anything from Core */
dvander@2086
  1120
		g_ShareSys.BindNativesToPlugin(pPlugin, true);
ds@1952
  1121
		pPlugin->InitIdentity();
ds@1952
  1122
		if (pPlugin->Call_AskPluginLoad(error, maxlength))
ds@1952
  1123
		{
ds@1952
  1124
			/* Autoload any modules */
ds@1952
  1125
			LoadOrRequireExtensions(pPlugin, 1, error, maxlength);
ds@1952
  1126
		}
ds@1952
  1127
		else
ds@1952
  1128
		{
ds@1952
  1129
			pPlugin->SetErrorState(Plugin_Failed, "%s", error);
ds@1952
  1130
		}
ds@1952
  1131
	}
ds@1952
  1132
ds@1952
  1133
	/* Save the time stamp */
ds@1952
  1134
	time_t t = pPlugin->GetFileTimeStamp();
ds@1952
  1135
	pPlugin->SetTimeStamp(t);
ds@1952
  1136
ds@1952
  1137
	if (_plugin)
ds@1952
  1138
	{
ds@1952
  1139
		*_plugin = pPlugin;
ds@1952
  1140
	}
ds@1952
  1141
ds@1952
  1142
	return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : LoadRes_Failure;
ds@1952
  1143
}
ds@1952
  1144
ds@1952
  1145
IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t maxlength, bool *wasloaded)
ds@1952
  1146
{
ds@1952
  1147
	CPlugin *pl;
ds@1952
  1148
	LoadRes res;
ds@1952
  1149
ds@1952
  1150
	*wasloaded = false;
ds@1952
  1151
	if ((res=_LoadPlugin(&pl, path, debug, type, error, maxlength)) == LoadRes_Failure)
ds@1952
  1152
	{
ds@1952
  1153
		delete pl;
ds@1952
  1154
		return NULL;
ds@1952
  1155
	}
ds@1952
  1156
ds@1952
  1157
	if (res == LoadRes_AlreadyLoaded)
ds@1952
  1158
	{
ds@1952
  1159
		*wasloaded = true;
ds@1952
  1160
		return pl;
ds@1952
  1161
	}
ds@1952
  1162
ds@1952
  1163
	if (res == LoadRes_NeverLoad)
ds@1952
  1164
	{
ds@1952
  1165
		if (m_LoadingLocked)
ds@1952
  1166
		{
ds@1952
  1167
			UTIL_Format(error, maxlength, "There is a global plugin loading lock in effect");
ds@1952
  1168
		}
ds@1952
  1169
		else
ds@1952
  1170
		{
ds@1952
  1171
			UTIL_Format(error, maxlength, "This plugin is blocked from loading (see plugin_settings.cfg)");
ds@1952
  1172
		}
ds@1952
  1173
		return NULL;
ds@1952
  1174
	}
ds@1952
  1175
ds@1952
  1176
	AddPlugin(pl);
ds@1952
  1177
ds@1952
  1178
	/* Run second pass if we need to */
ds@1952
  1179
	if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
ds@1952
  1180
	{
ds@1952
  1181
		if (!RunSecondPass(pl, error, maxlength))
ds@1952
  1182
		{
ds@1952
  1183
			UnloadPlugin(pl);
ds@1952
  1184
			return NULL;
ds@1952
  1185
		}
ds@1952
  1186
		pl->Call_OnAllPluginsLoaded();
ds@1952
  1187
	}
ds@1952
  1188
ds@1952
  1189
	return pl;
ds@1952
  1190
}
ds@1952
  1191
ds@1952
  1192
void CPluginManager::LoadAutoPlugin(const char *plugin)
ds@1952
  1193
{
ds@1952
  1194
	CPlugin *pl = NULL;
ds@1952
  1195
	LoadRes res;
ds@1952
  1196
	char error[255] = "Unknown error";
ds@1952
  1197
ds@1952
  1198
	if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure)
ds@1952
  1199
	{
ds@1952
  1200
		g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error);
ds@1952
  1201
		pl->SetErrorState(Plugin_Failed, "%s", error);
ds@1952
  1202
	}
ds@1952
  1203
ds@1952
  1204
	if (res == LoadRes_Successful)
ds@1952
  1205
	{
ds@1952
  1206
		AddPlugin(pl);
ds@1952
  1207
	}
ds@1952
  1208
}
ds@1952
  1209
ds@1952
  1210
void CPluginManager::AddPlugin(CPlugin *pPlugin)
ds@1952
  1211
{
ds@1952
  1212
	List<IPluginsListener *>::iterator iter;
ds@1952
  1213
	IPluginsListener *pListener;
ds@1952
  1214
ds@1952
  1215
	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
ds@1952
  1216
	{
ds@1952
  1217
		pListener = (*iter);
ds@1952
  1218
		pListener->OnPluginCreated(pPlugin);
ds@1952
  1219
	}
ds@1952
  1220
ds@1952
  1221
	m_plugins.push_back(pPlugin);
ds@1952
  1222
	sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
ds@1952
  1223
}
ds@1952
  1224
ds@1952
  1225
void CPluginManager::LoadAll_SecondPass()
ds@1952
  1226
{
ds@1952
  1227
	List<CPlugin *>::iterator iter;
ds@1952
  1228
	CPlugin *pPlugin;
ds@1952
  1229
ds@1952
  1230
	char error[256];
ds@1952
  1231
	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
ds@1952
  1232
	{
ds@1952
  1233
		pPlugin = (*iter);
ds@1952
  1234
		if (pPlugin->GetStatus() == Plugin_Loaded)
ds@1952
  1235
		{
ds@1952
  1236
			error[0] = '\0';
ds@1952
  1237
			if (!RunSecondPass(pPlugin, error, sizeof(error)))
ds@1952
  1238
			{
ds@1952
  1239
				g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
ds@1952
  1240
				pPlugin->SetErrorState(Plugin_Failed, "%s", error);
ds@1952
  1241
			}
ds@1952
  1242
		}
ds@1952
  1243
	}
ds@1952
  1244
ds@1952
  1245
	m_AllPluginsLoaded = true;
ds@1952
  1246
}
ds@1952
  1247
ds@1952
  1248
bool CPluginManager::FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength)
ds@1952
  1249
{
ds@1952
  1250
	struct _pl
ds@1952
  1251
	{
ds@1952
  1252
		cell_t name;
ds@1952
  1253
		cell_t file;
ds@1952
  1254
		cell_t required;
ds@1952
  1255
	} *pl;
ds@1952
  1256
ds@1952
  1257
	IPluginContext *pBase = pPlugin->GetBaseContext();
ds@1952
  1258
	uint32_t num = pBase->GetPubVarsNum();
ds@1952
  1259
	sp_pubvar_t *pubvar;
ds@1952
  1260
	char *name, *file;
ds@1952
  1261
	char pathfile[PLATFORM_MAX_PATH];
ds@1952
  1262
ds@1952
  1263
	for (uint32_t i=0; i<num; i++)
ds@1952
  1264
	{
ds@1952
  1265
		if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
ds@1952
  1266
		{
ds@1952
  1267
			continue;
ds@1952
  1268
		}
ds@1952
  1269
		if (strncmp(pubvar->name, "__pl_", 5) == 0)
ds@1952
  1270
		{
ds@1952
  1271
			pl = (_pl *)pubvar->offs;
ds@1952
  1272
			if (pBase->LocalToString(pl->file, &file) != SP_ERROR_NONE)
ds@1952
  1273
			{
ds@1952
  1274
				continue;
ds@1952
  1275
			}
ds@1952
  1276
			if (pBase->LocalToString(pl->name, &name) != SP_ERROR_NONE)
ds@1952
  1277
			{
ds@1952
  1278
				continue;
ds@1952
  1279
			}
ds@1952
  1280
			g_LibSys.GetFileFromPath(pathfile, sizeof(pathfile), pPlugin->GetFilename());
ds@1952
  1281
			if (strcmp(pathfile, file) == 0)
ds@1952
  1282
			{
ds@1952
  1283
				continue;
ds@1952
  1284
			}
ds@1952
  1285
			if (pl->required == false)
ds@1952
  1286
			{
ds@1952
  1287
				IPluginFunction *pFunc;
ds@1952
  1288
				char buffer[64];
ds@1952
  1289
				UTIL_Format(buffer, sizeof(buffer), "__pl_%s_SetNTVOptional", &pubvar->name[5]);
ds@1952
  1290
				if ((pFunc=pBase->GetFunctionByName(buffer)))
ds@1952
  1291
				{
ds@1952
  1292
					cell_t res;
ds@1952
  1293
					pFunc->Execute(&res);
ds@1952
  1294
					if (pPlugin->GetContext()->n_err != SP_ERROR_NONE)
ds@1952
  1295
					{
ds@1952
  1296
						if (error)
ds@1952
  1297
						{
ds@1952
  1298
							snprintf(error, maxlength, "Fatal error during initializing plugin load");
ds@1952
  1299
						}
ds@1952
  1300
						return false;
ds@1952
  1301
					}
ds@1952
  1302
				}
ds@1952
  1303
			}
ds@1952
  1304
			else
ds@1952
  1305
			{
ds@1952
  1306
				/* Check that we aren't registering the same library twice */
ds@1952
  1307
				if (pPlugin->m_RequiredLibs.find(name) == pPlugin->m_RequiredLibs.end())
ds@1952
  1308
				{
ds@1952
  1309
					pPlugin->m_RequiredLibs.push_back(name);
ds@1952
  1310
				}
ds@1952
  1311
				else
ds@1952
  1312
				{
ds@1952
  1313
					continue;
ds@1952
  1314
				}
ds@1952
  1315
				List<CPlugin *>::iterator iter;
ds@1952
  1316
				CPlugin *pl;
ds@1952
  1317
				bool found = false;
ds@1952
  1318
				for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
ds@1952
  1319
				{
ds@1952
  1320
					pl = (*iter);
ds@1952
  1321
					if (pl->m_Libraries.find(name) != pl->m_Libraries.end())
ds@1952
  1322
					{
ds@1952
  1323
						found = true;
ds@1952
  1324
						break;
ds@1952
  1325
					}
ds@1952
  1326
				}
ds@1952
  1327
				if (!found)
ds@1952
  1328
				{
ds@1952
  1329
					if (error)
ds@1952
  1330
					{
ds@1952
  1331
						snprintf(error, maxlength, "Could not find required plugin \"%s\"", name);
ds@1952
  1332
					}
ds@1952
  1333
					return false;
ds@1952
  1334
				}
ds@1952
  1335
			}
ds@1952
  1336
		}
ds@1952
  1337
	}
ds@1952
  1338
ds@1952
  1339
	return true;
ds@1952
  1340
}
ds@1952
  1341
ds@1952
  1342
bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
ds@1952
  1343
{
ds@1952
  1344
	/* Find any extensions this plugin needs */
ds@1952
  1345
	struct _ext
ds@1952
  1346
	{
ds@1952
  1347
		cell_t name;
ds@1952
  1348
		cell_t file;
ds@1952
  1349
		cell_t autoload;
ds@1952
  1350
		cell_t required;
ds@1952
  1351
	} *ext;
ds@1952
  1352
ds@1952
  1353
	IPluginContext *pBase = pPlugin->GetBaseContext();
ds@1952
  1354
	uint32_t num = pBase->GetPubVarsNum();
ds@1952
  1355
	sp_pubvar_t *pubvar;
ds@1952
  1356
	IExtension *pExt;
ds@1952
  1357
	char path[PLATFORM_MAX_PATH];
ds@1952
  1358
	char *file, *name;
ds@1952
  1359
	for (uint32_t i=0; i<num; i++)
ds@1952
  1360
	{
ds@1952
  1361
		if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
ds@1952
  1362
		{
ds@1952
  1363
			continue;
ds@1952
  1364
		}
ds@1952
  1365
		if (strncmp(pubvar->name, "__ext_", 6) == 0)
ds@1952
  1366
		{
ds@1952
  1367
			ext = (_ext *)pubvar->offs;
ds@1952
  1368
			if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
ds@1952
  1369
			{
ds@1952
  1370
				continue;
ds@1952
  1371
			}
ds@1952
  1372
			if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
ds@1952
  1373
			{
ds@1952
  1374
				continue;
ds@1952
  1375
			}
ds@1952
  1376
			if (pass == 1)
ds@1952
  1377
			{
ds@1952
  1378
				/* Attempt to auto-load if necessary */
ds@1952
  1379
				if (ext->autoload)
ds@1952
  1380
				{
ds@1952
  1381
					g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
ds@1952
  1382
					g_Extensions.LoadAutoExtension(path);
ds@1952
  1383
				}
ds@1952
  1384
			}
ds@1952
  1385
			else if (pass == 2)
ds@1952
  1386
			{
ds@1952
  1387
				/* Is this required? */
ds@1952
  1388
				if (ext->required)
ds@1952
  1389
				{
ds@1952
  1390
					g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
ds@1952
  1391
					if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
ds@1952
  1392
					{
ds@1952
  1393
						pExt = g_Extensions.FindExtensionByName(name);
ds@1952
  1394
					}
ds@1952
  1395
					/* :TODO: should we bind to unloaded extensions?
ds@1952
  1396
					 * Currently the extension manager will ignore this.
ds@1952
  1397
					 */
ds@1952
  1398
					if (!pExt || !pExt->IsRunning(NULL, 0))
ds@1952
  1399
					{
ds@1952
  1400
						if (error)
ds@1952
  1401
						{
ds@1952
  1402
							snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
ds@1952
  1403
						}
ds@1952
  1404
						return false;
ds@1952
  1405
					}
ds@1952
  1406
					else
ds@1952
  1407
					{
ds@1952
  1408
						g_Extensions.BindChildPlugin(pExt, pPlugin);
ds@1952
  1409
					}
ds@1952
  1410
				}
ds@1952
  1411
				else
ds@1952
  1412
				{
ds@1952
  1413
					IPluginFunction *pFunc;
ds@1952
  1414
					char buffer[64];
ds@1952
  1415
					UTIL_Format(buffer, sizeof(buffer), "__ext_%s_SetNTVOptional", &pubvar->name[6]);
ds@1952
  1416
ds@1952
  1417
					if ((pFunc = pBase->GetFunctionByName(buffer)) != NULL)
ds@1952
  1418
					{
ds@1952
  1419
						cell_t res;
ds@1952
  1420
						pFunc->Execute(&res);
ds@1952
  1421
						if (pPlugin->GetContext()->n_err != SP_ERROR_NONE)
ds@1952
  1422
						{
ds@1952
  1423
							if (error)
ds@1952
  1424
							{
ds@1952
  1425
								UTIL_Format(error, maxlength, "Fatal error during plugin initialization (ext req)");
ds@1952
  1426
							}
ds@1952
  1427
							return false;
ds@1952
  1428
						}
ds@1952
  1429
					}
ds@1952
  1430
				}
ds@1952
  1431
			}
ds@1952
  1432
		}
ds@1952
  1433
	}
ds@1952
  1434
ds@1952
  1435
	return true;
ds@1952
  1436
}
ds@1952
  1437
ds@1952
  1438
bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
ds@1952
  1439
{
ds@1952
  1440
	/* Second pass for extension requirements */
ds@1952
  1441
	if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
ds@1952
  1442
	{
ds@1952
  1443
		return false;
ds@1952
  1444
	}
ds@1952
  1445
ds@1952
  1446
	if (!FindOrRequirePluginDeps(pPlugin, error, maxlength))
ds@1952
  1447
	{
ds@1952
  1448
		return false;
ds@1952
  1449
	}
ds@1952
  1450
dvander@2086
  1451
	/* Run another binding pass */
dvander@2086
  1452
	g_ShareSys.BindNativesToPlugin(pPlugin, false);
ds@1952
  1453
dvander@2086
  1454
	/* Find any unbound natives. Right now, these are not allowed. */
ds@1952
  1455
	IPluginContext *pContext = pPlugin->GetBaseContext();
ds@1952
  1456
	uint32_t num = pContext->GetNativesNum();
ds@1952
  1457
	sp_native_t *native;
ds@1952
  1458
	for (unsigned int i=0; i<num; i++)
ds@1952
  1459
	{
ds@1952
  1460
		if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
ds@1952
  1461
		{
ds@1952
  1462
			break;
ds@1952
  1463
		}
ds@1952
  1464
		if (native->status == SP_NATIVE_UNBOUND
ds@1952
  1465
			&& native->name[0] != '@'
ds@1952
  1466
			&& !(native->flags & SP_NTVFLAG_OPTIONAL))
ds@1952
  1467
		{
ds@1952
  1468
			if (error)
ds@1952
  1469
			{
ds@1952
  1470
				snprintf(error, maxlength, "Native \"%s\" was not found.", native->name);
ds@1952
  1471
			}
ds@1952
  1472
			return false;
ds@1952
  1473
		}
ds@1952
  1474
	}
ds@1952
  1475
ds@1952
  1476
	/* Finish by telling all listeners */
ds@1952
  1477
	List<IPluginsListener *>::iterator iter;
ds@1952
  1478
	IPluginsListener *pListener;
ds@1952
  1479
	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
ds@1952
  1480
	{
ds@1952
  1481
		pListener = (*iter);
ds@1952
  1482
		pListener->OnPluginLoaded(pPlugin);
ds@1952
  1483
	}
ds@1952
  1484
	
ds@1952
  1485
	/* Tell this plugin to finish initializing itself */
ds@1952
  1486
	pPlugin->Call_OnPluginStart();
ds@1952
  1487
ds@1952
  1488
	/* Now, if we have fake natives, go through all plugins that might need rebinding */
dvander@2086
  1489
	if (pPlugin->GetStatus() <= Plugin_Paused && pPlugin->m_Natives.size())
ds@1952
  1490
	{
ds@1952
  1491
		List<CPlugin *>::iterator pl_iter;
ds@1952
  1492
		CPlugin *pOther;
ds@1952
  1493
		for (pl_iter = m_plugins.begin();
ds@1952
  1494
			 pl_iter != m_plugins.end();
ds@1952
  1495
			 pl_iter++)
ds@1952
  1496
		{
ds@1952
  1497
			pOther = (*pl_iter);
ds@1952
  1498
			if ((pOther->GetStatus() == Plugin_Error
ds@1952
  1499
				&& (pOther->m_FakeNativesMissing || pOther->m_LibraryMissing))
ds@1952
  1500
				|| pOther->m_FakeNativesMissing)
ds@1952
  1501
			{
ds@1952
  1502
				TryRefreshDependencies(pOther);
ds@1952
  1503
			}
dvander@2086
  1504
			else if ((pOther->GetStatus() == Plugin_Running
dvander@2086
  1505
					  || pOther->GetStatus() == Plugin_Paused)
dvander@2086
  1506
					 && pOther != pPlugin)
dvander@2086
  1507
			{
dvander@2086
  1508
				List<NativeEntry *>::iterator nv_iter;
dvander@2086
  1509
				for (nv_iter = pPlugin->m_Natives.begin();
dvander@2086
  1510
					 nv_iter != pPlugin->m_Natives.end();
dvander@2086
  1511
					 nv_iter++)
dvander@2086
  1512
				{
dvander@2086
  1513
					g_ShareSys.BindNativeToPlugin(pOther, (*nv_iter));
dvander@2086
  1514
				}
dvander@2086
  1515
			}
ds@1952
  1516
		}
ds@1952
  1517
	}
ds@1952
  1518
ds@1952
  1519
	/* Go through our libraries and tell other plugins they're added */
ds@1952
  1520
	List<String>::iterator s_iter;
ds@1952
  1521
	for (s_iter = pPlugin->m_Libraries.begin();
ds@1952
  1522
		s_iter != pPlugin->m_Libraries.end();
ds@1952
  1523
		s_iter++)
ds@1952
  1524
	{
ds@1952
  1525
		OnLibraryAction((*s_iter).c_str(), true, false);
ds@1952
  1526
	}
ds@1952
  1527
dvander@2042
  1528
	/* :TODO: optimize? does this even matter? */
dvander@2042
  1529
	pPlugin->GetPhrases()->AddPhraseFile("core.phrases");
ds@1952
  1530
ds@1952
  1531
	return true;
ds@1952
  1532
}
ds@1952
  1533
ds@1952
  1534
void CPluginManager::TryRefreshDependencies(CPlugin *pPlugin)
ds@1952
  1535
{
ds@1952
  1536
	assert(pPlugin->GetBaseContext() != NULL);
ds@1952
  1537
dvander@2086
  1538
	g_ShareSys.BindNativesToPlugin(pPlugin, false);
ds@1952
  1539
ds@1952
  1540
	List<String>::iterator lib_iter;
ds@1952
  1541
	List<String>::iterator req_iter;
ds@1952
  1542
	List<CPlugin *>::iterator pl_iter;
ds@1952
  1543
	CPlugin *pl;
ds@1952
  1544
	for (req_iter=pPlugin->m_RequiredLibs.begin(); req_iter!=pPlugin->m_RequiredLibs.end(); req_iter++)
ds@1952
  1545
	{
ds@1952
  1546
		bool found = false;
ds@1952
  1547
		for (pl_iter=m_plugins.begin(); pl_iter!=m_plugins.end(); pl_iter++)
ds@1952
  1548
		{
ds@1952
  1549
			pl = (*pl_iter);
ds@1952
  1550
			for (lib_iter=pl->m_Libraries.begin(); lib_iter!=pl->m_Libraries.end(); lib_iter++)
ds@1952
  1551
			{
ds@1952
  1552
				if ((*req_iter) == (*lib_iter))
ds@1952
  1553
				{
ds@1952
  1554
					found = true;
ds@1952
  1555
				}
ds@1952
  1556
			}
ds@1952
  1557
		}
ds@1952
  1558
		if (!found)
ds@1952
  1559
		{
ds@1952
  1560
			pPlugin->SetErrorState(Plugin_Error, "Library not found: %s", (*req_iter).c_str());
ds@1952
  1561
			return;
ds@1952
  1562
		}
ds@1952
  1563
	}
ds@1952
  1564
ds@1952
  1565
	/* Find any unbound natives
ds@1952
  1566
	 * Right now, these are not allowed
ds@1952
  1567
	 */
ds@1952
  1568
	IPluginContext *pContext = pPlugin->GetBaseContext();
ds@1952
  1569
	uint32_t num = pContext->GetNativesNum();
ds@1952
  1570
	sp_native_t *native;
ds@1952
  1571
	for (unsigned int i=0; i<num; i++)
ds@1952
  1572
	{
ds@1952
  1573
		if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
ds@1952
  1574
		{
ds@1952
  1575
			break;
ds@1952
  1576
		}
ds@1952
  1577
		if (native->status == SP_NATIVE_UNBOUND && !(native->flags & SP_NTVFLAG_OPTIONAL))
ds@1952
  1578
		{
ds@1952
  1579
			pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name);
ds@1952
  1580
			return;
ds@1952
  1581
		}
ds@1952
  1582
	}
ds@1952
  1583
ds@1952
  1584
	if (pPlugin->GetStatus() == Plugin_Error)
ds@1952
  1585
	{
ds@1952
  1586
		/* If we got here, all natives are okay again! */
ds@1952
  1587
		pPlugin->m_status = Plugin_Running;
ds@1952
  1588
		if ((pPlugin->m_ctx.ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED)
ds@1952
  1589
		{
ds@1952
  1590
			pPlugin->m_ctx.ctx->flags &= ~SPFLAG_PLUGIN_PAUSED;
ds@1952
  1591
			_SetPauseState(pPlugin, false);
ds@1952
  1592
		}
ds@1952
  1593
	}
ds@1952
  1594
}
ds@1952
  1595
ds@1952
  1596
bool CPluginManager::UnloadPlugin(IPlugin *plugin)
ds@1952
  1597
{
ds@1952
  1598
	CPlugin *pPlugin = (CPlugin *)plugin;
ds@1952
  1599
ds@1952
  1600
	/* This prevents removal during insertion or anything else weird */
ds@1952
  1601
	if (m_plugins.find(pPlugin) == m_plugins.end())
ds@1952
  1602
	{
ds@1952
  1603
		return false;
ds@1952
  1604
	}
ds@1952
  1605
ds@1952
  1606
	IPluginContext *pContext = plugin->GetBaseContext();
ds@1952
  1607
	if (pContext->IsInExec())
ds@1952
  1608
	{
ds@1952
  1609
		char buffer[255];
ds@1952
  1610
		UTIL_Format(buffer, sizeof(buffer), "sm plugins unload %s\n", plugin->GetFilename());
ds@1952
  1611
		engine->ServerCommand(buffer);
ds@1952
  1612
		return false;
ds@1952
  1613
	}
ds@1952
  1614
ds@1952
  1615
	/* Remove us from the lookup table and linked list */
ds@1952
  1616
	m_plugins.remove(pPlugin);
ds@1952
  1617
	sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
ds@1952
  1618
ds@1952
  1619
	/* Go through our libraries and tell other plugins they're gone */
ds@1952
  1620
	List<String>::iterator s_iter;
ds@1952
  1621
	for (s_iter = pPlugin->m_Libraries.begin();
ds@1952
  1622
		 s_iter != pPlugin->m_Libraries.end();
ds@1952
  1623
		 s_iter++)
ds@1952
  1624
	{
ds@1952
  1625
		OnLibraryAction((*s_iter).c_str(), true, true);
ds@1952
  1626
	}
ds@1952
  1627
ds@1952
  1628
	List<IPluginsListener *>::iterator iter;
ds@1952
  1629
	IPluginsListener *pListener;
ds@1952
  1630
ds@1952
  1631
	if (pPlugin->GetStatus() <= Plugin_Error)
ds@1952
  1632
	{
ds@1952
  1633
		/* Notify listeners of unloading */
ds@1952
  1634
		for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
ds@1952
  1635
		{
ds@1952
  1636
			pListener = (*iter);
ds@1952
  1637
			pListener->OnPluginUnloaded(pPlugin);
ds@1952
  1638
		}
ds@1952
  1639
		/* Notify plugin */
ds@1952
  1640
		pPlugin->Call_OnPluginEnd();
ds@1952
  1641
	}
ds@1952
  1642
dvander@2086
  1643
	pPlugin->DropEverything();
ds@1952
  1644
ds@1952
  1645
	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
ds@1952
  1646
	{
ds@1952
  1647
		/* Notify listeners of destruction */
ds@1952
  1648
		pListener = (*iter);
ds@1952
  1649
		pListener->OnPluginDestroyed(pPlugin);
ds@1952
  1650
	}
ds@1952
  1651
	
ds@1952
  1652
	/* Tell the plugin to delete itself */
ds@1952
  1653
	delete pPlugin;
ds@1952
  1654
ds@1952
  1655
	return true;
ds@1952
  1656
}
ds@1952
  1657
ds@1952
  1658
IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
ds@1952
  1659
{
ds@1952
  1660
	IPlugin *pl = (IPlugin *)ctx->user[SM_CONTEXTVAR_MYSELF];
ds@1952
  1661
	return pl;
ds@1952
  1662
}
ds@1952
  1663
ds@1952
  1664
unsigned int CPluginManager::GetPluginCount()
ds@1952
  1665
{
ds@1952
  1666
	return m_plugins.size();
ds@1952
  1667
}
ds@1952
  1668
ds@1952
  1669
void CPluginManager::AddPluginsListener(IPluginsListener *listener)
ds@1952
  1670
{
ds@1952
  1671
	m_listeners.push_back(listener);
ds@1952
  1672
}
ds@1952
  1673
ds@1952
  1674
void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
ds@1952
  1675
{
ds@1952
  1676
	m_listeners.remove(listener);
ds@1952
  1677
}
ds@1952
  1678
ds@1952
  1679
IPluginIterator *CPluginManager::GetPluginIterator()
ds@1952
  1680
{
ds@1952
  1681
	if (m_iters.empty())
ds@1952
  1682
	{
ds@1952
  1683
		return new CPluginIterator(&m_plugins);
ds@1952
  1684
	} else {
ds@1952
  1685
		CPluginIterator *iter = m_iters.front();
ds@1952
  1686
		m_iters.pop();
ds@1952
  1687
		iter->Reset();
ds@1952
  1688
		return iter;
ds@1952
  1689
	}
ds@1952
  1690
}
ds@1952
  1691
ds@1952
  1692
void CPluginManager::ReleaseIterator(CPluginIterator *iter)
ds@1952
  1693
{
ds@1952
  1694
	m_iters.push(iter);
ds@1952
  1695
}
ds@1952
  1696
ds@1952
  1697
bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
ds@1952
  1698
{
ds@1952
  1699
	/* As an optimization, we do not call strlen, but compute the length in the first pass */
ds@1952
  1700
	size_t alias_len = 0;
ds@1952
  1701
	size_t local_len = 0;
ds@1952
  1702
ds@1952
  1703
	const char *ptr = alias;
ds@1952
  1704
	unsigned int alias_explicit_paths = 0;
ds@1952
  1705
	unsigned int alias_path_end = 0;
ds@1952
  1706
	while (*ptr != '\0')
ds@1952
  1707
	{
ds@1952
  1708
		if (*ptr == '\\' || *ptr == '/')
ds@1952
  1709
		{
ds@1952
  1710
			alias_explicit_paths++;
ds@1952
  1711
			alias_path_end = alias_len;
ds@1952
  1712
		}
ds@1952
  1713
		alias_len++;
ds@1952
  1714
		ptr++;
ds@1952
  1715
	}
ds@1952
  1716
ds@1952
  1717
	if (alias_explicit_paths && alias_path_end == alias_len - 1)
ds@1952
  1718
	{
ds@1952
  1719
		/* Trailing slash is totally invalid here */
ds@1952
  1720
		return false;
ds@1952
  1721
	}
ds@1952
  1722
ds@1952
  1723
	ptr = localpath;
ds@1952
  1724
	unsigned int local_explicit_paths = 0;
ds@1952
  1725
	unsigned int local_path_end = 0;
ds@1952
  1726
	while (*ptr != '\0')
ds@1952
  1727
	{
ds@1952
  1728
		if (*ptr == '\\' || *ptr == '/')
ds@1952
  1729
		{
ds@1952
  1730
			local_explicit_paths++;
ds@1952
  1731
			local_path_end = local_len;
ds@1952
  1732
		}
ds@1952
  1733
		local_len++;
ds@1952
  1734
		ptr++;
ds@1952
  1735
	}
ds@1952
  1736
ds@1952
  1737
	/* If the alias has more explicit paths than the real path,
ds@1952
  1738
	 * no match will be possible.
ds@1952
  1739
	 */
ds@1952
  1740
	if (alias_explicit_paths > local_explicit_paths)
ds@1952
  1741
	{
ds@1952
  1742
		return false;
ds@1952
  1743
	}
ds@1952
  1744
ds@1952
  1745
	if (alias_explicit_paths)
ds@1952
  1746
	{
ds@1952
  1747
		/* We need to find if the paths match now.  For example, these should all match:
ds@1952
  1748
		 * csdm     csdm
ds@1952
  1749
		 * csdm     optional/csdm
ds@1952
  1750
		 * csdm/ban optional/crab/csdm/ban
ds@1952
  1751
		*/
ds@1952
  1752
		const char *aliasptr = alias;
ds@1952
  1753
		const char *localptr = localpath;
ds@1952
  1754
		bool match = true;
ds@1952
  1755
		do
ds@1952
  1756
		{
ds@1952
  1757
			if (*aliasptr != *localptr)
ds@1952
  1758
			{
ds@1952
  1759
				/* We have to knock one path off */
ds@1952
  1760
				local_explicit_paths--;
ds@1952
  1761
				if (alias_explicit_paths > local_explicit_paths)
ds@1952
  1762
				{
ds@1952
  1763
					/* Skip out if we're gonna have an impossible match */
ds@1952
  1764
					return false;
ds@1952
  1765
				}
ds@1952
  1766
				/* Eat up localptr tokens until we get a result */
ds@1952
  1767
				while (((localptr - localpath) < (int)local_path_end)
ds@1952
  1768
					&& *localptr != '/'
ds@1952
  1769
					&& *localptr != '\\')
ds@1952
  1770
				{
ds@1952
  1771
					localptr++;
ds@1952
  1772
				}
ds@1952
  1773
				/* Check if we hit the end of our searchable area.
ds@1952
  1774
				 * This probably isn't possible because of the path 
ds@1952
  1775
				 * count check, but it's a good idea anyway.
ds@1952
  1776
				 */
ds@1952
  1777
				if ((localptr - localpath) >= (int)local_path_end)
ds@1952
  1778
				{
ds@1952
  1779
					return false;
ds@1952
  1780
				} else {
ds@1952
  1781
					/* Consume the slash token */
ds@1952
  1782
					localptr++;
ds@1952
  1783
				}
ds@1952
  1784
				/* Reset the alias pointer so we can continue consuming */
ds@1952
  1785
				aliasptr = alias;
ds@1952
  1786
				match = false;
ds@1952
  1787
				continue;
ds@1952
  1788
			}
ds@1952
  1789
			/* Note:
ds@1952
  1790
			 * This is safe because if localptr terminates early, aliasptr will too
ds@1952
  1791
			 */
ds@1952
  1792
			do
ds@1952
  1793
			{
ds@1952
  1794
				/* We should never reach the end of the string because of this check. */
ds@1952
  1795
				bool aliasend = (aliasptr - alias) > (int)alias_path_end;
ds@1952
  1796
				bool localend = (localptr - localpath) > (int)local_path_end;
ds@1952
  1797
				if (aliasend || localend)
ds@1952
  1798
				{
ds@1952
  1799
					if (aliasend && localend)
ds@1952
  1800
					{
ds@1952
  1801
						/* we matched, and we can break out now */
ds@1952
  1802
						match = true;
ds@1952
  1803
						break;
ds@1952
  1804
					}
ds@1952
  1805
					/* Otherwise, we've hit the end somehow and rest won't match up.  Break out. */
ds@1952
  1806
					match = false;
ds@1952
  1807
					break;
ds@1952
  1808
				}
ds@1952
  1809
ds@1952
  1810
				/* If we got here, it's safe to compare the next two tokens */
ds@1952
  1811
				if (*localptr != *aliasptr)
ds@1952
  1812
				{
ds@1952
  1813
					match = false;
ds@1952
  1814
					break;
ds@1952
  1815
				}
ds@1952
  1816
				localptr++;
ds@1952
  1817
				aliasptr++;
ds@1952
  1818
			} while (true);
ds@1952
  1819
		} while (!match);
ds@1952
  1820
	}
ds@1952
  1821
ds@1952
  1822
	/* If we got here, it's time to compare filenames */
ds@1952
  1823
	const char *aliasptr = alias;
ds@1952
  1824
	const char *localptr = localpath;
ds@1952
  1825
ds@1952
  1826
	if (alias_explicit_paths)
ds@1952
  1827
	{
ds@1952
  1828
		aliasptr = &alias[alias_path_end + 1];
ds@1952
  1829
	}
ds@1952
  1830
ds@1952
  1831
	if (local_explicit_paths)
ds@1952
  1832
	{
ds@1952
  1833
		localptr = &localpath[local_path_end + 1];
ds@1952
  1834
	}
ds@1952
  1835
ds@1952
  1836
	while (true)
ds@1952
  1837
	{
ds@1952
  1838
		if (*aliasptr == '*')
ds@1952
  1839
		{
ds@1952
  1840
			/* First, see if this is the last character */
ds@1952
  1841
			if ((unsigned)(aliasptr - alias) == alias_len - 1)
ds@1952
  1842
			{
ds@1952
  1843
				/* If so, there's no need to match anything else */
ds@1952
  1844
				return true;
ds@1952
  1845
			}
ds@1952
  1846
			/* Otherwise, we need to search for an appropriate matching sequence in local.
ds@1952
  1847
			 * Note that we only need to search up to the next asterisk.
ds@1952
  1848
			 */
ds@1952
  1849
			aliasptr++;
ds@1952
  1850
			bool match = true;
ds@1952
  1851
			const char *local_orig = localptr;
ds@1952
  1852
			do
ds@1952
  1853
			{
ds@1952
  1854
				match = true;
ds@1952
  1855
				while (*aliasptr != '\0' && *aliasptr != '*')
ds@1952
  1856
				{
ds@1952
  1857
					/* Since aliasptr is never '\0', localptr hitting the end will fail */
ds@1952
  1858
					if (*aliasptr != *localptr)
ds@1952
  1859
					{
ds@1952
  1860
						match = false;
ds@1952
  1861
						break;
ds@1952
  1862
					}
ds@1952
  1863
					aliasptr++;
ds@1952
  1864
					localptr++;
ds@1952
  1865
				}
ds@1952
  1866
				if (!match)
ds@1952
  1867
				{
ds@1952
  1868
					/* If we didn't get a match, we need to advance the search stream.
ds@1952
  1869
					 * This will let us skip tokens while still searching for another match.
ds@1952
  1870
					 */
ds@1952
  1871
					localptr = ++local_orig;
ds@1952
  1872
					/* Make sure we don't go out of bounds */
ds@1952
  1873
					if (*localptr == '\0')
ds@1952
  1874
					{
ds@1952
  1875
						break;
ds@1952
  1876
					}
ds@1952
  1877
				}
ds@1952
  1878
			} while (!match);
ds@1952
  1879
ds@1952
  1880
			if (!match)
ds@1952
  1881
			{
ds@1952
  1882
				return false;
ds@1952
  1883
			} else {
ds@1952
  1884
				/* If we got a match, move on to the next token */
ds@1952
  1885
				continue;
ds@1952
  1886
			}
ds@1952
  1887
		} else if (*aliasptr == '\0') {
ds@1952
  1888
			if (*localptr == '\0'
ds@1952
  1889
				||
ds@1952
  1890
				strcmp(localptr, ".smx") == 0)
ds@1952
  1891
			{
ds@1952
  1892
				return true;
ds@1952
  1893
			} else {
ds@1952
  1894
				return false;
ds@1952
  1895
			}
ds@1952
  1896
		} else if (*aliasptr != *localptr) {
ds@1952
  1897
			return false;
ds@1952
  1898
		}
ds@1952
  1899
		aliasptr++;
ds@1952
  1900
		localptr++;
ds@1952
  1901
	}
ds@1952
  1902
ds@1952
  1903
	return true;
ds@1952
  1904
}
ds@1952
  1905
ds@1952
  1906
bool CPluginManager::IsLateLoadTime() const
ds@1952
  1907
{
ds@1952
  1908
	return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading());
ds@1952
  1909
}
ds@1952
  1910
ds@1952
  1911
void CPluginManager::OnSourceModAllInitialized()
ds@1952
  1912
{
ds@1952
  1913
	m_MyIdent = g_ShareSys.CreateCoreIdentity();
ds@1952
  1914
ds@1952
  1915
	HandleAccess sec;
ds@1952
  1916
	g_HandleSys.InitAccessDefaults(NULL, &sec);
ds@1952
  1917
ds@1952
  1918
	sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
ds@1952
  1919
	sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
ds@1952
  1920
ds@1952
  1921
	g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
ds@1952
  1922
	g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
ds@1952
  1923
ds@1952
  1924
	g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this);
ds@1952
  1925
ds@1952
  1926
	g_ShareSys.AddInterface(NULL, this);
ds@1952
  1927
}
ds@1952
  1928
ds@1952
  1929
void CPluginManager::OnSourceModShutdown()
ds@1952
  1930
{
ds@1952
  1931
	g_RootMenu.RemoveRootConsoleCommand("plugins", this);
ds@1952
  1932
ds@1952
  1933
	UnloadAll();
ds@1952
  1934
ds@1952
  1935
	g_HandleSys.RemoveType(g_PluginType, m_MyIdent);
ds@1952
  1936
	g_ShareSys.DestroyIdentType(g_PluginIdent);
ds@1952
  1937
	g_ShareSys.DestroyIdentity(m_MyIdent);
ds@1952
  1938
}
ds@1952
  1939
ds@1952
  1940
void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
ds@1952
  1941
{
ds@1952
  1942
	/* We don't care about the internal object, actually */
ds@1952
  1943
}
ds@1952
  1944
ds@1952
  1945
bool CPluginManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
ds@1952
  1946
{
ds@1952
  1947
	*pSize = ((CPlugin *)object)->CalcMemUsage();
ds@1952
  1948
	return true;
ds@1952
  1949
}
ds@1952
  1950
ds@1952
  1951
IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
ds@1952
  1952
{
ds@1952
  1953
	IPlugin *pPlugin;
ds@1952
  1954
	HandleError _err;
ds@1952
  1955
ds@1952
  1956
	HandleSecurity sec;
ds@1952
  1957
ds@1952
  1958
	sec.pOwner = NULL;
ds@1952
  1959
	sec.pIdentity = m_MyIdent;
ds@1952
  1960
ds@1952
  1961
	if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
ds@1952
  1962
	{
ds@1952
  1963
		pPlugin = NULL;
ds@1952
  1964
	}
ds@1952
  1965
ds@1952
  1966
	if (err)
ds@1952
  1967
	{
ds@1952
  1968
		*err = _err;
ds@1952
  1969
	}
ds@1952
  1970
ds@1952
  1971
	return pPlugin;
ds@1952
  1972
}
ds@1952
  1973
ds@1952
  1974
CPlugin *CPluginManager::GetPluginByOrder(int num)
ds@1952
  1975
{
ds@1952
  1976
	if (num < 1 || num > (int)GetPluginCount())
ds@1952
  1977
	{
ds@1952
  1978
		return NULL;
ds@1952
  1979
	}
ds@1952
  1980
ds@1952
  1981
	CPlugin *pl;
ds@1952
  1982
	int id = 1;
ds@1952
  1983
ds@1952
  1984
	IPluginIterator *iter = GetPluginIterator();
ds@1952
  1985
	for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++) {}
ds@1952
  1986
ds@1952
  1987
	pl = (CPlugin *)(iter->GetPlugin());
ds@1952
  1988
	iter->Release();
ds@1952
  1989
ds@1952
  1990
	return pl;
ds@1952
  1991
}
ds@1952
  1992
ds@1952
  1993
const char *CPluginManager::GetStatusText(PluginStatus st)
ds@1952
  1994
{
ds@1952
  1995
	switch (st)
ds@1952
  1996
	{
ds@1952
  1997
	case Plugin_Running:
ds@1952
  1998
		return "Running";
ds@1952
  1999
	case Plugin_Paused:
ds@1952
  2000
		return "Paused";
ds@1952
  2001
	case Plugin_Error:
ds@1952
  2002
		return "Error";
ds@1952
  2003
	case Plugin_Uncompiled:
ds@1952
  2004
		return "Uncompiled";
ds@1952
  2005
	case Plugin_BadLoad:
ds@1952
  2006
		return "Bad Load";
ds@1952
  2007
	case Plugin_Failed:
ds@1952
  2008
		return "Failed";
ds@1952
  2009
	default:
ds@1952
  2010
		assert(false);
ds@1952
  2011
		return "-";
ds@1952
  2012
	}
ds@1952
  2013
}
ds@1952
  2014
ds@1952
  2015
void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
ds@1952
  2016
{
ds@1952
  2017
	int argcount = command.ArgC();
ds@1952
  2018
	if (argcount >= 3)
ds@1952
  2019
	{
ds@1952
  2020
		const char *cmd = command.Arg(2);
ds@1952
  2021
		if (strcmp(cmd, "list") == 0)
ds@1952
  2022
		{
ds@1952
  2023
			char buffer[256];
ds@1952
  2024
			unsigned int id = 1;
ds@1952
  2025
			int plnum = GetPluginCount();
ds@1952
  2026
ds@1952
  2027
			if (!plnum)
ds@1952
  2028
			{
ds@1952
  2029
				g_RootMenu.ConsolePrint("[SM] No plugins loaded");
ds@1952
  2030
				return;
ds@1952
  2031
			}
ds@1952
  2032
			else
ds@1952
  2033
			{
ds@1952
  2034
				g_RootMenu.ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
ds@1952
  2035
			}
ds@1952
  2036
ds@1952
  2037
			SourceHook::List<IPlugin *> m_FailList;
ds@1952
  2038
ds@1952
  2039
			IPluginIterator *iter = GetPluginIterator();
ds@1952
  2040
			for (; iter->MorePlugins(); iter->NextPlugin(), id++)
ds@1952
  2041
			{
ds@1952
  2042
				IPlugin *pl = iter->GetPlugin();
ds@1952
  2043
				assert(pl->GetStatus() != Plugin_Created);
ds@1952
  2044
				int len = 0;
ds@1952
  2045
				const sm_plugininfo_t *info = pl->GetPublicInfo();
ds@1952
  2046
				if (pl->GetStatus() != Plugin_Running)
ds@1952
  2047
				{
ds@1952
  2048
					len += UTIL_Format(buffer, sizeof(buffer), "  %02d <%s>", id, GetStatusText(pl->GetStatus()));
ds@1952
  2049
ds@1952
  2050
					if (pl->GetStatus() <= Plugin_Error)
ds@1952
  2051
					{
ds@1952
  2052
						/* Plugin has failed to load. */
ds@1952
  2053
						m_FailList.push_back(pl);
ds@1952
  2054
					}
ds@1952
  2055
				}
ds@1952
  2056
				else
ds@1952
  2057
				{
ds@1952
  2058
					len += UTIL_Format(buffer, sizeof(buffer), "  %02d", id);
ds@1952
  2059
				}
ds@1952
  2060
				len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
ds@1952
  2061
				if (IS_STR_FILLED(info->version))
ds@1952
  2062
				{
ds@1952
  2063
					len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
ds@1952
  2064
				}
ds@1952
  2065
				if (IS_STR_FILLED(info->author))
ds@1952
  2066
				{
ds@1952
  2067
					UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
ds@1952
  2068
				}
ds@1952
  2069
				g_RootMenu.ConsolePrint("%s", buffer);
ds@1952
  2070
			}
ds@1952
  2071
ds@1952
  2072
			iter->Release();
ds@1952
  2073
ds@1952
  2074
			if (!m_FailList.empty())
ds@1952
  2075
			{
ds@1952
  2076
				g_RootMenu.ConsolePrint("Load Errors:");
ds@1952
  2077
ds@1952
  2078
				SourceHook::List<IPlugin *>::iterator _iter;
ds@1952
  2079
ds@1952
  2080
				CPlugin *pl;
ds@1952
  2081
ds@1952
  2082
				for (_iter=m_FailList.begin(); _iter!=m_FailList.end(); _iter++)
ds@1952
  2083
				{
ds@1952
  2084
					pl = (CPlugin *)*_iter;
ds@1952
  2085
					g_RootMenu.ConsolePrint("%s: %s",(IS_STR_FILLED(pl->GetPublicInfo()->name)) ? pl->GetPublicInfo()->name : pl->GetFilename(), pl->m_errormsg);
ds@1952
  2086
				}
ds@1952
  2087
			}
ds@1952
  2088
ds@1952
  2089
			return;
ds@1952
  2090
		}
ds@1952
  2091
		else if (strcmp(cmd, "load") == 0)
ds@1952
  2092
		{
ds@1952
  2093
			if (argcount < 4)
ds@1952
  2094
			{
ds@1952
  2095
				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load <file>");
ds@1952
  2096
				return;
ds@1952
  2097
			}
ds@1952
  2098
ds@1952
  2099
			char error[128];
ds@1952
  2100
			bool wasloaded;
ds@1952
  2101
			const char *filename = command.Arg(3);
ds@1952
  2102
ds@1952
  2103
			char pluginfile[256];
ds@1952
  2104
			const char *ext = g_LibSys.GetFileExtension(filename) ? "" : ".smx";
ds@1952
  2105
			UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", filename, ext);
ds@1952
  2106
ds@1952
  2107
			IPlugin *pl = LoadPlugin(pluginfile, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded);
ds@1952
  2108
ds@1952
  2109
			if (wasloaded)
ds@1952
  2110
			{
ds@1952
  2111
				g_RootMenu.ConsolePrint("[SM] Plugin %s is already loaded.", pluginfile);
ds@1952
  2112
				return;
ds@1952
  2113
			}
ds@1952
  2114
ds@1952
  2115
			if (pl)
ds@1952
  2116
			{
ds@1952
  2117
				g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", pluginfile);
ds@1952
  2118
			}
ds@1952
  2119
			else
ds@1952
  2120
			{
ds@1952
  2121
				g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", pluginfile, error);
ds@1952
  2122
			}
ds@1952
  2123
ds@1952
  2124
			return;
ds@1952
  2125
		}
ds@1952
  2126
		else if (strcmp(cmd, "unload") == 0)
ds@1952
  2127
		{
ds@1952
  2128
			if (argcount < 4)
ds@1952
  2129
			{
ds@1952
  2130
				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#|file>");
ds@1952
  2131
				return;
ds@1952
  2132
			}
ds@1952
  2133
ds@1952
  2134
			CPlugin *pl;
ds@1952
  2135
			char *end;
ds@1952
  2136
			const char *arg = command.Arg(3);
ds@1952
  2137
			int id = strtol(arg, &end, 10);
ds@1952
  2138
ds@1952
  2139
			if (*end == '\0')
ds@1952
  2140
			{
ds@1952
  2141
				pl = GetPluginByOrder(id);
ds@1952
  2142
				if (!pl)
ds@1952
  2143
				{
ds@1952
  2144
					g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
ds@1952
  2145
					return;
ds@1952
  2146
				}
ds@1952
  2147
			}
ds@1952
  2148
			else
ds@1952
  2149
			{
ds@1952
  2150
				char pluginfile[256];
ds@1952
  2151
				const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
ds@1952
  2152
				UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
ds@1952
  2153
ds@1952
  2154
				if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
ds@1952
  2155
				{
ds@1952
  2156
					g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
ds@1952
  2157
					return;
ds@1952
  2158
				}
ds@1952
  2159
			}
ds@1952
  2160
ds@1952
  2161
			char name[PLATFORM_MAX_PATH];
ds@1952
  2162
			const sm_plugininfo_t *info = pl->GetPublicInfo();
ds@1952
  2163
			strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
ds@1952
  2164
ds@1952
  2165
			if (UnloadPlugin(pl))
ds@1952
  2166
			{
ds@1952
  2167
				g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
ds@1952
  2168
			}
ds@1952
  2169
			else
ds@1952
  2170
			{
ds@1952
  2171
				g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name);
ds@1952
  2172
			}
ds@1952
  2173
ds@1952
  2174
			return;
ds@1952
  2175
		}
ds@1952
  2176
		else if (strcmp(cmd, "unload_all") == 0)
ds@1952
  2177
		{
ds@1952
  2178
			g_PluginSys.UnloadAll();
ds@1952
  2179
			g_RootMenu.ConsolePrint("[SM] All plugins have been unloaded.");
ds@1952
  2180
			return;
ds@1952
  2181
		}
ds@1952
  2182
		else if (strcmp(cmd, "load_lock") == 0)
ds@1952
  2183
		{
ds@1952
  2184
			if (m_LoadingLocked)
ds@1952
  2185
			{
ds@1952
  2186
				g_RootMenu.ConsolePrint("[SM] There is already a loading lock in effect.");
ds@1952
  2187
			}
ds@1952
  2188
			else
ds@1952
  2189
			{
ds@1952
  2190
				m_LoadingLocked = true;
ds@1952
  2191
				g_RootMenu.ConsolePrint("[SM] Loading is now locked; no plugins will be loaded or re-loaded.");
ds@1952
  2192
			}
ds@1952
  2193
			return;
ds@1952
  2194
		}
ds@1952
  2195
		else if (strcmp(cmd, "load_unlock") == 0)
ds@1952
  2196
		{
ds@1952
  2197
			if (m_LoadingLocked)
ds@1952
  2198
			{
ds@1952
  2199
				m_LoadingLocked = false;
ds@1952
  2200
				g_RootMenu.ConsolePrint("[SM] The loading lock is no longer in effect.");
ds@1952
  2201
			}
ds@1952
  2202
			else
ds@1952
  2203
			{
ds@1952
  2204
				g_RootMenu.ConsolePrint("[SM] There was no loading lock in effect.");
ds@1952
  2205
			}
ds@1952
  2206
			return;
ds@1952
  2207
		}
ds@1952
  2208
		else if (strcmp(cmd, "info") == 0)
ds@1952
  2209
		{
ds@1952
  2210
			if (argcount < 4)
ds@1952
  2211
			{
ds@1952
  2212
				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>");
ds@1952
  2213
				return;
ds@1952
  2214
			}
ds@1952
  2215
ds@1952
  2216
			CPlugin *pl;
ds@1952
  2217
			char *end;
ds@1952
  2218
			const char *arg = command.Arg(3);
ds@1952
  2219
			int id = strtol(arg, &end, 10);
ds@1952
  2220
ds@1952
  2221
			if (*end == '\0')
ds@1952
  2222
			{
ds@1952
  2223
				pl = GetPluginByOrder(id);
ds@1952
  2224
				if (!pl)
ds@1952
  2225
				{
ds@1952
  2226
					g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
ds@1952
  2227
					return;
ds@1952
  2228
				}
ds@1952
  2229
			}
ds@1952
  2230
			else
ds@1952
  2231
			{
ds@1952
  2232
				char pluginfile[256];
ds@1952
  2233
				const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
ds@1952
  2234
				UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
ds@1952
  2235
ds@1952
  2236
				if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
ds@1952
  2237
				{
ds@1952
  2238
					g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
ds@1952
  2239
					return;
ds@1952
  2240
				}
ds@1952
  2241
			}
ds@1952
  2242
ds@1952
  2243
			const sm_plugininfo_t *info = pl->GetPublicInfo();
ds@1952
  2244
ds@1952
  2245
			g_RootMenu.ConsolePrint("  Filename: %s", pl->GetFilename());
ds@1952
  2246
			if (pl->GetStatus() <= Plugin_Error)
ds@1952
  2247
			{
ds@1952
  2248
				if (IS_STR_FILLED(info->name))
ds@1952
  2249
				{
ds@1952
  2250
					if (IS_STR_FILLED(info->description))
ds@1952
  2251
					{
ds@1952
  2252
						g_RootMenu.ConsolePrint("  Title: %s (%s)", info->name, info->description);
ds@1952
  2253
					} else {
ds@1952
  2254
						g_RootMenu.ConsolePrint("  Title: %s", info->name);
ds@1952
  2255
					}
ds@1952
  2256
				}
ds@1952
  2257
				if (IS_STR_FILLED(info->author))
ds@1952
  2258
				{
ds@1952
  2259
					g_RootMenu.ConsolePrint("  Author: %s", info->author);
ds@1952
  2260
				}
ds@1952
  2261
				if (IS_STR_FILLED(info->version))
ds@1952
  2262
				{
ds@1952
  2263
					g_RootMenu.ConsolePrint("  Version: %s", info->version);
ds@1952
  2264
				}
ds@1952
  2265
				if (IS_STR_FILLED(info->url))
ds@1952
  2266
				{
ds@1952
  2267
					g_RootMenu.ConsolePrint("  URL: %s", info->url);
ds@1952
  2268
				}
ds@1952
  2269
				if (pl->GetStatus() == Plugin_Error)
ds@1952
  2270
				{
ds@1952
  2271
					g_RootMenu.ConsolePrint("  Error: %s", pl->m_errormsg);
ds@1952
  2272
				}
ds@1952
  2273
				else
ds@1952
  2274
				{
ds@1952
  2275
					g_RootMenu.ConsolePrint("  Debugging: %s", pl->IsDebugging() ? "Yes" : "No");
ds@1952
  2276
					g_RootMenu.ConsolePrint("  Running: %s", pl->GetStatus() == Plugin_Running ? "Yes" : "No");
ds@1952
  2277
ds@1952
  2278
					const char *typestr = "";
ds@1952
  2279
					switch (pl->GetType())
ds@1952
  2280
					{
ds@1952
  2281
					case PluginType_MapUpdated:
ds@1952
  2282
						typestr = "Map Change if Updated";
ds@1952
  2283
						break;
ds@1952
  2284
					case PluginType_MapOnly:
ds@1952
  2285
						typestr = "Map Change";
ds@1952
  2286
						break;
ds@1952
  2287
					case PluginType_Private:
ds@1952
  2288
					case PluginType_Global:
ds@1952
  2289
						typestr = "Never";
ds@1952
  2290
						break;
ds@1952
  2291
					}
ds@1952
  2292
ds@1952
  2293
					g_RootMenu.ConsolePrint("  Reloads: %s", typestr);
ds@1952
  2294
				}
ds@1952
  2295
			}
ds@1952
  2296
			else
ds@1952
  2297
			{
ds@1952
  2298
				g_RootMenu.ConsolePrint("  Load error: %s", pl->m_errormsg);
ds@1952
  2299
				g_RootMenu.ConsolePrint("  File info: (title \"%s\") (version \"%s\")", 
ds@1952
  2300
										info->name ? info->name : "<none>",
ds@1952
  2301
										info->version ? info->version : "<none>");
ds@1952
  2302
				if (IS_STR_FILLED(info->url))
ds@1952
  2303
				{
ds@1952
  2304
					g_RootMenu.ConsolePrint("  File URL: %s", info->url);
ds@1952
  2305
				}
ds@1952
  2306
			}
ds@1952
  2307
ds@1952
  2308
			return;
ds@1952
  2309
		}
ds@1952
  2310
		else if (strcmp(cmd, "debug") == 0)
ds@1952
  2311
		{
ds@1952
  2312
			if (argcount < 5)
ds@1952
  2313
			{
ds@1952
  2314
				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins debug <#> [on|off]");
ds@1952
  2315
				return;
ds@1952
  2316
			}
ds@1952
  2317
ds@1952
  2318
			CPlugin *pl;
ds@1952
  2319
			char *end;
ds@1952
  2320
			const char *arg = command.Arg(3);
ds@1952
  2321
			int id = strtol(arg, &end, 10);
ds@1952
  2322
ds@1952
  2323
			if (*end == '\0')
ds@1952
  2324
			{
ds@1952
  2325
				pl = GetPluginByOrder(id);
ds@1952
  2326
				if (!pl)
ds@1952
  2327
				{
ds@1952
  2328
					g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
ds@1952
  2329
					return;
ds@1952
  2330
				}
ds@1952
  2331
			}
ds@1952
  2332
			else
ds@1952
  2333
			{
ds@1952
  2334
				char pluginfile[256];
ds@1952
  2335
				const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
ds@1952
  2336
				UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
ds@1952
  2337
ds@1952
  2338
				if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
ds@1952
  2339
				{
ds@1952
  2340
					g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
ds@1952
  2341
					return;
ds@1952
  2342
				}
ds@1952
  2343
			}
ds@1952
  2344
ds@1952
  2345
			int res;
ds@1952
  2346
			const char *mode = command.Arg(4);
ds@1952
  2347
			if ((res=strcmp("on", mode)) && strcmp("off", mode))
ds@1952
  2348
			{
ds@1952
  2349
				g_RootMenu.ConsolePrint("[SM] The only possible options are \"on\" and \"off.\"");
ds@1952
  2350
				return;
ds@1952
  2351
			}
ds@1952
  2352
ds@1952
  2353
			bool debug;
ds@1952
  2354
			if (!res)
ds@1952
  2355
			{
ds@1952
  2356
				debug = true;
ds@1952
  2357
			}
ds@1952
  2358
			else
ds@1952
  2359
			{
ds@1952
  2360
				debug = false;
ds@1952
  2361
			}
ds@1952
  2362
ds@1952
  2363
			if (debug && pl->IsDebugging())
ds@1952
  2364
			{
ds@1952
  2365
				g_RootMenu.ConsolePrint("[SM] This plugin is already in debug mode.");
ds@1952
  2366
				return;
ds@1952
  2367
			}
ds@1952
  2368
			else if (!debug && !pl->IsDebugging())
ds@1952
  2369
			{
ds@1952
  2370
				g_RootMenu.ConsolePrint("[SM] Debug mode is already disabled in this plugin.");
ds@1952
  2371
				return;
ds@1952
  2372
			}
ds@1952
  2373
ds@1952
  2374
			char error[256];
ds@1952
  2375
			if (pl->ToggleDebugMode(debug, error, sizeof(error)))
ds@1952
  2376
			{
ds@1952
  2377
				g_RootMenu.ConsolePrint("[SM] Successfully toggled debug mode on plugin %s.", pl->GetFilename());
ds@1952
  2378
				return;
ds@1952
  2379
			}
ds@1952
  2380
			else
ds@1952
  2381
			{
ds@1952
  2382
				g_RootMenu.ConsolePrint("[SM] Could not toggle debug mode on plugin %s.", pl->GetFilename());
ds@1952
  2383
				g_RootMenu.ConsolePrint("[SM] Plugin returned error: %s", error);
ds@1952
  2384
				return;
ds@1952
  2385
			}
ds@1952
  2386
		}
ds@1952
  2387
		else if (strcmp(cmd, "refresh") == 0)
ds@1952
  2388
		{
ds@1952
  2389
			g_SourceMod.DoGlobalPluginLoads();
ds@1952
  2390
			g_RootMenu.ConsolePrint("[SM] The plugin list has been refreshed and reloaded.");
ds@1952
  2391
			return;
ds@1952
  2392
		}
ds@1952
  2393
		else if (strcmp(cmd, "reload") == 0)
ds@1952
  2394
		{
ds@1952
  2395
			if (argcount < 4)
ds@1952
  2396
			{
ds@1952
  2397
				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins reload <#|file>");
ds@1952
  2398
				return;
ds@1952
  2399
			}
ds@1952
  2400
ds@1952
  2401
			CPlugin *pl;
ds@1952
  2402
			char *end;
ds@1952
  2403
			const char *arg = command.Arg(3);
ds@1952
  2404
			int id = strtol(arg, &end, 10);
ds@1952
  2405
ds@1952
  2406
			if (*end == '\0')
ds@1952
  2407
			{
ds@1952
  2408
				pl = GetPluginByOrder(id);
ds@1952
  2409
				if (!pl)
ds@1952
  2410
				{
ds@1952
  2411
					g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
ds@1952
  2412
					return;
ds@1952
  2413
				}
ds@1952
  2414
			}
ds@1952
  2415
			else
ds@1952
  2416
			{
ds@1952
  2417
				char pluginfile[256];
ds@1952
  2418
				const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
ds@1952
  2419
				UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
ds@1952
  2420
ds@1952
  2421
				if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
ds@1952
  2422
				{
ds@1952
  2423
					g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
ds@1952
  2424
					return;
ds@1952
  2425
				}
ds@1952
  2426
			}
ds@1952
  2427
ds@1952
  2428
			char name[PLATFORM_MAX_PATH];
ds@1952
  2429
			const sm_plugininfo_t *info = pl->GetPublicInfo();
ds@1952
  2430
			strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
ds@1952
  2431
ds@1952
  2432
			if (ReloadPlugin(pl))
ds@1952
  2433
			{
ds@1952
  2434
				g_RootMenu.ConsolePrint("[SM] Plugin %s reloaded successfully.", name);
ds@1952
  2435
			}
ds@1952
  2436
			else
ds@1952
  2437
			{
ds@1952
  2438
				g_RootMenu.ConsolePrint("[SM] Failed to reload plugin %s.", name);
ds@1952
  2439
			}
ds@1952
  2440
ds@1952
  2441
			return;
ds@1952
  2442
		}
ds@1952
  2443
	}
ds@1952
  2444
ds@1952
  2445
	/* Draw the main menu */
ds@1952
  2446
	g_RootMenu.ConsolePrint("SourceMod Plugins Menu:");
ds@1952
  2447
	g_RootMenu.DrawGenericOption("debug", "Toggle debug mode on a plugin");
ds@1952
  2448
	g_RootMenu.DrawGenericOption("info", "Information about a plugin");
ds@1952
  2449
	g_RootMenu.DrawGenericOption("list", "Show loaded plugins");
ds@1952
  2450
	g_RootMenu.DrawGenericOption("load", "Load a plugin");
ds@1952
  2451
	g_RootMenu.DrawGenericOption("load_lock", "Prevents any more plugins from being loaded");
ds@1952
  2452
	g_RootMenu.DrawGenericOption("load_unlock", "Re-enables plugin loading");
ds@1952
  2453
	g_RootMenu.DrawGenericOption("refresh", "Reloads/refreshes all plugins in the plugins folder");
ds@1952
  2454
	g_RootMenu.DrawGenericOption("reload", "Reloads a plugin");
ds@1952
  2455
	g_RootMenu.DrawGenericOption("unload", "Unload a plugin");
ds@1952
  2456
	g_RootMenu.DrawGenericOption("unload_all", "Unloads all plugins");
ds@1952
  2457
}
ds@1952
  2458
ds@1952
  2459
bool CPluginManager::ReloadPlugin(CPlugin *pl)
ds@1952
  2460
{
ds@1952
  2461
	List<CPlugin *>::iterator iter;
ds@1952
  2462
	char filename[PLATFORM_MAX_PATH];
ds@1952
  2463
	bool debug, wasloaded;
ds@1952
  2464
	PluginType ptype;
ds@1952
  2465
	IPlugin *newpl;
ds@1952
  2466
	int id = 1;
ds@1952
  2467
ds@1952
  2468
	strcpy(filename, pl->m_filename);
ds@1952
  2469
	debug = pl->IsDebugging();
ds@1952
  2470
	ptype = pl->GetType();
ds@1952
  2471
ds@1952
  2472
	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++, id++)
ds@1952
  2473
	{
ds@1952
  2474
		if ((*iter) == pl)
ds@1952
  2475
		{
ds@1952
  2476
			break;
ds@1952
  2477
		}
ds@1952
  2478
	}
ds@1952
  2479
ds@1952
  2480
	if (!UnloadPlugin(pl))
ds@1952
  2481
	{
ds@1952
  2482
		return false;
ds@1952
  2483
	}
ds@1952
  2484
	if (!(newpl=LoadPlugin(filename, debug, ptype, NULL, 0, &wasloaded)))
ds@1952
  2485
	{
ds@1952
  2486
		return false;
ds@1952
  2487
	}
ds@1952
  2488
ds@1952
  2489
	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
ds@1952
  2490
	{
ds@1952
  2491
		if ((*iter) == (CPlugin *)newpl)
ds@1952
  2492
		{
ds@1952
  2493
			m_plugins.erase(iter);
ds@1952
  2494
			break;
ds@1952
  2495
		}
ds@1952
  2496
	}
ds@1952
  2497
ds@1952
  2498
	int i;
ds@1952
  2499
	for (i=1, iter=m_plugins.begin(); iter!=m_plugins.end() && i<id; iter++, i++)
ds@1952
  2500
	{
ds@1952
  2501
	}
ds@1952
  2502
	m_plugins.insert(iter, (CPlugin *)newpl);
ds@1952
  2503
ds@1952
  2504
	return true;
ds@1952
  2505
}
ds@1952
  2506
ds@1952
  2507
void CPluginManager::ReloadOrUnloadPlugins()
ds@1952
  2508
{
ds@1952
  2509
	/* If we're in a load lock, just skip this whole bit. */
ds@1952
  2510
	if (m_LoadingLocked)
ds@1952
  2511
	{
ds@1952
  2512
		return;
ds@1952
  2513
	}
ds@1952
  2514
ds@1952
  2515
	List<CPlugin *>::iterator iter;
ds@1952
  2516
	List<CPlugin *> tmp_list = m_plugins;
ds@1952
  2517
	CPlugin *pl;
ds@1952
  2518
	time_t t;
ds@1952
  2519
ds@1952
  2520
	for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++)
ds@1952
  2521
	{
ds@1952
  2522
		pl = (*iter);
ds@1952
  2523
		if (pl->GetType() ==  PluginType_MapOnly)
ds@1952
  2524
		{
ds@1952
  2525
			UnloadPlugin((IPlugin *)pl);
ds@1952
  2526
		}
ds@1952
  2527
		else if (pl->GetType() == PluginType_MapUpdated)
ds@1952
  2528
		{
ds@1952
  2529
			t = pl->GetFileTimeStamp();
ds@1952
  2530
			if (!t || t > pl->GetTimeStamp())
ds@1952
  2531
			{
ds@1952
  2532
				pl->SetTimeStamp(t);
ds@1952
  2533
				UnloadPlugin((IPlugin *)pl);
ds@1952
  2534
			}
ds@1952
  2535
		}
ds@1952
  2536
	}
ds@1952
  2537
}
ds@1952
  2538
ds@1952
  2539
void CPluginManager::_SetPauseState(CPlugin *pl, bool paused)
ds@1952
  2540
{
ds@1952
  2541
	List<IPluginsListener *>::iterator iter;
ds@1952
  2542
	IPluginsListener *pListener;
ds@1952
  2543
	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
ds@1952
  2544
	{
ds@1952
  2545
		pListener = (*iter);
ds@1952
  2546
		pListener->OnPluginPauseChange(pl, paused);
ds@1952
  2547
	}	
ds@1952
  2548
}
ds@1952
  2549
ds@1952
  2550
void CPluginManager::AddFunctionsToForward(const char *name, IChangeableForward *pForward)
ds@1952
  2551
{
ds@1952
  2552
	List<CPlugin *>::iterator iter;
ds@1952
  2553
	CPlugin *pPlugin;
ds@1952
  2554
	IPluginFunction *pFunction;
ds@1952
  2555
ds@1952
  2556
	for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
ds@1952
  2557
	{
ds@1952
  2558
		pPlugin = (*iter);
ds@1952
  2559
ds@1952
  2560
		if (pPlugin->GetStatus() <= Plugin_Paused)
ds@1952
  2561
		{
ds@1952
  2562
			pFunction = pPlugin->GetBaseContext()->GetFunctionByName(name);
ds@1952
  2563
ds@1952
  2564
			if (pFunction)
ds@1952
  2565
			{
ds@1952
  2566
				pForward->AddFunction(pFunction);
ds@1952
  2567
			}
ds@1952
  2568
		}
ds@1952
  2569
	}
ds@1952
  2570
}
ds@1952
  2571
ds@1952
  2572
CPlugin *CPluginManager::GetPluginFromIdentity(IdentityToken_t *pToken)
ds@1952
  2573
{
ds@1952
  2574
	if (pToken->type != g_PluginIdent)
ds@1952
  2575
	{
ds@1952
  2576
		return NULL;
ds@1952
  2577
	}
ds@1952
  2578
ds@1952
  2579
	return (CPlugin *)(pToken->ptr);
ds@1952
  2580
}
ds@1952
  2581
ds@1952
  2582
void CPluginManager::OnLibraryAction(const char *lib, bool is_a_plugin, bool drop)
ds@1952
  2583
{
ds@1952
  2584
	List<CPlugin *>::iterator iter;
ds@1952
  2585
ds@1952
  2586
	struct _pl
ds@1952
  2587
	{
ds@1952
  2588
		cell_t name;
ds@1952
  2589
		cell_t file;
ds@1952
  2590
		cell_t required;
ds@1952
  2591
	} *plc;
ds@1952
  2592
ds@1952
  2593
	struct _ext
ds@1952
  2594
	{
ds@1952
  2595
		cell_t name;
ds@1952
  2596
		cell_t file;
ds@1952
  2597
		cell_t autoload;
ds@1952
  2598
		cell_t required;
ds@1952
  2599
	} *ext;
ds@1952
  2600
ds@1952
  2601
	const char *name = drop ? "OnLibraryRemoved" : "OnLibraryAdded";
ds@1952
  2602
ds@1952
  2603
	for (iter=m_plugins.begin();
ds@1952
  2604
		 iter!=m_plugins.end();
ds@1952
  2605
		 iter++)
ds@1952
  2606
	{
ds@1952
  2607
		CPlugin *pl = (*iter);
ds@1952
  2608
		if (pl->GetStatus() != Plugin_Running)
ds@1952
  2609
		{
ds@1952
  2610
			continue;
ds@1952
  2611
		}
ds@1952
  2612
		IPluginContext *pContext = pl->GetBaseContext();
ds@1952
  2613
		IPluginFunction *pf = pContext->GetFunctionByName(name);
ds@1952
  2614
		if (!pf)
ds@1952
  2615
		{
ds@1952
  2616
			continue;
ds@1952
  2617
		}
ds@1952
  2618
		uint32_t num_vars = pContext->GetPubVarsNum();
ds@1952
  2619
		for (uint32_t i=0; i<num_vars; i++)
ds@1952
  2620
		{
ds@1952
  2621
			sp_pubvar_t *pubvar;
ds@1952
  2622
			if (pContext->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
ds@1952
  2623
			{
ds@1952
  2624
				continue;
ds@1952
  2625
			}
ds@1952
  2626
			if (is_a_plugin && strncmp(pubvar->name, "__pl_", 5) == 0)
ds@1952
  2627
			{
ds@1952
  2628
				plc = (_pl *)pubvar->offs;
ds@1952
  2629
				if (plc->required)
ds@1952
  2630
				{
ds@1952
  2631
					continue;
ds@1952
  2632
				}
ds@1952
  2633
				char *str;
ds@1952
  2634
				pContext->LocalToString(plc->name, &str);
ds@1952
  2635
				if (strcmp(str, lib) != 0)
ds@1952
  2636
				{
ds@1952
  2637
					continue;
ds@1952
  2638
				}
ds@1952
  2639
				pf->PushString(lib);
ds@1952
  2640
				pf->Execute(NULL);
ds@1952
  2641
			}
ds@1952
  2642
			else if (!is_a_plugin && strncmp(pubvar->name, "__ext_", 6) == 0)
ds@1952
  2643
			{
ds@1952
  2644
				ext = (_ext *)pubvar->offs;
ds@1952
  2645
				if (ext->required)
ds@1952
  2646
				{
ds@1952
  2647
					continue;
ds@1952
  2648
				}
ds@1952
  2649
				char *str;
ds@1952
  2650
				pContext->LocalToString(ext->name, &str);
ds@1952
  2651
				if (strcmp(str, lib) != 0)
ds@1952
  2652
				{
ds@1952
  2653
					continue;
ds@1952
  2654
				}
ds@1952
  2655
				pf->PushString(lib);
ds@1952
  2656
				pf->Execute(NULL);
ds@1952
  2657
			}
ds@1952
  2658
		}
ds@1952
  2659
	}
ds@1952
  2660
}
ds@1952
  2661
ds@1952
  2662
bool CPluginManager::LibraryExists(const char *lib)
ds@1952
  2663
{
ds@1952
  2664
	List<CPlugin *>::iterator iter;
ds@1952
  2665
ds@1952
  2666
	for (iter=m_plugins.begin();
ds@1952
  2667
		 iter!=m_plugins.end();
ds@1952
  2668
		 iter++)
ds@1952
  2669
	{
ds@1952
  2670
		CPlugin *pl = (*iter);
ds@1952
  2671
		if (pl->GetStatus() != Plugin_Running)
ds@1952
  2672
		{
ds@1952
  2673
			continue;
ds@1952
  2674
		}
ds@1952
  2675
		List<String>::iterator s_iter;
ds@1952
  2676
		for (s_iter = pl->m_Libraries.begin();
ds@1952
  2677
			 s_iter != pl->m_Libraries.end();
ds@1952
  2678
			 s_iter++)
ds@1952
  2679
		{
ds@1952
  2680
			if ((*s_iter).compare(lib) == 0)
ds@1952
  2681
			{
ds@1952
  2682
				return true;
ds@1952
  2683
			}
ds@1952
  2684
		}
ds@1952
  2685
	}
ds@1952
  2686
ds@1952
  2687
	return false;
ds@1952
  2688
}
ds@1952
  2689
ds@1952
  2690
void CPluginManager::AllPluginsLoaded()
ds@1952
  2691
{
ds@1952
  2692
	List<CPlugin *>::iterator iter;
ds@1952
  2693
	CPlugin *pl;
ds@1952
  2694
ds@1952
  2695
	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
ds@1952
  2696
	{
ds@1952
  2697
		pl = (*iter);
ds@1952
  2698
		pl->Call_OnAllPluginsLoaded();
ds@1952
  2699
	}
ds@1952
  2700
}
ds@1952
  2701
ds@1952
  2702
void CPluginManager::UnloadAll()
ds@1952
  2703
{
ds@1952
  2704
	List<CPlugin *>::iterator iter;
ds@1952
  2705
	while ( (iter = m_plugins.begin()) != m_plugins.end() )
ds@1952
  2706
	{
ds@1952
  2707
		UnloadPlugin((*iter));
ds@1952
  2708
	}
ds@1952
  2709
}
ds@1952
  2710
ds@1952
  2711
int CPluginManager::GetOrderOfPlugin(IPlugin *pl)
ds@1952
  2712
{
ds@1952
  2713
	int id = 1;
ds@1952
  2714
	List<CPlugin *>::iterator iter;
ds@1952
  2715
ds@1952
  2716
	for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
ds@1952
  2717
	{
ds@1952
  2718
		if ((*iter) == pl)
ds@1952
  2719
		{
ds@1952
  2720
			return id;
ds@1952
  2721
		}
ds@1952
  2722
	}
ds@1952
  2723
ds@1952
  2724
	return -1;
ds@1952
  2725
}
ds@1952
  2726
ds@1952
  2727
CPlugin *CPluginManager::FindPluginByConsoleArg(const char *arg)
ds@1952
  2728
{
ds@1952
  2729
	int id;
ds@1952
  2730
	char *end;
ds@1952
  2731
	CPlugin *pl;
ds@1952
  2732
	
ds@1952
  2733
	id = strtol(arg, &end, 10);
ds@1952
  2734
ds@1952
  2735
	if (*end == '\0')
ds@1952
  2736
	{
ds@1952
  2737
		pl = GetPluginByOrder(id);
ds@1952
  2738
		if (!pl)
ds@1952
  2739
		{
ds@1952
  2740
			return NULL;
ds@1952
  2741
		}
ds@1952
  2742
	}
ds@1952
  2743
	else
ds@1952
  2744
	{
ds@1952
  2745
		char pluginfile[256];
ds@1952
  2746
		const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
ds@1952
  2747
		UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
ds@1952
  2748
ds@1952
  2749
		if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
ds@1952
  2750
		{
ds@1952
  2751
			return NULL;
ds@1952
  2752
		}
ds@1952
  2753
	}
ds@1952
  2754
ds@1952
  2755
	return pl;
ds@1952
  2756
}