Add datatable, netvar, sdk, trace, entity, player, weapon classes
This commit is contained in:
parent
e21469f7b1
commit
1f57477ac7
103
src/datatable.hh
Normal file
103
src/datatable.hh
Normal file
@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
namespace sdk {
|
||||
class RecvTable;
|
||||
class RecvProp;
|
||||
class RecvDecoder;
|
||||
|
||||
struct RecvProxyData {
|
||||
public:
|
||||
const RecvProp *prop; // The property it's receiving.
|
||||
u8 value[16]; // The value given to you to store.
|
||||
int element; // Which array element you're getting.
|
||||
int id; // The object being referred to.
|
||||
};
|
||||
|
||||
class RecvProp {
|
||||
public:
|
||||
using RecvVarProxyFn = void(const RecvProxyData *data, void *this_ptr, void *out);
|
||||
using ArrayLengthRecvProxyFn = void(void *this_ptr, int id, int cur_length);
|
||||
using DataTableRecvVarProxyFn = void(const RecvProp *prop, void **out, void *data, int id);
|
||||
|
||||
RecvProp() = delete;
|
||||
|
||||
auto as_datatable() const {
|
||||
return data_table;
|
||||
}
|
||||
|
||||
auto as_array() const {
|
||||
return array_prop;
|
||||
}
|
||||
|
||||
auto is_inside_array() const {
|
||||
return inside_array;
|
||||
}
|
||||
|
||||
// If it's one of the numbered "000", "001", etc properties in an array, then
|
||||
// these can be used to get its array property name for debugging.
|
||||
auto parent_name() {
|
||||
return parent_array_name;
|
||||
}
|
||||
|
||||
// Member ordering must remain the same!
|
||||
|
||||
public:
|
||||
const char *network_name;
|
||||
int recv_type;
|
||||
int flags;
|
||||
int string_buffer_size;
|
||||
|
||||
bool inside_array; // Set to true by the engine if this property sits inside an array.
|
||||
|
||||
// Extra data that certain special property types bind to the property here.
|
||||
const void *extra_data;
|
||||
|
||||
// If this is an array (DPT_Array).
|
||||
RecvProp *array_prop;
|
||||
|
||||
ArrayLengthRecvProxyFn *array_length_proxy;
|
||||
|
||||
RecvVarProxyFn *proxy_fn;
|
||||
|
||||
DataTableRecvVarProxyFn *datatable_proxy_fn; // For RDT_DataTable.
|
||||
RecvTable * data_table; // For RDT_DataTable.
|
||||
int offset;
|
||||
|
||||
int element_stride;
|
||||
int element_count;
|
||||
|
||||
private:
|
||||
// If it's one of the numbered "000", "001", etc properties in an array, then
|
||||
// these can be used to get its array property name for debugging.
|
||||
const char *parent_array_name;
|
||||
};
|
||||
|
||||
class RecvTable {
|
||||
public:
|
||||
RecvTable() = delete;
|
||||
|
||||
auto prop(int i) {
|
||||
assert(i < prop_count);
|
||||
return &props[i];
|
||||
}
|
||||
|
||||
// C++11 iterator functions
|
||||
auto begin() { return prop(0); }
|
||||
auto end() { return props + prop_count; }
|
||||
|
||||
public:
|
||||
// Properties described in a table.
|
||||
RecvProp *props;
|
||||
int prop_count;
|
||||
|
||||
// The decoder. NOTE: this covers each RecvTable AND all its children (ie: its children
|
||||
// will have their own decoders that include props for all their children).
|
||||
RecvDecoder *decoder;
|
||||
|
||||
const char *name; // The name matched between client and server.
|
||||
};
|
||||
} // namespace sdk
|
49
src/entity.cc
Normal file
49
src/entity.cc
Normal file
@ -0,0 +1,49 @@
|
||||
#include "stdafx.hh"
|
||||
|
||||
#include "entity.hh"
|
||||
#include "player.hh"
|
||||
#include "weapon.hh"
|
||||
|
||||
#include "netvar.hh"
|
||||
|
||||
#include "sdk.hh"
|
||||
|
||||
using namespace sdk;
|
||||
|
||||
auto Entity::to_handle() -> EntityHandle & {
|
||||
return_virtual_func(to_handle, 2, 0, 0, 0);
|
||||
}
|
||||
|
||||
auto Entity::is_valid() -> bool {
|
||||
// get around clangs "correctly formed code never has a null thisptr"
|
||||
auto thisptr = reinterpret_cast<uptr>(this);
|
||||
if (thisptr == 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Entity::to_player() -> class Player * {
|
||||
auto clientclass = client_class();
|
||||
|
||||
// TODO: do not hardcode this value
|
||||
if (clientclass->class_id == 246) return static_cast<class Player *>(this);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto Entity::to_weapon() -> class Weapon * {
|
||||
// TODO: checks...
|
||||
return static_cast<class Weapon *>(this);
|
||||
}
|
||||
|
||||
auto Entity::client_class() -> struct ClientClass * {
|
||||
return_virtual_func(client_class, 2, 0, 0, 8);
|
||||
}
|
||||
|
||||
auto Entity::dormant() -> bool {
|
||||
return_virtual_func(dormant, 8, 0, 0, 8);
|
||||
}
|
||||
|
||||
auto Entity::index() -> u32 {
|
||||
return_virtual_func(index, 9, 0, 0, 8);
|
||||
}
|
36
src/entity.hh
Normal file
36
src/entity.hh
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.hh"
|
||||
#include "vfunc.hh"
|
||||
|
||||
namespace sdk {
|
||||
struct EntityHandle {
|
||||
u32 serial_index;
|
||||
};
|
||||
|
||||
class Entity {
|
||||
public:
|
||||
Entity() = delete;
|
||||
|
||||
// helper functions
|
||||
bool is_valid();
|
||||
|
||||
EntityHandle &to_handle();
|
||||
|
||||
template <typename T, u32 offset>
|
||||
auto set(T data) { *reinterpret_cast<T *>(reinterpret_cast<uptr>(this) + offset) = data; }
|
||||
|
||||
template <typename T, u32 offset>
|
||||
auto get() { return *reinterpret_cast<T *>(reinterpret_cast<uptr>(this) + offset); }
|
||||
|
||||
// upcasts
|
||||
class Player *to_player();
|
||||
class Weapon *to_weapon();
|
||||
|
||||
// virtual functions
|
||||
struct ClientClass *client_class();
|
||||
|
||||
bool dormant();
|
||||
u32 index();
|
||||
};
|
||||
} // namespace sdk
|
@ -7,7 +7,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Hooks {
|
||||
namespace hooks {
|
||||
template <typename T, u32 offset>
|
||||
class HookInstance {
|
||||
|
||||
@ -161,4 +161,4 @@ public:
|
||||
template <typename T, u32 offset>
|
||||
std::vector<HookInstance<T, offset> *> HookFunction<T, offset>::hooks;
|
||||
|
||||
} // namespace Hooks
|
||||
} // namespace hooks
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include "interface.hh"
|
||||
#include "signature.hh"
|
||||
|
||||
#include "platform.hh"
|
||||
|
||||
using InstantiateInterfaceFn = void *(*)();
|
||||
|
||||
class InterfaceReg {
|
||||
@ -16,14 +18,14 @@ public:
|
||||
InterfaceReg *next;
|
||||
};
|
||||
|
||||
void *InterfaceHelpers::find_interface(const char *module_name, const char *interface_name) {
|
||||
void *interface_helpers::find_interface(const char *module_name, const char *interface_name) {
|
||||
|
||||
InterfaceReg *interface_reg_head;
|
||||
|
||||
if constexpr (DoghookPlatform::windows()) {
|
||||
interface_reg_head = **Signature::find_pattern<InterfaceReg ***>(module_name, "8B 35 ? ? ? ? 57 85 F6 74 38", 2);
|
||||
} else if constexpr (DoghookPlatform::linux()) {
|
||||
} else if constexpr (DoghookPlatform::osx()) {
|
||||
if constexpr (doghook_platform::windows()) {
|
||||
interface_reg_head = **signature::find_pattern<InterfaceReg ***>(module_name, "8B 35 ? ? ? ? 57 85 F6 74 38", 2);
|
||||
} else if constexpr (doghook_platform::linux()) {
|
||||
} else if constexpr (doghook_platform::osx()) {
|
||||
}
|
||||
|
||||
assert(interface_reg_head);
|
||||
|
@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace InterfaceHelpers {
|
||||
namespace interface_helpers {
|
||||
|
||||
// this auto-resolves the library using Signature::
|
||||
void *find_interface(const char *module_name, const char *interface_name);
|
||||
} // namespace InterfaceHelpers
|
||||
} // namespace interface_helpers
|
||||
|
||||
template <typename T>
|
||||
class Interface {
|
||||
@ -16,7 +16,7 @@ public:
|
||||
// e.g. "VClient017" -> "VClient"
|
||||
static auto set_from_interface(const char *module_name, const char *interface_name) {
|
||||
value = static_cast<T *>(
|
||||
InterfaceHelpers::find_interface(module_name, interface_name));
|
||||
interface_helpers::find_interface(module_name, interface_name));
|
||||
}
|
||||
|
||||
// set from a pointer (you should only do this for non-exported
|
||||
|
@ -9,7 +9,7 @@
|
||||
#undef max
|
||||
#undef min
|
||||
|
||||
namespace Math {
|
||||
namespace math {
|
||||
constexpr auto PI = 3.14159265358979323846f;
|
||||
|
||||
inline auto to_radians(float x) {
|
||||
@ -252,8 +252,7 @@ public:
|
||||
out.z = data[row][2];
|
||||
}
|
||||
|
||||
//
|
||||
auto rotate_vector(const Math::Vector &in) const {
|
||||
auto rotate_vector(const Vector &in) const {
|
||||
Vector out;
|
||||
Vector row;
|
||||
|
||||
@ -350,4 +349,4 @@ inline void matrix_angles(const Matrix3x4 &matrix, Vector &angles, Vector &posit
|
||||
matrix_angles(matrix, &angles.x);
|
||||
}
|
||||
|
||||
} // namespace Math
|
||||
} // namespace math
|
||||
|
87
src/netvar.cc
Normal file
87
src/netvar.cc
Normal file
@ -0,0 +1,87 @@
|
||||
#include "stdafx.hh"
|
||||
|
||||
#include "interface.hh"
|
||||
#include "netvar.hh"
|
||||
#include "sdk.hh"
|
||||
|
||||
using namespace sdk;
|
||||
|
||||
static Netvar *head;
|
||||
|
||||
void Netvar::Tree::populate_recursive(RecvTable *t, netvar_tree *nodes) {
|
||||
for (auto i = 0; i < t->prop_count; i++) {
|
||||
auto * prop = t->prop(i);
|
||||
const auto new_node = new node();
|
||||
new_node->p = prop;
|
||||
|
||||
if (prop->recv_type == 6) populate_recursive(prop->as_datatable(), &new_node->children);
|
||||
|
||||
nodes->emplace_back(std::make_pair(prop->network_name, new_node));
|
||||
}
|
||||
}
|
||||
|
||||
Netvar::Tree::Tree() {
|
||||
}
|
||||
|
||||
void Netvar::Tree::init() {
|
||||
if (prop_tree.size() > 0) return;
|
||||
|
||||
auto cc = IFace<Client>()->get_all_classes();
|
||||
while (cc != nullptr) {
|
||||
const auto new_node = new node();
|
||||
new_node->p = nullptr;
|
||||
|
||||
populate_recursive(cc->recv_table, &new_node->children);
|
||||
prop_tree.emplace_back(cc->recv_table->name, new_node);
|
||||
|
||||
cc = cc->next;
|
||||
}
|
||||
}
|
||||
|
||||
uptr Netvar::Tree::find_offset(std::vector<const char *> t) {
|
||||
uptr total = 0;
|
||||
auto nodes = &prop_tree;
|
||||
|
||||
for (auto &name : t) {
|
||||
|
||||
auto old_nodes = nodes;
|
||||
|
||||
auto end = nodes->end();
|
||||
for (auto it = nodes->begin(); it != end; ++it) {
|
||||
auto p = *it;
|
||||
|
||||
if (strcmp(name, p.first) == 0) {
|
||||
nodes = &p.second->children;
|
||||
if (p.second->p != nullptr)
|
||||
total += p.second->p->offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nodes == old_nodes) {
|
||||
// TODO:
|
||||
//Log::msg("[Netvar] Unable to find '%s'", name);
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
Netvar::Tree Netvar::netvar_tree;
|
||||
|
||||
void Netvar::add_to_init_list() {
|
||||
next = head;
|
||||
head = this;
|
||||
}
|
||||
|
||||
void Netvar::init() {
|
||||
offset = netvar_tree.find_offset(tables);
|
||||
}
|
||||
|
||||
void Netvar::init_all() {
|
||||
netvar_tree.init();
|
||||
|
||||
for (auto &n = head; n != nullptr; n = n->next) {
|
||||
n->init();
|
||||
}
|
||||
}
|
58
src/netvar.hh
Normal file
58
src/netvar.hh
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
namespace sdk {
|
||||
class Netvar {
|
||||
|
||||
class Tree {
|
||||
|
||||
struct node;
|
||||
using netvar_tree = std::vector<std::pair<const char *, node *>>;
|
||||
|
||||
struct node {
|
||||
netvar_tree children;
|
||||
class RecvProp *p;
|
||||
};
|
||||
|
||||
netvar_tree prop_tree;
|
||||
|
||||
void populate_recursive(class RecvTable *t, netvar_tree *nodes);
|
||||
|
||||
public:
|
||||
Tree();
|
||||
|
||||
void init();
|
||||
|
||||
uptr find_offset(std::vector<const char *> t);
|
||||
};
|
||||
static Tree netvar_tree;
|
||||
|
||||
// the offset of this netvar from its parent instance
|
||||
uptr offset;
|
||||
|
||||
// used internally for registering
|
||||
Netvar *next;
|
||||
|
||||
std::vector<const char *> tables;
|
||||
|
||||
void add_to_init_list();
|
||||
void init();
|
||||
|
||||
public:
|
||||
template <typename... A>
|
||||
Netvar(A... args) : tables({args...}) {
|
||||
add_to_init_list();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto &get(void *instance) {
|
||||
return *reinterpret_cast<T *>(static_cast<char *>(instance) + offset);
|
||||
}
|
||||
|
||||
static void init_all();
|
||||
};
|
||||
} // namespace sdk
|
@ -40,7 +40,7 @@
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
namespace DoghookPlatform {
|
||||
namespace doghook_platform {
|
||||
inline constexpr bool windows() { return doghook_platform_windows(); }
|
||||
inline constexpr bool linux() { return doghook_platform_linux(); }
|
||||
inline constexpr bool osx() { return doghook_platform_osx(); }
|
||||
@ -48,7 +48,7 @@ inline constexpr bool osx() { return doghook_platform_osx(); }
|
||||
inline constexpr bool msvc() { return doghook_platform_msvc(); }
|
||||
inline constexpr bool clang() { return doghook_platform_clang(); }
|
||||
inline constexpr bool gcc() { return doghook_platform_gcc(); }
|
||||
} // namespace DoghookPlatform
|
||||
} // namespace doghook_platform
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
|
259
src/player.cc
Normal file
259
src/player.cc
Normal file
@ -0,0 +1,259 @@
|
||||
#include "stdafx.hh"
|
||||
|
||||
#include "player.hh"
|
||||
#include "weapon.hh"
|
||||
|
||||
#include "netvar.hh"
|
||||
#include "vfunc.hh"
|
||||
|
||||
#include "sdk.hh"
|
||||
|
||||
using namespace sdk;
|
||||
|
||||
auto Player::local() -> Player * {
|
||||
return static_cast<Player *>(IFace<EntList>()->entity(IFace<Engine>()->local_player_index()));
|
||||
}
|
||||
|
||||
static auto health = Netvar("DT_BasePlayer", "m_iHealth");
|
||||
auto Player::health() -> int & {
|
||||
return ::health.get<int>(this);
|
||||
}
|
||||
|
||||
static auto lifestate = Netvar("DT_BasePlayer", "m_lifeState");
|
||||
auto Player::alive() -> bool {
|
||||
return ::lifestate.get<u8>(this) == 0;
|
||||
}
|
||||
|
||||
auto Player::origin() -> math::Vector & {
|
||||
return_virtual_func(origin, 9, 0, 0, 0);
|
||||
}
|
||||
|
||||
auto Player::set_origin(const math::Vector &v) -> void {
|
||||
if constexpr (doghook_platform::windows()) {
|
||||
static auto original = signature::find_pattern<void(__thiscall *)(Player *, const math::Vector &)>("client", "55 8B EC 56 57 8B F1 E8 ? ? ? ? 8B 7D 08 F3 0F 10 07", 0);
|
||||
assert(original);
|
||||
return original(this, v);
|
||||
} else if constexpr (doghook_platform::linux()) {
|
||||
static_assert(doghook_platform::linux() == false);
|
||||
} else if constexpr (doghook_platform::osx()) {
|
||||
static_assert(doghook_platform::osx() == false);
|
||||
}
|
||||
}
|
||||
|
||||
auto Player::angles() -> math::Vector & {
|
||||
return_virtual_func(angles, 10, 0, 0, 0);
|
||||
}
|
||||
|
||||
auto Player::set_angles(const math::Vector &v) -> void {
|
||||
if constexpr (doghook_platform::windows()) {
|
||||
static auto original = signature::find_pattern<void(__thiscall *)(Player *, const math::Vector &)>("client", "55 8B EC 83 EC 60 56 57 8B F1", 0);
|
||||
assert(original);
|
||||
return original(this, v);
|
||||
} else if constexpr (doghook_platform::linux()) {
|
||||
static_assert(doghook_platform::linux() == false);
|
||||
} else if constexpr (doghook_platform::osx()) {
|
||||
static_assert(doghook_platform::osx() == false);
|
||||
}
|
||||
}
|
||||
|
||||
static auto team = Netvar("DT_BaseEntity", "m_iTeamNum");
|
||||
auto Player::team() -> int {
|
||||
return ::team.get<int>(this);
|
||||
}
|
||||
|
||||
static auto cond = Netvar("DT_TFPlayer", "m_Shared", "m_nPlayerCond");
|
||||
auto Player::cond() -> u32 & {
|
||||
return ::cond.get<u32>(this);
|
||||
}
|
||||
|
||||
auto Player::render_bounds() -> std::pair<math::Vector, math::Vector> {
|
||||
auto func = vfunc::Func<void (Player::*)(math::Vector &, math::Vector &)>(this, 20, 0, 0, 4);
|
||||
|
||||
std::pair<math::Vector, math::Vector> ret;
|
||||
|
||||
func.invoke(ret.first, ret.second);
|
||||
|
||||
auto origin = this->origin();
|
||||
|
||||
ret.first += origin;
|
||||
ret.second += origin;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static auto collideable_min = Netvar("DT_BaseEntity", "m_Collision", "m_vecMinsPreScaled");
|
||||
static auto collideable_max = Netvar("DT_BaseEntity", "m_Collision", "m_vecMaxsPreScaled");
|
||||
auto Player::collision_bounds() -> std::pair<math::Vector &, math::Vector &> {
|
||||
|
||||
auto &min = ::collideable_min.get<math::Vector>(this);
|
||||
auto &max = ::collideable_max.get<math::Vector>(this);
|
||||
|
||||
return std::make_pair(std::ref(min), std::ref(max));
|
||||
}
|
||||
|
||||
static auto view_offset = Netvar("DT_BasePlayer", "localdata", "m_vecViewOffset[0]");
|
||||
auto Player::view_offset() -> math::Vector & {
|
||||
return ::view_offset.get<math::Vector>(this);
|
||||
}
|
||||
|
||||
static auto tf_class = Netvar("DT_TFPlayer", "m_PlayerClass", "m_iClass");
|
||||
auto Player::tf_class() -> int {
|
||||
return ::tf_class.get<int>(this);
|
||||
}
|
||||
|
||||
static auto tick_base = Netvar("DT_BasePlayer", "localdata", "m_nTickBase");
|
||||
auto Player::tick_base() -> int {
|
||||
return ::tick_base.get<int>(this);
|
||||
}
|
||||
|
||||
static auto active_weapon_handle = Netvar("DT_BaseCombatCharacter", "m_hActiveWeapon");
|
||||
auto Player::active_weapon() -> Weapon * {
|
||||
return static_cast<Weapon *>(IFace<EntList>()->from_handle(::active_weapon_handle.get<EntityHandle>(this)));
|
||||
}
|
||||
|
||||
static auto sim_time = Netvar("DT_BaseEntity", "m_flSimulationTime");
|
||||
auto Player::sim_time() -> float & {
|
||||
return ::sim_time.get<float>(this);
|
||||
}
|
||||
|
||||
static auto anim_time = Netvar("DT_BaseEntity", "AnimTimeMustBeFirst", "m_flAnimTime");
|
||||
auto Player::anim_time() -> float & {
|
||||
return ::anim_time.get<float>(this);
|
||||
}
|
||||
|
||||
static auto cycle = Netvar("DT_BaseAnimating", "serveranimdata", "m_flCycle");
|
||||
auto Player::cycle() -> float & {
|
||||
return ::cycle.get<float>(this);
|
||||
}
|
||||
|
||||
static auto fov_time = Netvar("DT_BasePlayer", "m_flFOVTime");
|
||||
auto Player::fov_time() -> float {
|
||||
return ::fov_time.get<float>(this);
|
||||
}
|
||||
|
||||
static auto render_origin = Netvar("DT_BaseEntity", "m_vecOrigin");
|
||||
auto Player::render_origin() -> math::Vector & {
|
||||
return ::render_origin.get<math::Vector>(this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct UtlVector {
|
||||
T * mem;
|
||||
int alloc_count;
|
||||
int grow_size;
|
||||
int size;
|
||||
T * dbg_elements;
|
||||
};
|
||||
|
||||
static auto player_anim_layer_vector(Player *p) -> UtlVector<AnimationLayer> & {
|
||||
// TODO: we need a better, cross platform method for doing this.
|
||||
// check for "%8.4f : %30s : %5.3f : %4.2f : %1d\n"
|
||||
|
||||
if constexpr (doghook_platform::windows()) {
|
||||
UtlVector<AnimationLayer> *anim_overlays = reinterpret_cast<UtlVector<AnimationLayer> *>(p + 2216);
|
||||
return *anim_overlays;
|
||||
} else if constexpr (doghook_platform::linux()) {
|
||||
} else if constexpr (doghook_platform::osx()) {
|
||||
}
|
||||
}
|
||||
|
||||
auto Player::anim_layer(u32 index) -> AnimationLayer & {
|
||||
return player_anim_layer_vector(this).mem[index];
|
||||
}
|
||||
|
||||
auto Player::anim_layer_count() -> u32 {
|
||||
return player_anim_layer_vector(this).size;
|
||||
}
|
||||
|
||||
auto Player::update_client_side_animation() -> void {
|
||||
// Look for the string "UpdateClientSideAnimations
|
||||
return_virtual_func(update_client_side_animation, 191, 0, 0, 0);
|
||||
}
|
||||
|
||||
auto Player::invalidate_physics_recursive(u32 flags) -> void {
|
||||
typedef void(__thiscall * InvalidatePhysicsRecursiveFn)(Player *, u32 flags);
|
||||
|
||||
if constexpr (doghook_platform::windows()) {
|
||||
static auto fn = signature::find_pattern<InvalidatePhysicsRecursiveFn>("client", "55 8B EC 51 53 8B 5D 08 56 8B F3 83 E6 04", 0);
|
||||
fn(this, flags);
|
||||
} else if constexpr (doghook_platform::linux()) {
|
||||
} else if constexpr (doghook_platform::osx()) {
|
||||
}
|
||||
}
|
||||
|
||||
static auto sequence = Netvar("DT_BaseAnimating", "m_nSequence");
|
||||
auto Player::sequence() -> int & {
|
||||
return ::sequence.get<int>(this);
|
||||
}
|
||||
|
||||
auto Player::view_position() -> math::Vector {
|
||||
return origin() + view_offset();
|
||||
}
|
||||
|
||||
auto Player::bone_transforms(math::Matrix3x4 *hitboxes_out, u32 max_bones, u32 bone_mask, float current_time) -> bool {
|
||||
return_virtual_func(bone_transforms, 16, 0, 0, 4, hitboxes_out, max_bones, bone_mask, current_time);
|
||||
}
|
||||
|
||||
auto Player::model_handle() -> const ModelHandle * {
|
||||
return_virtual_func(model_handle, 9, 0, 0, 4);
|
||||
}
|
||||
|
||||
auto Player::studio_model() -> const StudioModel * {
|
||||
return IFace<ModelInfo>()->studio_model(this->model_handle());
|
||||
}
|
||||
|
||||
static auto get_hitboxes_internal(Player *player, const StudioModel *model, PlayerHitboxes *hitboxes, bool create_pose) {
|
||||
math::Matrix3x4 bone_to_world[128];
|
||||
|
||||
// #define BONE_USED_BY_ANYTHING 0x0007FF00
|
||||
bool success = player->bone_transforms(bone_to_world, 128, 0x0007FF00, IFace<Engine>()->last_timestamp());
|
||||
assert(success);
|
||||
|
||||
auto hitbox_set_ptr = model->hitbox_set(0);
|
||||
assert(hitbox_set_ptr);
|
||||
|
||||
auto &hitbox_set = *hitbox_set_ptr;
|
||||
|
||||
auto hitboxes_count = std::min(128u, hitbox_set.hitboxes_count);
|
||||
|
||||
math::Vector origin;
|
||||
math::Vector centre;
|
||||
|
||||
for (u32 i = 0; i < hitboxes_count; ++i) {
|
||||
auto box = hitbox_set[i];
|
||||
assert(box);
|
||||
|
||||
math::Vector rotation;
|
||||
math::matrix_angles(bone_to_world[box->bone], rotation, origin);
|
||||
|
||||
math::Matrix3x4 rotate_matrix;
|
||||
rotate_matrix.from_angle(rotation);
|
||||
|
||||
math::Vector rotated_min, rotated_max;
|
||||
|
||||
rotated_min = rotate_matrix.rotate_vector(box->min);
|
||||
rotated_max = rotate_matrix.rotate_vector(box->max);
|
||||
|
||||
centre = rotated_min.lerp(rotated_max, 0.5);
|
||||
|
||||
hitboxes->centre[i] = origin + centre;
|
||||
hitboxes->min[i] = origin + rotated_min;
|
||||
hitboxes->max[i] = origin + rotated_max;
|
||||
|
||||
#ifdef _DEBUG
|
||||
hitboxes->rotation[i] = rotation;
|
||||
hitboxes->origin[i] = origin;
|
||||
hitboxes->raw_min[i] = box->min;
|
||||
hitboxes->raw_max[i] = box->max;
|
||||
#endif
|
||||
}
|
||||
|
||||
return hitboxes_count;
|
||||
}
|
||||
|
||||
auto Player::hitboxes(PlayerHitboxes *hitboxes_out, bool create_pose) -> u32 {
|
||||
auto model = studio_model();
|
||||
assert(model);
|
||||
|
||||
return get_hitboxes_internal(this, model, hitboxes_out, create_pose);
|
||||
}
|
88
src/player.hh
Normal file
88
src/player.hh
Normal file
@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
#include "entity.hh"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
namespace sdk {
|
||||
struct PlayerHitboxes {
|
||||
math::Vector centre[128];
|
||||
|
||||
math::Vector min[128];
|
||||
math::Vector max[128];
|
||||
|
||||
#ifdef _DEBUG
|
||||
// These are used for debugging
|
||||
math::Vector origin[128];
|
||||
math::Vector rotation[128];
|
||||
|
||||
math::Vector raw_min[128];
|
||||
math::Vector raw_max[128];
|
||||
#endif
|
||||
};
|
||||
|
||||
class Player : public Entity {
|
||||
public:
|
||||
Player() = delete;
|
||||
|
||||
// helper functions
|
||||
static auto local() -> Player *;
|
||||
|
||||
auto world_space_centre() -> math::Vector &;
|
||||
|
||||
auto model_handle() -> const class ModelHandle *;
|
||||
auto studio_model() -> const class StudioModel *;
|
||||
|
||||
auto view_position() -> math::Vector;
|
||||
|
||||
auto hitboxes(PlayerHitboxes *hitboxes_out, bool create_pose) -> u32;
|
||||
|
||||
// netvars
|
||||
auto health() -> int &;
|
||||
|
||||
auto alive() -> bool;
|
||||
auto team() -> int;
|
||||
|
||||
auto cond() -> u32 &;
|
||||
|
||||
auto view_offset() -> math::Vector &;
|
||||
|
||||
auto tf_class() -> int;
|
||||
|
||||
auto tick_base() -> int;
|
||||
auto sim_time() -> float &;
|
||||
auto anim_time() -> float &;
|
||||
auto cycle() -> float &;
|
||||
auto sequence() -> int &;
|
||||
|
||||
auto fov_time() -> float;
|
||||
|
||||
auto render_origin() -> math::Vector &;
|
||||
|
||||
auto active_weapon() -> Weapon *;
|
||||
|
||||
// virtual functions
|
||||
auto origin() -> math::Vector &;
|
||||
auto set_origin(const math::Vector &v) -> void;
|
||||
|
||||
auto render_bounds() -> std::pair<math::Vector, math::Vector>;
|
||||
|
||||
auto angles() -> math::Vector &;
|
||||
auto set_angles(const math::Vector &v) -> void;
|
||||
|
||||
auto anim_layer(u32 index) -> class AnimationLayer &;
|
||||
auto anim_layer_count() -> u32;
|
||||
|
||||
auto update_client_side_animation() -> void;
|
||||
|
||||
auto invalidate_physics_recursive(u32 flags) -> void;
|
||||
|
||||
// These are relative to the origin
|
||||
auto collision_bounds() -> std::pair<math::Vector &, math::Vector &>;
|
||||
|
||||
auto bone_transforms(math::Matrix3x4 *hitboxes_out, u32 max_bones, u32 bone_mask, float current_time) -> bool;
|
||||
};
|
||||
} // namespace sdk
|
445
src/sdk.hh
Normal file
445
src/sdk.hh
Normal file
@ -0,0 +1,445 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.hh"
|
||||
#include "vfunc.hh"
|
||||
|
||||
#include "datatable.hh"
|
||||
#include "entity.hh"
|
||||
#include "interface.hh"
|
||||
#include "signature.hh"
|
||||
|
||||
#include "trace.hh"
|
||||
|
||||
namespace sdk {
|
||||
class UserCmd {
|
||||
public:
|
||||
virtual ~UserCmd(){};
|
||||
|
||||
int command_number;
|
||||
int tick_count;
|
||||
math::Vector viewangles;
|
||||
float forwardmove;
|
||||
float sidemove;
|
||||
float upmove;
|
||||
int buttons;
|
||||
u8 impulse;
|
||||
int weapon_select;
|
||||
int weapon_subtype;
|
||||
int random_seed;
|
||||
short mouse_dx;
|
||||
short mouse_dy;
|
||||
bool has_been_predicted;
|
||||
};
|
||||
|
||||
struct ClientClass {
|
||||
void * create_fn;
|
||||
void * create_event_fn; // Only called for event objects.
|
||||
const char * network_name;
|
||||
RecvTable * recv_table;
|
||||
ClientClass *next;
|
||||
int class_id; // Managed by the engine.
|
||||
};
|
||||
|
||||
class Client {
|
||||
public:
|
||||
Client() = delete;
|
||||
|
||||
auto get_all_classes() -> ClientClass * {
|
||||
return_virtual_func(get_all_classes, 8, 8, 8, 0);
|
||||
}
|
||||
};
|
||||
|
||||
class ClientMode {
|
||||
public:
|
||||
ClientMode() = delete;
|
||||
};
|
||||
|
||||
class NetChannel {
|
||||
public:
|
||||
enum class Flow {
|
||||
outgoing,
|
||||
incoming
|
||||
};
|
||||
|
||||
NetChannel() = delete;
|
||||
|
||||
auto get_sequence_number(sdk::NetChannel::Flow f) -> i32 {
|
||||
return_virtual_func(get_sequence_number, 16, 16, 16, 0, f);
|
||||
}
|
||||
|
||||
auto queued_packets() -> i32 & {
|
||||
static auto queued_packets_offset = []() {
|
||||
if constexpr (doghook_platform::windows()) {
|
||||
return *signature::find_pattern<u32 *>("engine", "83 BE ? ? ? ? ? 0F 9F C0 84 C0", 2);
|
||||
} else if constexpr (doghook_platform::linux()) {
|
||||
} else if constexpr (doghook_platform::osx()) {
|
||||
}
|
||||
}();
|
||||
|
||||
assert(queued_packets_offset);
|
||||
|
||||
auto &queued_packets = *reinterpret_cast<i32 *>(reinterpret_cast<u8 *>(this) + queued_packets_offset);
|
||||
|
||||
return queued_packets;
|
||||
}
|
||||
};
|
||||
|
||||
class Globals {
|
||||
public:
|
||||
float realtime;
|
||||
int framecount;
|
||||
|
||||
float absolute_frametime;
|
||||
float curtime;
|
||||
|
||||
float frametime;
|
||||
|
||||
int max_clients;
|
||||
|
||||
int tickcount;
|
||||
float interval_per_tick;
|
||||
|
||||
float interpolation_amount;
|
||||
int sim_ticks_this_frame;
|
||||
|
||||
int network_protocol;
|
||||
|
||||
class CSaveRestoreData *save_data;
|
||||
|
||||
bool is_client;
|
||||
};
|
||||
|
||||
class Engine {
|
||||
public:
|
||||
Engine() = delete;
|
||||
|
||||
auto last_timestamp() -> float {
|
||||
return_virtual_func(last_timestamp, 15, 15, 15, 0);
|
||||
}
|
||||
|
||||
auto time() -> float {
|
||||
return_virtual_func(time, 14, 14, 14, 0);
|
||||
}
|
||||
|
||||
auto in_game() -> bool {
|
||||
return_virtual_func(in_game, 26, 26, 26, 0);
|
||||
}
|
||||
|
||||
auto local_player_index() -> u32 {
|
||||
return_virtual_func(local_player_index, 12, 12, 12, 0);
|
||||
}
|
||||
|
||||
auto net_channel_info() -> NetChannel * {
|
||||
return_virtual_func(net_channel_info, 72, 72, 72, 0);
|
||||
}
|
||||
|
||||
auto is_box_visible(const math::Vector &min, const math::Vector &max) -> bool {
|
||||
return_virtual_func(is_box_visible, 31, 31, 31, 0, min, max);
|
||||
}
|
||||
|
||||
auto max_clients() -> u32 {
|
||||
return_virtual_func(max_clients, 21, 21, 21, 0);
|
||||
}
|
||||
|
||||
auto set_view_angles(const math::Vector &v) -> void {
|
||||
return_virtual_func(set_view_angles, 20, 20, 20, 0, v);
|
||||
}
|
||||
};
|
||||
|
||||
class EntList {
|
||||
public:
|
||||
EntList() = delete;
|
||||
|
||||
auto entity(u32 index) -> Entity * {
|
||||
return_virtual_func(entity, 3, 0, 0, 0, index);
|
||||
}
|
||||
|
||||
auto from_handle(EntityHandle h) -> Entity * {
|
||||
return_virtual_func(from_handle, 4, 4, 4, 0, h);
|
||||
}
|
||||
|
||||
auto max_entity_index() -> u32 {
|
||||
return_virtual_func(max_entity_index, 6, 0, 0, 0);
|
||||
}
|
||||
|
||||
class EntityRange {
|
||||
EntList *parent;
|
||||
|
||||
u32 max_entity;
|
||||
|
||||
public:
|
||||
class Iterator {
|
||||
u32 index;
|
||||
EntList *parent;
|
||||
|
||||
public:
|
||||
// TODO: should we use 1 here or should we use 0 and force people
|
||||
// that loop to deal with that themselves...
|
||||
Iterator(EntList *parent) : index(1), parent(parent) {}
|
||||
explicit Iterator(u32 index, EntList *parent)
|
||||
: index(index), parent(parent) {}
|
||||
|
||||
auto &operator++() {
|
||||
++index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator*() {
|
||||
return parent->entity(index);
|
||||
}
|
||||
|
||||
auto operator==(const Iterator &b) {
|
||||
return index == b.index;
|
||||
}
|
||||
|
||||
auto operator!=(const Iterator &b) {
|
||||
return !(*this == b);
|
||||
}
|
||||
};
|
||||
|
||||
EntityRange(EntList *parent) : parent(parent), max_entity(parent->max_entity_index()) {}
|
||||
|
||||
explicit EntityRange(EntList *parent, u32 max_entity) : parent(parent), max_entity(max_entity) {}
|
||||
|
||||
auto begin() { return Iterator(parent); }
|
||||
|
||||
auto end() { return Iterator(max_entity, parent); }
|
||||
};
|
||||
|
||||
auto get_range() { return EntityRange(this); }
|
||||
auto get_range(u32 max_entity) { return EntityRange(this, max_entity + 1); }
|
||||
|
||||
}; // namespace sdk
|
||||
|
||||
class Input {
|
||||
public:
|
||||
Input() = delete;
|
||||
|
||||
auto get_user_cmd(u32 sequence_number) -> UserCmd * {
|
||||
static auto array_offset = []() {
|
||||
if constexpr (doghook_platform::windows()) {
|
||||
return *signature::find_pattern<u32 *>("client", "8B 87 ? ? ? ? 8B CA", 2);
|
||||
} else if constexpr (doghook_platform::linux()) {
|
||||
} else if constexpr (doghook_platform::osx()) {
|
||||
}
|
||||
}();
|
||||
// this should not be 0
|
||||
assert(array_offset != 0);
|
||||
|
||||
auto cmd_array = *reinterpret_cast<UserCmd **>(reinterpret_cast<u8 *>(this) + array_offset);
|
||||
|
||||
return &cmd_array[sequence_number % 90];
|
||||
}
|
||||
|
||||
auto get_verified_user_cmd(u32 sequence_number) -> class VerifiedCmd * {
|
||||
// 03 B7 ? ? ? ? 8D 04 88
|
||||
}
|
||||
};
|
||||
|
||||
// defined in convar.cc
|
||||
class ConCommandBase;
|
||||
|
||||
class Cvar {
|
||||
public:
|
||||
Cvar() = delete;
|
||||
|
||||
auto allocate_dll_identifier() -> u32 {
|
||||
return_virtual_func(allocate_dll_identifier, 5, 5, 5, 0);
|
||||
}
|
||||
|
||||
auto register_command(ConCommandBase *command) -> void {
|
||||
return_virtual_func(register_command, 6, 6, 6, 0, command);
|
||||
}
|
||||
|
||||
auto unregister_command(ConCommandBase *command) -> void {
|
||||
return_virtual_func(unregister_command, 7, 7, 7, 0, command);
|
||||
}
|
||||
};
|
||||
|
||||
class Trace {
|
||||
public:
|
||||
Trace() = delete;
|
||||
|
||||
auto trace_ray(const trace::Ray &ray, u32 mask, trace::Filter *filter, trace::TraceResult *results) -> void {
|
||||
return_virtual_func(trace_ray, 4, 4, 4, 0, ray, mask, filter, results);
|
||||
}
|
||||
};
|
||||
|
||||
// These are defined to give us distinct types for these
|
||||
// very different objects, to help us differentiate between them
|
||||
|
||||
class ModelHandle; // model_t
|
||||
|
||||
// mstudiobbox
|
||||
class StudioHitbox {
|
||||
public:
|
||||
StudioHitbox() = delete;
|
||||
|
||||
int bone;
|
||||
int group;
|
||||
|
||||
math::Vector min;
|
||||
math::Vector max;
|
||||
|
||||
int name_index;
|
||||
|
||||
private:
|
||||
int unused[8];
|
||||
};
|
||||
|
||||
// mstudiobboxset
|
||||
class StudioHitboxSet {
|
||||
public:
|
||||
StudioHitboxSet() = delete;
|
||||
|
||||
int name_index;
|
||||
auto name() const -> const char * {
|
||||
assert(0);
|
||||
return "";
|
||||
}
|
||||
|
||||
u32 hitboxes_count;
|
||||
u32 hitbox_index;
|
||||
|
||||
const auto operator[](u32 index) const {
|
||||
assert(index < hitboxes_count);
|
||||
|
||||
return reinterpret_cast<const StudioHitbox *>(
|
||||
reinterpret_cast<const u8 *>(this) + hitbox_index) +
|
||||
index;
|
||||
}
|
||||
};
|
||||
|
||||
// studiohdr_t
|
||||
class StudioModel {
|
||||
public:
|
||||
StudioModel() = delete;
|
||||
|
||||
int id;
|
||||
int version;
|
||||
int checksum;
|
||||
char name[64];
|
||||
int length;
|
||||
|
||||
math::Vector eye_position; // ideal eye position
|
||||
|
||||
math::Vector illumination_position; // illumination center
|
||||
|
||||
math::Vector hull_min; // ideal movement hull size
|
||||
math::Vector hull_max;
|
||||
|
||||
math::Vector view_bbmin; // clipping bounding box
|
||||
math::Vector view_bbmax;
|
||||
|
||||
int flags;
|
||||
|
||||
int bones_count;
|
||||
int bone_index;
|
||||
|
||||
int bone_controllers_count;
|
||||
int bone_controller_index;
|
||||
|
||||
u32 hitbox_sets_count;
|
||||
u32 hitbox_set_index;
|
||||
|
||||
const auto hitbox_set(u32 index) const {
|
||||
assert(index < hitbox_sets_count);
|
||||
|
||||
return reinterpret_cast<const StudioHitboxSet *>(
|
||||
reinterpret_cast<const u8 *>(this) + hitbox_set_index) +
|
||||
index;
|
||||
}
|
||||
|
||||
//... TODO:
|
||||
};
|
||||
|
||||
class ModelInfo {
|
||||
public:
|
||||
ModelInfo() = delete;
|
||||
|
||||
auto model_name(const ModelHandle *m) -> const char * {
|
||||
return_virtual_func(model_name, 3, 0, 0, 0, m);
|
||||
}
|
||||
|
||||
auto studio_model(const ModelHandle *m) -> const StudioModel * {
|
||||
return_virtual_func(studio_model, 28, 0, 0, 0, m);
|
||||
}
|
||||
};
|
||||
|
||||
// This should only be used for debugging.
|
||||
// For real output use the overlay
|
||||
class DebugOverlay {
|
||||
public:
|
||||
using OverlayText_t = void;
|
||||
|
||||
virtual void add_entity_text_overlay(int ent_index, int line_offset, float duration, int r, int g, int b, int a, const char *format, ...) = 0;
|
||||
virtual void add_box_overlay(const math::Vector &origin, const math::Vector &mins, const math::Vector &max, const math ::Vector &orientation, int r, int g, int b, int a, float duration) = 0;
|
||||
virtual void add_triangle_overlay(const math::Vector &p1, const math::Vector &p2, const math::Vector &p3, int r, int g, int b, int a, bool no_depth_test, float duration) = 0;
|
||||
virtual void add_line_overlay(const math::Vector &origin, const math::Vector &dest, int r, int g, int b, bool no_depth_test, float duration) = 0;
|
||||
virtual void add_text_overlay(const math::Vector &origin, float duration, const char *format, ...) = 0;
|
||||
virtual void add_text_overlay(const math::Vector &origin, int line_offset, float duration, const char *format, ...) = 0;
|
||||
virtual void add_screen_text_overlay(float x_pos, float y_pos, float duration, int r, int g, int b, int a, const char *text) = 0;
|
||||
virtual void add_swept_box_overlay(const math::Vector &start, const math::Vector &end, const math::Vector &mins, const math::Vector &max, const math::Vector &angles, int r, int g, int b, int a, float duration) = 0;
|
||||
virtual void add_grid_overlay(const math::Vector &origin) = 0;
|
||||
virtual int screen_position(const math::Vector &point, math::Vector &screen) = 0;
|
||||
virtual int screen_position(float x_pos, float y_pos, math::Vector &screen) = 0;
|
||||
|
||||
virtual OverlayText_t *get_first(void) = 0;
|
||||
virtual OverlayText_t *get_next(OverlayText_t *current) = 0;
|
||||
virtual void clear_dead_overlays() = 0;
|
||||
virtual void clear_all_overlays() = 0;
|
||||
|
||||
virtual void add_text_overlay_rgb(const math::Vector &origin, int line_offset, float duration, float r, float g, float b, float alpha, const char *format, ...) = 0;
|
||||
virtual void add_text_overlay_rgb(const math::Vector &origin, int line_offset, float duration, int r, int g, int b, int a, const char *format, ...) = 0;
|
||||
|
||||
virtual void add_line_overlay_alpha(const math::Vector &origin, const math::Vector &dest, int r, int g, int b, int a, bool noDepthTest, float duration) = 0;
|
||||
//virtual void add_box_overlay2(const math::Vector &origin, const math::Vector &mins, const math::Vector &max, const math::Vector &orientation, const Color &faceColor, const Color &edgeColor, float duration) = 0;
|
||||
};
|
||||
|
||||
// This is from server.dll
|
||||
class PlayerInfoManager {
|
||||
public:
|
||||
auto globals() -> Globals * {
|
||||
return_virtual_func(globals, 1, 1, 1, 0);
|
||||
}
|
||||
};
|
||||
|
||||
class MoveHelper;
|
||||
|
||||
class Prediction {
|
||||
public:
|
||||
auto setup_move(Player *player, UserCmd *ucmd, MoveHelper *helper, void *move) -> void {
|
||||
return_virtual_func(setup_move, 18, 18, 18, 0, player, ucmd, helper, move);
|
||||
}
|
||||
void finish_move(Player *player, UserCmd *ucmd, void *move) {
|
||||
return_virtual_func(finish_move, 19, 19, 19, 0, player, ucmd, move);
|
||||
}
|
||||
};
|
||||
|
||||
class GameMovement {
|
||||
public:
|
||||
void process_movement(Player *player, void *move) {
|
||||
return_virtual_func(process_movement, 1, 1, 1, 0, player, move);
|
||||
}
|
||||
};
|
||||
|
||||
class AnimationLayer {
|
||||
public:
|
||||
int sequence;
|
||||
float prev_cycle;
|
||||
float weight;
|
||||
int order;
|
||||
|
||||
float playback_rate;
|
||||
float cycle;
|
||||
|
||||
float layer_anim_time;
|
||||
float layer_fade_time;
|
||||
|
||||
float blend_in;
|
||||
float blend_out;
|
||||
|
||||
bool client_blend;
|
||||
};
|
||||
|
||||
} // namespace sdk
|
@ -11,6 +11,8 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
using namespace signature;
|
||||
|
||||
static constexpr auto in_range(char x, char a, char b) {
|
||||
return (x >= a && x <= b);
|
||||
}
|
||||
@ -53,7 +55,7 @@ static u8 *find_pattern_internal(uptr start, uptr end, const char *pattern) {
|
||||
}
|
||||
|
||||
static std::pair<uptr, uptr> find_module_code_section(const char *module_name) {
|
||||
auto module = Signature::resolve_library(module_name);
|
||||
auto module = resolve_library(module_name);
|
||||
|
||||
#if doghook_platform_windows()
|
||||
auto module_addr = reinterpret_cast<uptr>(module);
|
||||
@ -113,7 +115,7 @@ static std::pair<uptr, uptr> find_module_code_section(const char *module_name) {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
auto Signature::resolve_library(const char *name) -> void * {
|
||||
auto signature::resolve_library(const char *name) -> void * {
|
||||
#if doghook_platform_windows()
|
||||
// TODO: actually check directories for this dll instead of
|
||||
// letting the loader do the work
|
||||
@ -162,7 +164,7 @@ auto Signature::resolve_library(const char *name) -> void * {
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
void *Signature::resolve_import(void *handle, const char *name) {
|
||||
void *signature::resolve_import(void *handle, const char *name) {
|
||||
#if doghook_platform_windows()
|
||||
return reinterpret_cast<void *>(GetProcAddress(static_cast<HMODULE>(handle), name));
|
||||
#elif doghook_platform_linux()
|
||||
@ -172,17 +174,17 @@ void *Signature::resolve_import(void *handle, const char *name) {
|
||||
#endif
|
||||
}
|
||||
|
||||
u8 *Signature::find_pattern(const char *module_name, const char *pattern) {
|
||||
u8 *signature::find_pattern(const char *module_name, const char *pattern) {
|
||||
auto code_section = find_module_code_section(module_name);
|
||||
|
||||
return find_pattern_internal(code_section.first, code_section.second, pattern);
|
||||
}
|
||||
|
||||
u8 *Signature::find_pattern(const char *pattern, uptr start, uptr length) {
|
||||
u8 *signature::find_pattern(const char *pattern, uptr start, uptr length) {
|
||||
return find_pattern_internal(start, start + length, pattern);
|
||||
}
|
||||
|
||||
void *Signature::resolve_callgate(void *address) {
|
||||
void *signature::resolve_callgate(void *address) {
|
||||
// TODO: are these the only instructions here?
|
||||
assert(reinterpret_cast<i8 *>(address)[0] == '\xE8' || reinterpret_cast<i8 *>(address)[0] == '\xE9');
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#include "platform.hh"
|
||||
|
||||
namespace Signature {
|
||||
namespace signature {
|
||||
void *resolve_library(const char *name);
|
||||
void *resolve_import(void *handle, const char *name);
|
||||
|
||||
@ -16,4 +16,4 @@ auto find_pattern(const char *module, const char *pattern, u32 offset) {
|
||||
return reinterpret_cast<T>(find_pattern(module, pattern) + offset);
|
||||
}
|
||||
void *resolve_callgate(void *address);
|
||||
} // namespace Signature
|
||||
} // namespace signature
|
||||
|
158
src/trace.hh
Normal file
158
src/trace.hh
Normal file
@ -0,0 +1,158 @@
|
||||
#pragma once
|
||||
|
||||
#include "platform.hh"
|
||||
|
||||
namespace trace {
|
||||
class VectorAligned : public math::Vector {
|
||||
float pad;
|
||||
|
||||
public:
|
||||
VectorAligned() {}
|
||||
|
||||
explicit VectorAligned(const math::Vector &other) {
|
||||
x = other.x;
|
||||
y = other.y;
|
||||
z = other.z;
|
||||
}
|
||||
|
||||
explicit VectorAligned(const math::Vector &&other) {
|
||||
x = other.x;
|
||||
y = other.y;
|
||||
z = other.z;
|
||||
}
|
||||
|
||||
auto operator=(const Vector &other) {
|
||||
x = other.x;
|
||||
y = other.y;
|
||||
z = other.z;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator=(const Vector &&other) {
|
||||
x = other.x;
|
||||
y = other.y;
|
||||
z = other.z;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator Vector() {
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct Plane {
|
||||
math::Vector normal;
|
||||
float dist;
|
||||
u8 type; // for fast side tests
|
||||
u8 signbits; // signx + (signy<<1) + (signz<<1)
|
||||
u8 pad[2];
|
||||
};
|
||||
|
||||
struct Model {
|
||||
math::Vector min, max;
|
||||
math::Vector origin; // for sounds or lights
|
||||
int head_node;
|
||||
|
||||
struct VCollide {
|
||||
unsigned short solidCount : 15;
|
||||
unsigned short isPacked : 1;
|
||||
unsigned short descSize;
|
||||
// VPhysicsSolids
|
||||
void **solids;
|
||||
char * pKeyValues;
|
||||
};
|
||||
|
||||
VCollide vcollision_data;
|
||||
};
|
||||
|
||||
struct Surface {
|
||||
const char * name;
|
||||
short surface_props;
|
||||
unsigned short flags;
|
||||
};
|
||||
|
||||
struct TraceResult {
|
||||
math::Vector start_pos; // start position
|
||||
math::Vector end_pos; // final position
|
||||
Plane plane; // surface normal at impact
|
||||
|
||||
float fraction; // time completed, 1.0 = didn't hit anything
|
||||
|
||||
int contents; // contents on other side of surface hit
|
||||
unsigned short disp_flags; // displacement flags for marking surfaces with data
|
||||
|
||||
bool all_solid; // if true, plane is not valid
|
||||
bool start_solid; // if true, the initial point was in a solid area
|
||||
|
||||
float fraction_left_solid; // time we left a solid, only valid if we started in solid
|
||||
Surface surface; // surface hit (impact surface)
|
||||
|
||||
int hitgroup; // 0 == generic, non-zero is specific body part
|
||||
short physicsbone; // physics bone hit by trace in studio
|
||||
|
||||
sdk::Entity *entity;
|
||||
|
||||
// NOTE: this member is overloaded.
|
||||
// If hEnt points at the world entity, then this is the static prop index.
|
||||
// Otherwise, this is the hitbox index.
|
||||
int hitbox; // box hit by trace in studio
|
||||
};
|
||||
|
||||
class Ray {
|
||||
public:
|
||||
VectorAligned start;
|
||||
VectorAligned delta;
|
||||
VectorAligned start_offset;
|
||||
VectorAligned extents;
|
||||
bool is_ray;
|
||||
bool is_swept;
|
||||
|
||||
// init for point ray
|
||||
auto init(const math::Vector &start, const math::Vector &end) {
|
||||
delta = end - start;
|
||||
is_swept = (delta.length_sqr() != 0);
|
||||
is_ray = true;
|
||||
|
||||
extents = math::Vector::zero();
|
||||
start_offset = math::Vector::zero();
|
||||
|
||||
this->start = start;
|
||||
}
|
||||
|
||||
// init for box ray
|
||||
auto init(const math::Vector &start, const math::Vector &end, const math::Vector &min, const math::Vector &max) {
|
||||
delta = end - start;
|
||||
|
||||
is_swept = (delta.length_sqr() != 0);
|
||||
|
||||
extents = max - min;
|
||||
extents *= 0.5;
|
||||
is_ray = false;
|
||||
|
||||
start_offset = min + max;
|
||||
start_offset *= 0.5f;
|
||||
|
||||
this->start = start_offset + start;
|
||||
start_offset *= -1.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class Filter {
|
||||
sdk::Entity *ignore_self;
|
||||
sdk::Entity *ignore_entity;
|
||||
|
||||
public:
|
||||
virtual auto should_hit_entity(sdk::Entity *handle_entity, int contents_mask) -> bool;
|
||||
virtual auto GetTraceType() const -> u32 {
|
||||
return 0; // hit everything
|
||||
}
|
||||
|
||||
Filter() : ignore_self(nullptr), ignore_entity(nullptr) {}
|
||||
|
||||
Filter(sdk::Entity *ignore_self) : ignore_self(ignore_self) {}
|
||||
Filter(sdk::Entity *ignore_self, sdk::Entity *ignore_entity) : ignore_self(ignore_self), ignore_entity(ignore_entity) {}
|
||||
};
|
||||
|
||||
} // namespace trace
|
14
src/vfunc.hh
14
src/vfunc.hh
@ -7,7 +7,7 @@
|
||||
#include "types.hh"
|
||||
|
||||
// helpers for calling virtual functions
|
||||
namespace VFunc {
|
||||
namespace vfunc {
|
||||
inline auto get_table(void *inst, u32 offset) -> void ** {
|
||||
return *reinterpret_cast<void ***>(reinterpret_cast<u8 *>(inst) + offset);
|
||||
}
|
||||
@ -54,15 +54,15 @@ public:
|
||||
//assert(instance != nullptr);
|
||||
|
||||
auto index = 0u;
|
||||
if constexpr (BluePlatform::windows())
|
||||
if constexpr (doghook_platform::windows())
|
||||
index = index_windows;
|
||||
else if constexpr (BluePlatform::linux())
|
||||
else if constexpr (doghook_platform::linux())
|
||||
index = index_linux;
|
||||
else if constexpr (BluePlatform::osx())
|
||||
else if constexpr (doghook_platform::osx())
|
||||
index = index_osx;
|
||||
|
||||
auto offset = 0u;
|
||||
if constexpr (BluePlatform::windows()) {
|
||||
if constexpr (doghook_platform::windows()) {
|
||||
offset = offset_windows;
|
||||
|
||||
this->instance = reinterpret_cast<ObjectType *>(
|
||||
@ -79,7 +79,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace VFunc
|
||||
} // namespace vfunc
|
||||
|
||||
// macro for easier definitions of wrapper calls
|
||||
// name is the name of the function
|
||||
@ -88,4 +88,4 @@ public:
|
||||
#define return_virtual_func(name, windows, linux, osx, off, ...) \
|
||||
using c = std::remove_reference<decltype(*this)>::type; \
|
||||
using t = decltype(&c::name); \
|
||||
return VFunc::Func<t>(this, windows, linux, osx, off).invoke(__VA_ARGS__)
|
||||
return vfunc::Func<t>(this, windows, linux, osx, off).invoke(__VA_ARGS__)
|
||||
|
27
src/weapon.cc
Normal file
27
src/weapon.cc
Normal file
@ -0,0 +1,27 @@
|
||||
#include "stdafx.hh"
|
||||
|
||||
#include "interface.hh"
|
||||
#include "netvar.hh"
|
||||
#include "sdk.hh"
|
||||
#include "weapon.hh"
|
||||
|
||||
using namespace sdk;
|
||||
|
||||
auto next_primary_attack = Netvar("DT_BaseCombatWeapon", "LocalActiveWeaponData", "m_flNextPrimaryAttack");
|
||||
auto Weapon::next_primary_attack() -> float {
|
||||
return ::next_primary_attack.get<float>(this);
|
||||
}
|
||||
|
||||
auto next_secondary_attack = Netvar("DT_BaseCombatWeapon", "LocalActiveWeaponData", "m_flNextSecondaryAttack");
|
||||
auto Weapon::next_secondary_attack() -> float {
|
||||
return ::next_secondary_attack.get<float>(this);
|
||||
}
|
||||
|
||||
auto Weapon::can_shoot(u32 tickbase) -> bool {
|
||||
return tickbase * IFace<Globals>()->interval_per_tick > next_primary_attack();
|
||||
}
|
||||
|
||||
auto Weapon::owner() -> Entity * {
|
||||
// TODO:
|
||||
return nullptr;
|
||||
}
|
15
src/weapon.hh
Normal file
15
src/weapon.hh
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "entity.hh"
|
||||
|
||||
namespace sdk {
|
||||
class Weapon : public Entity {
|
||||
public:
|
||||
auto can_shoot(u32 tickbase) -> bool;
|
||||
|
||||
auto next_primary_attack() -> float;
|
||||
auto next_secondary_attack() -> float;
|
||||
|
||||
auto owner() -> Entity *;
|
||||
};
|
||||
} // namespace sdk
|
Reference in New Issue
Block a user