diff --git a/PackageScript b/PackageScript index 36ce6e4..b30ab78 100644 --- a/PackageScript +++ b/PackageScript @@ -8,8 +8,8 @@ builder.SetBuildFolder('package') # Add any folders you need to this list folder_list = [ 'addons/sourcemod/extensions', - #'addons/sourcemod/scripting/include', - #'addons/sourcemod/gamedata', + 'addons/sourcemod/scripting/include', + 'addons/sourcemod/gamedata', #'addons/sourcemod/configs', ] @@ -29,16 +29,14 @@ def CopyFiles(src, dest, files): builder.AddCopy(source_path, dest_entry) # Include files -#CopyFiles('include', 'addons/sourcemod/scripting/include', -# [ 'sample.inc', ] -#) +CopyFiles('sourcemod/scripting/include', 'addons/sourcemod/scripting/include', + [ 'proxysend.inc', ] +) # GameData files -#CopyFiles('gamedata', 'addons/sourcemod/gamedata', -# [ 'myfile.txt', -# 'file2.txt' -# ] -#) +CopyFiles('sourcemod/gamedata', 'addons/sourcemod/gamedata', + [ 'proxysend.txt' ] +) # Config Files #CopyFiles('configs', 'addons/sourcemod/configs', diff --git a/extension.cpp b/extension.cpp index ddb52c7..1d323f5 100644 --- a/extension.cpp +++ b/extension.cpp @@ -57,10 +57,6 @@ static Sample g_Sample; /**< Global singleton for extension's main interface */ SMEXT_LINK(&g_Sample); static IGameConfig *gameconf{nullptr}; - -static std::mutex mux; - -static ISDKTools *g_pSDKTools{nullptr}; static ISDKHooks *g_pSDKHooks{nullptr}; class CFrameSnapshot; @@ -78,63 +74,6 @@ class CBaseEntity : public IServerEntity { }; -struct opaque_ptr final -{ - inline opaque_ptr(opaque_ptr &&other) noexcept - { operator=(std::move(other)); } - - template - static void del_hlpr(void *ptr_) noexcept - { delete[] static_cast(ptr_); } - - opaque_ptr() = default; - - template - void emplace(std::size_t num, Args &&...args) noexcept { - if(del_func && ptr) { - del_func(ptr); - } - ptr = static_cast(new T{std::forward(args)...}); - del_func = del_hlpr; - } - - template - T &get(std::size_t element) noexcept - { return static_cast(ptr)[element]; } - template - T *get() noexcept - { return static_cast(ptr); } - - template - const T &get(std::size_t element) const noexcept - { return static_cast(ptr)[element]; } - template - const T *get() const noexcept - { return static_cast(ptr); } - - ~opaque_ptr() noexcept { - if(del_func && ptr) { - del_func(ptr); - } - } - - opaque_ptr &operator=(opaque_ptr &&other) noexcept - { - ptr = other.ptr; - other.ptr = nullptr; - del_func = other.del_func; - other.del_func = nullptr; - return *this; - } - -private: - opaque_ptr(const opaque_ptr &) = delete; - opaque_ptr &operator=(const opaque_ptr &) = delete; - - void *ptr{nullptr}; - void (*del_func)(void *) {nullptr}; -}; - enum prop_types : unsigned char { int_, @@ -147,6 +86,9 @@ enum prop_types : unsigned char vector, qangle, cstring, + ehandle, + bool_, + color32_, unknown, }; @@ -199,27 +141,89 @@ private: static const CStandardSendProxies *std_proxies; -static prop_types guess_prop_type(const SendProp *pProp, std::size_t &elements) noexcept +static const SendProp *m_nPlayerCond{nullptr}; +static const SendProp *_condition_bits{nullptr}; +static const SendProp *m_nPlayerCondEx{nullptr}; +static const SendProp *m_nPlayerCondEx2{nullptr}; +static const SendProp *m_nPlayerCondEx3{nullptr}; +static const SendProp *m_nPlayerCondEx4{nullptr}; + +static prop_types guess_prop_type(const SendProp *pProp) noexcept { - elements = 1; + if(pProp == m_nPlayerCond || + pProp == _condition_bits || + pProp == m_nPlayerCondEx || + pProp == m_nPlayerCondEx2 || + pProp == m_nPlayerCondEx3 || + pProp == m_nPlayerCondEx4) { + return prop_types::unsigned_int; + } switch(pProp->GetType()) { case DPT_Int: { SendVarProxyFn pRealProxy{pProp->GetProxyFn()}; if(pProp->GetFlags() & SPROP_UNSIGNED) { if(pRealProxy == std_proxies->m_UInt8ToInt32) { + if(pProp->m_nBits == 1) { + return prop_types::bool_; + } + return prop_types::unsigned_char; } else if(pRealProxy == std_proxies->m_UInt16ToInt32) { return prop_types::unsigned_short; + } else if(pRealProxy == std_proxies->m_UInt32ToInt32) { + return prop_types::unsigned_int; } else { + { + if(pProp->m_nBits == NUM_NETWORKED_EHANDLE_BITS) { + struct dummy_t { + CBaseHandle val{}; + } dummy; + + DVariant out{}; + pRealProxy(pProp, static_cast(&dummy), static_cast(&dummy.val), &out, 0, 0); + if(out.m_Int == INVALID_NETWORKED_EHANDLE_VALUE) { + return prop_types::ehandle; + } + } + } + + { + if(pProp->m_nBits == 32) { + struct dummy_t { + color32 val{150, 150, 150, 150}; + } dummy; + + DVariant out{}; + pRealProxy(pProp, static_cast(&dummy), static_cast(&dummy.val), &out, 0, 0); + if(out.m_Int == *reinterpret_cast(&dummy.val)) { + return prop_types::color32_; + } + } + } + return prop_types::unsigned_int; } } else { - if(pRealProxy == std_proxies->m_UInt8ToInt32) { + if(pRealProxy == std_proxies->m_Int8ToInt32) { return prop_types::char_; - } else if(pRealProxy == std_proxies->m_UInt16ToInt32) { + } else if(pRealProxy == std_proxies->m_Int16ToInt32) { return prop_types::short_; + } else if(pRealProxy == std_proxies->m_Int32ToInt32) { + return prop_types::int_; } else { + { + struct dummy_t { + short val{SHRT_MAX-1}; + } dummy; + + DVariant out{}; + pRealProxy(pProp, static_cast(&dummy), static_cast(&dummy.val), &out, 0, 0); + if(out.m_Int == dummy.val+1) { + return prop_types::short_; + } + } + return prop_types::int_; } } @@ -411,11 +415,13 @@ private: struct callback_t final : prop_reference_t { - callback_t(SendProp *pProp, prop_types type_, std::size_t elements_, std::size_t offset_) noexcept - : prop_reference_t{pProp}, offset{offset_}, elements{elements_}, type{type_}, prop{pProp} + callback_t(int index_, SendProp *pProp, prop_types type_, std::size_t offset_) noexcept + : prop_reference_t{pProp}, offset{offset_}, type{type_}, prop{pProp}, index{index_} { if(type == prop_types::cstring) { - fwd = forwards->CreateForwardEx(nullptr, ET_Hook, 7, nullptr, Param_Cell, Param_String, Param_String, Param_Cell, Param_Cell, Param_Cell); + fwd = forwards->CreateForwardEx(nullptr, ET_Hook, 6, nullptr, Param_Cell, Param_String, Param_String, Param_Cell, Param_Cell, Param_Cell); + } else if(type == prop_types::color32_) { + fwd = forwards->CreateForwardEx(nullptr, ET_Hook, 8, nullptr, Param_Cell, Param_String, Param_CellByRef, Param_CellByRef, Param_CellByRef, Param_CellByRef, Param_Cell, Param_Cell); } else { ParamType value_param_type; @@ -425,7 +431,9 @@ struct callback_t final : prop_reference_t case prop_types::char_: case prop_types::unsigned_int: case prop_types::unsigned_short: - case prop_types::unsigned_char: { + case prop_types::unsigned_char: + case prop_types::bool_: + case prop_types::ehandle: { value_param_type = Param_CellByRef; } break; case prop_types::float_: { @@ -437,14 +445,139 @@ struct callback_t final : prop_reference_t } break; } - fwd = forwards->CreateForwardEx(nullptr, ET_Hook, 6, nullptr, Param_Cell, Param_String, value_param_type, Param_Cell, Param_Cell); + fwd = forwards->CreateForwardEx(nullptr, ET_Hook, 5, nullptr, Param_Cell, Param_String, value_param_type, Param_Cell, Param_Cell); } } + inline bool has_any_per_client_func() const noexcept + { return !per_client_funcs.empty(); } + + void change_edict_state() noexcept + { + if(index != -1) { + edict_t *edict{gamehelpers->EdictOfIndex(index)}; + if(edict) { + gamehelpers->SetEdictStateChanged(edict, offset); + } + } + } + + void add_function(IPluginFunction *func, bool per_client) noexcept + { + fwd->RemoveFunction(func); + fwd->AddFunction(func); + + if(per_client) { + if(std::find(per_client_funcs.cbegin(), per_client_funcs.cend(), func) == per_client_funcs.cend()) { + per_client_funcs.emplace_back(func); + } + } + + change_edict_state(); + } + + void remove_function(IPluginFunction *func) noexcept + { + fwd->RemoveFunction(func); + + per_client_funcs_t::const_iterator it_func{std::find(per_client_funcs.cbegin(), per_client_funcs.cend(), func)}; + if(it_func != per_client_funcs.cend()) { + per_client_funcs.erase(it_func); + } + + change_edict_state(); + } + + void remove_functions_of_plugin(IPlugin *plugin) noexcept + { + per_client_funcs_t::const_iterator it_func{per_client_funcs.cbegin()}; + while(it_func != per_client_funcs.cend()) { + if((*it_func)->GetParentContext() == plugin->GetBaseContext()) { + it_func = per_client_funcs.erase(it_func); + continue; + } + ++it_func; + } + + fwd->RemoveFunctionsOfPlugin(plugin); + + change_edict_state(); + } + ~callback_t() noexcept override final { if(fwd) { forwards->ReleaseForward(fwd); } + change_edict_state(); + } + + static int get_current_client_slot() noexcept + { + if(!current_packentity_params || + current_packentity_params->current_slot == -1) { + return -1; + } + + return current_packentity_params->current_slot; + } + + static int get_current_client_entity() noexcept + { + int slot{get_current_client_slot()}; + if(slot == -1) { + return -1; + } + + return slot+1; + } + + void fwd_call_ehandle(const SendProp *pProp, const void *pStructBase, const void *pData, DVariant *pOut, int iElement, int objectID) const noexcept + { + fwd->PushCell(objectID); + fwd->PushString(pProp->GetName()); + const CBaseHandle &hndl{*reinterpret_cast(pData)}; + edict_t *edict{gamehelpers->GetHandleEntity(const_cast(hndl))}; + cell_t sp_value{static_cast(edict ? gamehelpers->IndexOfEdict(edict) : -1)}; + fwd->PushCellByRef(&sp_value); + fwd->PushCell(iElement); + fwd->PushCell(get_current_client_entity()); + cell_t res{Pl_Continue}; + fwd->Execute(&res); + if(res == Pl_Changed) { + edict = gamehelpers->EdictOfIndex(sp_value); + CBaseHandle new_value{}; + if(edict) { + gamehelpers->SetHandleEntity(new_value, edict); + } + restore->pRealProxy(pProp, pStructBase, static_cast(&new_value), pOut, iElement, objectID); + } else { + restore->pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID); + } + } + + void fwd_call_color32(const SendProp *pProp, const void *pStructBase, const void *pData, DVariant *pOut, int iElement, int objectID) const noexcept + { + fwd->PushCell(objectID); + fwd->PushString(pProp->GetName()); + const color32 &clr{*reinterpret_cast(pData)}; + cell_t sp_r{static_cast(clr.r)}; + cell_t sp_g{static_cast(clr.g)}; + cell_t sp_b{static_cast(clr.b)}; + cell_t sp_a{static_cast(clr.a)}; + fwd->PushCellByRef(&sp_r); + fwd->PushCellByRef(&sp_g); + fwd->PushCellByRef(&sp_b); + fwd->PushCellByRef(&sp_a); + fwd->PushCell(iElement); + fwd->PushCell(get_current_client_entity()); + cell_t res{Pl_Continue}; + fwd->Execute(&res); + if(res == Pl_Changed) { + const color32 new_value{static_cast(sp_r), static_cast(sp_g), static_cast(sp_b), static_cast(sp_a)}; + restore->pRealProxy(pProp, pStructBase, static_cast(&new_value), pOut, iElement, objectID); + } else { + restore->pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID); + } } template @@ -455,11 +588,22 @@ struct callback_t final : prop_reference_t cell_t sp_value{static_cast(*reinterpret_cast(pData))}; fwd->PushCellByRef(&sp_value); fwd->PushCell(iElement); - fwd->PushCell(current_packentity_params->current_slot+1); + fwd->PushCell(get_current_client_entity()); cell_t res{Pl_Continue}; fwd->Execute(&res); if(res == Pl_Changed) { const T new_value{static_cast(sp_value)}; + if constexpr(std::is_same_v) { + if(pProp == m_nPlayerCond || + pProp == _condition_bits || + pProp == m_nPlayerCondEx || + pProp == m_nPlayerCondEx2 || + pProp == m_nPlayerCondEx3 || + pProp == m_nPlayerCondEx4) { + std_proxies->m_UInt32ToInt32(pProp, pStructBase, static_cast(&new_value), pOut, iElement, objectID); + return; + } + } restore->pRealProxy(pProp, pStructBase, static_cast(&new_value), pOut, iElement, objectID); } else { restore->pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID); @@ -473,7 +617,7 @@ struct callback_t final : prop_reference_t float sp_value{static_cast(*reinterpret_cast(pData))}; fwd->PushFloatByRef(&sp_value); fwd->PushCell(iElement); - fwd->PushCell(current_packentity_params->current_slot+1); + fwd->PushCell(get_current_client_entity()); cell_t res{Pl_Continue}; fwd->Execute(&res); if(res == Pl_Changed) { @@ -496,7 +640,7 @@ struct callback_t final : prop_reference_t }; fwd->PushArray(sp_value, 3, SM_PARAM_COPYBACK); fwd->PushCell(iElement); - fwd->PushCell(current_packentity_params->current_slot+1); + fwd->PushCell(get_current_client_entity()); cell_t res{Pl_Continue}; fwd->Execute(&res); if(res == Pl_Changed) { @@ -520,7 +664,7 @@ struct callback_t final : prop_reference_t fwd->PushStringEx(sp_value, sizeof(sp_value), SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); fwd->PushCell(sizeof(sp_value)); fwd->PushCell(iElement); - fwd->PushCell(current_packentity_params->current_slot+1); + fwd->PushCell(get_current_client_entity()); cell_t res{Pl_Continue}; fwd->Execute(&res); if(res == Pl_Changed) { @@ -532,7 +676,7 @@ struct callback_t final : prop_reference_t void proxy_call(const SendProp *pProp, const void *pStructBase, const void *pData, DVariant *pOut, int iElement, int objectID) const noexcept { - if(!current_packentity_params || current_packentity_params->current_slot == -1) { + if(!fwd) { restore->pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID); return; } @@ -541,6 +685,9 @@ struct callback_t final : prop_reference_t case prop_types::int_: { fwd_call_int(pProp, pStructBase, pData, pOut, iElement, objectID); } break; + case prop_types::bool_: { + fwd_call_int(pProp, pStructBase, pData, pOut, iElement, objectID); + } break; case prop_types::short_: { fwd_call_int(pProp, pStructBase, pData, pOut, iElement, objectID); } break; @@ -565,6 +712,12 @@ struct callback_t final : prop_reference_t case prop_types::qangle: { fwd_call_vec(pProp, pStructBase, pData, pOut, iElement, objectID); } break; + case prop_types::color32_: { + fwd_call_color32(pProp, pStructBase, pData, pOut, iElement, objectID); + } break; + case prop_types::ehandle: { + fwd_call_ehandle(pProp, pStructBase, pData, pOut, iElement, objectID); + } break; case prop_types::cstring: { fwd_call_str(pProp, pStructBase, pData, pOut, iElement, objectID); } break; @@ -582,16 +735,21 @@ struct callback_t final : prop_reference_t prop = other.prop; other.prop = nullptr; offset = other.offset; - elements = other.elements; type = other.type; + index = other.index; + other.index = -1; + per_client_funcs = std::move(other.per_client_funcs); return *this; } IChangeableForward *fwd{nullptr}; std::size_t offset{-1}; - std::size_t elements{1}; prop_types type{prop_types::unknown}; SendProp *prop{nullptr}; + int index{-1}; + + using per_client_funcs_t = std::vector; + per_client_funcs_t per_client_funcs{}; private: callback_t(const callback_t &) = delete; @@ -604,12 +762,35 @@ using callbacks_t = std::unordered_map; struct proxyhook_t final { callbacks_t callbacks; + int index{-1}; - inline proxyhook_t(const ServerClass *pServer) noexcept + inline proxyhook_t(int index_) noexcept + : index{index_} { } - ~proxyhook_t() noexcept = default; + ~proxyhook_t() noexcept + { + if(index != -1) { + edict_t *edict{gamehelpers->EdictOfIndex(index)}; + if(edict) { + for(callbacks_t::value_type &it : callbacks) { + gamehelpers->SetEdictStateChanged(edict, it.second.offset); + it.second.index = -1; + } + } + } + } + + void add_callback(std::string &&name, IPluginFunction *func, SendProp *pProp, prop_types type, int offset, bool per_client) noexcept + { + callbacks_t::iterator it_callback{callbacks.find(name)}; + if(it_callback == callbacks.end()) { + it_callback = callbacks.emplace(std::pair{std::move(name), callback_t{index, pProp, type, offset}}).first; + } + + it_callback->second.add_function(func, per_client); + } inline proxyhook_t(proxyhook_t &&other) noexcept { operator=(std::move(other)); } @@ -617,6 +798,8 @@ struct proxyhook_t final proxyhook_t &operator=(proxyhook_t &&other) noexcept { callbacks = std::move(other.callbacks); + index = other.index; + other.index = -1; return *this; } @@ -841,10 +1024,16 @@ DETOUR_DECL_STATIC3(SV_ComputeClientPacks, void, int, clientCount, CGameClient * hooks_t::const_iterator it_hook{chooks.find(snapshot->m_pValidEntities[i])}; if(it_hook != chooks.cend()) { edict_t *edict{gamehelpers->EdictOfIndex(snapshot->m_pValidEntities[i])}; + bool any_per_client{false}; for(const auto &it_callback : it_hook->second.callbacks) { - gamehelpers->SetEdictStateChanged(edict, it_callback.second.offset); + if(it_callback.second.has_any_per_client_func()) { + gamehelpers->SetEdictStateChanged(edict, it_callback.second.offset); + any_per_client = true; + } + } + if(any_per_client) { + entities.emplace_back(snapshot->m_pValidEntities[i]); } - entities.emplace_back(snapshot->m_pValidEntities[i]); } } @@ -893,15 +1082,6 @@ static void global_send_proxy(const SendProp *pProp, const void *pStructBase, co } } -static void OnGameFrame(bool simulating) noexcept -{ - if(!simulating) { - return; - } - - -} - static cell_t proxysend_hook(IPluginContext *pContext, const cell_t *params) noexcept { int idx{gamehelpers->ReferenceToBCompatRef(params[1])}; @@ -923,8 +1103,7 @@ static cell_t proxysend_hook(IPluginContext *pContext, const cell_t *params) noe } SendProp *pProp{info.prop}; - std::size_t elements{1}; - prop_types type{guess_prop_type(pProp, elements)}; + prop_types type{guess_prop_type(pProp)}; if(type == prop_types::unknown) { return pContext->ThrowNativeError("Unsupported prop"); } @@ -937,16 +1116,10 @@ static cell_t proxysend_hook(IPluginContext *pContext, const cell_t *params) noe hooks_t::iterator it_hook{hooks.find(idx)}; if(it_hook == hooks.end()) { - it_hook = hooks.emplace(std::pair{idx, proxyhook_t{pServer}}).first; + it_hook = hooks.emplace(std::pair{idx, proxyhook_t{idx}}).first; } - callbacks_t::iterator it_callback{it_hook->second.callbacks.find(name)}; - if(it_callback == it_hook->second.callbacks.end()) { - it_callback = it_hook->second.callbacks.emplace(std::pair{std::move(name), callback_t{pProp, type, elements, info.actual_offset}}).first; - } - - it_callback->second.fwd->RemoveFunction(callback); - it_callback->second.fwd->AddFunction(callback); + it_hook->second.add_callback(std::move(name), callback, pProp, type, info.actual_offset, static_cast(params[4])); return 0; } @@ -972,7 +1145,7 @@ static cell_t proxysend_unhook(IPluginContext *pContext, const cell_t *params) n if(it_hook != hooks.end()) { callbacks_t::iterator it_callback{it_hook->second.callbacks.find(name)}; if(it_callback != it_hook->second.callbacks.end()) { - it_callback->second.fwd->RemoveFunction(callback); + it_callback->second.remove_function(callback); #ifdef _DEBUG printf("removed %s hook for %i\n", name.c_str(), idx); #endif @@ -1026,16 +1199,28 @@ bool Sample::SDK_OnLoad(char *error, size_t maxlen, bool late) noexcept CBaseServer_WriteDeltaEntities_detour = DETOUR_CREATE_MEMBER(CBaseServer_WriteDeltaEntities, "CBaseServer::WriteDeltaEntities"); CBaseServer_WriteDeltaEntities_detour->EnableDetour(); - sharesys->AddDependency(myself, "sdktools.ext", true, true); sharesys->AddDependency(myself, "sdkhooks.ext", true, true); sharesys->RegisterLibrary(myself, "proxysend"); - smutils->AddGameFrameHook(OnGameFrame); plsys->AddPluginsListener(this); sharesys->AddNatives(myself, natives); + sm_sendprop_info_t info{}; + gamehelpers->FindSendPropInfo("CTFPlayer", "m_nPlayerCond", &info); + m_nPlayerCond = info.prop; + gamehelpers->FindSendPropInfo("CTFPlayer", "_condition_bits", &info); + _condition_bits = info.prop; + gamehelpers->FindSendPropInfo("CTFPlayer", "m_nPlayerCondEx", &info); + m_nPlayerCondEx = info.prop; + gamehelpers->FindSendPropInfo("CTFPlayer", "m_nPlayerCondEx2", &info); + m_nPlayerCondEx2 = info.prop; + gamehelpers->FindSendPropInfo("CTFPlayer", "m_nPlayerCondEx3", &info); + m_nPlayerCondEx3 = info.prop; + gamehelpers->FindSendPropInfo("CTFPlayer", "m_nPlayerCondEx4", &info); + m_nPlayerCondEx4 = info.prop; + return true; } @@ -1077,7 +1262,6 @@ void Sample::SDK_OnUnload() noexcept gameconfs->CloseGameConfigFile(gameconf); - smutils->RemoveGameFrameHook(OnGameFrame); plsys->RemovePluginsListener(this); g_pSDKHooks->RemoveEntityListener(this); } @@ -1102,7 +1286,7 @@ void Sample::OnPluginUnloaded(IPlugin *plugin) noexcept while(it_hook != hooks.end()) { callbacks_t::iterator it_callback{it_hook->second.callbacks.begin()}; while(it_callback != it_hook->second.callbacks.end()) { - it_callback->second.fwd->RemoveFunctionsOfPlugin(plugin); + it_callback->second.remove_functions_of_plugin(plugin); if(it_callback->second.fwd->GetFunctionCount() == 0) { it_callback = it_hook->second.callbacks.erase(it_callback); continue; @@ -1119,7 +1303,6 @@ void Sample::OnPluginUnloaded(IPlugin *plugin) noexcept void Sample::SDK_OnAllLoaded() noexcept { - SM_GET_LATE_IFACE(SDKTOOLS, g_pSDKTools); SM_GET_LATE_IFACE(SDKHOOKS, g_pSDKHooks); g_pSDKHooks->AddEntityListener(this); diff --git a/extension.h b/extension.h index acd485d..78a5b3f 100644 --- a/extension.h +++ b/extension.h @@ -39,7 +39,6 @@ #include "smsdk_ext.h" #include -#include /** * @brief Sample implementation of the SDK Extension. diff --git a/sourcemod/scripting/include/proxysend.inc b/sourcemod/scripting/include/proxysend.inc index 7ce1a1e..45cec08 100644 --- a/sourcemod/scripting/include/proxysend.inc +++ b/sourcemod/scripting/include/proxysend.inc @@ -6,12 +6,14 @@ typeset proxysend_callbacks { function Action (int entity, const char[] prop, int &value, int element, int client); + function Action (int entity, const char[] prop, bool &value, int element, int client); function Action (int entity, const char[] prop, float &value, int element, int client); - function Action (int entity, const char[] prop, char[] value, int size, int element, int client); function Action (int entity, const char[] prop, float value[3], int element, int client); + function Action (int entity, const char[] prop, int &r, int &g, int &b, int &a, int element, int client); + function Action (int entity, const char[] prop, char[] value, int size, int element, int client); }; -native void proxysend_hook(int entity, const char[] prop, proxysend_callbacks callback); +native void proxysend_hook(int entity, const char[] prop, proxysend_callbacks callback, bool per_client); native void proxysend_unhook(int entity, const char[] prop, proxysend_callbacks callback); #if !defined REQUIRE_EXTENSIONS