diff --git a/core/interfaces/IHandleSys.h b/core/interfaces/IHandleSys.h index f5fc71a2..ab77a428 100644 --- a/core/interfaces/IHandleSys.h +++ b/core/interfaces/IHandleSys.h @@ -194,7 +194,7 @@ namespace SourceMod * only perform any further action if the counter hits 0. * * @param type Handle_t identifier to destroy. - * @param ident Identity token, for destroying secure handles (0 for none). + * @param ident Identity token, for destroying secure handles (NULL for none). * @return A HandleError error code. */ virtual HandleError FreeHandle(Handle_t handle, IdentityToken_t *ident) =0; @@ -215,9 +215,9 @@ namespace SourceMod * @brief Retrieves the contents of a handle. * * @param handle Handle_t from which to retrieve contents. - * @param type Expected type to read as. + * @param type Expected type to read as. 0 ignores typing rules. * @param ident Identity token to validate as. - * @param object Address to store object in. + * @param object Optional address to store object in. * @return HandleError error code. */ virtual HandleError ReadHandle(Handle_t handle, HandleType_t type, IdentityToken_t *ident, void **object) =0; diff --git a/core/smn_handles.cpp b/core/smn_handles.cpp new file mode 100644 index 00000000..ca60e8c5 --- /dev/null +++ b/core/smn_handles.cpp @@ -0,0 +1,72 @@ +#include "sm_globals.h" +#include "HandleSys.h" +#include "PluginSys.h" + +static cell_t sm_IsValidHandle(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + + HandleError err = g_HandleSys.ReadHandle(hndl, 0, NULL, NULL); + + if (err != HandleError_Access + && err != HandleError_None) + { + return 0; + } + + return 1; +} + +static cell_t sm_CloseHandle(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + + HandleError err = g_HandleSys.FreeHandle(hndl, NULL); + + if (err == HandleError_None) + { + return 1; + } else if (err == HandleError_Access) { + return 0; + } else { + return pContext->ThrowNativeError("Handle %x is invalid (error %d)", hndl, err); + } +} + +static cell_t sm_CloneHandle(IPluginContext *pContext, const cell_t *params) +{ + Handle_t new_hndl; + Handle_t hndl = static_cast(params[1]); + IPlugin *pPlugin; + HandleError err; + + if (params[2] == 0) + { + pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext()); + } else { + Handle_t hPlugin = static_cast(params[2]); + pPlugin = g_PluginSys.PluginFromHandle(hPlugin, &err); + if (!pPlugin) + { + return pContext->ThrowNativeError("Plugin handle %x is invalid (error %d)", hndl, err); + } + } + + err = g_HandleSys.CloneHandle(hndl, &new_hndl, pPlugin->GetIdentity(), NULL); + + if (err == HandleError_Access) + { + return 0; + } else if (err == HandleError_None) { + return new_hndl; + } else { + return pContext->ThrowNativeError("Handle to clone %x is invalid (error %d)", hndl, err); + } +} + +REGISTER_NATIVES(handles) +{ + {"IsValidHandle", sm_IsValidHandle}, + {"CloseHandle", sm_CloseHandle}, + {"CloneHandle", sm_CloneHandle}, +}; diff --git a/core/systems/HandleSys.cpp b/core/systems/HandleSys.cpp index be91460a..b4ffa916 100644 --- a/core/systems/HandleSys.cpp +++ b/core/systems/HandleSys.cpp @@ -465,7 +465,7 @@ HandleError HandleSystem::ReadHandle(Handle_t handle, { return HandleError_Type; } - } else { + } else if (type) { if (pHandle->type != type) { return HandleError_Type; diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp index 2f087967..03ece023 100644 --- a/core/systems/PluginSys.cpp +++ b/core/systems/PluginSys.cpp @@ -1141,3 +1141,21 @@ void CPluginManager::RegisterNativesFromCore(sp_nativeinfo_t *natives) { m_natives.push_back(natives); } + +IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err) +{ + IPlugin *pPlugin; + HandleError _err; + + if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, m_MyIdent, (void **)&pPlugin)) != HandleError_None) + { + pPlugin = NULL; + } + + if (err) + { + *err = _err; + } + + return pPlugin; +} diff --git a/core/systems/PluginSys.h b/core/systems/PluginSys.h index f5e5c462..904cd127 100644 --- a/core/systems/PluginSys.h +++ b/core/systems/PluginSys.h @@ -236,6 +236,11 @@ public: * Adds natives from core into the native pool. */ void RegisterNativesFromCore(sp_nativeinfo_t *natives); + + /** + * Converts a Handle to an IPlugin if possible. + */ + IPlugin *PluginFromHandle(Handle_t handle, HandleError *err); private: /** * Recursively loads all plugins in the given directory. diff --git a/plugins/include/handles.h b/plugins/include/handles.h new file mode 100644 index 00000000..be3640b8 --- /dev/null +++ b/plugins/include/handles.h @@ -0,0 +1,58 @@ +/** + * :TODO: license info + */ + + +#if defined _handles_included + #endinput +#endif +#define _handles_included + +enum Handle +{ + INVALID_HANDLE = 0, +}; + +/** + * @brief Returns if a handle is valid or not. + * @note It is not a good idea to call this on every Handle. If you code properly, + * all of your Handles will either be valid or will expose important bugs to fix. + * This is provided for situations only where testing for handle validity is needed. + * + * @param hndl Handle to test for validity. + * @return True if handle is valid, false otherwise. + */ +native bool:IsValidHandle(Handle:hndl); + +/** + * @brief Closes a Handle. If the handle has multiple copies open, + * it is not destroyed unless all copies are closed. + * + * @note Closing a Handle has a different meaning for each Handle type. Make + * sure you read the documentation on whatever provided the Handle. + * + * @param hndl Handle to close. + * @return True if successful, false if not closeable. + * @error Invalid handles will cause a run time error. + */ +native bool:CloseHandle(Handle:hndl); + +/** + * @brief Clones a Handle. When passing handles in between plugins, caching handles + * can result in accidental invalidation when one plugin releases the Handle, or is its owner + * is unloaded from memory. To prevent this, the Handle may be "cloned" with a new owner. + * + * @note Usually, you will be cloning Handles for other plugins. This means that if you clone + * the Handle without specifying the new owner, it will assume the identity of your original calling + * plugin, which is not very useful. You should either specify that the receiving plugin should + * clone the handle on its own, or you should explicitly clone the Handle using the receiving plugin's + * identity Handle. + * + * @param hndl Handle to clone/duplicate. + * @param plugin Optional Handle to another plugin to mark as the new owner. + * If no owner is passed, the owner becomes the calling plugin. + * @return Handle on success, INVALID_HANDLE if not cloneable. + * @error Invalid handles will cause a run time error. + */ +native Handle:CloneHandle(Handle:hndl, Handle:plugin=INVALID_HANDLE); +