2
0
mirror of https://github.com/rehlds/reapi.git synced 2025-01-30 15:37:56 +03:00

Add natives SetThink, SetTouch, SetUse, SetBlocked, SetMoveDone

Minor refactoring
This commit is contained in:
s1lent 2017-10-03 03:30:35 +07:00
parent 69669ef6ce
commit e636819f02
No known key found for this signature in database
GPG Key ID: 0FE401DC73916B5C
18 changed files with 658 additions and 96 deletions

View File

@ -6,6 +6,62 @@
#include <reapi_gamedll_const>
/*
* Sets callback for entity
*
* @param entity Entity index
* @param callback The forward to call
* @note Use "" to reset callback
*
* @noreturn
*/
native SetThink(const ent, const callback[]);
/*
* Sets callback for entity
*
* @param entity Entity index
* @param callback The forward to call
* @note Use "" to reset callback
*
* @noreturn
*/
native SetTouch(const ent, const callback[]);
/*
* Sets callback for entity
*
* @param entity Entity index
* @param callback The forward to call
* @note Use "" to reset callback
*
* @noreturn
*/
native SetUse(const ent, const callback[]);
/*
* Sets callback for entity
*
* @param entity Entity index
* @param callback The forward to call
* @note Use "" to reset callback
*
* @noreturn
*/
native SetBlocked(const ent, const callback[]);
/*
* Sets callback for entity
* @note Entity should be inherited from CBaseToggle, otherwise server can crash
*
* @param entity Entity index
* @param callback The forward to call
* @note Use "" to reset callback
*
* @noreturn
*/
native SetMoveDone(const ent, const callback[]);
/*
* Sets a value to CSGameRules_Members members.
*

View File

@ -110,6 +110,23 @@ public:
void (CBaseEntity::*m_pfnUse)(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value);
void (CBaseEntity::*m_pfnBlocked)(CBaseEntity *pOther);
void EXT_FUNC DLLEXPORT SUB_Think();
void EXT_FUNC DLLEXPORT SUB_Touch(CBaseEntity *pOther);
void EXT_FUNC DLLEXPORT SUB_Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value);
void EXT_FUNC DLLEXPORT SUB_Blocked(CBaseEntity *pOther);
using thinkfn_t = decltype(m_pfnThink);
inline void SetThink(thinkfn_t pfn) { m_pfnThink = pfn; }
using touchfn_t = decltype(m_pfnTouch);
inline void SetTouch(touchfn_t pfn) { m_pfnTouch = pfn; }
using usefn_t = decltype(m_pfnUse);
inline void SetUse(usefn_t pfn) { m_pfnUse = pfn; }
using blockedfn_t = decltype(m_pfnBlocked);
inline void SetBlocked(blockedfn_t pfn) { m_pfnBlocked = pfn; }
virtual void Think() = 0;
virtual void Touch(CBaseEntity *pOther) = 0;
virtual void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType = USE_OFF, float value = 0.0f) = 0;
@ -230,6 +247,8 @@ public:
virtual int Restore(CRestore &restore) = 0;
virtual int GetToggleState() = 0;
virtual float GetDelay() = 0;
void EXT_FUNC DLLEXPORT SUB_MoveDone();
public:
TOGGLE_STATE m_toggle_state;
float m_flActivateFinished; // like attack_finished, but for doors
@ -247,7 +266,11 @@ public:
int m_cTriggersLeft; // trigger_counter only, # of activations remaining
float m_flHeight;
EHANDLE m_hActivator;
void (CBaseToggle::*m_pfnCallWhenMoveDone)();
using movedonefn_t = decltype(m_pfnCallWhenMoveDone);
inline void SetMoveDone(movedonefn_t pfn) { m_pfnCallWhenMoveDone = pfn; }
Vector m_vecFinalDest;
Vector m_vecFinalAngle;

View File

@ -202,7 +202,9 @@
<ClInclude Include="..\include\reunion_api.h" />
<ClInclude Include="..\include\vtc_api.h" />
<ClInclude Include="..\src\amxxmodule.h" />
<ClInclude Include="..\src\amx_hook.h" />
<ClInclude Include="..\src\api_config.h" />
<ClInclude Include="..\src\entity_callback.h" />
<ClInclude Include="..\src\hook_manager.h" />
<ClInclude Include="..\src\hook_callback.h" />
<ClInclude Include="..\src\hook_list.h" />
@ -246,6 +248,7 @@
<ClCompile Include="..\src\api_config.cpp" />
<ClCompile Include="..\src\dllapi.cpp" />
<ClCompile Include="..\src\engine_api.cpp" />
<ClCompile Include="..\src\entity_callback.cpp" />
<ClCompile Include="..\src\hook_manager.cpp" />
<ClCompile Include="..\src\hook_callback.cpp" />
<ClCompile Include="..\src\hook_list.cpp" />

View File

@ -696,6 +696,12 @@
<ClInclude Include="..\common\info.h">
<Filter>common</Filter>
</ClInclude>
<ClInclude Include="..\src\entity_callback.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="..\src\amx_hook.h">
<Filter>src</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\include\cssdk\common\parsemsg.cpp">
@ -797,6 +803,9 @@
<ClCompile Include="..\common\info.cpp">
<Filter>common</Filter>
</ClCompile>
<ClCompile Include="..\src\entity_callback.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\extra\amxmodx\scripting\include\reapi.inc">

83
reapi/src/amx_hook.h Normal file
View File

@ -0,0 +1,83 @@
#pragma once
enum fwdstate
{
FSTATE_INVALID = 0,
FSTATE_ENABLED,
FSTATE_PAUSED,
FSTATE_STOPPED
};
template <typename T = int>
class CAmxxHook
{
public:
~CAmxxHook()
{
if (m_index != -1) {
g_amxxapi.UnregisterSPForward(m_index);
m_index = -1;
}
}
CAmxxHook(AMX *amx, const char *funcname, int index, T data = (T)0) :
m_index(index),
m_state(FSTATE_ENABLED),
m_amx(amx),
m_uniqueData(data)
{
Q_strlcpy(m_CallbackName, funcname);
};
T GetUnique() const;
int GetIndex() const;
fwdstate GetState() const;
AMX *GetAmx() const;
const char *GetCallbackName() const;
void SetState(fwdstate st);
private:
T m_uniqueData;
int m_index;
char m_CallbackName[64];
fwdstate m_state;
AMX *m_amx;
};
// Inlines
template <typename T>
inline T CAmxxHook<T>::GetUnique() const
{
return m_uniqueData;
}
template <typename T>
inline AMX *CAmxxHook<T>::GetAmx() const
{
return m_amx;
}
template <typename T>
inline const char *CAmxxHook<T>::GetCallbackName() const
{
return m_CallbackName;
}
template <typename T>
inline int CAmxxHook<T>::GetIndex() const
{
return m_index;
}
template <typename T>
inline fwdstate CAmxxHook<T>::GetState() const
{
return m_state;
}
template <typename T>
inline void CAmxxHook<T>::SetState(fwdstate st)
{
m_state = st;
}

View File

@ -502,24 +502,4 @@ inline cell* getAmxAddr(AMX *amx, cell amx_addr)
return (cell *)(amx->base + (int)(((AMX_HEADER *)amx->base)->dat + amx_addr));
}
struct getAmxString
{
getAmxString(cell* src, size_t* len = nullptr)
{
getAmxStringTemp(src, temp, sizeof temp - 1, len);
}
getAmxString(AMX* amx, cell addr, size_t* len = nullptr)
{
getAmxStringTemp(getAmxAddr(amx, addr), temp, sizeof temp - 1, len);
}
operator char *()
{
return temp;
}
char temp[1024];
};
#endif // __AMXXMODULE_H__

View File

@ -109,6 +109,15 @@ DLL_FUNCTIONS gFunctionTable_Post =
NULL, // pfnAllowLagCompensation
};
NEW_DLL_FUNCTIONS g_NewDLLFuncTable =
{
&OnFreeEntPrivateData, //! pfnOnFreeEntPrivateData() Called right before the object's memory is freed. Calls its destructor.
NULL, //! pfnGameShutdown()
NULL, //! pfnShouldCollide()
NULL, //! pfnCvarValue()
NULL, //! pfnCvarValue2()
};
C_DLLEXPORT int GetEntityAPI2(DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion)
{
if (!pFunctionTable)
@ -149,3 +158,23 @@ C_DLLEXPORT int GetEntityAPI2_Post(DLL_FUNCTIONS *pFunctionTable, int *interface
memcpy(pFunctionTable, &gFunctionTable_Post, sizeof(DLL_FUNCTIONS));
return TRUE;
}
C_DLLEXPORT int GetNewDLLFunctions(NEW_DLL_FUNCTIONS *pNewFunctionTable, int *interfaceVersion)
{
if (!pNewFunctionTable)
{
ALERT(at_logged, "GetNewDLLFunctions called with null pNewFunctionTable");
return(FALSE);
}
if (*interfaceVersion != NEW_DLL_FUNCTIONS_VERSION)
{
ALERT(at_logged, "GetNewDLLFunctions version mismatch; requested=%d ours=%d", *interfaceVersion, NEW_DLL_FUNCTIONS_VERSION);
//! Tell metamod what version we had, so it can figure out who is out of date.
*interfaceVersion = NEW_DLL_FUNCTIONS_VERSION;
return FALSE;
}
memcpy(pNewFunctionTable, &g_NewDLLFuncTable, sizeof(NEW_DLL_FUNCTIONS));
return TRUE;
}

View File

@ -0,0 +1,154 @@
#include "precompiled.h"
CEntityCallback g_entCallback;
void CEntityCallback::PurgeCallbacks(CBaseEntity *pEntity, CallbackType_e type)
{
auto it = m_callbacks.begin();
while (it != m_callbacks.end())
{
EntityCallback_t data = (*it)->GetUnique();
// this callback was already sets, need to unregister the current forward
if (data.m_callbackType == type && data.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())
{
EntityCallback_t data = (*it)->GetUnique();
if (data.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)
{
PurgeCallbacks(pEntity, CType_Think);
int fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_DONE);
if (fwdid == -1) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__);
return false;
}
EntityCallback_t entry(pEntity, CType_Think);
m_callbacks.push_back(new CAmxxHook<EntityCallback_t>(amx, pszCallback, fwdid, entry));
pEntity->SetThink(&CBaseEntity::SUB_Think);
return true;
}
bool CEntityCallback::SetTouch(AMX *amx, CBaseEntity *pEntity, const char *pszCallback)
{
PurgeCallbacks(pEntity, CType_Touch);
int fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_DONE);
if (fwdid == -1) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__);
return false;
}
EntityCallback_t entry(pEntity, CType_Touch);
m_callbacks.push_back(new CAmxxHook<EntityCallback_t>(amx, pszCallback, fwdid, entry));
pEntity->SetTouch(&CBaseEntity::SUB_Touch);
return true;
}
bool CEntityCallback::SetUse(AMX *amx, CBaseEntity *pEntity, const char *pszCallback)
{
PurgeCallbacks(pEntity, CType_Use);
int fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_FLOAT, FP_DONE);
if (fwdid == -1) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__);
return false;
}
EntityCallback_t entry(pEntity, CType_Use);
m_callbacks.push_back(new CAmxxHook<EntityCallback_t>(amx, pszCallback, fwdid, entry));
pEntity->SetUse(&CBaseEntity::SUB_Use);
return true;
}
bool CEntityCallback::SetBlocked(AMX *amx, CBaseEntity *pEntity, const char *pszCallback)
{
PurgeCallbacks(pEntity, CType_Blocked);
int fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_CELL, FP_DONE);
if (fwdid == -1) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__);
return false;
}
EntityCallback_t entry(pEntity, CType_Blocked);
m_callbacks.push_back(new CAmxxHook<EntityCallback_t>(amx, pszCallback, fwdid, entry));
pEntity->SetTouch(&CBaseEntity::SUB_Blocked);
return true;
}
bool CEntityCallback::SetMoveDone(AMX *amx, CBaseEntity *pEntity, const char *pszCallback)
{
PurgeCallbacks(pEntity, CType_MoveDone);
int fwdid = g_amxxapi.RegisterSPForwardByName(amx, pszCallback, FP_CELL, FP_DONE);
if (fwdid == -1) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: failed to register forward.", __FUNCTION__);
return false;
}
EntityCallback_t entry(pEntity, CType_MoveDone);
m_callbacks.push_back(new CAmxxHook<EntityCallback_t>(amx, pszCallback, fwdid, entry));
// 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

@ -0,0 +1,49 @@
#pragma once
#include "amx_hook.h"
class CEntityCallback
{
public:
void Clear(CBaseEntity *pEntity = nullptr);
bool SetThink (AMX *amx, CBaseEntity *pEntity, const char *pszCallback);
bool SetTouch (AMX *amx, CBaseEntity *pEntity, const char *pszCallback);
bool SetUse (AMX *amx, CBaseEntity *pEntity, const char *pszCallback);
bool SetBlocked (AMX *amx, CBaseEntity *pEntity, const char *pszCallback);
bool SetMoveDone(AMX *amx, CBaseEntity *pEntity, const char *pszCallback);
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) {
if (fwd->GetUnique().m_entity == pEntity && fwd->GetUnique().m_callbackType == type) {
g_amxxapi.ExecuteForward(fwd->GetIndex(), args...);
}
}
}
private:
void PurgeCallbacks(CBaseEntity *pEntity, CallbackType_e type);
struct EntityCallback_t
{
EntityCallback_t(CBaseEntity *pEntity, CallbackType_e type) :
m_entity(pEntity), m_callbackType(type) {};
CBaseEntity *m_entity;
CallbackType_e m_callbackType;
};
std::vector<CAmxxHook<EntityCallback_t> *> m_callbacks;
};
extern CEntityCallback g_entCallback;

View File

@ -5,14 +5,14 @@
#define BEGIN_FUNC_REGION(x) (MAX_REGION_RANGE * hooklist_t::hooks_tables_e::ht_##x)
typedef bool (*reqfunc_t)();
typedef int (*regfunc_t)(AMX *, const char *);
typedef bool (*reqfunc_t) ();
typedef int (*regfunc_t) (AMX *, const char *);
typedef void (*regchain_t)();
struct hook_t
{
std::vector<class CAmxxHook *> pre; // pre forwards
std::vector<class CAmxxHook *> post; // post forwards
std::vector<class CAmxxHook<> *> pre; // pre forwards
std::vector<class CAmxxHook<> *> post; // post forwards
const char *func_name; // function name
const char *depend_name; // platform dependency

View File

@ -2,7 +2,7 @@
CHookManager g_hookManager;
int CHookManager::addHandler(AMX* amx, int func, const char *funcname, int forward, bool post) const
int CHookManager::addHandler(AMX *amx, int func, const char *funcname, int forward, bool post) const
{
auto hook = m_hooklist.getHookSafe(func);
@ -13,47 +13,22 @@ int CHookManager::addHandler(AMX* amx, int func, const char *funcname, int forwa
}
auto& dest = post ? hook->post : hook->pre;
dest.push_back(new CAmxxHook(amx, funcname, forward));
dest.push_back(new CAmxxHook<>(amx, funcname, forward));
int id = func * MAX_HOOK_FORWARDS + dest.size();
return post ? -id : id; // use unsigned ids for post hooks
}
AMX* CAmxxHook::GetAmx() const
{
return m_amx;
}
const char *CAmxxHook::GetCallbackName() const
{
return m_CallbackName;
}
int CAmxxHook::GetIndex() const
{
return m_index;
}
fwdstate CAmxxHook::GetState() const
{
return m_state;
}
void CAmxxHook::SetState(fwdstate st)
{
m_state = st;
}
void CHookManager::Clear() const
{
m_hooklist.clear();
}
hook_t* CHookManager::getHook(size_t func) const
hook_t *CHookManager::getHook(size_t func) const
{
return m_hooklist.getHookSafe(func);
}
CAmxxHook* CHookManager::getAmxxHook(cell handle) const
CAmxxHook<> *CHookManager::getAmxxHook(cell handle) const
{
bool post = handle < 0;

View File

@ -1,46 +1,17 @@
#pragma once
#include "amx_hook.h"
#include "hook_list.h"
enum fwdstate
{
FSTATE_INVALID = 0,
FSTATE_ENABLED,
FSTATE_PAUSED,
FSTATE_STOPPED
};
class CAmxxHook
{
public:
CAmxxHook(AMX* amx, const char *funcname, int index) : m_index(index), m_state(FSTATE_ENABLED), m_amx(amx)
{
strncpy(m_CallbackName, funcname, sizeof(m_CallbackName) - 1);
m_CallbackName[sizeof(m_CallbackName) - 1] = '\0';
};
int GetIndex() const;
fwdstate GetState() const;
AMX* GetAmx() const;
const char *GetCallbackName() const;
void SetState(fwdstate st);
private:
int m_index;
char m_CallbackName[64];
fwdstate m_state;
AMX* m_amx;
};
class CHookManager
{
public:
void Clear() const;
cell addHandler(AMX* amx, int func, const char *funcname, int forward, bool post) const;
hook_t* getHook(size_t func) const;
CAmxxHook* getAmxxHook(cell hook) const;
cell addHandler(AMX *amx, int func, const char *funcname, int forward, bool post) const;
hook_t *getHook(size_t func) const;
CAmxxHook<> *getAmxxHook(cell hook) const;
hook_t* getHookFast(size_t func) const {
hook_t *getHookFast(size_t func) const {
return m_hooklist[func];
}

View File

@ -69,6 +69,7 @@ void ServerDeactivate_Post()
api_cfg.ServerDeactivate();
g_hookManager.Clear();
g_queryFileManager.Clear();
g_entCallback.Clear();
g_pFunctionTable->pfnSpawn = DispatchSpawn;
g_pFunctionTable->pfnKeyValue = KeyValue;
@ -118,3 +119,14 @@ void ResetGlobalState()
SET_META_RESULT(MRES_IGNORED);
}
void OnFreeEntPrivateData(edict_t *pEdict)
{
CBaseEntity *pEntity = getPrivate<CBaseEntity>(pEdict);
if (!pEntity){
return;
}
g_entCallback.Clear(pEntity);
SET_META_RESULT(MRES_IGNORED);
}

View File

@ -13,6 +13,7 @@ void OnAmxxAttach();
bool OnMetaAttach();
void OnMetaDetach();
void OnFreeEntPrivateData(edict_t *pEdict);
void ServerActivate_Post(edict_t *pEdictList, int edictCount, int clientMax);
void ServerDeactivate_Post();
int DispatchSpawn(edict_t* pEntity);

View File

@ -255,6 +255,197 @@ cell AMX_NATIVE_CALL amx_GetAttachment(AMX *amx, cell *params)
return TRUE;
}
/*
* Sets callback for entity
*
* @param entity Entity index
* @param callback The forward to call
* @note Use "" to reset callback
*
* @noreturn
*
* native SetThink(const ent, const callback[]);
*/
cell AMX_NATIVE_CALL amx_SetThink(AMX *amx, cell *params)
{
enum args_e { arg_count, arg_index, arg_handler };
CHECK_ISENTITY(arg_index);
CBaseEntity *pEntity = getPrivate<CBaseEntity>(params[arg_index]);
if (unlikely(pEntity == nullptr)) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: invalid or uninitialized entity", __FUNCTION__);
return FALSE;
}
int funcid;
const char *funcname = getAmxString(amx, params[arg_handler]);
if (unlikely(funcname == nullptr || funcname[0] == '\0')) {
pEntity->SetThink(nullptr);
return TRUE;
}
if (unlikely(g_amxxapi.amx_FindPublic(amx, funcname, &funcid) != AMX_ERR_NONE)) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: public function \"%s\" not found.", __FUNCTION__, funcname);
return FALSE;
}
return (cell)g_entCallback.SetThink(amx, pEntity, funcname);
}
/*
* Sets callback for entity
*
* @param entity Entity index
* @param callback The forward to call
* @note Use "" to reset callback
*
* @noreturn
*
* native SetTouch(const ent, const callback[]);
*/
cell AMX_NATIVE_CALL amx_SetTouch(AMX *amx, cell *params)
{
enum args_e { arg_count, arg_index, arg_handler };
CHECK_ISENTITY(arg_index);
CBaseEntity *pEntity = getPrivate<CBaseEntity>(params[arg_index]);
if (unlikely(pEntity == nullptr)) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: invalid or uninitialized entity", __FUNCTION__);
return FALSE;
}
int funcid;
const char *funcname = getAmxString(amx, params[arg_handler]);
if (unlikely(funcname == nullptr || funcname[0] == '\0')) {
pEntity->SetTouch(nullptr);
return TRUE;
}
if (unlikely(g_amxxapi.amx_FindPublic(amx, funcname, &funcid) != AMX_ERR_NONE)) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: public function \"%s\" not found.", __FUNCTION__, funcname);
return FALSE;
}
return (cell)g_entCallback.SetTouch(amx, pEntity, funcname);
}
/*
* Sets callback for entity
*
* @param entity Entity index
* @param callback The forward to call
* @note Use "" to reset callback
*
* @noreturn
*
* native SetUse(const ent, const callback[]);
*/
cell AMX_NATIVE_CALL amx_SetUse(AMX *amx, cell *params)
{
enum args_e { arg_count, arg_index, arg_handler };
CHECK_ISENTITY(arg_index);
CBaseEntity *pEntity = getPrivate<CBaseEntity>(params[arg_index]);
if (unlikely(pEntity == nullptr)) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: invalid or uninitialized entity", __FUNCTION__);
return FALSE;
}
int funcid;
const char *funcname = getAmxString(amx, params[arg_handler]);
if (unlikely(funcname == nullptr || funcname[0] == '\0')) {
pEntity->SetUse(nullptr);
return TRUE;
}
if (unlikely(g_amxxapi.amx_FindPublic(amx, funcname, &funcid) != AMX_ERR_NONE)) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: public function \"%s\" not found.", __FUNCTION__, funcname);
return FALSE;
}
return (cell)g_entCallback.SetUse(amx, pEntity, funcname);
}
/*
* Sets callback for entity
*
* @param entity Entity index
* @param callback The forward to call
* @note Use "" to reset callback
*
* @noreturn
*
* native SetBlocked(const ent, const callback[]);
*/
cell AMX_NATIVE_CALL amx_SetBlocked(AMX *amx, cell *params)
{
enum args_e { arg_count, arg_index, arg_handler };
CHECK_ISENTITY(arg_index);
CBaseEntity *pEntity = getPrivate<CBaseEntity>(params[arg_index]);
if (unlikely(pEntity == nullptr)) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: invalid or uninitialized entity", __FUNCTION__);
return FALSE;
}
int funcid;
const char *funcname = getAmxString(amx, params[arg_handler]);
if (unlikely(funcname == nullptr || funcname[0] == '\0')) {
pEntity->SetBlocked(nullptr);
return TRUE;
}
if (unlikely(g_amxxapi.amx_FindPublic(amx, funcname, &funcid) != AMX_ERR_NONE)) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: public function \"%s\" not found.", __FUNCTION__, funcname);
return FALSE;
}
return (cell)g_entCallback.SetBlocked(amx, pEntity, funcname);
}
/*
* Sets callback for entity
* @note Entity should be inherited from CBaseToggle, otherwise server can crash
*
* @param entity Entity index
* @param callback The forward to call
* @note Use "" to reset callback
*
* @noreturn
*
* native SetMoveDone(const ent, const callback[]);
*/
cell AMX_NATIVE_CALL amx_SetMoveDone(AMX *amx, cell *params)
{
enum args_e { arg_count, arg_index, arg_handler };
CHECK_ISENTITY(arg_index);
CBaseEntity *pEntity = getPrivate<CBaseEntity>(params[arg_index]);
if (unlikely(pEntity == nullptr)) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: invalid or uninitialized entity", __FUNCTION__);
return FALSE;
}
int funcid;
const char *funcname = getAmxString(amx, params[arg_handler]);
if (unlikely(funcname == nullptr || funcname[0] == '\0')) {
((CBaseToggle *)pEntity)->SetMoveDone(nullptr);
return TRUE;
}
if (unlikely(g_amxxapi.amx_FindPublic(amx, funcname, &funcid) != AMX_ERR_NONE)) {
MF_LogError(amx, AMX_ERR_NATIVE, "%s: public function \"%s\" not found.", __FUNCTION__, funcname);
return FALSE;
}
return (cell)g_entCallback.SetMoveDone(amx, pEntity, funcname);
}
AMX_NATIVE_INFO Natives_Common[] =
{
{ "FClassnameIs", amx_FClassnameIs },
@ -265,6 +456,11 @@ AMX_NATIVE_INFO Natives_Common[] =
{ "set_key_value", amx_set_key_value },
{ "GetBonePosition", amx_GetBonePosition },
{ "GetAttachment", amx_GetAttachment },
{ "SetThink", amx_SetThink },
{ "SetTouch", amx_SetTouch },
{ "SetUse", amx_SetUse },
{ "SetBlocked", amx_SetBlocked },
{ "SetMoveDone", amx_SetMoveDone },
{ nullptr, nullptr }
};

