Skip to content

Instantly share code, notes, and snippets.

@nosoop
Last active September 2, 2019 06:10
Show Gist options
  • Save nosoop/8970c545797dd9592b2b140a7ae1f583 to your computer and use it in GitHub Desktop.
Save nosoop/8970c545797dd9592b2b140a7ae1f583 to your computer and use it in GitHub Desktop.
Some code to pull properties out of entities in a nice-looking way with a SourceMod extension
#pragma once
#include "re.h"
#include <sm_stringhashmap.h>
template <typename T>
inline T* sendprop_ptr(const void* pEntity, const char* classname, const char* prop) {
sm_sendprop_info_t info;
if (!gamehelpers->FindSendPropInfo(classname, prop, &info)) {
return nullptr;
}
return offset_ptr<T>(pEntity, info.actual_offset);
}
template <typename T>
inline T sendprop_value(const void* pEntity, const char* classname, const char* prop,
T defaultValue = T()) {
T* entprop = sendprop_ptr<T>(pEntity, classname, prop);
return entprop? *entprop : defaultValue;
}
template <typename T>
inline T* dataprop_ptr(const CBaseEntity* pEntity, const char* prop) {
auto pMap = gamehelpers->GetDataMap(const_cast<CBaseEntity*>(pEntity));
sm_datatable_info_t info;
if (!gamehelpers->FindDataMapInfo(pMap, prop, &info)) {
return nullptr;
}
return offset_ptr<T>(pEntity, info.actual_offset);
}
template <typename T>
inline T dataprop_value(const CBaseEntity* pEntity, const char* prop, T defaultValue = T()) {
T* entprop = dataprop_ptr<T>(pEntity, prop);
return entprop? *entprop : defaultValue;
}
/**
* Implements a per-class sendprop accessor.
*/
template <typename T>
class SendPropGetter {
public:
using SendPropOffsetMap = StringHashMap<size_t>;
SendPropGetter(const char* szNetClass) : m_szNetClass{szNetClass}, map{} {};
template <typename M>
M GetValue(const T* pEntity, const char* prop, M defaultValue = M()) const {
M* value = this->GetPointer<M>(pEntity, prop);
return value? *value : defaultValue;
}
template <typename M>
M* GetPointer(const T* pEntity, const char* prop) const {
size_t offset;
return this->Lookup(prop, offset)? offset_ptr<M>(pEntity, offset) : nullptr;
}
private:
bool Lookup(const char* prop, size_t &offset) const {
SendPropOffsetMap::Insert p = map.findForAdd(prop);
if (!p.found()) {
sm_sendprop_info_t info;
if (!gamehelpers->FindSendPropInfo(m_szNetClass, prop, &info)) {
return false;
}
map.add(p, prop);
p->value = info.actual_offset;
}
offset = p->value;
return true;
}
const char* m_szNetClass;
mutable SendPropOffsetMap map;
};
/**
* Implements a sendprop accessor for the given class. Note that the member still has to be
* initialized with the game's classname.
*/
template <typename T>
class ISendProp
{
public:
template <typename M>
M GetSendProp(const char* prop, M defaultValue = M()) const {
return ISendProp<T>::sendprops.GetValue(static_cast<const T*>(this), prop, defaultValue);
}
template <typename M>
M* GetSendPropPtr(const char* prop) const {
return ISendProp<T>::sendprops.template GetPointer<M>(static_cast<const T*>(this), prop);
}
private:
static const SendPropGetter<T> sendprops;
};
/**
* Utility macros and functions for reverse engineered classes.
*/
#pragma once
#define DECL_STUB_CLASS(TYPE, SIZE) class TYPE {\
private: \
uint8_t bytes[SIZE]; \
}
#define DECL_STUB_SUBCLASS(TYPE, SUPERCLASS, SIZE) class TYPE : public SUPERCLASS {\
private: \
uint8_t bytes[SIZE]; \
}
// https://stackoverflow.com/a/1597129
#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define PAD(_size_) uint8_t TOKENPASTE2(m_unk_, __LINE__)[_size_]
#define SIZE_CHECK(_t, _size) static_assert(sizeof(_t) == _size, "sizeof(" #_t ") == " #_size)
#define DEBUG_SIZE(_t) char (*__kaboom)[sizeof(_t)] = 1
// https://stackoverflow.com/a/20141143
template<typename T, typename U> constexpr size_t offsetOf(U T::*member) {
return (char*)&((T*)nullptr->*member) - (char*)nullptr;
}
/**
* Utility function that returns a pointer of type T from a base address plus offset.
* Used to access non-pointer class members, or to get pointers to class values.
*/
template<typename T>
inline T* offset_ptr(const void* ptr, size_t offset) {
return (T*) ((unsigned char*) ptr + offset);
}
/**
* Utility function that returns a value of type T from a base address plus offset.
* Same as dereferencing the result of offset_ptr. Used to read values from offsets.
*/
template<typename T>
inline T offset_value(const void* ptr, size_t offset) {
return *offset_ptr<T>(ptr, offset);
}
/**
* Utility function that does a placement new, allocating enough space to hold an instance of
* class T (assuming the class definition of T has the same size as the server's
* implementation).
*/
template <class T>
inline T* alloc_raw_class() {
return reinterpret_cast<T*>(::operator new(sizeof(T)));
}
#include "entprops.h"
// implement sendprops for the given class
class CCaptureZone : public ISendProp<CCaptureZone> {};
// actually implement the sendprop accessor
template<>
const SendPropGetter<CCaptureZone> ISendProp<CCaptureZone>::sendprops("CCaptureZone");
// you can do something like `auto disabled = reinterpret_cast<CCaptureZone*>(ptr)->GetSendProp<bool>("m_bDisabled");`
// of course no typechecks are done, enjoy your footguns
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment