Added global class initialization automation

Finalized basics of plugin loading
Began redoing how dependencies will be tracked
Renamed some bad names
Finished some stuff in ForwardSys

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40216
This commit is contained in:
David Anderson
2006-12-15 13:38:04 +00:00
parent a93faa3cbf
commit 90d1f4495e
16 changed files with 509 additions and 131 deletions
+3 -3
View File
@@ -112,7 +112,7 @@ int CFunction::PushArray(cell_t *inarray, unsigned int cells, cell_t **phys_addr
int CFunction::PushString(const char *string)
{
return _PushString(string, SP_STRING_COPY, 0, strlen(string)+1);
return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
}
int CFunction::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
@@ -141,12 +141,12 @@ int CFunction::_PushString(const char *string, int sz_flags, int cp_flags, size_
m_params[m_curparam] = info->local_addr;
m_curparam++; /* Prevent a leak */
if (!(sz_flags & SP_STRING_COPY))
if (!(sz_flags & SM_PARAM_STRING_COPY))
{
goto skip_localtostr;
}
if (sz_flags & SP_STRING_UTF8)
if (sz_flags & SM_PARAM_STRING_UTF8)
{
if ((err=base->StringToLocalUTF8(info->local_addr, len, string, NULL)) != SP_ERROR_NONE)
{
+53 -6
View File
@@ -28,6 +28,16 @@ CForwardManager g_Forwards;
* X Push vararg strings (copyback tested = :TODO:)
*/
void CForwardManager::OnSourceModAllInitialized()
{
g_PluginSys.AddPluginsListener(this);
}
void CForwardManager::OnSourceModShutdown()
{
g_PluginSys.RemovePluginsListener(this);
}
IForward *CForwardManager::CreateForward(const char *name, ExecType et, unsigned int num_params, ParamType *types, ...)
{
CForward *fwd;
@@ -38,6 +48,11 @@ IForward *CForwardManager::CreateForward(const char *name, ExecType et, unsigned
va_end(ap);
if (fwd)
{
m_managed.push_back(fwd);
}
return fwd;
}
@@ -51,9 +66,43 @@ IChangeableForward *CForwardManager::CreateForwardEx(const char *name, ExecType
va_end(ap);
if (fwd)
{
m_unmanaged.push_back(fwd);
}
return fwd;
}
void CForwardManager::OnPluginLoaded(IPlugin *plugin)
{
/* Attach any globally managed forwards */
List<CForward *>::iterator iter;
CForward *fwd;
for (iter=m_managed.begin(); iter!=m_managed.end(); iter++)
{
fwd = (*iter);
IPluginFunction *pFunc = plugin->GetFunctionByName(fwd->GetForwardName());
if (pFunc)
{
fwd->AddFunction(pFunc);
}
}
}
void CForwardManager::OnPluginUnloaded(IPlugin *plugin)
{
List<CForward *>::iterator iter;
CForward *fwd;
for (iter=m_managed.begin(); iter!=m_managed.end(); iter++)
{
fwd = (*iter);
fwd->RemoveFunctionsOfPlugin(plugin);
}
}
IForward *CForwardManager::FindForward(const char *name, IChangeableForward **ifchng)
{
List<CForward *>::iterator iter;
@@ -243,9 +292,7 @@ int CForward::Execute(cell_t *result, IForwardFilter *filter)
}
}
/* Call the function and deal with the return value.
* :TODO: only pass reader if we know we have an array in the list
*/
/* Call the function and deal with the return value. */
if ((err=func->Execute(&cur_result)) != SP_ERROR_NONE)
{
bool handled = false;
@@ -478,7 +525,7 @@ int CForward::PushString(const char *string)
m_params[m_curparam].pushedas = Param_String;
}
_Int_PushString((cell_t *)string, strlen(string)+1, SP_STRING_COPY, 0);
_Int_PushString((cell_t *)string, strlen(string)+1, SM_PARAM_STRING_COPY, 0);
m_curparam++;
return SP_ERROR_NONE;
@@ -521,7 +568,7 @@ void CForward::Cancel()
bool CForward::AddFunction(sp_context_t *ctx, funcid_t index)
{
IPlugin *pPlugin = g_PluginMngr.FindPluginByContext(ctx);
IPlugin *pPlugin = g_PluginSys.FindPluginByContext(ctx);
if (!pPlugin)
{
return false;
@@ -590,7 +637,7 @@ bool CForward::AddFunction(IPluginFunction *func)
return false;
}
//:TODO: eventually we will tell the plugin we're using it
//:TODO: eventually we will tell the plugin we're using it [?]
m_functions.push_back(func);
return true;
+12 -2
View File
@@ -6,6 +6,7 @@
#include "sm_globals.h"
#include <sh_list.h>
#include <sh_stack.h>
#include "sourcemod.h"
using namespace SourceHook;
@@ -83,10 +84,13 @@ protected:
int m_errstate;
};
class CForwardManager : public IForwardManager
class CForwardManager :
public IForwardManager,
public IPluginsListener,
public SMGlobalClass
{
friend class CForward;
public:
public: //IForwardManager
virtual IForward *CreateForward(const char *name,
ExecType et,
unsigned int num_params,
@@ -99,6 +103,12 @@ public:
...);
virtual IForward *FindForward(const char *name, IChangeableForward **ifchng);
virtual void ReleaseForward(IForward *forward);
public: //IPluginsListener
virtual void OnPluginLoaded(IPlugin *plugin);
virtual void OnPluginUnloaded(IPlugin *plugin);
public: //SMGlobalClass
virtual void OnSourceModAllInitialized();
virtual void OnSourceModShutdown();
protected:
CForward *ForwardMake();
void ForwardFree(CForward *fwd);
+1 -1
View File
@@ -85,7 +85,7 @@ PluginSettings *CPluginInfoDatabase::GetSettingsIfMatch(unsigned int index, cons
return NULL;
}
if (!g_PluginMngr.TestAliasMatch(name, filename))
if (!g_PluginSys.TestAliasMatch(name, filename))
{
return NULL;
}
+209 -32
View File
@@ -5,11 +5,7 @@
#include "sourcemod.h"
#include "CTextParsers.h"
CPluginManager g_PluginMngr;
CPluginManager::CPluginManager()
{
}
CPluginManager g_PluginSys;
CPlugin::CPlugin(const char *file)
{
@@ -54,7 +50,7 @@ CPlugin::~CPlugin()
{
for (uint32_t i=0; i<m_plugin->info.publics_num; i++)
{
g_PluginMngr.ReleaseFunctionToPool(m_pub_funcs[i]);
g_PluginSys.ReleaseFunctionToPool(m_pub_funcs[i]);
}
delete [] m_pub_funcs;
m_pub_funcs = NULL;
@@ -64,7 +60,7 @@ CPlugin::~CPlugin()
{
for (unsigned int i=0; i<m_funcsnum; i++)
{
g_PluginMngr.ReleaseFunctionToPool(m_priv_funcs[i]);
g_PluginSys.ReleaseFunctionToPool(m_priv_funcs[i]);
}
delete [] m_priv_funcs;
m_priv_funcs = NULL;
@@ -196,6 +192,8 @@ bool CPlugin::FinishMyCompile(char *error, size_t maxlength)
m_pub_funcs = NULL;
}
m_status = Plugin_Created;
UpdateInfo();
return true;
@@ -226,7 +224,7 @@ IPluginFunction *CPlugin::GetFunctionById(funcid_t func_id)
pFunc = m_pub_funcs[func_id];
if (!pFunc)
{
pFunc = g_PluginMngr.GetFunctionFromPool(save, this);
pFunc = g_PluginSys.GetFunctionFromPool(save, this);
m_pub_funcs[func_id] = pFunc;
}
} else {
@@ -239,7 +237,7 @@ IPluginFunction *CPlugin::GetFunctionById(funcid_t func_id)
pFunc = m_priv_funcs[func_id];
if (!pFunc)
{
pFunc = g_PluginMngr.GetFunctionFromPool(save, this);
pFunc = g_PluginSys.GetFunctionFromPool(save, this);
m_priv_funcs[func_id] = pFunc;
}
}
@@ -264,7 +262,7 @@ IPluginFunction *CPlugin::GetFunctionByName(const char *public_name)
base->GetPublicByIndex(index, &pub);
if (pub)
{
pFunc = g_PluginMngr.GetFunctionFromPool(pub->funcid, this);
pFunc = g_PluginSys.GetFunctionFromPool(pub->funcid, this);
m_pub_funcs[index] = pFunc;
}
}
@@ -307,6 +305,77 @@ void CPlugin::UpdateInfo()
m_info.version = m_info.version ? m_info.version : "";
}
void CPlugin::Call_OnPluginInit()
{
if (m_status != Plugin_Loaded)
{
return;
}
m_status = Plugin_Running;
int err;
cell_t result;
IPluginFunction *pFunction = GetFunctionByName("OnPluginInit");
if (!pFunction)
{
return;
}
/* :TODO: push our own handle */
pFunction->PushCell(0);
if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
{
/* :TODO: log into debugger instead */
SetErrorState(Plugin_Error, "Runtime error %d", err);
}
}
bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
{
if (m_status != Plugin_Created)
{
return false;
}
if (!error)
{
error = m_errormsg;
maxlength = sizeof(m_errormsg);
}
int err;
cell_t result;
IPluginFunction *pFunction = GetFunctionByName("AskPluginLoad");
if (!pFunction)
{
return true;
}
pFunction->PushCell(0); //:TODO: handle to ourself
pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
pFunction->PushCell(maxlength);
if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
{
/* :TODO: debugging system */
snprintf(error, maxlength, "Plugin load returned run time error %d", err);
m_status = Plugin_Error;
return false;
}
if (!result)
{
m_status = Plugin_Error;
return false;
}
m_status = Plugin_Loaded;
return true;
}
const sp_plugin_t *CPlugin::GetPluginStructure() const
{
return m_plugin;
@@ -397,7 +466,7 @@ void CPluginManager::CPluginIterator::NextPlugin()
void CPluginManager::CPluginIterator::Release()
{
g_PluginMngr.ReleaseIterator(this);
g_PluginSys.ReleaseIterator(this);
}
CPluginManager::CPluginIterator::~CPluginIterator()
@@ -413,7 +482,20 @@ void CPluginManager::CPluginIterator::Reset()
* PLUGIN MANAGER *
******************/
void CPluginManager::RefreshOrLoadPlugins(const char *config, const char *basedir)
CPluginManager::CPluginManager()
{
m_LoadLookup = sm_trie_create();
m_AllPluginsLoaded = false;
}
CPluginManager::~CPluginManager()
{
//:TODO: we need a good way to free what we're holding
sm_trie_destroy(m_LoadLookup);
}
void CPluginManager::LoadAll_FirstPass( const char *config, const char *basedir )
{
/* First read in the database of plugin settings */
SMCParseError err;
@@ -467,29 +549,51 @@ void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpa
} else if (dir->IsEntryFile()) {
const char *name = dir->GetEntryName();
size_t len = strlen(name);
if (len < 4
|| strcmp(&name[len-4], ".smx") != 0)
if (len >= 4
&& strcmp(&name[len-4], ".smx") == 0)
{
continue;
/* If the filename matches, load the plugin */
char plugin[PLATFORM_MAX_PATH+1];
if (localpath == NULL)
{
snprintf(plugin, sizeof(plugin), "%s", name);
} else {
g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
}
LoadAutoPlugin(plugin);
}
/* If the filename matches, load the plugin */
char plugin[PLATFORM_MAX_PATH+1];
if (localpath == NULL)
{
snprintf(plugin, sizeof(plugin), "%s", name);
} else {
g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
}
LoadAutoPlugin(plugin);
}
dir->NextEntry();
}
g_LibSys.CloseDirectory(dir);
}
//well i have discovered that gabe newell is very fat, so i wrote this comment now
//:TODO: remove this function, create a better wrapper for LoadPlugin()/LoadAutoPlugin()
void CPluginManager::LoadAutoPlugin(const char *file)
{
CPlugin *pPlugin = CPlugin::CreatePlugin(file, NULL, 0);
/**
* Does this plugin already exist?
*/
CPlugin *pPlugin;
if (sm_trie_retrieve(m_LoadLookup, file, (void **)&pPlugin))
{
/* First check the type */
PluginType type = pPlugin->GetType();
if (type == PluginType_Private
|| type == PluginType_Global)
{
return;
}
/* Check to see if we should try reloading it */
if (pPlugin->GetStatus() == Plugin_BadLoad
|| pPlugin->GetStatus() == Plugin_Error)
{
UnloadPlugin(pPlugin);
}
}
pPlugin = CPlugin::CreatePlugin(file, NULL, 0);
assert(pPlugin != NULL);
@@ -539,12 +643,36 @@ void CPluginManager::LoadAutoPlugin(const char *file)
co = NULL;
}
InitAndAddPlugin(pPlugin);
/* We don't care about the return value */
if (pPlugin->GetStatus() == Plugin_Created)
{
AddCoreNativesToPlugin(pPlugin);
pPlugin->Call_AskPluginLoad(NULL, 0);
}
AddPlugin(pPlugin);
}
IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t err_max)
{
CPlugin *pPlugin = CPlugin::CreatePlugin(path, error, err_max);
/* See if this plugin is already loaded... reformat to get sep chars right */
char checkpath[PLATFORM_MAX_PATH+1];
g_LibSys.PathFormat(checkpath, sizeof(checkpath), "%s", path);
/**
* In manually loading a plugin, any sort of load error causes a deletion.
* This is because it's assumed manually loaded plugins will not be managed.
* For managed plugins, we need the UI to report them properly.
*/
CPlugin *pPlugin;
if (sm_trie_retrieve(m_LoadLookup, checkpath, (void **)&pPlugin))
{
snprintf(error, err_max, "Plugin file is alread loaded");
return NULL;
}
pPlugin = CPlugin::CreatePlugin(path, error, err_max);
if (!pPlugin)
{
@@ -568,14 +696,24 @@ IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType typ
pPlugin->m_type = type;
InitAndAddPlugin(pPlugin);
AddCoreNativesToPlugin(pPlugin);
/* Finally, ask the plugin if it wants to be loaded */
if (!pPlugin->Call_AskPluginLoad(error, err_max))
{
delete pPlugin;
return NULL;
}
AddPlugin(pPlugin);
return pPlugin;
}
void CPluginManager::InitAndAddPlugin(CPlugin *pPlugin)
void CPluginManager::AddPlugin(CPlugin *pPlugin)
{
m_plugins.push_back(pPlugin);
sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
List<IPluginsListener *>::iterator iter;
IPluginsListener *pListener;
@@ -585,7 +723,43 @@ void CPluginManager::InitAndAddPlugin(CPlugin *pPlugin)
pListener->OnPluginCreated(pPlugin);
}
/* :TODO: a lot more... */
/* If the second pass was already completed, we have to run the pass on this plugin */
if (m_AllPluginsLoaded && pPlugin->GetStatus() == Plugin_Loaded)
{
RunSecondPass(pPlugin);
}
}
void CPluginManager::RunSecondPass(CPlugin *pPlugin)
{
/* Tell this plugin to finish initializing itself */
pPlugin->Call_OnPluginInit();
/* Finish by telling all listeners */
List<IPluginsListener *>::iterator iter;
IPluginsListener *pListener;
for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
{
pListener = (*iter);
pListener->OnPluginLoaded(pPlugin);
}
}
void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin)
{
List<sp_nativeinfo_t *>::iterator iter;
for (iter=m_natives.begin(); iter!=m_natives.end(); iter++)
{
sp_nativeinfo_t *natives = (*iter);
IPluginContext *ctx = pPlugin->GetBaseContext();
unsigned int i=0;
/* Attempt to bind every native! */
while (natives[i].func != NULL)
{
ctx->BindNative(&natives[i++]);
}
}
}
bool CPluginManager::UnloadPlugin(IPlugin *plugin)
@@ -601,6 +775,9 @@ bool CPluginManager::UnloadPlugin(IPlugin *plugin)
pListener = (*iter);
pListener->OnPluginDestroyed(pPlugin);
}
m_plugins.remove(plugin);
sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
delete pPlugin;
@@ -877,7 +1054,7 @@ bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
return true;
}
CPluginManager::~CPluginManager()
bool CPluginManager::IsLateLoadTime()
{
//:TODO: we need a good way to free what we're holding
return (m_AllPluginsLoaded || g_SourceMod.IsLateLoadInMap());
}
+79 -5
View File
@@ -1,12 +1,14 @@
#ifndef _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
#define _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
#include <time.h>
#include <IPluginSys.h>
#include <sh_list.h>
#include <sh_stack.h>
#include "sm_globals.h"
#include "CFunction.h"
#include "PluginInfoDatabase.h"
#include "sm_trie.h"
using namespace SourceHook;
@@ -29,6 +31,31 @@ using namespace SourceHook;
* plugins that failed to load from the internal loading mechanism are always tracked. This
* allows users to see which automatically loaded plugins failed, and makes the interface a bit
* more flexible.
*
* Once a plugin is compiled, it sets its own state to Plugin_Created. This state is still invalid
* for execution. SourceMod is a two pass system, and even though the second pass is not implemented
* yet, it is structured so Plugin_Created must be switched to Plugin_Running in the second pass. When
* implemented, a Created plugin will be switched to Error in the second pass if it not loadable.
*
* The two pass loading mechanism is described below. Modules/natives are not implemented yet.
* PASS ONE: All loadable plugins are found and have the following steps performed:
* 1. Loading and compilation is attempted.
* 2. If successful, all natives from Core are added.
* 3. OnPluginLoad() is called.
* 4. If failed, any user natives are scrapped and the process halts here.
* 5. If successful, the plugin is ready for Pass 2.
* INTERMEDIATE:
* 1. All forced modules are loaded.
* PASS TWO: All loaded plugins are found and have these steps performed:
* 1. Any modules referenced in the plugin that are not already loaded, are loaded.
* 2. If any module fails to load and the plugin requires it, load fails and jump to step 6.
* 3. If any natives are unresolved, check if they are found in the user-natives pool.
* 4. If yes, load succeeds. If not, natives are passed through a native acceptance filter.
* 5. If the filter fails, the plugin is marked as failed.
* 6. If the plugin has failed to load at this point, any dynamic natives it has added are scrapped.
* Furthermore, any plugin that referenced these natives must now have pass 2 re-ran.
* PASS THREE (not a real pass):
* 7. Once all plugins are deemed to be loaded, OnPluginInit() is called
*/
#define SM_CONTEXTVAR_MYSELF 0
@@ -88,6 +115,24 @@ public:
* Sets an error state on the plugin
*/
void SetErrorState(PluginStatus status, const char *error_fmt, ...);
/**
* Calls the OnPluginLoad function, and sets any failed states if necessary.
* NOTE: Valid pre-states are: Plugin_Created
* If validated, plugin state is changed to Plugin_Loaded
*
* If the error buffer is NULL, the error message is cached locally.
*/
bool Call_AskPluginLoad(char *error, size_t maxlength);
/**
* Calls the OnPluginInit function.
* NOTE: Valid pre-states are: Plugin_Created
* NOTE: Pre-state will be changed to Plugin_Running
*/
void Call_OnPluginInit();
public:
time_t HasUpdatedFile();
protected:
void UpdateInfo();
private:
@@ -102,6 +147,7 @@ private:
CFunction **m_priv_funcs;
CFunction **m_pub_funcs;
char m_errormsg[256];
time_t m_LastAccess;
};
class CPluginManager : public IPluginManager
@@ -142,9 +188,14 @@ public: //IPluginManager
virtual void RemovePluginsListener(IPluginsListener *listener);
public:
/**
* Refreshes and loads plugins, usually used on mapchange
* Loads all plugins not yet loaded
*/
void RefreshOrLoadPlugins(const char *config, const char *basedir);
void LoadAll_FirstPass(const char *config, const char *basedir);
/**
* Runs the second loading pass for all plugins
*/
void LoadAll_SecondPass();
/**
* Tests a plugin file mask against a local folder.
@@ -156,6 +207,16 @@ public:
* Wildcards are allowed in the filename.
*/
bool TestAliasMatch(const char *alias, const char *localdir);
/**
* Registers natives in core itself ONLY.
*/
void RegisterGlobalNatives(sp_nativeinfo_t *info[]);
/**
* Returns whether anything loaded will be a late load.
*/
bool IsLateLoadTime();
private:
/**
* Recursively loads all plugins in the given directory.
@@ -169,9 +230,19 @@ private:
void LoadAutoPlugin(const char *file);
/**
* Adds and initializes a plugin object. This is wrapped by LoadPlugin functions.
* Adds a plugin object. This is wrapped by LoadPlugin functions.
*/
void InitAndAddPlugin(CPlugin *pPlugin);
void AddPlugin(CPlugin *pPlugin);
/**
* Runs the second loading pass on a plugin.
*/
void RunSecondPass(CPlugin *pPlugin);
/**
* Adds any globally registered natives to a plugin
*/
void AddCoreNativesToPlugin(CPlugin *pPlugin);
protected:
/**
* Caching internal objects
@@ -182,11 +253,14 @@ protected:
private:
List<IPluginsListener *> m_listeners;
List<IPlugin *> m_plugins;
List<sp_nativeinfo_t *> m_natives;
CStack<CPluginManager::CPluginIterator *> m_iters;
CStack<CFunction *> m_funcpool;
CPluginInfoDatabase m_PluginInfo;
Trie *m_LoadLookup;
bool m_AllPluginsLoaded;
};
extern CPluginManager g_PluginMngr;
extern CPluginManager g_PluginSys;
#endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_