2
0
mirror of https://github.com/rehlds/reapi.git synced 2024-12-29 08:05:36 +03:00

Reworked entity callback dispatcher

Fixed crash when callback was deleted in middle callback dispatcher
This commit is contained in:
s1lentq 2023-09-03 09:06:27 +07:00
parent a60e2b51e2
commit 2bceff94cf
8 changed files with 420 additions and 296 deletions

View File

@ -29,29 +29,3 @@ private:
fwdstate m_state; fwdstate m_state;
AMX *m_amx; AMX *m_amx;
}; };
template <typename T>
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;
};

View File

@ -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<eCallback_t>(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<eCallback_t>(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<eCallback_t>(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<eCallback_t>(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<eCallback_t>(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));
}

View File

@ -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 <typename ...f_args>
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<CAmxxHookUnique<eCallback_t> *> m_callbacks;
};
extern CEntityCallback g_entCallback;

View File

@ -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<EntityCallback *>::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<EntityCallback *>::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<CBaseToggle *>(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<CBaseToggle *>(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;
}

View File

@ -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 <typename ...f_args>
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<EntityCallback *>::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<EntityCallback *>::const_iterator it = m_callbacksMarkForDeletion.begin();
it != m_callbacksMarkForDeletion.end(); it++)
{
const EntityCallback *toDelete = (*it);
const std::list<EntityCallback *>::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<EntityCallback *> m_callbacks;
// List of callbacks marked for deletion after dispatching callbacks is complete
std::list<EntityCallback *> m_callbacksMarkForDeletion;
};
CEntityCallbackDispatcher &EntityCallbackDispatcher();

View File

@ -69,7 +69,7 @@ void ServerDeactivate_Post()
api_cfg.ServerDeactivate(); api_cfg.ServerDeactivate();
g_hookManager.Clear(); g_hookManager.Clear();
g_queryFileManager.Clear(); g_queryFileManager.Clear();
g_entCallback.Clear(); EntityCallbackDispatcher().DeleteAllCallbacks();
g_pFunctionTable->pfnSpawn = DispatchSpawn; g_pFunctionTable->pfnSpawn = DispatchSpawn;
g_pFunctionTable->pfnKeyValue = KeyValue; g_pFunctionTable->pfnKeyValue = KeyValue;
@ -140,6 +140,6 @@ void OnFreeEntPrivateData(edict_t *pEdict)
return; return;
} }
g_entCallback.Clear(pEntity); EntityCallbackDispatcher().DeleteExistingCallbacks(pEntity);
SET_META_RESULT(MRES_IGNORED); SET_META_RESULT(MRES_IGNORED);
} }

View File

@ -287,7 +287,7 @@ cell AMX_NATIVE_CALL amx_SetThink(AMX *amx, cell *params)
char namebuf[256]; char namebuf[256];
const char *funcname = getAmxString(amx, params[arg_handler], namebuf); const char *funcname = getAmxString(amx, params[arg_handler], namebuf);
if (unlikely(funcname == nullptr || funcname[0] == '\0')) { if (unlikely(funcname == nullptr || funcname[0] == '\0')) {
pEntity->SetThink(nullptr); EntityCallbackDispatcher().UnsetThink(amx, pEntity);
return TRUE; 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; 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]; char namebuf[256];
const char *funcname = getAmxString(amx, params[arg_handler], namebuf); const char *funcname = getAmxString(amx, params[arg_handler], namebuf);
if (unlikely(funcname == nullptr || funcname[0] == '\0')) { if (unlikely(funcname == nullptr || funcname[0] == '\0')) {
pEntity->SetTouch(nullptr); EntityCallbackDispatcher().UnsetTouch(amx, pEntity);
return TRUE; 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; 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]; char namebuf[256];
const char *funcname = getAmxString(amx, params[arg_handler], namebuf); const char *funcname = getAmxString(amx, params[arg_handler], namebuf);
if (unlikely(funcname == nullptr || funcname[0] == '\0')) { if (unlikely(funcname == nullptr || funcname[0] == '\0')) {
pEntity->SetUse(nullptr); EntityCallbackDispatcher().UnsetUse(amx, pEntity);
return TRUE; 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; 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]; char namebuf[256];
const char *funcname = getAmxString(amx, params[arg_handler], namebuf); const char *funcname = getAmxString(amx, params[arg_handler], namebuf);
if (unlikely(funcname == nullptr || funcname[0] == '\0')) { if (unlikely(funcname == nullptr || funcname[0] == '\0')) {
pEntity->SetBlocked(nullptr); EntityCallbackDispatcher().UnsetBlocked(amx, pEntity);
return TRUE; 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; 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]; char namebuf[256];
const char *funcname = getAmxString(amx, params[arg_handler], namebuf); const char *funcname = getAmxString(amx, params[arg_handler], namebuf);
if (unlikely(funcname == nullptr || funcname[0] == '\0')) { if (unlikely(funcname == nullptr || funcname[0] == '\0')) {
((CBaseToggle *)pEntity)->SetMoveDone(nullptr); EntityCallbackDispatcher().UnsetMoveDone(amx, pEntity);
return TRUE; 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; 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[] = AMX_NATIVE_INFO Natives_Common[] =

View File

@ -9,6 +9,7 @@
// C++ // C++
#include <vector> // std::vector #include <vector> // std::vector
#include <list> // std::list
// platform defs // platform defs
#include "platform.h" #include "platform.h"
@ -60,7 +61,7 @@
#include "api_config.h" #include "api_config.h"
#include "hook_manager.h" #include "hook_manager.h"
#include "hook_callback.h" #include "hook_callback.h"
#include "entity_callback.h" #include "entity_callback_dispatcher.h"
#include "member_list.h" #include "member_list.h"
// natives // natives