View File

@ -3,7 +3,7 @@
#define PARAMS_COUNT (params[0] / sizeof(cell))
#define CHECK_ISPLAYER(x) if (unlikely(params[x] <= 0 || params[x] > gpGlobals->maxClients)) { MF_LogError(amx, AMX_ERR_NATIVE, "%s: invalid player index %i [%s]", __FUNCTION__, params[x], #x); return FALSE; }
#define CHECK_ISENTITY(x) if (unlikely(params[x] > gpGlobals->maxEntities)) { MF_LogError(amx, AMX_ERR_NATIVE, "%s: invalid entity index %i [%s]", __FUNCTION__, params[x], #x); return FALSE; }
#define CHECK_ISENTITY(x) if (unlikely(params[x] < 0 || params[x] > gpGlobals->maxEntities)) { MF_LogError(amx, AMX_ERR_NATIVE, "%s: invalid entity index %i [%s]", __FUNCTION__, params[x], #x); return FALSE; }
#define CHECK_GAMERULES() if (unlikely(!g_pGameRules)) { MF_LogError(amx, AMX_ERR_NATIVE, "%s: gamerules not initialized", __FUNCTION__); return FALSE; }
#define CHECK_CONNECTED(x, y) if (unlikely(x == nullptr || x->has_disconnected)) { MF_LogError(amx, AMX_ERR_NATIVE, "%s: player %i is not connected", __FUNCTION__, params[y]); return FALSE; }
@ -98,6 +98,26 @@ private:
cell* m_params;
};
struct getAmxString
{
getAmxString(cell* src, size_t* len = nullptr)
{
getAmxStringTemp(src, temp, sizeof temp - 1, len);
}
getAmxString(AMX* amx, cell addr, size_t* len = nullptr)
{
getAmxStringTemp(getAmxAddr(amx, addr), temp, sizeof temp - 1, len);
}
operator char *()
{
return temp;
}
char temp[1024];
};
inline void fillNatives(AMX_NATIVE_INFO* table, cell (AMX_NATIVE_CALL with)(AMX *, cell *))
{
for (size_t i = 0; table[i].name; i++)

View File

@ -63,6 +63,7 @@
#include "api_config.h"
#include "hook_manager.h"
#include "hook_callback.h"
#include "entity_callback.h"
#include "member_list.h"
// natives