Fix all issues related to running doghook on linux (#21)

* add more linux signatures

* Update premake script to generate correct symbols on respective platforms

* Add debugger gdb for linux builds

* Change from -ggdb to -gdwarf

* change from -gdward to -g3

* Update linux signatures

* Fix issues with virtual function calling

* Fix a bunch of crashes.

* Update prediction vfunc indexes

* Update more indexes

* No more crashes!

* Fix usercmd padding

* Fix more index issues

* Change attach scripts to reflect what they do

* Fix windows compilation issues - remove hardcoded path from attach-basic

* Update attach script
This commit is contained in:
F1ssi0N 2018-03-18 19:24:49 +00:00 committed by GitHub
parent 5357480f24
commit 7d54105fec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 322 additions and 48 deletions

61
attach-basic.sh Executable file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env bash
# cwd into where this script is located
line=$(pidof hl2_linux)
arr=($line)
inst=$1
if [ $# == 0 ]; then
inst=0
fi
if [ ${#arr[@]} == 0 ]; then
echo TF2 isn\'t running!
exit
fi
if [ $inst -gt ${#arr[@]} ] || [ $inst == ${#arr[@]} ]; then
echo wrong index!
exit
fi
proc=${arr[$inst]}
echo Running instances: "${arr[@]}"
echo Attaching to "$proc"
#sudo ./detach $inst bin/libcathook.so
#if grep -q "$(realpath bin/libcathook.so)" /proc/"$proc"/maps; then
# echo already loaded
# exit
#fi
# pBypass for crash dumps being sent
# You may also want to consider using -nobreakpad in your launch options.
sudo rm -rf /tmp/dumps # Remove if it exists
sudo mkdir /tmp/dumps # Make it as root
sudo chmod 000 /tmp/dumps # No permissions
#FILENAME="/tmp/.gl$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 6 | head -n 1)"
#cp "bin/Debug/libdoghook.so" "$FILENAME"
FILENAME=$(readlink -f "${0%/*}/bin/Debug/libdoghook.so")
echo loading "$FILENAME" to "$proc"
sudo killall -19 steam
sudo killall -19 steamwebhelper
sudo gdb -n -q -batch \
-ex "attach $proc" \
-ex "set \$dlopen = (void*(*)(char*, int)) dlopen" \
-ex "call \$dlopen(\"$FILENAME\", 1)" \
-ex "call dlerror()" \
-ex 'print (char *) $2' \
-ex "detach" \
-ex "quit"
#rm $FILENAME
sudo killall -18 steamwebhelper
sudo killall -18 steam

105
attach-interactive.sh Executable file
View File

@ -0,0 +1,105 @@
#!/usr/bin/env bash
pid=$(pidof hl2_linux) # Default to TF2
if [[ -n "$1" ]]; then
if [ "$1" -eq "$1" ] 2>/dev/null; then
# First argument is a int (PID)
pid=$1
else
# First argument is a string (Process ID)
pid=$(pidof $1)
fi
fi
if [ -z "$pid" ]; then
echo -e "\e[91mCould not find target process.\e[39m"
exit -1
fi
# Get the full path of the .so that we'll be faking
victim_lib=$(cat /proc/${pid}/maps | grep ".so" | shuf -n 1)
echo $victim_lib
victim_lib=${victim_lib##* }
# Some magic that I copied straight from StackOverflow
victim_lib_array=(${victim_lib//./ })
number_to_spoof=${victim_lib_array[-1]}
library_path=$(IFS=. ; echo "${victim_lib_array[*]}")
if [ "$number_to_spoof" == "so" ]; then
# If the shared library doesn't have multiple versions, let's just append .0 at the end.
number_to_spoof="$number_to_spoof.0"
else
# else we'll increase the found version by one
number_to_spoof=$(($number_to_spoof + 1))
fi
victim_lib_array[-1]="$number_to_spoof"
library_path=$(IFS=. ; echo "${victim_lib_array[*]}")
if [ -e "$library_path" ]; then
echo -e "\e[91mA error occurred. Please retry.\e[39m"
exit -1
else
sudo cp "bin/Debug/libdoghook.so" "$library_path"
# Get the file name of the resulting library
result_lib_name=$(basename ${library_path})
result_lib="${result_lib_name%.*}"
# and patch the soname
sudo patchelf --set-soname "$library_path" "$result_lib"
fi
echo "Attaching as $result_lib ($library_path) to PID $pid"
# Allows only root to use ptrace. This is temporary until the user reboots the machine.
ptrace_input=$(sudo echo "2" | sudo tee /proc/sys/kernel/yama/ptrace_scope)
# Prevent crash dumps from being sent to kisak
sudo rm -rf /tmp/dumps
sudo mkdir /tmp/dumps
sudo chmod 000 /tmp/dumps
# Pause Steam
sudo killall -19 steam
sudo killall -19 steamwebhelper
# Uses dlmopen instead of normal dlopen - Credit to LWSS
sudo gdb -n \
-ex "set logging on" \
-ex "set logging file /dev/null" \
-ex "attach $pid" \
-ex "set \$linkMapID = (long int)0" \
-ex "set \$dlopen = (void*(*)(char*, int)) dlopen" \
-ex "set \$dlmopen = (void*(*)(long int, char*, int)) dlmopen" \
-ex "set \$dlinfo = (int (*)(void*, int, void*)) dlinfo" \
-ex "set \$malloc = (void*(*)(long long)) malloc" \
-ex "set \$target = \$dlopen(\"$library_path\", 2)" \
-ex "p \$target" \
-ex "p \$linkMapID" \
-ex "call \$dlmopen(0, \"$library_path\", 1)" \
-ex "set \$error = call dlerror()" \
-ex "x/s \$error"
# Resume Steam
sleep 1
sudo killall -18 steamwebhelper
sudo killall -18 steam
sudo rm -rf "$library_path"
if grep -q "$library_path" /proc/${pid}/maps; then
echo "Successfully attached to PID $pid."
else
echo -e "\e[91mA error occurred. See gdb.txt\e[39m"
exit -1
fi
if [ -f "$(pwd)/gdb.txt" ]; then
sudo rm -rf gdb.txt
fi

View File

@ -70,7 +70,7 @@ sudo killall -19 steam
sudo killall -19 steamwebhelper
# Uses dlmopen instead of normal dlopen - Credit to LWSS
sudo gdb -n \
sudo gdb -n -batch \
-ex "set logging on" \
-ex "set logging file /dev/null" \
-ex "attach $pid" \
@ -85,7 +85,11 @@ sudo gdb -n \
-ex "call \$dlmopen(0, \"$library_path\", 1)" \
-ex "set \$error = call dlerror()" \
-ex "x/s \$error" \
-ex "catch syscall exit exit_group"
-ex "catch syscall exit exit_group" \
-ex "continue" \
-ex "backtrace" \
-ex "detach" \
-ex "quit"
# Resume Steam
sleep 1

View File

@ -25,7 +25,13 @@ workspace "doghook"
filter "configurations:Debug"
defines { "DEBUG", "_DEBUG" }
optimize "Off"
symbols "Full"
filter "system:windows"
symbols "Full"
filter "system:linux"
symbols "On"
buildoptions "-g3"
filter ""
runtime "Debug"
filter "configurations:Release"

View File

@ -1,5 +1,7 @@
#include "precompiled.hh"
#include "doghook.hh"
#include "sdk/gamesystem.hh"
#include "sdk/log.hh"
@ -14,6 +16,8 @@
#include "hooks/createmove.hh"
#include "sdk/signature.hh"
// Singleton for doing init / deinit of doghook
// and dealing with hooks from gamesystem
@ -39,14 +43,33 @@ public:
IFace<sdk::Client>().set_from_interface("client", "VClient");
IFace<sdk::Engine>().set_from_interface("engine", "VEngineClient");
IFace<sdk::EntList>().set_from_interface("client", "VClientEntityList");
IFace<sdk::Input>().set_from_pointer(**reinterpret_cast<sdk::Input ***>(
vfunc::get_func<u8 *>(IFace<sdk::Client>().get(), 15, 0) + 0x2));
if constexpr (doghook_platform::windows())
IFace<sdk::Input>().set_from_pointer(**reinterpret_cast<sdk::Input ***>(
vfunc::get_func<u8 *>(IFace<sdk::Client>().get(), 15, 0) + 0x2));
else if constexpr (doghook_platform::linux())
IFace<sdk::Input>().set_from_pointer(**reinterpret_cast<sdk::Input ***>(
vfunc::get_func<u8 *>(IFace<sdk::Client>().get(), 15, 0) + 0x1));
IFace<sdk::Cvar>().set_from_interface("vstdlib", "VEngineCvar");
// TODO linux signatures
IFace<sdk::ClientMode>().set_from_pointer(
*signature::find_pattern<sdk::ClientMode **>(
"client", "B9 ? ? ? ? A3 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? E8 ? ? ? ? 83 C4 04 C7 05", 1));
if constexpr (doghook_platform::windows())
IFace<sdk::ClientMode>().set_from_pointer(
*signature::find_pattern<sdk::ClientMode **>(
"client", "B9 ? ? ? ? A3 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? E8 ? ? ? ? 83 C4 04 C7 05", 1));
else if constexpr (doghook_platform::linux()) {
// ClientMode is a magic static. So getting a sig for it is difficult (conflicts with all other magic statics)
// So we are going to do some multistage shit in order to retrieve it
auto outer_function = signature::find_pattern<void *>("client", "55 89 E5 83 EC 18 E8 ? ? ? ? A3 ? ? ? ? E8", 6);
assert(outer_function);
auto inner_function = static_cast<u8 *>(signature::resolve_callgate(outer_function));
assert(outer_function);
IFace<sdk::ClientMode>().set_from_pointer(*reinterpret_cast<sdk::ClientMode **>(inner_function + 10));
assert(IFace<sdk::ClientMode>().get());
}
IFace<sdk::ModelInfo>().set_from_interface("engine", "VModelInfoClient");
IFace<sdk::Trace>().set_from_interface("engine", "EngineTraceClient");
IFace<sdk::DebugOverlay>().set_from_interface("engine", "VDebugOverlay");
@ -59,17 +82,32 @@ public:
// TODO: this globals_real_address is windows only!
if constexpr (doghook_platform::windows()) {
auto globals_real_address = (u32)*signature::find_pattern<sdk::Globals **>("engine", "A1 ? ? ? ? 8B 11 68", 8);
IFace<sdk::Globals>().set_from_pointer((sdk::Globals *)globals_real_address);
} else if constexpr (doghook_platform::linux()) {
auto globals_real_address = (u32) * *signature::find_pattern<sdk::Globals ***>("client", "8B 15 ? ? ? ? F3 0F 10 88 D0 08 00 00", 2);
IFace<sdk::Globals>().set_from_pointer((sdk::Globals *)globals_real_address);
}
IFace<sdk::GameMovement>().set_from_interface("client", "GameMovement");
IFace<sdk::Prediction>().set_from_interface("client", "VClientPrediction");
IFace<sdk::MoveHelper>().set_from_pointer(
*signature::find_pattern<sdk::MoveHelper **>(
"client", "8B 0D ? ? ? ? 8B 01 FF 50 28 56", 2));
if constexpr (doghook_platform::windows())
IFace<sdk::MoveHelper>().set_from_pointer(
*signature::find_pattern<sdk::MoveHelper **>(
"client", "8B 0D ? ? ? ? 8B 01 FF 50 28 56", 2));
else if constexpr (doghook_platform::linux())
IFace<sdk::MoveHelper>().set_from_pointer(nullptr);
inited = true;
}
void process_attach() {
// Wait for init...
while (!inited) {
std::this_thread::yield();
}
Log::msg("process_attach()");
// make sure that the netvars are initialised
@ -130,4 +168,8 @@ auto __stdcall doghook_process_attach([[maybe_unused]] void *hmodule) -> u32 {
return 0;
}
#elif doghook_platform_linux()
void doghook_process_attach() {
doghook.process_attach();
}
#endif

1
src/doghook.hh Normal file
View File

@ -0,0 +1 @@
#pragma once

View File

@ -8,6 +8,8 @@
#include "modules/aimbot.hh"
#include "utils/math.hh"
using namespace sdk;
namespace create_move {
@ -28,18 +30,28 @@ static inline auto local_player_prediction(Player *local, UserCmd *cmd) {
IFace<Globals>()->tickcount = local->tick_base();
// Set the current usercmd and run prediction
local->set<UserCmd *, 0x107C>(cmd);
if constexpr (doghook_platform::windows())
local->set<UserCmd *, 0x107C>(cmd);
else if constexpr (doghook_platform::linux())
local->set<UserCmd *, 0x105C>(cmd);
IFace<Prediction>()->setup_move(local, cmd, IFace<MoveHelper>().get(), move_data_buffer);
IFace<GameMovement>()->process_movement(local, move_data_buffer);
IFace<Prediction>()->finish_move(local, cmd, move_data_buffer);
local->set<UserCmd *, 0x107C>(0);
if constexpr (doghook_platform::windows())
local->set<UserCmd *, 0x107C>(0);
else if constexpr (doghook_platform::linux())
local->set<UserCmd *, 0x105C>(0);
// Cleanup from prediction
IFace<Globals>()->curtime = old_cur_time;
IFace<Globals>()->frametime = old_frame_time;
IFace<Globals>()->tickcount = old_tick_count;
// TODO: if you do this then make sure to change the fov time calculation
// in aimbot::try_autoshoot!!
//local->tick_base() += 1;
}
hooks::HookFunction<ClientMode, 0> *create_move_hook = nullptr;
@ -51,14 +63,13 @@ bool hooked_create_move(void *instance, float sample_framerate, UserCmd *user_cm
#endif
{
auto local_player = Player::local();
if (local_player == nullptr) return true;
assert(local_player);
// TODO: call original
// Currently possible but the method is slow
// Fixme in HookFunction!
// Do create_move_pre_predict()
aimbot::create_move_pre_predict(user_cmd);
local_player_prediction(local_player, user_cmd);
@ -74,7 +85,7 @@ void level_init() {
Log::msg("=> Hooking up!");
assert(create_move_hook == nullptr);
create_move_hook = new hooks::HookFunction<ClientMode, 0>(IFace<ClientMode>().get(), 21, reinterpret_cast<void *>(&hooked_create_move));
create_move_hook = new hooks::HookFunction<ClientMode, 0>(IFace<ClientMode>().get(), 21, 22, 22, reinterpret_cast<void *>(&hooked_create_move));
}
void level_shutdown() {

View File

@ -1,5 +1,7 @@
#include "precompiled.hh"
#include <thread>
#if doghook_platform_windows()
extern u32 __stdcall doghook_process_attach(void *a);
@ -18,7 +20,11 @@ __declspec(dllexport) BOOL APIENTRY DllMain(HMODULE hModule,
#else
// TODO: send process attach over to gamesystem
void __attribute__((constructor)) startup() {}
extern void doghook_process_attach();
void __attribute__((constructor)) startup() {
std::thread{&doghook_process_attach}.detach();
}
void __attribute__((destructor)) shutdown() {}
#endif

View File

@ -241,7 +241,7 @@ auto find_targets() {
if (e->dormant()) continue;
if (valid_target(e)) {
auto pos = math::Vector::invalid();
auto pos = math::Vector::invalid();
if (visible_target(e, pos)) {
finished_target(Target{e, pos});
}
@ -301,7 +301,7 @@ static Convar<bool> doghook_aimbot_aim_if_not_attack = Convar<bool>{"
static Convar<bool> doghook_aimbot_disallow_attack_if_no_target = Convar<bool>{"doghook_aimbot_disallow_attack_if_no_target", false, nullptr};
void create_move(sdk::UserCmd *cmd) {
if (local_weapon == nullptr) return;
if (local_weapon == nullptr || !can_find_targets) return;
find_targets();
@ -341,6 +341,11 @@ void create_move_pre_predict(sdk::UserCmd *cmd) {
local_player = Player::local();
assert(local_player);
if (local_player->alive() == false) {
can_find_targets = false;
return;
}
local_weapon = local_player->active_weapon()->to_weapon();
local_team = local_player->team();

View File

@ -10,5 +10,6 @@
#include <algorithm>
#include <array>
#include <thread>
#include <utility>
#include <vector>

View File

@ -9,6 +9,9 @@
// implementation of a source convar
namespace sdk {
// Helper to prevent crashes if Cvar isnt initied properly yet
bool can_init_convars_at_construction_time = false;
class IConVar;
using ChangeCallbackFn = void (*)(IConVar *, const char *, float);
@ -33,7 +36,9 @@ public:
virtual const char *get_name() const { return name; }
virtual const char *get_help_text() const { return help_string; }
virtual bool is_registered() const { return registered; }
virtual bool is_registered() const {
return registered;
}
virtual int get_dll_identifier() const {
if (dll_identifier == -1) dll_identifier = IFace<Cvar>()->allocate_dll_identifier();
@ -55,7 +60,7 @@ public:
// We might not have Cvar here (global variables)
if (auto cvar = IFace<Cvar>()) {
if (auto cvar = IFace<Cvar>() && can_init_convars_at_construction_time) {
IFace<Cvar>()->register_command(this);
}
}
@ -158,12 +163,19 @@ public:
static auto to_iconvar(ConCommandBase *b) { return reinterpret_cast<IConVar *>(reinterpret_cast<u8 *>(b) + 24); }
auto to_iconvar() -> IConVar * { return ConCommandBase::to_iconvar(this); }
#if doghook_platform_windows()
#define DEFINE_THUNK(type, name, real_name) \
static void __fastcall name(IConVar *ecx, void *edx, type v) { \
auto *real = ConCommandBase::from_iconvar(ecx); \
real->real_name(v); \
}
#elif doghook_platform_linux()
#define DEFINE_THUNK(type, name, real_name) \
static void name(IConVar *ecx, type v) { \
auto *real = ConCommandBase::from_iconvar(ecx); \
real->real_name(v); \
}
#endif
DEFINE_THUNK(const char *, set_value_string_thunk, set_value);
DEFINE_THUNK(float, set_value_float_thunk, set_value);
DEFINE_THUNK(int, set_value_int_thunk, set_value);
@ -233,7 +245,7 @@ public:
// Convar is an mi class and therefore needs this
// TODO: is this windows only - what is the linux equivilent??
void *convar_vtable;
void *convar_vtable; // Should be at 0x24
// Convar members
ConCommandBase *parent;
@ -317,6 +329,8 @@ ConvarBase::~ConvarBase() {
void ConvarBase::init_all() {
assert(IFace<sdk::Cvar>());
sdk::can_init_convars_at_construction_time = true;
// We have to do this goofy loop here as register_command() will
// change the `next` pointer to the next in its chain
// which will cause all kinds of problems for us

View File

@ -11,7 +11,7 @@
using namespace sdk;
auto Entity::to_handle() -> EntityHandle & {
return_virtual_func(to_handle, 2, 0, 0, 0);
return_virtual_func(to_handle, 2, 3, 3, 0);
}
auto Entity::is_valid() -> bool {
@ -37,13 +37,13 @@ auto Entity::to_weapon() -> class Weapon * {
}
auto Entity::client_class() -> struct ClientClass * {
return_virtual_func(client_class, 2, 0, 0, 8);
return_virtual_func(client_class, 2, 17, 17, 8);
}
auto Entity::dormant() -> bool {
return_virtual_func(dormant, 8, 0, 0, 8);
return_virtual_func(dormant, 8, 75, 75, 8);
}
auto Entity::index() -> u32 {
return_virtual_func(index, 9, 0, 0, 8);
return_virtual_func(index, 9, 79, 79, 8);
}

View File

@ -7,6 +7,8 @@
#include <utility>
#include <vector>
// TODO: in all of these cases shouldnt offset be in terms of bytes instead of dwords???
namespace hooks {
template <typename T, u32 offset>
class HookInstance {
@ -42,7 +44,6 @@ class HookInstance {
public:
HookInstance(T *instance) : instance(instance) {
assert(instance);
original_table = get_table();
@ -127,7 +128,7 @@ class HookFunction {
u32 index;
public:
HookFunction(T *instance, u32 index, void *f) {
HookFunction(T *instance, u32 index_windows, u32 index_linux, u32 index_osx, void *f) {
assert(instance);
HookInstance<T, offset> *val = nullptr;
@ -138,6 +139,13 @@ public:
}
}
// TODO: osx
u32 index;
if constexpr (doghook_platform::windows())
index = index_windows;
else if constexpr (doghook_platform::linux())
index = index_linux;
if (val == nullptr) {
// we havent hooked this instance yet, so do that now
create_hook_instance(instance)->hook_function(index, f);

View File

@ -39,7 +39,7 @@ public:
};
template <typename T>
T *Interface<T>::value;
T *Interface<T>::value = nullptr;
template <typename T>
using IFace = Interface<T>;

View File

@ -25,7 +25,7 @@ auto Player::alive() -> bool {
}
auto Player::origin() -> math::Vector & {
return_virtual_func(origin, 9, 0, 0, 0);
return_virtual_func(origin, 9, 11, 11, 0);
}
auto Player::set_origin(const math::Vector &v) -> void {
@ -39,7 +39,7 @@ auto Player::set_origin(const math::Vector &v) -> void {
}
auto Player::angles() -> math::Vector & {
return_virtual_func(angles, 10, 0, 0, 0);
return_virtual_func(angles, 10, 12, 12, 0);
}
auto Player::set_angles(const math::Vector &v) -> void {
@ -63,7 +63,7 @@ auto Player::cond() -> u32 & {
}
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);
auto func = vfunc::Func<void (Player::*)(math::Vector &, math::Vector &)>(this, 20, 60, 60, 4);
std::pair<math::Vector, math::Vector> ret;
@ -98,7 +98,7 @@ auto Player::tf_class() -> int {
}
static auto tick_base = Netvar("DT_BasePlayer", "localdata", "m_nTickBase");
auto Player::tick_base() -> int {
auto Player::tick_base() -> int & {
return ::tick_base.get<int>(this);
}
@ -189,11 +189,11 @@ auto Player::view_position() -> math::Vector {
}
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);
return_virtual_func(bone_transforms, 16, 96, 96, 4, hitboxes_out, max_bones, bone_mask, current_time);
}
auto Player::model_handle() -> const ModelHandle * {
return_virtual_func(model_handle, 9, 0, 0, 4);
return_virtual_func(model_handle, 9, 55, 55, 4);
}
auto Player::studio_model() -> const StudioModel * {

View File

@ -52,7 +52,7 @@ public:
auto tf_class() -> int;
auto tick_base() -> int;
auto tick_base() -> int &;
auto sim_time() -> float &;
auto anim_time() -> float &;
auto cycle() -> float &;

View File

@ -154,15 +154,19 @@ public:
EntList() = delete;
auto entity(u32 index) -> Entity * {
return_virtual_func(entity, 3, 0, 0, 0, index);
return_virtual_func(entity, 3, 3, 3, 0, index);
}
auto from_handle(EntityHandle h) -> Entity * {
return_virtual_func(from_handle, 4, 4, 4, 0, h);
if constexpr (doghook_platform::windows()) {
return_virtual_func(from_handle, 4, 4, 4, 0, h);
} else if constexpr (doghook_platform::linux())
return entity(h.serial_index & 0xFFF);
}
auto max_entity_index() -> u32 {
return_virtual_func(max_entity_index, 6, 0, 0, 0);
return_virtual_func(max_entity_index, 6, 6, 6, 0);
}
class EntityRange {
@ -365,11 +369,11 @@ public:
ModelInfo() = delete;
auto model_name(const ModelHandle *m) -> const char * {
return_virtual_func(model_name, 3, 0, 0, 0, m);
return_virtual_func(model_name, 3, 4, 4, 0, m);
}
auto studio_model(const ModelHandle *m) -> const StudioModel * {
return_virtual_func(studio_model, 28, 0, 0, 0, m);
return_virtual_func(studio_model, 28, 29, 29, 0, m);
}
};
@ -416,17 +420,17 @@ 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);
return_virtual_func(setup_move, 18, 19, 19, 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);
return_virtual_func(finish_move, 19, 20, 20, 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);
return_virtual_func(process_movement, 1, 2, 2, 0, player, move);
}
};

View File

@ -35,7 +35,11 @@ class Func;
template <typename ObjectType, typename Ret, typename... Args>
class Func<Ret (ObjectType::*)(Args...)> {
#if doghook_platform_windows()
using function_type = Ret(__thiscall *)(ObjectType *, Args...);
#elif doghook_platform_linux()
using function_type = Ret (*)(ObjectType *, Args...);
#endif
function_type f;
// On windows this will take into account the offset

View File

@ -140,12 +140,12 @@ public:
else
pitch = 90;
} else {
yaw = (atan2(y, x) * 180 / PI);
yaw = (to_degrees(atan2(y, x)));
if (yaw < 0)
yaw += 360;
tmp = sqrt(x * x + y * y);
pitch = (atan2(-z, tmp) * 180 / PI);
pitch = (to_degrees(atan2(-z, tmp)));
if (pitch < 0)
pitch += 360;
}
@ -350,3 +350,5 @@ inline void matrix_angles(const Matrix3x4 &matrix, Vector &angles, Vector &posit
}
} // namespace math
#define Vector_split_components(v) (v).x, (v).y, (v).z