diff --git a/reapi/src/amx_hook.h b/reapi/src/amx_hook.h index 8b84a35..68e7c6e 100644 --- a/reapi/src/amx_hook.h +++ b/reapi/src/amx_hook.h @@ -29,29 +29,3 @@ private: fwdstate m_state; AMX *m_amx; }; - -template -class CAmxxHookUnique: public CAmxxHookBase -{ -public: - ~CAmxxHookUnique() - { - if (m_uniqueData) - { - delete m_uniqueData; - m_uniqueData = nullptr; - } - } - - CAmxxHookUnique(AMX *amx, const char *funcname, int index, T *data = nullptr) : - CAmxxHookBase(amx, funcname, index, -1), - m_uniqueData(data) - { - - } - - T *GetUnique() const { return m_uniqueData; } - -private: - T *m_uniqueData; -}; diff --git a/reapi/src/entity_callback.cpp b/reapi/src/entity_callback.cpp deleted file mode 100644 index a11b59d..0000000 --- a/reapi/src/entity_callback.cpp +++ /dev/null @@ -1,179 +0,0 @@ -#include "precompiled.h" - -CEntityCallback g_entCallback; - -void CEntityCallback::PurgeCallbacks(CBaseEntity *pEntity, CallbackType_e type) -{ - auto it = m_callbacks.begin(); - while (it != m_callbacks.end()) - { - eCallback_t *pUnique = (*it)->GetUnique(); - - // this callback was already sets, need to unregister the current forward - if (pUnique->m_callbackType == type && pUnique->m_entity == pEntity) { - delete (*it); - it = m_callbacks.erase(it); - } else { - it++; - } - } -} - -void CEntityCallback::Clear(CBaseEntity *pEntity) -{ - if (pEntity) - { - auto it = m_callbacks.begin(); - while (it != m_callbacks.end()) - { - eCallback_t *pUnique = (*it)->GetUnique(); - if (pUnique->m_entity == pEntity) { - delete (*it); - it = m_callbacks.erase(it); - } else { - it++; - } - } - } - else - { - for (auto h : m_callbacks) { - delete h; - } - - m_callbacks.clear(); - } -} - -bool CEntityCallback::SetThink(AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen) -{ - PurgeCallbacks(pEntity, CType_Think); - - int fwdid; - if (iParamsLen > 0) { - fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_ARRAY, FP_DONE); - } else { - fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_DONE); - } - - if (fwdid == -1) { - AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__); - return false; - } - - m_callbacks.push_back(new CAmxxHookUnique(amx, pszCallback, fwdid, new eCallback_t(pEntity, pParams, iParamsLen, CType_Think))); - pEntity->SetThink(&CBaseEntity::SUB_Think); - return true; -} - -bool CEntityCallback::SetTouch(AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen) -{ - PurgeCallbacks(pEntity, CType_Touch); - - int fwdid; - if (iParamsLen > 0) { - fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_ARRAY, FP_DONE); - } else { - fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_DONE); - } - - if (fwdid == -1) { - AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__); - return false; - } - - m_callbacks.push_back(new CAmxxHookUnique(amx, pszCallback, fwdid, new eCallback_t(pEntity, pParams, iParamsLen, CType_Touch))); - pEntity->SetTouch(&CBaseEntity::SUB_Touch); - return true; -} - -bool CEntityCallback::SetUse(AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen) -{ - PurgeCallbacks(pEntity, CType_Use); - - int fwdid; - if (iParamsLen > 0) { - fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_FLOAT, FP_ARRAY, FP_DONE); - } else { - fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_FLOAT, FP_DONE); - } - - if (fwdid == -1) { - AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__); - return false; - } - - m_callbacks.push_back(new CAmxxHookUnique(amx, pszCallback, fwdid, new eCallback_t(pEntity, pParams, iParamsLen, CType_Use))); - pEntity->SetUse(&CBaseEntity::SUB_Use); - return true; -} - -bool CEntityCallback::SetBlocked(AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen) -{ - PurgeCallbacks(pEntity, CType_Blocked); - - int fwdid; - if (iParamsLen > 0) { - fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_ARRAY, FP_DONE); - } else { - fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_DONE); - } - - if (fwdid == -1) { - AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__); - return false; - } - - m_callbacks.push_back(new CAmxxHookUnique(amx, pszCallback, fwdid, new eCallback_t(pEntity, pParams, iParamsLen, CType_Blocked))); - pEntity->SetBlocked(&CBaseEntity::SUB_Blocked); - return true; -} - -bool CEntityCallback::SetMoveDone(AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen) -{ - PurgeCallbacks(pEntity, CType_MoveDone); - - int fwdid; - if (iParamsLen > 0) { - fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_ARRAY, FP_DONE); - } else { - fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_DONE); - } - - if (fwdid == -1) { - AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__); - return false; - } - - m_callbacks.push_back(new CAmxxHookUnique(amx, pszCallback, fwdid, new eCallback_t(pEntity, pParams, iParamsLen, CType_MoveDone))); - - // TODO: Make sure that the entity actually inherited from CBaseToggle - ((CBaseToggle *)pEntity)->SetMoveDone(&CBaseToggle::SUB_MoveDone); - return true; -} - -// Fundamental callbacks -void CBaseEntity::SUB_Think() -{ - g_entCallback.FireCallbacks(this, CEntityCallback::CType_Think, indexOfEdict(pev)); -} - -void CBaseEntity::SUB_Touch(CBaseEntity *pOther) -{ - g_entCallback.FireCallbacks(this, CEntityCallback::CType_Touch, indexOfEdict(pev), indexOfEdict(pOther->pev)); -} - -void CBaseEntity::SUB_Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - g_entCallback.FireCallbacks(this, CEntityCallback::CType_Use, indexOfEdict(pev), indexOfEdict(pActivator->pev), indexOfEdict(pCaller->pev), useType, value); -} - -void CBaseEntity::SUB_Blocked(CBaseEntity *pOther) -{ - g_entCallback.FireCallbacks(this, CEntityCallback::CType_Blocked, indexOfEdict(pev), indexOfEdict(pOther->pev)); -} - -void CBaseToggle::SUB_MoveDone() -{ - g_entCallback.FireCallbacks(this, CEntityCallback::CType_MoveDone, indexOfEdict(pev)); -} diff --git a/reapi/src/entity_callback.h b/reapi/src/entity_callback.h deleted file mode 100644 index 6220259..0000000 --- a/reapi/src/entity_callback.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include "amx_hook.h" - -class CEntityCallback -{ -public: - void Clear(CBaseEntity *pEntity = nullptr); - - bool SetThink (AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen); - bool SetTouch (AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen); - bool SetUse (AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen); - bool SetBlocked (AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen); - bool SetMoveDone(AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen); - - enum CallbackType_e - { - CType_Think, - CType_Touch, - CType_Use, - CType_Blocked, - CType_MoveDone, - }; - - template - void FireCallbacks(CBaseEntity *pEntity, CallbackType_e type, volatile f_args... args) - { - for (auto fwd : m_callbacks) { - auto data = fwd->GetUnique(); - if (data->m_entity == pEntity && data->m_callbackType == type) - { - if (data->m_iParamLen > 0) { - g_amxxapi.ExecuteForward(fwd->GetFwdIndex(), args..., g_amxxapi.PrepareCellArrayA(data->m_pParams, data->m_iParamLen, true)); - } else { - g_amxxapi.ExecuteForward(fwd->GetFwdIndex(), args...); - } - } - } - } - -private: - void PurgeCallbacks(CBaseEntity *pEntity, CallbackType_e type); - struct eCallback_t - { - eCallback_t(CBaseEntity *pEntity, const cell *pParams, size_t iParamsLen, CallbackType_e type) : - m_entity(pEntity), m_callbackType(type) - { - if (iParamsLen > 0) { - m_iParamLen = iParamsLen + 1; - m_pParams = new cell[m_iParamLen]; - Q_memcpy(m_pParams, pParams, sizeof(cell) * iParamsLen); - m_pParams[iParamsLen] = 0; - } else { - m_iParamLen = 0; - m_pParams = nullptr; - } - }; - - ~eCallback_t() - { - if (m_pParams) { - delete[] m_pParams; - m_pParams = nullptr; - } - - m_iParamLen = 0; - } - - CBaseEntity *m_entity; - CallbackType_e m_callbackType; - cell *m_pParams; - size_t m_iParamLen; - }; - - std::vector *> m_callbacks; -}; - -extern CEntityCallback g_entCallback; diff --git a/reapi/src/entity_callback_dispatcher.cpp b/reapi/src/entity_callback_dispatcher.cpp new file mode 100644 index 0000000..54fdbb9 --- /dev/null +++ b/reapi/src/entity_callback_dispatcher.cpp @@ -0,0 +1,234 @@ +#include "precompiled.h" + +// Deletes all registered callbacks for the specified entity, or all entities if pEntity is nullptr +void CEntityCallbackDispatcher::DeleteExistingCallbacks(CBaseEntity *pEntity, CallbackType type) +{ + for (std::list::const_iterator it = m_callbacks.begin(); + it != m_callbacks.end(); ) + { + EntityCallback *callback = (*it); + + // This callback was already sets, need to unregister the current forward + if (!pEntity || (callback->m_pEntity == pEntity && (callback->m_callbackType == type || None == type))) + { + // Are we in the middle of processing callbacks? + if (IsProcessingCallbacks()) + { + // Sets the mark for the object to be deleted later + const std::list::const_iterator callbackIt = + std::find(m_callbacksMarkForDeletion.begin(), m_callbacksMarkForDeletion.end(), callback); + + if (callbackIt == m_callbacksMarkForDeletion.end()) + m_callbacksMarkForDeletion.push_back(callback); + + it++; + } + else + { + it = m_callbacks.erase(it); + delete callback; + } + } + else + { + it++; + } + } + + if (!pEntity && !IsProcessingCallbacks()) + m_callbacks.clear(); +} + +// Deletes all registered callbacks +void CEntityCallbackDispatcher::DeleteAllCallbacks() +{ + DeleteExistingCallbacks(nullptr); +} + +bool CEntityCallbackDispatcher::SetThink(AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen) +{ + DeleteExistingCallbacks(pEntity, Think); + + int fwdid; + if (iParamsLen > 0) + fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_ARRAY, FP_DONE); + else + fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_DONE); + + if (fwdid == -1) + { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__); + return false; + } + + m_callbacks.push_back(new EntityCallback(amx, pszCallback, fwdid, pEntity, pParams, iParamsLen, Think)); + pEntity->SetThink(&CBaseEntity::SUB_Think); + return true; +} + +bool CEntityCallbackDispatcher::SetTouch(AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen) +{ + DeleteExistingCallbacks(pEntity, Touch); + + int fwdid; + if (iParamsLen > 0) + fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_ARRAY, FP_DONE); + else + fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_DONE); + + if (fwdid == -1) + { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__); + return false; + } + + m_callbacks.push_back(new EntityCallback(amx, pszCallback, fwdid, pEntity, pParams, iParamsLen, Touch)); + pEntity->SetTouch(&CBaseEntity::SUB_Touch); + return true; +} + +bool CEntityCallbackDispatcher::SetUse(AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen) +{ + DeleteExistingCallbacks(pEntity, Use); + + int fwdid; + if (iParamsLen > 0) + fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_FLOAT, FP_ARRAY, FP_DONE); + else + fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_FLOAT, FP_DONE); + + if (fwdid == -1) + { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__); + return false; + } + + m_callbacks.push_back(new EntityCallback(amx, pszCallback, fwdid, pEntity, pParams, iParamsLen, Use)); + pEntity->SetUse(&CBaseEntity::SUB_Use); + return true; +} + +bool CEntityCallbackDispatcher::SetBlocked(AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen) +{ + DeleteExistingCallbacks(pEntity, Blocked); + + int fwdid; + if (iParamsLen > 0) + fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_ARRAY, FP_DONE); + else + fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_DONE); + + if (fwdid == -1) + { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__); + return false; + } + + m_callbacks.push_back(new EntityCallback(amx, pszCallback, fwdid, pEntity, pParams, iParamsLen, Blocked)); + pEntity->SetBlocked(&CBaseEntity::SUB_Blocked); + return true; +} + +bool CEntityCallbackDispatcher::SetMoveDone(AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen) +{ + DeleteExistingCallbacks(pEntity, MoveDone); + + int fwdid; + if (iParamsLen > 0) + fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_ARRAY, FP_DONE); + else + fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_DONE); + + if (fwdid == -1) + { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__); + return false; + } + + m_callbacks.push_back(new EntityCallback(amx, pszCallback, fwdid, pEntity, pParams, iParamsLen, MoveDone)); + + // Make sure that the entity actually inherited from CBaseToggle + CBaseToggle *pEntityToggle = dynamic_cast(pEntity); + if (!pEntityToggle) + { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: '%s' has no refs to the base class of an entity 'CBaseToggle'", __FUNCTION__, STRING(pEntity->pev->classname)); + return false; + } + + // TODO: Make sure that the entity actually inherited from CBaseToggle + pEntityToggle->SetMoveDone(&CBaseToggle::SUB_MoveDone); + return true; +} + +void CEntityCallbackDispatcher::UnsetThink(AMX *amx, CBaseEntity *pEntity) +{ + pEntity->SetThink(nullptr); + DeleteExistingCallbacks(pEntity, Think); +} + +void CEntityCallbackDispatcher::UnsetTouch(AMX *amx, CBaseEntity *pEntity) +{ + pEntity->SetTouch(nullptr); + DeleteExistingCallbacks(pEntity, Touch); +} + +void CEntityCallbackDispatcher::UnsetUse(AMX *amx, CBaseEntity *pEntity) +{ + pEntity->SetUse(nullptr); + DeleteExistingCallbacks(pEntity, Use); +} + +void CEntityCallbackDispatcher::UnsetBlocked(AMX *amx, CBaseEntity *pEntity) +{ + pEntity->SetBlocked(nullptr); + DeleteExistingCallbacks(pEntity, Blocked); +} + +void CEntityCallbackDispatcher::UnsetMoveDone(AMX *amx, CBaseEntity *pEntity) +{ + // Make sure that the entity actually inherited from CBaseToggle + CBaseToggle *pEntityToggle = dynamic_cast(pEntity); + if (!pEntityToggle) + { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: '%s' has no refs to the base class of an entity 'CBaseToggle'", __FUNCTION__, STRING(pEntity->pev->classname)); + return; + } + + pEntityToggle->SetMoveDone(nullptr); + DeleteExistingCallbacks(pEntity, MoveDone); +} + +// Fundamental callbacks +void CBaseEntity::SUB_Think() +{ + EntityCallbackDispatcher().DispatchCallbacks(this, CEntityCallbackDispatcher::Think, indexOfEdict(pev)); +} + +void CBaseEntity::SUB_Touch(CBaseEntity *pOther) +{ + EntityCallbackDispatcher().DispatchCallbacks(this, CEntityCallbackDispatcher::Touch, indexOfEdict(pev), indexOfEdict(pOther->pev)); +} + +void CBaseEntity::SUB_Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + EntityCallbackDispatcher().DispatchCallbacks(this, CEntityCallbackDispatcher::Use, indexOfEdict(pev), indexOfEdict(pActivator->pev), indexOfEdict(pCaller->pev), useType, value); +} + +void CBaseEntity::SUB_Blocked(CBaseEntity *pOther) +{ + EntityCallbackDispatcher().DispatchCallbacks(this, CEntityCallbackDispatcher::Blocked, indexOfEdict(pev), indexOfEdict(pOther->pev)); +} + +void CBaseToggle::SUB_MoveDone() +{ + EntityCallbackDispatcher().DispatchCallbacks(this, CEntityCallbackDispatcher::MoveDone, indexOfEdict(pev)); +} + +// Instance of the callback dispatcher +static CEntityCallbackDispatcher s_EntityCallbackDispatcher = {}; + +// Returns the instance of the callback dispatcher +CEntityCallbackDispatcher &EntityCallbackDispatcher() +{ + return s_EntityCallbackDispatcher; +} diff --git a/reapi/src/entity_callback_dispatcher.h b/reapi/src/entity_callback_dispatcher.h new file mode 100644 index 0000000..7a70139 --- /dev/null +++ b/reapi/src/entity_callback_dispatcher.h @@ -0,0 +1,172 @@ +#pragma once + +#include "amx_hook.h" + +// Manages entity member functions such as (m_pfnThink, m_pfnTouch, m_pfnUse) and dispatches callbacks to AMXX plugins +class CEntityCallbackDispatcher +{ +public: + bool SetThink (AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen); + bool SetTouch (AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen); + bool SetUse (AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen); + bool SetBlocked (AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen); + bool SetMoveDone (AMX *amx, CBaseEntity *pEntity, const char *pszCallback, const cell *pParams, size_t iParamsLen); + + void UnsetThink (AMX *amx, CBaseEntity *pEntity); + void UnsetTouch (AMX *amx, CBaseEntity *pEntity); + void UnsetUse (AMX *amx, CBaseEntity *pEntity); + void UnsetBlocked (AMX *amx, CBaseEntity *pEntity); + void UnsetMoveDone(AMX *amx, CBaseEntity *pEntity); + + // Enumeration of possible callback types for entity events + enum CallbackType + { + None, // No specific callback type, means unset + Think, + Touch, + Use, + Blocked, + MoveDone, + }; + + // + // @brief Deletes all registered callbacks + void DeleteAllCallbacks(); + + // + // @brief Deletes existing callbacks of the specified type associated with the given entity + // + // @param pEntity Pointer to the entity for which callbacks should be deleted. + // @param type Type of callback to delete (default:None to delete all types) + // + void DeleteExistingCallbacks(CBaseEntity *pEntity, CallbackType type = None); + + // Are we in the middle of processing callbacks? + bool IsProcessingCallbacks() const { return m_bIsProcessingCallbacks; } + + // + // @brief Dispatches callbacks associated with the specified entity and callback type + // + // This function iterates through registered callbacks and executes them for the + // given entity and callback type + // + // + // @param pEntity Pointer to the entity for which callbacks should be dispatched + // @param type Type of callback to dispatch + // @param args Arguments to pass to the callbacks + // + template + void DispatchCallbacks(CBaseEntity *pEntity, CallbackType type, volatile f_args... args) + { + // Flag to indicate that callback processing is currently active. + // This flag is set to true while callback processing to prevent immediate deletion + // of registered callbacks that may be involved in caused AMXX plugin callbacks + // Callbacks marked for deletion are prune after callback processing is complete + m_bIsProcessingCallbacks = true; + + // Iterate through the list of registered callbacks + for (std::list::const_iterator it = m_callbacks.begin(); + it != m_callbacks.end(); it++) + { + const EntityCallback *callback = (*it); + + // Check if the callback is associated with the specified entity and callback type + if (callback->m_pEntity == pEntity && callback->m_callbackType == type) + { + // Check if user parameters provided for this callback + if (callback->m_nUserParamBlockSize > 0) + { + // Execute the callback with the provided arguments and user parameters + g_amxxapi.ExecuteForward(callback->GetFwdIndex(), args..., g_amxxapi.PrepareCellArrayA(callback->m_pUserParams, callback->m_nUserParamBlockSize, true)); + } + else + { + // Execute the callback with the provided arguments + g_amxxapi.ExecuteForward(callback->GetFwdIndex(), args...); + } + } + } + + // Reset the flag to indicate that callback processing has concluded + // From this point onward, entity callbacks will be immediately deleted on the spot as it is, + // without any deferred removals or processing + m_bIsProcessingCallbacks = false; + + // Is there at least one candidate marked for deletion? + if (!m_callbacksMarkForDeletion.empty()) + { + for (std::list::const_iterator it = m_callbacksMarkForDeletion.begin(); + it != m_callbacksMarkForDeletion.end(); it++) + { + const EntityCallback *toDelete = (*it); + + const std::list::const_iterator callbackIt = + std::find(m_callbacks.begin(), m_callbacks.end(), toDelete); + + // Check if the marked for deletion callback + // was found in the list of registered callbacks + if (callbackIt != m_callbacks.end()) + { + m_callbacks.erase(callbackIt); + delete toDelete; + } + } + + m_callbacksMarkForDeletion.clear(); + } + } + +private: + // EntityCallback - a class representing a registered callback for an entity + class EntityCallback: public CAmxxHookBase + { + public: + EntityCallback(AMX *amx, const char *funcname, int index, + CBaseEntity *pEntity, const cell *pParams, size_t iParamsLen, CallbackType type + ) : + CAmxxHookBase(amx, funcname, index, -1), + m_pEntity(pEntity), m_callbackType(type) + { + if (iParamsLen > 0) { + m_nUserParamBlockSize = iParamsLen + 1; + m_pUserParams = new cell[m_nUserParamBlockSize]; + Q_memcpy(m_pUserParams, pParams, sizeof(cell) * iParamsLen); + m_pUserParams[iParamsLen] = 0; + } else { + m_nUserParamBlockSize = 0; + m_pUserParams = nullptr; + } + } + + ~EntityCallback() + { + if (m_pUserParams) + delete[] m_pUserParams; + m_pUserParams = nullptr; + m_nUserParamBlockSize = 0; + } + + // Pointer to the entity for which the callback is registered + CBaseEntity *m_pEntity; + + // Type of the callback (e.g., Think, Touch, Use, and so on) + CallbackType m_callbackType; + + // User-provided data to be passed to their callback function + cell *m_pUserParams; + + // The length of user-provided parameters to be passed to their callback function + size_t m_nUserParamBlockSize; + }; + + // Flag indicating that callback processing is currently in progress + bool m_bIsProcessingCallbacks; + + // List of registered callbacks + std::list m_callbacks; + + // List of callbacks marked for deletion after dispatching callbacks is complete + std::list m_callbacksMarkForDeletion; +}; + +CEntityCallbackDispatcher &EntityCallbackDispatcher(); diff --git a/reapi/src/main.cpp b/reapi/src/main.cpp index 263786b..d82b425 100644 --- a/reapi/src/main.cpp +++ b/reapi/src/main.cpp @@ -69,7 +69,7 @@ void ServerDeactivate_Post() api_cfg.ServerDeactivate(); g_hookManager.Clear(); g_queryFileManager.Clear(); - g_entCallback.Clear(); + EntityCallbackDispatcher().DeleteAllCallbacks(); g_pFunctionTable->pfnSpawn = DispatchSpawn; g_pFunctionTable->pfnKeyValue = KeyValue; @@ -140,6 +140,6 @@ void OnFreeEntPrivateData(edict_t *pEdict) return; } - g_entCallback.Clear(pEntity); + EntityCallbackDispatcher().DeleteExistingCallbacks(pEntity); SET_META_RESULT(MRES_IGNORED); } diff --git a/reapi/src/natives/natives_common.cpp b/reapi/src/natives/natives_common.cpp index 8b2b850..fff9064 100644 --- a/reapi/src/natives/natives_common.cpp +++ b/reapi/src/natives/natives_common.cpp @@ -287,7 +287,7 @@ cell AMX_NATIVE_CALL amx_SetThink(AMX *amx, cell *params) char namebuf[256]; const char *funcname = getAmxString(amx, params[arg_handler], namebuf); if (unlikely(funcname == nullptr || funcname[0] == '\0')) { - pEntity->SetThink(nullptr); + EntityCallbackDispatcher().UnsetThink(amx, pEntity); return TRUE; } @@ -298,7 +298,7 @@ cell AMX_NATIVE_CALL amx_SetThink(AMX *amx, cell *params) } cell *pParams = (PARAMS_COUNT >= 3) ? getAmxAddr(amx, params[arg_params]) : nullptr; - return (cell)g_entCallback.SetThink(amx, pEntity, funcname, pParams, params[arg_len]); + return (cell)EntityCallbackDispatcher().SetThink(amx, pEntity, funcname, pParams, params[arg_len]); } /* @@ -331,7 +331,7 @@ cell AMX_NATIVE_CALL amx_SetTouch(AMX *amx, cell *params) char namebuf[256]; const char *funcname = getAmxString(amx, params[arg_handler], namebuf); if (unlikely(funcname == nullptr || funcname[0] == '\0')) { - pEntity->SetTouch(nullptr); + EntityCallbackDispatcher().UnsetTouch(amx, pEntity); return TRUE; } @@ -342,7 +342,7 @@ cell AMX_NATIVE_CALL amx_SetTouch(AMX *amx, cell *params) } cell *pParams = (PARAMS_COUNT >= 3) ? getAmxAddr(amx, params[arg_params]) : nullptr; - return (cell)g_entCallback.SetTouch(amx, pEntity, funcname, pParams, params[arg_len]); + return (cell)EntityCallbackDispatcher().SetTouch(amx, pEntity, funcname, pParams, params[arg_len]); } /* @@ -375,7 +375,7 @@ cell AMX_NATIVE_CALL amx_SetUse(AMX *amx, cell *params) char namebuf[256]; const char *funcname = getAmxString(amx, params[arg_handler], namebuf); if (unlikely(funcname == nullptr || funcname[0] == '\0')) { - pEntity->SetUse(nullptr); + EntityCallbackDispatcher().UnsetUse(amx, pEntity); return TRUE; } @@ -386,7 +386,7 @@ cell AMX_NATIVE_CALL amx_SetUse(AMX *amx, cell *params) } cell *pParams = (PARAMS_COUNT >= 3) ? getAmxAddr(amx, params[arg_params]) : nullptr; - return (cell)g_entCallback.SetUse(amx, pEntity, funcname, pParams, params[arg_len]); + return (cell)EntityCallbackDispatcher().SetUse(amx, pEntity, funcname, pParams, params[arg_len]); } /* @@ -419,7 +419,7 @@ cell AMX_NATIVE_CALL amx_SetBlocked(AMX *amx, cell *params) char namebuf[256]; const char *funcname = getAmxString(amx, params[arg_handler], namebuf); if (unlikely(funcname == nullptr || funcname[0] == '\0')) { - pEntity->SetBlocked(nullptr); + EntityCallbackDispatcher().UnsetBlocked(amx, pEntity); return TRUE; } @@ -430,7 +430,7 @@ cell AMX_NATIVE_CALL amx_SetBlocked(AMX *amx, cell *params) } cell *pParams = (PARAMS_COUNT >= 3) ? getAmxAddr(amx, params[arg_params]) : nullptr; - return (cell)g_entCallback.SetBlocked(amx, pEntity, funcname, pParams, params[arg_len]); + return (cell)EntityCallbackDispatcher().SetBlocked(amx, pEntity, funcname, pParams, params[arg_len]); } /* @@ -464,7 +464,7 @@ cell AMX_NATIVE_CALL amx_SetMoveDone(AMX *amx, cell *params) char namebuf[256]; const char *funcname = getAmxString(amx, params[arg_handler], namebuf); if (unlikely(funcname == nullptr || funcname[0] == '\0')) { - ((CBaseToggle *)pEntity)->SetMoveDone(nullptr); + EntityCallbackDispatcher().UnsetMoveDone(amx, pEntity); return TRUE; } @@ -475,7 +475,7 @@ cell AMX_NATIVE_CALL amx_SetMoveDone(AMX *amx, cell *params) } cell *pParams = (PARAMS_COUNT >= 3) ? getAmxAddr(amx, params[arg_params]) : nullptr; - return (cell)g_entCallback.SetMoveDone(amx, pEntity, funcname, pParams, params[arg_len]); + return (cell)EntityCallbackDispatcher().SetMoveDone(amx, pEntity, funcname, pParams, params[arg_len]); } AMX_NATIVE_INFO Natives_Common[] = diff --git a/reapi/src/precompiled.h b/reapi/src/precompiled.h index 742ef8c..e9a4ae4 100644 --- a/reapi/src/precompiled.h +++ b/reapi/src/precompiled.h @@ -9,6 +9,7 @@ // C++ #include // std::vector +#include // std::list // platform defs #include "platform.h" @@ -60,7 +61,7 @@ #include "api_config.h" #include "hook_manager.h" #include "hook_callback.h" -#include "entity_callback.h" +#include "entity_callback_dispatcher.h" #include "member_list.h" // natives