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:
parent
a60e2b51e2
commit
2bceff94cf
@ -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;
|
|
||||||
};
|
|
||||||
|
@ -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();
|
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);
|
||||||
}
|
}
|
||||||
|
@ -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[] =
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user