mirror of
https://github.com/rehlds/reapi.git
synced 2024-12-28 07:35:31 +03:00
Reworked entity callback dispatcher
Fixed crash when callback was deleted in middle callback dispatcher
This commit is contained in:
parent
a60e2b51e2
commit
2bceff94cf
@ -29,29 +29,3 @@ private:
|
||||
fwdstate m_state;
|
||||
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;
|
||||
};
|
||||
|
@ -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));
|
||||
}
|
@ -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;
|
234
reapi/src/entity_callback_dispatcher.cpp
Normal file
234
reapi/src/entity_callback_dispatcher.cpp
Normal 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;
|
||||
}
|
172
reapi/src/entity_callback_dispatcher.h
Normal file
172
reapi/src/entity_callback_dispatcher.h
Normal 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();
|
@ -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);
|
||||
}
|
||||
|
@ -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[] =
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
// C++
|
||||
#include <vector> // std::vector
|
||||
#include <list> // 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
|
||||
|
Loading…
Reference in New Issue
Block a user