Add datatable, netvar, sdk, trace, entity, player, weapon classes

This commit is contained in:
F1ssi0N 2018-03-13 17:24:06 +00:00
parent e21469f7b1
commit 1f57477ac7
19 changed files with 1359 additions and 31 deletions

103
src/datatable.hh Normal file
View 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
View 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
View 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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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
View 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
View 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

View File

@ -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
View 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
View 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
View 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

View File

@ -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');

View File

@ -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
View 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

View File

@ -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
View 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
View 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