core/systems/PluginSys.cpp
author David Anderson <dvander@alliedmods.net>
Sat Nov 11 05:49:52 2006 +0000 (2006-11-11)
changeset 176 c1699993453c
parent 174 5611e4b3fa42
child 177 d29ade52d953
permissions -rw-r--r--
Implemented a lot more of the plugin system
added aggressive caching O(1) function object retrieval
     1 #include <stdio.h>
     2 #include "PluginSys.h"
     3 
     4 CPluginManager g_PluginMngr;
     5 
     6 CPluginManager::CPluginManager()
     7 {
     8 }
     9 
    10 void CFunction::Set(funcid_t funcid, CPlugin *plugin)
    11 {
    12 	m_funcid = funcid;
    13 	m_pPlugin = plugin;
    14 }
    15 
    16 int CFunction::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result)
    17 {
    18 	IPluginContext *ctx = m_pPlugin->m_ctx_current.base;
    19 
    20 	for (unsigned int i=0; i<num_params; i++)
    21 	{
    22 		ctx->PushCell(params[i]);
    23 	}
    24 
    25 	return ctx->Execute(m_funcid, result);
    26 }
    27 
    28 IPlugin *CFunction::GetParentPlugin()
    29 {
    30 	return m_pPlugin;
    31 }
    32 
    33 CFunction::CFunction(funcid_t funcid, CPlugin *plugin) : 
    34 	m_funcid(funcid), m_pPlugin(plugin)
    35 {
    36 }
    37 
    38 CPlugin *CPlugin::CreatePlugin(const char *file, 
    39 								bool debug_default, 
    40 								PluginType type, 
    41 								char *error, 
    42 								size_t maxlen)
    43 {
    44 	static unsigned int MySerial = 0;
    45 	FILE *fp = fopen(file, "rb");
    46 
    47 	if (!fp)
    48 	{
    49 		snprintf(error, maxlen, "Could not open file");
    50 		return NULL;
    51 	}
    52 
    53 	int err;
    54 	sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err);
    55 	if (pl == NULL)
    56 	{
    57 		snprintf(error, maxlen, "Could not load plugin, error %d", err);
    58 		return NULL;
    59 	}
    60 
    61 	fclose(fp);
    62 
    63 	ICompilation *co = g_pVM->StartCompilation(pl);
    64 	
    65 	if (debug_default)
    66 	{
    67 		if (!g_pVM->SetCompilationOption(co, "debug", "1"))
    68 		{
    69 			g_pVM->AbortCompilation(co);
    70 			snprintf(error, maxlen, "Could not set plugin to debug mode");
    71 			return NULL;
    72 		}
    73 	}
    74 
    75 	sp_context_t *ctx = g_pVM->CompileToContext(co, &err);
    76 	if (ctx == NULL)
    77 	{
    78 		snprintf(error, maxlen, "Plugin failed to load, JIT error: %d", err);
    79 		return NULL;
    80 	}
    81 
    82 	IPluginContext *base = g_pSourcePawn->CreateBaseContext(ctx);
    83 	CPlugin *pPlugin = new CPlugin;
    84 
    85 	snprintf(pPlugin->m_filename, PLATFORM_MAX_PATH, "%s", file);
    86 	pPlugin->m_debugging = debug_default;
    87 	pPlugin->m_ctx_current.base = base;
    88 	pPlugin->m_ctx_current.ctx = ctx;
    89 	pPlugin->m_type = type;
    90 	pPlugin->m_serial = ++MySerial;
    91 	pPlugin->m_status = Plugin_Loaded;
    92 	pPlugin->m_plugin = pl;
    93 
    94 	pPlugin->UpdateInfo();
    95 
    96 	ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)(IPlugin *)pPlugin;
    97 
    98 	/* Build function information loosely */
    99 	pPlugin->m_funcsnum = g_pVM->FunctionCount(ctx);
   100 
   101 	if (pPlugin->m_funcsnum)
   102 	{
   103 		pPlugin->m_priv_funcs = new CFunction *[pPlugin->m_funcsnum];
   104 		memset(pPlugin->m_priv_funcs, 0, sizeof(CFunction *) * pPlugin->m_funcsnum);
   105 	} else {
   106 		pPlugin->m_priv_funcs = NULL;
   107 	}
   108 
   109 	if (pl->info.publics_num)
   110 	{
   111 		pPlugin->m_pub_funcs = new CFunction *[pl->info.publics_num];
   112 		memset(pPlugin->m_pub_funcs, 0, sizeof(CFunction *) * pl->info.publics_num);
   113 	} else {
   114 		pPlugin->m_pub_funcs = NULL;
   115 	}
   116 
   117 	return pPlugin;
   118 }
   119 
   120 IPluginFunction *CPlugin::GetFunctionById(funcid_t func_id)
   121 {
   122 	CFunction *pFunc = NULL;
   123 	funcid_t save = func_id;
   124 
   125 	if (func_id & 1)
   126 	{
   127 		func_id >>= 1;
   128 		if (func_id >= m_plugin->info.publics_num)
   129 		{
   130 			return NULL;
   131 		}
   132 		pFunc = m_pub_funcs[func_id];
   133 		if (!pFunc)
   134 		{
   135 			pFunc = g_PluginMngr.GetFunctionFromPool(save, this);
   136 			m_pub_funcs[func_id] = pFunc;
   137 		}
   138 	} else {
   139 		func_id >>= 1;
   140 		unsigned int index;
   141 		if (!g_pVM->FunctionLookup(m_ctx_current.ctx, func_id, &index))
   142 		{
   143 			return NULL;
   144 		}
   145 		pFunc = m_priv_funcs[func_id];
   146 		if (!pFunc)
   147 		{
   148 			pFunc = g_PluginMngr.GetFunctionFromPool(save, this);
   149 			m_priv_funcs[func_id] = pFunc;
   150 		}
   151 	}
   152 
   153 	return pFunc;
   154 }
   155 
   156 IPluginFunction *CPlugin::GetFunctionByName(const char *public_name)
   157 {
   158 	uint32_t index;
   159 	IPluginContext *base = m_ctx_current.base;
   160 
   161 	if (base->FindPublicByName(public_name, &index) != SP_ERROR_NONE)
   162 	{
   163 		return NULL;
   164 	}
   165 
   166 	CFunction *pFunc = m_pub_funcs[index];
   167 	if (!pFunc)
   168 	{
   169 		sp_public_t *pub = NULL;
   170 		base->GetPublicByIndex(index, &pub);
   171 		if (pub)
   172 		{
   173 			pFunc = g_PluginMngr.GetFunctionFromPool(pub->funcid, this);
   174 			m_pub_funcs[index] = pFunc;
   175 		}
   176 	}
   177 
   178 	return pFunc;
   179 }
   180 
   181 void CPlugin::UpdateInfo()
   182 {
   183 	/* Now grab the info */
   184 	uint32_t idx;
   185 	IPluginContext *base = GetBaseContext();
   186 	int err = base->FindPubvarByName("myinfo", &idx);
   187 
   188 	if (err == SP_ERROR_NONE)
   189 	{
   190 		struct sm_plugininfo_c_t
   191 		{
   192 			cell_t name;
   193 			cell_t description;
   194 			cell_t author;
   195 			cell_t version;
   196 			cell_t url;
   197 		};
   198 		sm_plugininfo_c_t *cinfo;
   199 		cell_t local_addr;
   200 
   201 		base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
   202 		base->LocalToString(cinfo->name, (char **)&m_info.name);
   203 		base->LocalToString(cinfo->description, (char **)&m_info.description);
   204 		base->LocalToString(cinfo->author, (char **)&m_info.author);
   205 		base->LocalToString(cinfo->url, (char **)&m_info.url);
   206 		base->LocalToString(cinfo->version, (char **)&m_info.version);
   207 	}
   208 
   209 	m_info.author = m_info.author ? m_info.author : "";
   210 	m_info.description = m_info.description ? m_info.description : "";
   211 	m_info.name = m_info.name ? m_info.name : "";
   212 	m_info.url = m_info.url ? m_info.url : "";
   213 	m_info.version = m_info.version ? m_info.version : "";
   214 }
   215 
   216 const sp_plugin_t *CPlugin::GetPluginStructure() const
   217 {
   218 	return m_plugin;
   219 }
   220 
   221 IPluginContext *CPlugin::GetBaseContext() const
   222 {
   223 	return m_ctx_current.base;
   224 }
   225 
   226 sp_context_t *CPlugin::GetContext() const
   227 {
   228 	return m_ctx_current.ctx;
   229 }
   230 
   231 const char *CPlugin::GetFilename() const
   232 {
   233 	return m_filename;
   234 }
   235 
   236 PluginType CPlugin::GetType() const
   237 {
   238 	return m_type;
   239 }
   240 
   241 const sm_plugininfo_t *CPlugin::GetPublicInfo() const
   242 {
   243 	return &m_info;
   244 }
   245 
   246 unsigned int CPlugin::GetSerial() const
   247 {
   248 	return m_serial;
   249 }
   250 
   251 PluginStatus CPlugin::GetStatus() const
   252 {
   253 	return m_status;
   254 }
   255 
   256 bool CPlugin::IsDebugging() const
   257 {
   258 	return m_debugging;
   259 }
   260 
   261 bool CPlugin::SetPauseState(bool paused)
   262 {
   263 	if (paused && GetStatus() != Plugin_Paused)
   264 	{
   265 		return false;
   266 	} else if (!paused && GetStatus() != Plugin_Running) {
   267 		return false;
   268 	}
   269 	
   270 	/* :TODO: execute some forwards or some crap */
   271 
   272 	return true;
   273 }
   274 
   275 /*******************
   276  * PLUGIN ITERATOR *
   277  *******************/
   278 CPluginManager::CPluginIterator::CPluginIterator(List<IPlugin *> *_mylist)
   279 {
   280 	mylist = _mylist;
   281 }
   282 
   283 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
   284 {
   285 	return (*current);
   286 }
   287 
   288 bool CPluginManager::CPluginIterator::MorePlugins()
   289 {
   290 	return (current != mylist->end());
   291 }
   292 
   293 void CPluginManager::CPluginIterator::NextPlugin()
   294 {
   295 	current++;
   296 }
   297 
   298 void CPluginManager::CPluginIterator::Release()
   299 {
   300 	g_PluginMngr.ReleaseIterator(this);
   301 }
   302 
   303 CPluginManager::CPluginIterator::~CPluginIterator()
   304 {
   305 }
   306 
   307 void CPluginManager::CPluginIterator::Reset()
   308 {
   309 	current = mylist->begin();
   310 }
   311 
   312 /******************
   313  * PLUGIN MANAGER *
   314  ******************/
   315 
   316 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t err_max)
   317 {
   318 	CPlugin *pPlugin = CPlugin::CreatePlugin(path, debug, type, error, err_max);
   319 
   320 	if (!pPlugin)
   321 	{
   322 		return NULL;
   323 	}
   324 
   325 	m_plugins.push_back(pPlugin);
   326 
   327 	List<IPluginsListener *>::iterator iter;
   328 	IPluginsListener *pListener;
   329 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
   330 	{
   331 		pListener = (*iter);
   332 		pListener->OnPluginCreated(pPlugin);
   333 	}
   334 
   335 	/* :TODO: a lot more... */
   336 
   337 	return pPlugin;
   338 }
   339 
   340 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
   341 {
   342 	CPlugin *pPlugin = (CPlugin *)plugin;
   343 
   344 	/* :TODO: More */
   345 
   346 	List<IPluginsListener *>::iterator iter;
   347 	IPluginsListener *pListener;
   348 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
   349 	{
   350 		pListener = (*iter);
   351 		pListener->OnPluginDestroyed(pPlugin);
   352 	}
   353 	
   354 	if (pPlugin->m_pub_funcs)
   355 	{
   356 		for (uint32_t i=0; i<pPlugin->m_plugin->info.publics_num; i++)
   357 		{
   358 			delete pPlugin->m_pub_funcs[i];
   359 		}
   360 		delete [] pPlugin->m_pub_funcs;
   361 		pPlugin->m_pub_funcs = NULL;
   362 	}
   363 
   364 	if (pPlugin->m_priv_funcs)
   365 	{
   366 		for (unsigned int i=0; i<pPlugin->m_funcsnum; i++)
   367 		{
   368 			delete pPlugin->m_priv_funcs[i];
   369 		}
   370 		delete [] pPlugin->m_priv_funcs;
   371 		pPlugin->m_priv_funcs = NULL;
   372 	}
   373 
   374 	if (pPlugin->m_ctx_current.base)
   375 	{
   376 		g_pSourcePawn->FreeBaseContext(pPlugin->m_ctx_current.base);
   377 	}
   378 	if (pPlugin->m_ctx_backup.base)
   379 	{
   380 		g_pSourcePawn->FreeBaseContext(pPlugin->m_ctx_backup.base);
   381 	}
   382 	if (pPlugin->m_ctx_current.ctx)
   383 	{
   384 		pPlugin->m_ctx_current.ctx->vmbase->FreeContext(pPlugin->m_ctx_current.ctx);
   385 	}
   386 	if (pPlugin->m_ctx_backup.ctx)
   387 	{
   388 		pPlugin->m_ctx_backup.ctx->vmbase->FreeContext(pPlugin->m_ctx_backup.ctx);
   389 	}
   390 
   391 	g_pSourcePawn->FreeFromMemory(pPlugin->m_plugin);
   392 
   393 	delete pPlugin;
   394 
   395 	return true;
   396 }
   397 
   398 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
   399 {
   400 	IPlugin *pl = (IPlugin *)ctx->user[SM_CONTEXTVAR_MYSELF];
   401 	return pl;
   402 }
   403 
   404 unsigned int CPluginManager::GetPluginCount()
   405 {
   406 	return m_plugins.size();
   407 }
   408 
   409 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
   410 {
   411 	m_listeners.push_back(listener);
   412 }
   413 
   414 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
   415 {
   416 	m_listeners.remove(listener);
   417 }
   418 
   419 IPluginIterator *CPluginManager::GetPluginIterator()
   420 {
   421 	if (m_iters.empty())
   422 	{
   423 		return new CPluginIterator(&m_plugins);
   424 	} else {
   425 		CPluginIterator *iter = m_iters.front();
   426 		m_iters.pop();
   427 		return iter;
   428 	}
   429 }
   430 
   431 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
   432 {
   433 	m_iters.push(iter);
   434 }
   435 
   436 void CPluginManager::ReleaseFunctionToPool(CFunction *func)
   437 {
   438 	m_funcpool.push(func);
   439 }
   440 
   441 CFunction *CPluginManager::GetFunctionFromPool(funcid_t f, CPlugin *plugin)
   442 {
   443 	if (m_funcpool.empty())
   444 	{
   445 		return new CFunction(f, plugin);
   446 	} else {
   447 		CFunction *func = m_funcpool.front();
   448 		m_funcpool.pop();
   449 		func->Set(f, plugin);
   450 		return func;
   451 	}
   452 }
   453 
   454 CPluginManager::~CPluginManager()
   455 {
   456 	//:TODO: we need a good way to free what we're holding
   457 }