diff --git a/core/systems/ExtensionSys.cpp b/core/systems/ExtensionSys.cpp index 78df64ec..8209b3ce 100644 --- a/core/systems/ExtensionSys.cpp +++ b/core/systems/ExtensionSys.cpp @@ -285,7 +285,22 @@ void CExtension::RemovePlugin(IPlugin *pPlugin) if ((*iter).pl == pPlugin) { iter = m_WeakNatives.erase(iter); - } else { + } + else + { + iter++; + } + } + + iter = m_ReplacedNatives.begin(); + while (iter != m_ReplacedNatives.end()) + { + if ((*iter).pl == pPlugin) + { + iter = m_ReplacedNatives.erase(iter); + } + else + { iter++; } } @@ -703,6 +718,7 @@ void CExtensionManager::BindAllNativesToPlugin(IPlugin *pPlugin) uint32_t natives = pContext->GetNativesNum(); sp_native_t *native; sm_extnative_t *x_native; + sm_repnative_t *r_native; for (uint32_t i=0; istatus == SP_NATIVE_BOUND) { + /* If it is bound, see if there is a replacement. */ + if ((r_native = m_RepNatives.retrieve(native->name)) == NULL) + { + continue; + } + + /* Rewrite the address. Whee! */ + native->pfn = r_native->info.func; + + /* Make sure this will unload safely */ + WeakNative wn((CPlugin *)pPlugin, i); + r_native->owner->m_ReplacedNatives.push_back(wn); + continue; } + /* See if we've got this native in our cache */ if ((x_native = m_ExtNatives.retrieve(native->name)) == NULL) { @@ -789,7 +820,7 @@ bool CExtensionManager::UnloadExtension(IExtension *_pExt) g_PluginSys.OnLibraryAction((*s_iter).c_str(), false, true); } - /* Unbound weak natives */ + /* Unbind weak natives */ List::iterator wkn_iter; for (wkn_iter=pExt->m_WeakNatives.begin(); wkn_iter!=pExt->m_WeakNatives.end(); wkn_iter++) { @@ -798,6 +829,21 @@ bool CExtensionManager::UnloadExtension(IExtension *_pExt) ctx->natives[wkn.idx].status = SP_NATIVE_UNBOUND; } + /* Unbind replacement natives, link them back to their originals */ + for (wkn_iter = pExt->m_ReplacedNatives.begin(); + wkn_iter != pExt->m_ReplacedNatives.end(); + wkn_iter++) + { + WeakNative & wkn = (*wkn_iter); + sp_context_t *ctx = wkn.pl->GetContext(); + sm_repnative_t *r_native = m_RepNatives.retrieve(ctx->natives[wkn.idx].name); + if (r_native == NULL || ctx->natives[wkn.idx].pfn != r_native->info.func) + { + continue; + } + ctx->natives[wkn.idx].pfn = r_native->original; + } + /* Notify and/or unload all dependencies */ List::iterator c_iter; CExtension *pDep; @@ -871,6 +917,22 @@ bool CExtensionManager::UnloadExtension(IExtension *_pExt) } } + /* Unbind our replacement natives */ + List::iterator rep_iter = m_RepNativeList.begin(); + while (rep_iter != m_RepNativeList.end()) + { + sm_repnative_t & r_native = (*rep_iter); + if (r_native.owner == pExt) + { + m_RepNatives.remove(r_native.info.name); + rep_iter = m_RepNativeList.erase(rep_iter); + } + else + { + rep_iter++; + } + } + /* Tell it to unload */ pAPI = pExt->GetAPI(); pAPI->OnExtensionUnload(); @@ -919,6 +981,25 @@ void CExtensionManager::AddNatives(IExtension *pOwner, const sp_nativeinfo_t *na } } +void CExtensionManager::OverrideNatives(IExtension *myself, const sp_nativeinfo_t *natives) +{ + SPVM_NATIVE_FUNC orig; + + for (unsigned int i = 0; natives[i].func != NULL && natives[i].name != NULL; i++) + { + if ((orig = g_PluginSys.FindCoreNative(natives[i].name)) == NULL) + { + continue; + } + sm_repnative_t rep; + rep.info = natives[i]; + rep.owner = (CExtension *)myself; + rep.original = orig; + m_RepNativeList.push_back(rep); + m_RepNatives.insert(natives[i].name, rep); + } +} + void CExtensionManager::MarkAllLoaded() { List::iterator iter; diff --git a/core/systems/ExtensionSys.h b/core/systems/ExtensionSys.h index 1fe29f1c..dbba34e6 100644 --- a/core/systems/ExtensionSys.h +++ b/core/systems/ExtensionSys.h @@ -55,6 +55,14 @@ struct sm_extnative_t const sp_nativeinfo_t *info; }; +/* Replacement native */ +struct sm_repnative_t +{ + CExtension *owner; + sp_nativeinfo_t info; + SPVM_NATIVE_FUNC original; +}; + class CExtension : public IExtension { friend class CExtensionManager; @@ -98,6 +106,7 @@ protected: List m_Plugins; List m_Natives; List m_WeakNatives; + List m_ReplacedNatives; List m_Libraries; unsigned int unload_code; bool m_bFullyLoaded; @@ -168,6 +177,7 @@ public: void TryAutoload(); void AddLibrary(IExtension *pSource, const char *library); bool LibraryExists(const char *library); + void OverrideNatives(IExtension *myself, const sp_nativeinfo_t *natives); public: CExtension *GetExtensionFromIdent(IdentityToken_t *ptr); void Shutdown(); @@ -175,6 +185,8 @@ private: CExtension *FindByOrder(unsigned int num); private: List m_Libs; + List m_RepNativeList; + KTrie m_RepNatives; KTrie m_ExtNatives; }; diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp index a0bdcce4..d18aa689 100644 --- a/core/systems/PluginSys.cpp +++ b/core/systems/PluginSys.cpp @@ -1440,6 +1440,18 @@ void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin) } } +SPVM_NATIVE_FUNC CPluginManager::FindCoreNative(const char *name) +{ + SPVM_NATIVE_FUNC pfn; + + if (!sm_trie_retrieve(m_pCoreNatives, name, (void **)&pfn)) + { + return NULL; + } + + return pfn; +} + void CPluginManager::TryRefreshDependencies(CPlugin *pPlugin) { assert(pPlugin->GetBaseContext() != NULL); diff --git a/core/systems/PluginSys.h b/core/systems/PluginSys.h index cc71c4ea..ce70a16f 100644 --- a/core/systems/PluginSys.h +++ b/core/systems/PluginSys.h @@ -489,6 +489,7 @@ public: } public: bool AddFakeNative(IPluginFunction *pFunction, const char *name, SPVM_FAKENATIVE_FUNC func); + SPVM_NATIVE_FUNC FindCoreNative(const char *name); private: void AddFakeNativesToPlugin(CPlugin *pPlugin); void TryRefreshDependencies(CPlugin *pOther); diff --git a/core/systems/ShareSys.cpp b/core/systems/ShareSys.cpp index 1154e4d4..c3bf2c2d 100644 --- a/core/systems/ShareSys.cpp +++ b/core/systems/ShareSys.cpp @@ -243,3 +243,8 @@ void ShareSystem::RegisterLibrary(IExtension *myself, const char *name) { g_Extensions.AddLibrary(myself, name); } + +void ShareSystem::OverrideNatives(IExtension *myself, const sp_nativeinfo_t *natives) +{ + g_Extensions.OverrideNatives(myself, natives); +} diff --git a/core/systems/ShareSys.h b/core/systems/ShareSys.h index 6c34f618..60e99c21 100644 --- a/core/systems/ShareSys.h +++ b/core/systems/ShareSys.h @@ -81,6 +81,7 @@ public: //IShareSys void DestroyIdentity(IdentityToken_t *identity); void AddDependency(IExtension *myself, const char *filename, bool require, bool autoload); void RegisterLibrary(IExtension *myself, const char *name); + void OverrideNatives(IExtension *myself, const sp_nativeinfo_t *natives); public: //SMGlobalClass /* Pre-empt in case anything tries to register idents early */ void OnSourceModStartup(bool late); diff --git a/public/IExtensionSys.h b/public/IExtensionSys.h index 6f427db8..3f4989a2 100644 --- a/public/IExtensionSys.h +++ b/public/IExtensionSys.h @@ -127,8 +127,11 @@ namespace SourceMod /** * @brief Version code of the IExtensionInterface API itself. + * + * Note: This is bumped when IShareSys is changed, because IShareSys + * itself is not versioned. */ - #define SMINTERFACE_EXTENSIONAPI_VERSION 2 + #define SMINTERFACE_EXTENSIONAPI_VERSION 3 /** * @brief The interface an extension must expose. diff --git a/public/IShareSys.h b/public/IShareSys.h index c97db5e5..84363bbf 100644 --- a/public/IShareSys.h +++ b/public/IShareSys.h @@ -200,6 +200,29 @@ namespace SourceMod * @param name Library name. */ virtual void RegisterLibrary(IExtension *myself, const char *name) =0; + + /** + * @brief Adds a list of natives to the global native pool, to be + * bound on plugin load. + * + * Unlike AddNatives(), this function implements natives that are + * ALWAYS bound, regardless of whether a previous function is bound. + * That means extensions can override Core natives. + * + * A Core version of each native must exist. If one does not, then + * Core will simply ignore that entry. + * + * Override natives represent a weak coupling. If the extension is + * unloaded, the native will be re-bound to the Core version. + * + * @param myself Identity token of parent object. + * @param natives Array of natives to add. The last entry in + * the array must be filled with NULLs to + * terminate the array. The array must be static + * as Core will cache the pointer for the + * lifetime of the extension. + */ + virtual void OverrideNatives(IExtension *myself, const sp_nativeinfo_t *natives) =0; }; }