|
|
|
|
@ -214,6 +214,10 @@ private:
|
|
|
|
|
using restores_t = std::unordered_map<SendProp *, std::unique_ptr<proxyrestore_t>>;
|
|
|
|
|
static restores_t restores;
|
|
|
|
|
|
|
|
|
|
static SendVarProxyFn SendProxy_StringT_To_String_ptr{nullptr};
|
|
|
|
|
static SendVarProxyFn SendProxy_Color32ToInt_ptr{nullptr};
|
|
|
|
|
static SendVarProxyFn SendProxy_EHandleToInt_ptr{nullptr};
|
|
|
|
|
|
|
|
|
|
static prop_types guess_prop_type(const SendProp *pProp, const SendTable *pTable) noexcept
|
|
|
|
|
{
|
|
|
|
|
#if defined _DEBUG
|
|
|
|
|
@ -278,6 +282,12 @@ static prop_types guess_prop_type(const SendProp *pProp, const SendTable *pTable
|
|
|
|
|
#endif
|
|
|
|
|
return prop_types::unsigned_int;
|
|
|
|
|
} else {
|
|
|
|
|
if(SendProxy_Color32ToInt_ptr && pRealProxy == SendProxy_Color32ToInt_ptr) {
|
|
|
|
|
return prop_types::color32_;
|
|
|
|
|
} else if(SendProxy_EHandleToInt_ptr && pRealProxy == SendProxy_EHandleToInt_ptr) {
|
|
|
|
|
return prop_types::ehandle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
if(pProp->m_nBits == 32) {
|
|
|
|
|
struct dummy_t {
|
|
|
|
|
@ -368,7 +378,11 @@ static prop_types guess_prop_type(const SendProp *pProp, const SendTable *pTable
|
|
|
|
|
case DPT_VectorXY:
|
|
|
|
|
return prop_types::vector;
|
|
|
|
|
case DPT_String: {
|
|
|
|
|
return prop_types::cstring;
|
|
|
|
|
if(SendProxy_StringT_To_String_ptr && pRealProxy == SendProxy_StringT_To_String_ptr) {
|
|
|
|
|
return prop_types::tstring;
|
|
|
|
|
} else {
|
|
|
|
|
return prop_types::cstring;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case DPT_Array:
|
|
|
|
|
return prop_types::unknown;
|
|
|
|
|
@ -388,7 +402,7 @@ template <typename T>
|
|
|
|
|
class thread_var_base
|
|
|
|
|
{
|
|
|
|
|
protected:
|
|
|
|
|
using ptr_ret_t = std::conditional_t<std::is_pointer_v<T>, T, T *>;
|
|
|
|
|
using ptr_ret_t = std::conditional_t<std::is_pointer<T>::value, T, T *>;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
inline void reset(std::nullptr_t) noexcept
|
|
|
|
|
@ -405,7 +419,9 @@ public:
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_var_base() noexcept = default;
|
|
|
|
|
inline thread_var_base() noexcept
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool operator!() const noexcept;
|
|
|
|
|
|
|
|
|
|
@ -442,9 +458,12 @@ protected:
|
|
|
|
|
if(!ptr) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
#if 0
|
|
|
|
|
if constexpr(std::is_pointer_v<T>) {
|
|
|
|
|
return *ptr;
|
|
|
|
|
} else {
|
|
|
|
|
} else
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -532,6 +551,11 @@ public:
|
|
|
|
|
return *ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline thread_var() noexcept
|
|
|
|
|
: thread_var_base<T>{}
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline thread_var(const T &val) noexcept
|
|
|
|
|
{ *this->get_or_allocate_ptr() = val; }
|
|
|
|
|
|
|
|
|
|
@ -584,6 +608,11 @@ public:
|
|
|
|
|
using thread_var_base<bool>::thread_var_base;
|
|
|
|
|
using thread_var_base<bool>::reset;
|
|
|
|
|
|
|
|
|
|
inline thread_var()
|
|
|
|
|
: thread_var_base<bool>{}
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool operator*() const noexcept
|
|
|
|
|
{ return get(); }
|
|
|
|
|
|
|
|
|
|
@ -624,6 +653,42 @@ private:
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
CBaseEntity *ReferenceToEntity(unsigned long ref)
|
|
|
|
|
{
|
|
|
|
|
union {
|
|
|
|
|
cell_t sp_val;
|
|
|
|
|
unsigned long e_val;
|
|
|
|
|
} u;
|
|
|
|
|
|
|
|
|
|
u.e_val = ref;
|
|
|
|
|
|
|
|
|
|
return gamehelpers->ReferenceToEntity(u.sp_val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned long EntityToReference(CBaseEntity *ent)
|
|
|
|
|
{
|
|
|
|
|
union {
|
|
|
|
|
cell_t sp_val;
|
|
|
|
|
unsigned long e_val;
|
|
|
|
|
} u;
|
|
|
|
|
|
|
|
|
|
u.sp_val = gamehelpers->EntityToReference(ent);
|
|
|
|
|
|
|
|
|
|
return u.e_val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned long IndexToReference(int objectID)
|
|
|
|
|
{
|
|
|
|
|
union {
|
|
|
|
|
cell_t sp_val;
|
|
|
|
|
unsigned long e_val;
|
|
|
|
|
} u;
|
|
|
|
|
|
|
|
|
|
u.sp_val = gamehelpers->IndexToReference(objectID);
|
|
|
|
|
|
|
|
|
|
return u.e_val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct packed_entity_data_t final
|
|
|
|
|
{
|
|
|
|
|
packed_entity_data_t(packed_entity_data_t &&other) noexcept
|
|
|
|
|
@ -640,7 +705,7 @@ struct packed_entity_data_t final
|
|
|
|
|
|
|
|
|
|
char *packedData{nullptr};
|
|
|
|
|
bf_write *writeBuf{nullptr};
|
|
|
|
|
int ref{INVALID_EHANDLE_INDEX};
|
|
|
|
|
unsigned long ref{INVALID_EHANDLE_INDEX};
|
|
|
|
|
|
|
|
|
|
bool allocated() const noexcept
|
|
|
|
|
{ return (packedData && writeBuf); }
|
|
|
|
|
@ -668,6 +733,7 @@ struct packed_entity_data_t final
|
|
|
|
|
reset();
|
|
|
|
|
|
|
|
|
|
packedData = static_cast<char *>(aligned_alloc(4, MAX_PACKEDENTITY_DATA));
|
|
|
|
|
memset(packedData, 0x0, MAX_PACKEDENTITY_DATA);
|
|
|
|
|
writeBuf = new bf_write{"SV_PackEntity->writeBuf", packedData, MAX_PACKEDENTITY_DATA};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -680,10 +746,10 @@ struct pack_entity_params_t final
|
|
|
|
|
{
|
|
|
|
|
std::vector<std::vector<packed_entity_data_t>> entity_data{};
|
|
|
|
|
std::vector<int> slots{};
|
|
|
|
|
std::vector<int> entities{};
|
|
|
|
|
std::vector<unsigned long> entities{};
|
|
|
|
|
int snapshot_index{-1};
|
|
|
|
|
|
|
|
|
|
pack_entity_params_t(std::vector<int> &&slots_, std::vector<int> &&entities_, int snapshot_index_) noexcept
|
|
|
|
|
pack_entity_params_t(std::vector<int> &&slots_, std::vector<unsigned long> &&entities_, int snapshot_index_) noexcept
|
|
|
|
|
: slots{std::move(slots_)}, entities{std::move(entities_)}, snapshot_index{snapshot_index_}
|
|
|
|
|
{
|
|
|
|
|
entity_data.resize(slots.size());
|
|
|
|
|
@ -697,13 +763,13 @@ private:
|
|
|
|
|
pack_entity_params_t &operator=(pack_entity_params_t &&) = delete;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static thread_var<bool> in_compute_packs;
|
|
|
|
|
static thread_var<bool> do_calc_delta;
|
|
|
|
|
static thread_var<bool> do_writedelta_entities;
|
|
|
|
|
static thread_var<int> writedeltaentities_client;
|
|
|
|
|
static thread_var<int> sendproxy_client_slot;
|
|
|
|
|
static thread_var<bool> in_compute_packs{};
|
|
|
|
|
static thread_var<bool> do_calc_delta{};
|
|
|
|
|
static thread_var<bool> do_writedelta_entities{};
|
|
|
|
|
static thread_var<int> writedeltaentities_client{};
|
|
|
|
|
static thread_var<int> sendproxy_client_slot{};
|
|
|
|
|
|
|
|
|
|
static std::unique_ptr<pack_entity_params_t> packentity_params;
|
|
|
|
|
static std::unique_ptr<pack_entity_params_t> packentity_params{};
|
|
|
|
|
|
|
|
|
|
static void Host_Error(const char *error, ...) noexcept
|
|
|
|
|
{
|
|
|
|
|
@ -723,7 +789,8 @@ struct prop_reference_t
|
|
|
|
|
{
|
|
|
|
|
restores_t::iterator it_restore{restores.find(pProp)};
|
|
|
|
|
if(it_restore == restores.end()) {
|
|
|
|
|
it_restore = restores.emplace(std::pair<SendProp *, std::unique_ptr<proxyrestore_t>>{pProp, new proxyrestore_t{pProp, type}}).first;
|
|
|
|
|
std::unique_ptr<proxyrestore_t> ptr{new proxyrestore_t{pProp, type}};
|
|
|
|
|
it_restore = restores.emplace(std::pair<SendProp *, std::unique_ptr<proxyrestore_t>>{pProp, std::move(ptr)}).first;
|
|
|
|
|
}
|
|
|
|
|
restore = it_restore->second.get();
|
|
|
|
|
++restore->ref;
|
|
|
|
|
@ -777,8 +844,11 @@ struct opaque_ptr final
|
|
|
|
|
{ operator=(std::move(other)); }
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
static void del_hlpr(void *ptr_) noexcept
|
|
|
|
|
static void del_hlpr_arr(void *ptr_) noexcept
|
|
|
|
|
{ delete[] static_cast<T *>(ptr_); }
|
|
|
|
|
template <typename T>
|
|
|
|
|
static void del_hlpr(void *ptr_) noexcept
|
|
|
|
|
{ delete static_cast<T *>(ptr_); }
|
|
|
|
|
|
|
|
|
|
opaque_ptr() = default;
|
|
|
|
|
|
|
|
|
|
@ -787,8 +857,16 @@ struct opaque_ptr final
|
|
|
|
|
if(del_func && ptr) {
|
|
|
|
|
del_func(ptr);
|
|
|
|
|
}
|
|
|
|
|
ptr = static_cast<void *>(new T{std::forward<Args>(args)...});
|
|
|
|
|
del_func = del_hlpr<T>;
|
|
|
|
|
if(num > 1) {
|
|
|
|
|
ptr = static_cast<void *>(new T[num]);
|
|
|
|
|
for(size_t i = 0; i < num; ++i) {
|
|
|
|
|
new (&static_cast<T *>(ptr)[i]) T{std::forward<Args>(args)...};
|
|
|
|
|
}
|
|
|
|
|
del_func = del_hlpr_arr<T>;
|
|
|
|
|
} else {
|
|
|
|
|
ptr = static_cast<void *>(new T{std::forward<Args>(args)...});
|
|
|
|
|
del_func = del_hlpr<T>;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void clear() noexcept {
|
|
|
|
|
@ -838,7 +916,7 @@ private:
|
|
|
|
|
|
|
|
|
|
struct callback_t final : prop_reference_t
|
|
|
|
|
{
|
|
|
|
|
callback_t(int ref_, SendProp *pProp, std::string &&name_, int element_, prop_types type_, std::size_t offset_) noexcept
|
|
|
|
|
callback_t(unsigned long ref_, SendProp *pProp, std::string &&name_, int element_, prop_types type_, std::size_t offset_) noexcept
|
|
|
|
|
: prop_reference_t{pProp, type_}, offset{offset_}, type{type_}, element{element_}, name{std::move(name_)}, prop{pProp}, ref{ref_}
|
|
|
|
|
{
|
|
|
|
|
if(type == prop_types::cstring || type == prop_types::tstring) {
|
|
|
|
|
@ -878,10 +956,12 @@ struct callback_t final : prop_reference_t
|
|
|
|
|
void change_edict_state() noexcept
|
|
|
|
|
{
|
|
|
|
|
if(ref != INVALID_EHANDLE_INDEX) {
|
|
|
|
|
CBaseEntity *pEntity{gamehelpers->ReferenceToEntity(ref)};
|
|
|
|
|
edict_t *edict{pEntity->GetNetworkable()->GetEdict()};
|
|
|
|
|
if(edict) {
|
|
|
|
|
gamehelpers->SetEdictStateChanged(edict, offset);
|
|
|
|
|
CBaseEntity *pEntity{::ReferenceToEntity(ref)};
|
|
|
|
|
if(pEntity) {
|
|
|
|
|
edict_t *edict{pEntity->GetNetworkable()->GetEdict()};
|
|
|
|
|
if(edict) {
|
|
|
|
|
gamehelpers->SetEdictStateChanged(edict, offset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -980,6 +1060,8 @@ struct callback_t final : prop_reference_t
|
|
|
|
|
pEntity = gamehelpers->ReferenceToEntity(sp_value);
|
|
|
|
|
if(pEntity) {
|
|
|
|
|
new_value = pEntity->GetRefEHandle();
|
|
|
|
|
} else {
|
|
|
|
|
new_value.Term();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
@ -1086,8 +1168,10 @@ struct callback_t final : prop_reference_t
|
|
|
|
|
fwd->PushCell(objectID);
|
|
|
|
|
fwd->PushStringEx((char *)name.c_str(), name.size()+1, SM_PARAM_STRING_COPY|SM_PARAM_STRING_UTF8, 0);
|
|
|
|
|
static char sp_value[4096];
|
|
|
|
|
strcpy(sp_value, reinterpret_cast<const char *>(old_pData));
|
|
|
|
|
fwd->PushStringEx(sp_value, sizeof(sp_value), SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
|
|
|
|
|
const char *str{reinterpret_cast<const char *>(old_pData)};
|
|
|
|
|
size_t len{strlen(str)};
|
|
|
|
|
strcpy(sp_value, str);
|
|
|
|
|
fwd->PushStringEx(sp_value, len, SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
|
|
|
|
|
fwd->PushCell(sizeof(sp_value));
|
|
|
|
|
fwd->PushCell(element);
|
|
|
|
|
fwd->PushCell(client);
|
|
|
|
|
@ -1107,8 +1191,10 @@ struct callback_t final : prop_reference_t
|
|
|
|
|
fwd->PushCell(objectID);
|
|
|
|
|
fwd->PushStringEx((char *)name.c_str(), name.size()+1, SM_PARAM_STRING_COPY|SM_PARAM_STRING_UTF8, 0);
|
|
|
|
|
static char sp_value[4096];
|
|
|
|
|
strcpy(sp_value, STRING(*reinterpret_cast<const string_t *>(old_pData)));
|
|
|
|
|
fwd->PushStringEx(sp_value, sizeof(sp_value), SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
|
|
|
|
|
const char *str{STRING(*reinterpret_cast<const string_t *>(old_pData))};
|
|
|
|
|
size_t len{strlen(str)};
|
|
|
|
|
strcpy(sp_value, str);
|
|
|
|
|
fwd->PushStringEx(sp_value, len, SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
|
|
|
|
|
fwd->PushCell(sizeof(sp_value));
|
|
|
|
|
fwd->PushCell(element);
|
|
|
|
|
fwd->PushCell(client);
|
|
|
|
|
@ -1207,7 +1293,7 @@ struct callback_t final : prop_reference_t
|
|
|
|
|
int element{0};
|
|
|
|
|
std::string name{};
|
|
|
|
|
SendProp *prop{nullptr};
|
|
|
|
|
int ref{INVALID_EHANDLE_INDEX};
|
|
|
|
|
unsigned long ref{INVALID_EHANDLE_INDEX};
|
|
|
|
|
|
|
|
|
|
struct per_client_func_t
|
|
|
|
|
{
|
|
|
|
|
@ -1248,9 +1334,9 @@ using callbacks_t = std::unordered_map<const SendProp *, callback_t>;
|
|
|
|
|
struct proxyhook_t final
|
|
|
|
|
{
|
|
|
|
|
callbacks_t callbacks;
|
|
|
|
|
int ref{INVALID_EHANDLE_INDEX};
|
|
|
|
|
unsigned long ref{INVALID_EHANDLE_INDEX};
|
|
|
|
|
|
|
|
|
|
inline proxyhook_t(int ref_) noexcept
|
|
|
|
|
inline proxyhook_t(unsigned long ref_) noexcept
|
|
|
|
|
: ref{ref_}
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
@ -1286,7 +1372,7 @@ private:
|
|
|
|
|
proxyhook_t() = delete;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using hooks_t = std::unordered_map<int, proxyhook_t>;
|
|
|
|
|
using hooks_t = std::unordered_map<unsigned long, proxyhook_t>;
|
|
|
|
|
static hooks_t hooks;
|
|
|
|
|
|
|
|
|
|
DETOUR_DECL_STATIC6(SendTable_Encode, bool, const SendTable *, pTable, const void *, pStruct, bf_write *, pOut, int, objectID, CUtlMemory<CSendProxyRecipients> *, pRecipients, bool, bNonZeroOnly)
|
|
|
|
|
@ -1305,14 +1391,15 @@ DETOUR_DECL_STATIC6(SendTable_Encode, bool, const SendTable *, pTable, const voi
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ref{gamehelpers->IndexToReference(objectID)};
|
|
|
|
|
unsigned long ref{::IndexToReference(objectID)};
|
|
|
|
|
|
|
|
|
|
const std::vector<int> &entities{packentity_params->entities};
|
|
|
|
|
const std::vector<unsigned long> &entities{packentity_params->entities};
|
|
|
|
|
if(std::find(entities.cbegin(), entities.cend(), ref) != entities.cend()) {
|
|
|
|
|
const std::size_t slots_size{packentity_params->slots.size()};
|
|
|
|
|
for(int i{0}; i < slots_size; ++i) {
|
|
|
|
|
for(std::size_t i{0}; i < slots_size; ++i) {
|
|
|
|
|
std::vector<packed_entity_data_t> &vec{packentity_params->entity_data[i]};
|
|
|
|
|
packed_entity_data_t &packedData{vec.emplace_back()};
|
|
|
|
|
vec.emplace_back();
|
|
|
|
|
packed_entity_data_t &packedData{vec.back()};
|
|
|
|
|
|
|
|
|
|
packedData.ref = ref;
|
|
|
|
|
packedData.allocate();
|
|
|
|
|
@ -1343,18 +1430,21 @@ DETOUR_DECL_STATIC8(SendTable_CalcDelta, int, const SendTable *, pTable, const v
|
|
|
|
|
int total_nChanges{global_nChanges};
|
|
|
|
|
|
|
|
|
|
if(total_nChanges < nMaxDeltaProps) {
|
|
|
|
|
int *client_deltaProps{new int[nMaxDeltaProps]};
|
|
|
|
|
int *client_deltaProps{new int[nMaxDeltaProps]{}};
|
|
|
|
|
|
|
|
|
|
int new_nChanges{total_nChanges};
|
|
|
|
|
|
|
|
|
|
unsigned long ref = ::IndexToReference(objectID);
|
|
|
|
|
|
|
|
|
|
const std::size_t slots_size{packentity_params->slots.size()};
|
|
|
|
|
for(int i{0}; i < slots_size; ++i) {
|
|
|
|
|
std::vector<packed_entity_data_t> &entity_data{packentity_params->entity_data[i]};
|
|
|
|
|
for(std::size_t i{0}; i < slots_size; ++i) {
|
|
|
|
|
using entity_data_t = std::vector<packed_entity_data_t>;
|
|
|
|
|
entity_data_t &entity_data{packentity_params->entity_data[i]};
|
|
|
|
|
|
|
|
|
|
packed_entity_data_t *packedData{nullptr};
|
|
|
|
|
for(auto it{entity_data.rbegin()}; it != entity_data.rend(); ++it) {
|
|
|
|
|
if(it->ref == gamehelpers->IndexToReference(objectID)) {
|
|
|
|
|
packedData = &*it;
|
|
|
|
|
for(entity_data_t::reverse_iterator it{entity_data.rbegin()}; it != entity_data.rend(); ++it) {
|
|
|
|
|
if(it->ref == ref) {
|
|
|
|
|
packedData = &(*it);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -1447,11 +1537,11 @@ DETOUR_DECL_MEMBER2(CFrameSnapshotManager_GetPackedEntity, PackedEntity *, CFram
|
|
|
|
|
|
|
|
|
|
const int slot{writedeltaentities_client};
|
|
|
|
|
|
|
|
|
|
int ref{gamehelpers->IndexToReference(entity)};
|
|
|
|
|
unsigned long ref{::IndexToReference(entity)};
|
|
|
|
|
|
|
|
|
|
const packed_entity_data_t *packedData{nullptr};
|
|
|
|
|
const std::size_t slots_size{packentity_params->slots.size()};
|
|
|
|
|
for(int i{0}; i < slots_size; ++i) {
|
|
|
|
|
for(std::size_t i{0}; i < slots_size; ++i) {
|
|
|
|
|
if(packentity_params->slots[i] == slot) {
|
|
|
|
|
const std::vector<packed_entity_data_t> &entity_data{packentity_params->entity_data[i]};
|
|
|
|
|
for(const packed_entity_data_t &it : entity_data) {
|
|
|
|
|
@ -1481,10 +1571,13 @@ static ConVar *sv_stressbots{nullptr};
|
|
|
|
|
|
|
|
|
|
static bool is_client_valid(CBaseClient *client) noexcept
|
|
|
|
|
{
|
|
|
|
|
if(client->IsHLTV() ||
|
|
|
|
|
client->IsReplay()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!sv_stressbots->GetBool()) {
|
|
|
|
|
if(client->IsFakeClient() ||
|
|
|
|
|
client->IsHLTV() ||
|
|
|
|
|
client->IsReplay()) {
|
|
|
|
|
if(client->IsFakeClient()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -1537,9 +1630,16 @@ void PostWriteDeltaEntities()
|
|
|
|
|
|
|
|
|
|
DETOUR_DECL_MEMBER4(CBaseServer_WriteDeltaEntities, void, CBaseClient *, client, CClientFrame *, to, CClientFrame *, from, bf_write &, pBuf)
|
|
|
|
|
{
|
|
|
|
|
PreWriteDeltaEntities(client);
|
|
|
|
|
DETOUR_MEMBER_CALL(CBaseServer_WriteDeltaEntities)(client, to, from, pBuf);
|
|
|
|
|
PostWriteDeltaEntities();
|
|
|
|
|
CBaseServer *pthis = (CBaseServer *)this;
|
|
|
|
|
|
|
|
|
|
if(pthis->IsHLTV() ||
|
|
|
|
|
pthis->IsReplay()) {
|
|
|
|
|
DETOUR_MEMBER_CALL(CBaseServer_WriteDeltaEntities)(client, to, from, pBuf);
|
|
|
|
|
} else {
|
|
|
|
|
PreWriteDeltaEntities(client);
|
|
|
|
|
DETOUR_MEMBER_CALL(CBaseServer_WriteDeltaEntities)(client, to, from, pBuf);
|
|
|
|
|
PostWriteDeltaEntities();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static CDetour *CBaseServer_WriteDeltaEntities_detour{nullptr};
|
|
|
|
|
@ -1585,7 +1685,7 @@ DETOUR_DECL_STATIC3(SV_ComputeClientPacks, void, int, clientCount, CGameClient *
|
|
|
|
|
}
|
|
|
|
|
const std::size_t slots_size{slots.size()};
|
|
|
|
|
|
|
|
|
|
std::vector<int> entities{};
|
|
|
|
|
std::vector<unsigned long> entities{};
|
|
|
|
|
|
|
|
|
|
bool any_hook{false};
|
|
|
|
|
|
|
|
|
|
@ -1593,10 +1693,10 @@ DETOUR_DECL_STATIC3(SV_ComputeClientPacks, void, int, clientCount, CGameClient *
|
|
|
|
|
|
|
|
|
|
for(int i{0}; i < snapshot->m_nValidEntities; ++i) {
|
|
|
|
|
int idx{snapshot->m_pValidEntities[i]};
|
|
|
|
|
int ref{gamehelpers->IndexToReference(idx)};
|
|
|
|
|
unsigned long ref{::IndexToReference(idx)};
|
|
|
|
|
|
|
|
|
|
for(auto it : g_Sample.pack_ent_listeners) {
|
|
|
|
|
CBaseEntity *pEntity{gamehelpers->ReferenceToEntity(ref)};
|
|
|
|
|
CBaseEntity *pEntity{::ReferenceToEntity(ref)};
|
|
|
|
|
if(pEntity) {
|
|
|
|
|
it->pre_pack_entity(pEntity);
|
|
|
|
|
}
|
|
|
|
|
@ -1647,7 +1747,7 @@ DETOUR_DECL_STATIC3(SV_ComputeClientPacks, void, int, clientCount, CGameClient *
|
|
|
|
|
g_Sample.is_parallel_pack_allowed()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
sv_parallel_sendsnapshot->SetValue(true);
|
|
|
|
|
//sv_parallel_sendsnapshot->SetValue(false);
|
|
|
|
|
sv_parallel_packentities->SetValue(parallel_pack);
|
|
|
|
|
|
|
|
|
|
in_compute_packs = true;
|
|
|
|
|
@ -1681,7 +1781,7 @@ bool Sample::add_listener(const parallel_pack_listener *ptr) noexcept
|
|
|
|
|
|
|
|
|
|
bool Sample::remove_listener(const parallel_pack_listener *ptr) noexcept
|
|
|
|
|
{
|
|
|
|
|
auto it{std::find(pack_ent_listeners.cbegin(), pack_ent_listeners.cend(), ptr)};
|
|
|
|
|
pack_ent_listeners_t::const_iterator it{std::find(pack_ent_listeners.cbegin(), pack_ent_listeners.cend(), ptr)};
|
|
|
|
|
if(it == pack_ent_listeners.cend()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@ -1696,7 +1796,7 @@ static void global_send_proxy(const SendProp *pProp, const void *pStructBase, co
|
|
|
|
|
|
|
|
|
|
if(objectID != -1) {
|
|
|
|
|
const hooks_t &chooks{hooks};
|
|
|
|
|
int ref{gamehelpers->IndexToReference(objectID)};
|
|
|
|
|
unsigned long ref{::IndexToReference(objectID)};
|
|
|
|
|
hooks_t::const_iterator it_hook{chooks.find(ref)};
|
|
|
|
|
if(it_hook != chooks.cend()) {
|
|
|
|
|
callbacks_t::const_iterator it_callback{it_hook->second.callbacks.find(pProp)};
|
|
|
|
|
@ -1735,7 +1835,29 @@ static void game_frame(bool simulating) noexcept
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// dumb nonsense so clients are fully aware that our hooked edicts are changing.
|
|
|
|
|
// this looks hacky, and it is, but there is not a better way i could find
|
|
|
|
|
// after tearing my hair out for 3 days of research
|
|
|
|
|
// sappho.io
|
|
|
|
|
for (auto& hook : hooks)
|
|
|
|
|
{
|
|
|
|
|
unsigned long ref = hook.first; // hook.second.ref;
|
|
|
|
|
//for (auto& cb : hook.second.callbacks)
|
|
|
|
|
//{
|
|
|
|
|
// cb.second.change_edict_state();
|
|
|
|
|
//}
|
|
|
|
|
CBaseEntity* pEntity = ::ReferenceToEntity(ref);
|
|
|
|
|
if (!pEntity)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
edict_t* edict = pEntity->GetNetworkable()->GetEdict();
|
|
|
|
|
if (!edict)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
gamehelpers->SetEdictStateChanged(edict, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DETOUR_DECL_MEMBER1(CGameServer_SendClientMessages, void, bool, bSendSnapshots)
|
|
|
|
|
@ -1812,11 +1934,13 @@ static bool UTIL_FindInSendTable(SendTable *pTable,
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static std::unordered_map<ServerClass *, std::unordered_map<std::string, sm_sendprop_info_ex_t>> propinfos;
|
|
|
|
|
using propinfo_t = std::unordered_map<std::string, sm_sendprop_info_ex_t>;
|
|
|
|
|
using propinfos_t = std::unordered_map<ServerClass *, propinfo_t>;
|
|
|
|
|
static propinfos_t propinfos;
|
|
|
|
|
|
|
|
|
|
bool Sample::remove_serverclass_from_cache(ServerClass *pClass) noexcept
|
|
|
|
|
{
|
|
|
|
|
auto it_props{propinfos.find(pClass)};
|
|
|
|
|
propinfos_t::iterator it_props{propinfos.find(pClass)};
|
|
|
|
|
if(it_props == propinfos.cend()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@ -1827,12 +1951,12 @@ bool Sample::remove_serverclass_from_cache(ServerClass *pClass) noexcept
|
|
|
|
|
|
|
|
|
|
static bool FindSendPropInfo(ServerClass *pClass, std::string &&name, sm_sendprop_info_ex_t *info) noexcept
|
|
|
|
|
{
|
|
|
|
|
auto it_props{propinfos.find(pClass)};
|
|
|
|
|
propinfos_t::iterator it_props{propinfos.find(pClass)};
|
|
|
|
|
if(it_props == propinfos.cend()) {
|
|
|
|
|
it_props = propinfos.emplace(std::pair<ServerClass *, std::unordered_map<std::string, sm_sendprop_info_ex_t>>{pClass, {}}).first;
|
|
|
|
|
it_props = propinfos.emplace(std::pair<ServerClass *, propinfo_t>{pClass, propinfo_t{}}).first;
|
|
|
|
|
}
|
|
|
|
|
if(it_props != propinfos.cend()) {
|
|
|
|
|
auto it_prop{it_props->second.find(name)};
|
|
|
|
|
propinfo_t::iterator it_prop{it_props->second.find(name)};
|
|
|
|
|
if(it_prop == it_props->second.cend()) {
|
|
|
|
|
if(UTIL_FindInSendTable(pClass->m_pTable, name.c_str(), info, 0)) {
|
|
|
|
|
it_prop = it_props->second.emplace(std::pair<std::string, sm_sendprop_info_ex_t>{std::move(name), std::move(*info)}).first;
|
|
|
|
|
@ -1846,7 +1970,7 @@ static bool FindSendPropInfo(ServerClass *pClass, std::string &&name, sm_sendpro
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static cell_t proxysend_handle_hook(IPluginContext *pContext, hooks_t::iterator it_hook, int idx, int offset, SendProp *pProp, std::string &&prop_name, int element, SendTable *pTable, IPluginFunction *callback, bool per_client)
|
|
|
|
|
static cell_t proxysend_handle_hook(IPluginContext *pContext, hooks_t::iterator it_hook, unsigned long ref, int offset, SendProp *pProp, std::string &&prop_name, int element, SendTable *pTable, IPluginFunction *callback, bool per_client)
|
|
|
|
|
{
|
|
|
|
|
prop_types type{prop_types::unknown};
|
|
|
|
|
restores_t::const_iterator it_restore{restores.find(pProp)};
|
|
|
|
|
@ -1860,7 +1984,7 @@ static cell_t proxysend_handle_hook(IPluginContext *pContext, hooks_t::iterator
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
|
printf("added %s %p hook for %i\n", pProp->GetName(), pProp, idx);
|
|
|
|
|
printf("added %s %p hook for %i\n", pProp->GetName(), pProp, ref);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
it_hook->second.add_callback(pProp, std::move(prop_name), element, type, offset, callback, per_client);
|
|
|
|
|
@ -1895,31 +2019,44 @@ static cell_t proxysend_hook(IPluginContext *pContext, const cell_t *params) noe
|
|
|
|
|
SendProp *pProp{info.prop};
|
|
|
|
|
std::string prop_name{pProp->GetName()};
|
|
|
|
|
|
|
|
|
|
int ref = gamehelpers->EntityToReference(pEntity);
|
|
|
|
|
unsigned long ref = ::EntityToReference(pEntity);
|
|
|
|
|
|
|
|
|
|
hooks_t::iterator it_hook{hooks.find(ref)};
|
|
|
|
|
if(it_hook == hooks.end()) {
|
|
|
|
|
it_hook = hooks.emplace(std::pair<int, proxyhook_t>{ref, proxyhook_t{ref}}).first;
|
|
|
|
|
it_hook = hooks.emplace(std::pair<unsigned long, proxyhook_t>{ref, proxyhook_t{ref}}).first;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
edict_t *edict{pEntity->GetNetworkable()->GetEdict()};
|
|
|
|
|
|
|
|
|
|
if(pProp->GetType() == DPT_DataTable) {
|
|
|
|
|
SendTable *pPropTable{pProp->GetDataTable()};
|
|
|
|
|
int NumProps{pPropTable->GetNumProps()};
|
|
|
|
|
for(int i = 0; i < NumProps; ++i) {
|
|
|
|
|
SendProp *pChildProp{pPropTable->GetProp(i)};
|
|
|
|
|
std::string tmp_name{prop_name};
|
|
|
|
|
cell_t ret{proxysend_handle_hook(pContext, it_hook, ref, info.actual_offset + pChildProp->GetOffset(), pChildProp, std::move(tmp_name), i, pTable, callback, per_client)};
|
|
|
|
|
int offset{info.actual_offset + pChildProp->GetOffset()};
|
|
|
|
|
cell_t ret{proxysend_handle_hook(pContext, it_hook, ref, offset, pChildProp, std::move(tmp_name), i, pTable, callback, per_client)};
|
|
|
|
|
if(ret != 0) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
if(edict) {
|
|
|
|
|
gamehelpers->SetEdictStateChanged(edict, offset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return proxysend_handle_hook(pContext, it_hook, ref, info.actual_offset, pProp, std::move(prop_name), 0, pTable, callback, per_client);
|
|
|
|
|
cell_t ret{proxysend_handle_hook(pContext, it_hook, ref, info.actual_offset, pProp, std::move(prop_name), 0, pTable, callback, per_client)};
|
|
|
|
|
if(ret == 0) {
|
|
|
|
|
if(edict) {
|
|
|
|
|
gamehelpers->SetEdictStateChanged(edict, info.actual_offset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void proxysend_handle_unhook(hooks_t::iterator it_hook, int ref, const SendProp *pProp, const char *name, IPluginFunction *callback)
|
|
|
|
|
static void proxysend_handle_unhook(hooks_t::iterator it_hook, unsigned long ref, const SendProp *pProp, const char *name, IPluginFunction *callback)
|
|
|
|
|
{
|
|
|
|
|
callbacks_t::iterator it_callback{it_hook->second.callbacks.find(pProp)};
|
|
|
|
|
if(it_callback != it_hook->second.callbacks.end()) {
|
|
|
|
|
@ -1958,7 +2095,7 @@ static cell_t proxysend_unhook(IPluginContext *pContext, const cell_t *params) n
|
|
|
|
|
|
|
|
|
|
IPluginFunction *callback{pContext->GetFunctionById(params[3])};
|
|
|
|
|
|
|
|
|
|
int ref = gamehelpers->EntityToReference(pEntity);
|
|
|
|
|
unsigned long ref = gamehelpers->EntityToReference(pEntity);
|
|
|
|
|
|
|
|
|
|
hooks_t::iterator it_hook{hooks.find(ref)};
|
|
|
|
|
if(it_hook != hooks.end()) {
|
|
|
|
|
@ -1977,6 +2114,11 @@ static cell_t proxysend_unhook(IPluginContext *pContext, const cell_t *params) n
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
edict_t *edict{pEntity->GetNetworkable()->GetEdict()};
|
|
|
|
|
if(edict) {
|
|
|
|
|
gamehelpers->SetEdictStateChanged(edict, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -2025,6 +2167,15 @@ bool Sample::SDK_OnLoad(char *error, size_t maxlen, bool late) noexcept
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gameconf->GetMemSig("SendProxy_StringT_To_String", (void **)&SendProxy_StringT_To_String_ptr);
|
|
|
|
|
if(SendProxy_StringT_To_String_ptr == nullptr) {
|
|
|
|
|
snprintf(error, maxlen, "could not get SendProxy_StringT_To_String address");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gameconf->GetMemSig("SendProxy_Color32ToInt", (void **)&SendProxy_Color32ToInt_ptr);
|
|
|
|
|
gameconf->GetMemSig("SendProxy_EHandleToInt", (void **)&SendProxy_EHandleToInt_ptr);
|
|
|
|
|
|
|
|
|
|
CDetourManager::Init(smutils->GetScriptingEngine(), gameconf);
|
|
|
|
|
|
|
|
|
|
SendTable_CalcDelta_detour = DETOUR_CREATE_STATIC(SendTable_CalcDelta, "SendTable_CalcDelta");
|
|
|
|
|
@ -2057,7 +2208,7 @@ bool Sample::SDK_OnLoad(char *error, size_t maxlen, bool late) noexcept
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if SOURCE_ENGINE == SE_TF2
|
|
|
|
|
#if SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_CSS
|
|
|
|
|
{
|
|
|
|
|
void **vtable = *(void ***)server;
|
|
|
|
|
int index = vfunc_index(&CBaseServer::WriteDeltaEntities);
|
|
|
|
|
@ -2122,7 +2273,7 @@ bool Sample::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool l
|
|
|
|
|
GET_V_IFACE_ANY(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION);
|
|
|
|
|
ConVar_Register(0, this);
|
|
|
|
|
|
|
|
|
|
#if SOURCE_ENGINE == SE_TF2
|
|
|
|
|
#if SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_CSS
|
|
|
|
|
server = engine->GetIServer();
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
@ -2172,7 +2323,7 @@ void Sample::OnEntityDestroyed(CBaseEntity *pEntity) noexcept
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int ref{gamehelpers->EntityToReference(pEntity)};
|
|
|
|
|
const unsigned long ref{::EntityToReference(pEntity)};
|
|
|
|
|
|
|
|
|
|
hooks_t::iterator it_hook{hooks.find(ref)};
|
|
|
|
|
if(it_hook != hooks.end()) {
|
|
|
|
|
|