mirror of
https://github.com/diamante0018/BlackOpsPlugin.git
synced 2025-04-19 18:12:54 +00:00
Fix crash
This commit is contained in:
parent
2793afac0e
commit
d20cdfbb41
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -7,3 +7,6 @@
|
||||
[submodule "deps/rapidjson"]
|
||||
path = deps/rapidjson
|
||||
url = https://github.com/Tencent/rapidjson.git
|
||||
[submodule "deps/asmjit"]
|
||||
path = deps/asmjit
|
||||
url = https://github.com/asmjit/asmjit.git
|
||||
|
@ -1,7 +1,7 @@
|
||||
[](https://github.com/diamante0018/BlackOpsPlugin/actions)
|
||||
|
||||
# Black-Ops-Plugin
|
||||
I have no clue as to what this does. I use this program to test experimental features.
|
||||
I use this program to test experimental features. Some may end up in the plutonium client. Some may not.
|
||||
|
||||
## Thanks to
|
||||
* [momo5502](https://github.com/momo5502) - Core component loader & utils
|
||||
|
1
deps/asmjit
vendored
Submodule
1
deps/asmjit
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit a4cb51b532af0f8137c4182914244c3b05d7745f
|
34
deps/premake/asmjit.lua
vendored
Normal file
34
deps/premake/asmjit.lua
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
asmjit = {
|
||||
source = path.join(dependencies.basePath, "asmjit"),
|
||||
}
|
||||
|
||||
function asmjit.import()
|
||||
links { "asmjit" }
|
||||
asmjit.includes()
|
||||
end
|
||||
|
||||
function asmjit.includes()
|
||||
includedirs {
|
||||
path.join(asmjit.source, "src")
|
||||
}
|
||||
|
||||
defines {
|
||||
"ASMJIT_STATIC"
|
||||
}
|
||||
end
|
||||
|
||||
function asmjit.project()
|
||||
project "asmjit"
|
||||
language "C++"
|
||||
|
||||
asmjit.includes()
|
||||
|
||||
files {
|
||||
path.join(asmjit.source, "src/**.cpp"),
|
||||
}
|
||||
|
||||
warnings "Off"
|
||||
kind "StaticLib"
|
||||
end
|
||||
|
||||
table.insert(dependencies, asmjit)
|
33
premake5.lua
33
premake5.lua
@ -51,7 +51,7 @@ editandcontinue "Off"
|
||||
warnings "Extra"
|
||||
characterset "ASCII"
|
||||
|
||||
flags { "NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks" }
|
||||
flags {"NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks"}
|
||||
|
||||
filter "platforms:Win*"
|
||||
defines {"_WINDOWS", "WIN32"}
|
||||
@ -60,7 +60,7 @@ filter {}
|
||||
filter "configurations:Release"
|
||||
optimize "Size"
|
||||
buildoptions {"/GL"}
|
||||
linkoptions { "/IGNORE:4702", "/LTCG" }
|
||||
linkoptions {"/IGNORE:4702", "/LTCG"}
|
||||
defines {"NDEBUG"}
|
||||
flags {"FatalCompileWarnings"}
|
||||
filter {}
|
||||
@ -70,13 +70,36 @@ filter "configurations:Debug"
|
||||
defines {"DEBUG", "_DEBUG"}
|
||||
filter {}
|
||||
|
||||
project "black-ops-plugin"
|
||||
project "common"
|
||||
kind "StaticLib"
|
||||
language "C++"
|
||||
|
||||
files {"./src/common/**.hpp", "./src/common/**.cpp"}
|
||||
|
||||
includedirs {"./src/common", "%{prj.location}/src"}
|
||||
|
||||
resincludedirs {"$(ProjectDir)src"}
|
||||
|
||||
dependencies.imports()
|
||||
|
||||
project "client"
|
||||
kind "SharedLib"
|
||||
language "C++"
|
||||
|
||||
files {"./src/**.hpp", "./src/**.cpp"}
|
||||
targetname "black-ops-plugin"
|
||||
|
||||
includedirs {"src"}
|
||||
pchheader "std_include.hpp"
|
||||
pchsource "src/client/std_include.cpp"
|
||||
|
||||
linkoptions {"/IGNORE:4254", "/PDBCompress"}
|
||||
|
||||
files {"./src/client/**.hpp", "./src/client/**.cpp"}
|
||||
|
||||
includedirs {"./src/client", "./src/common", "%{prj.location}/src"}
|
||||
|
||||
resincludedirs {"$(ProjectDir)src"}
|
||||
|
||||
links {"common"}
|
||||
|
||||
dependencies.imports()
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
#include <stdinc.hpp>
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "utils/hook.hpp"
|
||||
#include "../loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace ban {
|
||||
namespace {
|
@ -1,9 +1,9 @@
|
||||
#include <stdinc.hpp>
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "../loader/component_loader.hpp"
|
||||
|
||||
#include "utils/hook.hpp"
|
||||
#include "utils/io.hpp"
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/io.hpp>
|
||||
|
||||
namespace bots {
|
||||
namespace {
|
||||
@ -14,7 +14,7 @@ utils::hook::detour sv_bot_name_random_hook;
|
||||
// Json file is expected to contain a key for the bot's name. Value should be a
|
||||
// string for the clantag
|
||||
void load_bot_data() {
|
||||
const auto path = game::Dvar_FindVar("fs_homepath")->current.string;
|
||||
const auto* path = game::Dvar_FindVar("fs_homepath")->current.string;
|
||||
std::filesystem::current_path(path);
|
||||
|
||||
if (!utils::io::file_exists("bots/bots.json")) {
|
@ -1,8 +1,8 @@
|
||||
#include <stdinc.hpp>
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "utils/hook.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "../loader/component_loader.hpp"
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "command.hpp"
|
||||
|
||||
@ -11,26 +11,26 @@ namespace {
|
||||
std::mutex chat_mutex;
|
||||
std::unordered_set<std::uint64_t> mute_list{};
|
||||
|
||||
void mute_player(const game::client_s* cl) {
|
||||
void mute_player(const game::client_s* client) {
|
||||
std::unique_lock<std::mutex> _(chat_mutex);
|
||||
|
||||
if (mute_list.contains(cl->xuid)) {
|
||||
if (mute_list.contains(client->xuid)) {
|
||||
game::SV_GameSendServerCommand(
|
||||
-1, game::SV_CMD_CAN_IGNORE,
|
||||
utils::string::va("%c \"%s is already muted\"", 0x65, cl->name));
|
||||
utils::string::va("%c \"%s is already muted\"", 0x65, client->name));
|
||||
return;
|
||||
}
|
||||
|
||||
mute_list.insert(cl->xuid);
|
||||
mute_list.insert(client->xuid);
|
||||
}
|
||||
|
||||
void unmute_player(const game::client_s* cl) {
|
||||
void unmute_player(const game::client_s* client) {
|
||||
std::unique_lock<std::mutex> _(chat_mutex);
|
||||
|
||||
mute_list.erase(cl->xuid);
|
||||
mute_list.erase(client->xuid);
|
||||
|
||||
game::SV_GameSendServerCommand(
|
||||
cl->gentity->entnum, game::SV_CMD_CAN_IGNORE,
|
||||
client->gentity->entnum, game::SV_CMD_CAN_IGNORE,
|
||||
utils::string::va("%c \"You were unmuted\"", 0x65));
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
#include <stdinc.hpp>
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/nt.hpp"
|
||||
#include "utils/io.hpp"
|
||||
#include "../loader/component_loader.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/nt.hpp>
|
||||
|
||||
#include "command.hpp"
|
||||
|
||||
@ -12,6 +12,33 @@ constexpr auto CMD_MAX_NESTING = 8;
|
||||
namespace command {
|
||||
std::unordered_map<std::string, std::function<void(params&)>> handlers;
|
||||
|
||||
namespace {
|
||||
void cmd_vstr_f(const params& params) {
|
||||
if (params.size() < 2) {
|
||||
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
|
||||
"vstr <variablename> : execute a variable command\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* dvar_name = params.get(1);
|
||||
const auto* dvar = game::Dvar_FindVar(dvar_name);
|
||||
|
||||
if (dvar == nullptr) {
|
||||
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER, "%s doesn't exist\n",
|
||||
dvar_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dvar->type == game::DVAR_TYPE_STRING ||
|
||||
dvar->type == game::DVAR_TYPE_ENUM) {
|
||||
execute(dvar->current.string);
|
||||
} else {
|
||||
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
|
||||
"%s is not a string-based dvar\n", dvar->name);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void main_handler() {
|
||||
params params = {};
|
||||
|
||||
@ -63,26 +90,6 @@ void add(const char* name, const std::function<void(const params&)>& callback) {
|
||||
handlers[command] = callback;
|
||||
}
|
||||
|
||||
std::vector<std::string> script_commands;
|
||||
utils::memory::allocator allocator;
|
||||
|
||||
void add_script_command(const std::string& name,
|
||||
const std::function<void(const params&)>& callback) {
|
||||
script_commands.push_back(name);
|
||||
const auto _name = allocator.duplicate_string(name);
|
||||
add(_name, callback);
|
||||
}
|
||||
|
||||
void clear_script_commands() {
|
||||
for (const auto& name : script_commands) {
|
||||
handlers.erase(name);
|
||||
game::Cmd_RemoveCommand(name.data());
|
||||
}
|
||||
|
||||
allocator.clear();
|
||||
script_commands.clear();
|
||||
}
|
||||
|
||||
void execute(std::string command, const bool sync) {
|
||||
command += "\n";
|
||||
|
||||
@ -97,11 +104,20 @@ class component final : public component_interface {
|
||||
public:
|
||||
void post_unpack() override { add_commands_generic(); }
|
||||
|
||||
void pre_destroy() override { clear_script_commands(); }
|
||||
|
||||
private:
|
||||
static void add_commands_generic() {
|
||||
add("properQuit", [](const params&) { utils::nt::raise_hard_exception(); });
|
||||
|
||||
add("echo", [](const params& params) {
|
||||
for (auto i = 1; i < params.size(); i++) {
|
||||
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER, "%s ", params.get(i));
|
||||
}
|
||||
|
||||
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER, "\n");
|
||||
});
|
||||
|
||||
game::Cmd_RemoveCommand("vstr");
|
||||
add("vstr", cmd_vstr_f);
|
||||
}
|
||||
};
|
||||
} // namespace command
|
@ -18,9 +18,5 @@ private:
|
||||
void add_raw(const char* name, void (*callback)());
|
||||
void add(const char* name, const std::function<void(const params&)>& callback);
|
||||
|
||||
void add_script_command(const std::string& name,
|
||||
const std::function<void(const params&)>& callback);
|
||||
void clear_script_commands();
|
||||
|
||||
void execute(std::string command, bool sync = false);
|
||||
} // namespace command
|
30
src/client/component/gameplay.cpp
Normal file
30
src/client/component/gameplay.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "../loader/component_loader.hpp"
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace gameplay {
|
||||
namespace {
|
||||
game::dvar_s** player_sustainAmmo = nullptr;
|
||||
|
||||
utils::hook::detour pm_weapon_use_ammo_hook;
|
||||
|
||||
void pm_weapon_use_ammo_stub(void* ps, int wp, int amount) {
|
||||
if (!(*player_sustainAmmo)->current.enabled) {
|
||||
pm_weapon_use_ammo_hook.invoke<void>(ps, wp, amount);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_unpack() override {
|
||||
if (game::environment::t5zm()) {
|
||||
player_sustainAmmo = reinterpret_cast<game::dvar_s**>(0xBCD250);
|
||||
pm_weapon_use_ammo_hook.create(0x6979B0, &pm_weapon_use_ammo_stub);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace gameplay
|
||||
|
||||
REGISTER_COMPONENT(gameplay::component)
|
@ -1,4 +1,4 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "std_include.hpp"
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD ul_reason_for_call,
|
@ -1,4 +1,4 @@
|
||||
#include <stdinc.hpp>
|
||||
#include <std_include.hpp>
|
||||
|
||||
namespace game {
|
||||
gamemode current = reinterpret_cast<const char*>(0xA6840C) == "multiplayer"s
|
@ -19,6 +19,7 @@ public:
|
||||
|
||||
T* get() const {
|
||||
if (environment::t5mp()) {
|
||||
|
||||
return t5mp_;
|
||||
}
|
||||
|
@ -276,10 +276,9 @@ typedef struct dvar_s {
|
||||
DvarValue saved;
|
||||
DvarLimits domain;
|
||||
dvar_s* hashNext;
|
||||
unsigned char pad0[8];
|
||||
} dvar_t;
|
||||
|
||||
static_assert(sizeof(dvar_s) == 112);
|
||||
static_assert(sizeof(dvar_s) == 104);
|
||||
|
||||
enum playerFlag {
|
||||
PLAYER_FLAG_NOCLIP = 1 << 0,
|
@ -32,7 +32,7 @@ WEAK symbol<bool(netsrc_t, netadr_s, const char*)> NET_OutOfBandPrint{0x560BB0,
|
||||
0x472850};
|
||||
WEAK symbol<const char*(netadr_s)> NET_AdrToString{0x49F970, 0x40D790};
|
||||
|
||||
WEAK symbol<dvar_s*(const char*)> Dvar_FindVar{0x512F70, 0x5AE810};
|
||||
WEAK symbol<const dvar_s*(const char*)> Dvar_FindVar{0x512F70, 0x5AE810};
|
||||
WEAK symbol<const char*(const dvar_s*)> Dvar_DisplayableValue{0x681DD0,
|
||||
0x5B56F0};
|
||||
WEAK symbol<const char*(const dvar_s*)> Dvar_DisplayableLatchedValue{0x4AE1A0,
|
@ -1,4 +1,4 @@
|
||||
#include <stdinc.hpp>
|
||||
#include <std_include.hpp>
|
||||
#include "component_loader.hpp"
|
||||
|
||||
void component_loader::register_component(
|
1
src/client/std_include.cpp
Normal file
1
src/client/std_include.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "std_include.hpp"
|
@ -7,7 +7,7 @@
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <windows.h>
|
||||
#include <Windows.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
@ -17,6 +17,14 @@ public:
|
||||
} __;
|
||||
} // namespace
|
||||
|
||||
asmjit::Error assembler::call(void* target) {
|
||||
return Assembler::call(size_t(target));
|
||||
}
|
||||
|
||||
asmjit::Error assembler::jmp(void* target) {
|
||||
return Assembler::jmp(size_t(target));
|
||||
}
|
||||
|
||||
detour::detour(const size_t place, void* target)
|
||||
: detour(reinterpret_cast<void*>(place), target) {}
|
||||
|
||||
@ -153,4 +161,20 @@ void redirect_jump(void* pointer, void* data) {
|
||||
void redirect_jump(size_t pointer, void* data) {
|
||||
redirect_jump(reinterpret_cast<void*>(pointer), data);
|
||||
}
|
||||
|
||||
void* assemble(const std::function<void(assembler&)>& asm_function) {
|
||||
static asmjit::JitRuntime runtime;
|
||||
|
||||
asmjit::CodeHolder code;
|
||||
code.init(runtime.environment());
|
||||
|
||||
assembler a(&code);
|
||||
|
||||
asm_function(a);
|
||||
|
||||
void* result = nullptr;
|
||||
runtime.add(&result, &code);
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace utils::hook
|
@ -2,9 +2,24 @@
|
||||
#include "signature.hpp"
|
||||
|
||||
#define CalculateRelativeJMPAddress(X, Y) \
|
||||
(((std::uintptr_t)Y - (std::uintptr_t)X) - 5)
|
||||
(((std::uintptr_t)(Y) - (std::uintptr_t)(X)) - 5)
|
||||
|
||||
#include <asmjit/core/jitruntime.h>
|
||||
#include <asmjit/x86/x86assembler.h>
|
||||
|
||||
using namespace asmjit::x86;
|
||||
|
||||
namespace utils::hook {
|
||||
class assembler : public Assembler {
|
||||
public:
|
||||
using Assembler::Assembler;
|
||||
using Assembler::call;
|
||||
using Assembler::jmp;
|
||||
|
||||
asmjit::Error call(void* target);
|
||||
asmjit::Error jmp(void* target);
|
||||
};
|
||||
|
||||
class detour {
|
||||
public:
|
||||
detour() = default;
|
||||
@ -70,6 +85,8 @@ void jump(std::uintptr_t address, void* destination);
|
||||
void redirect_jump(void* pointer, void* data);
|
||||
void redirect_jump(size_t pointer, void* data);
|
||||
|
||||
void* assemble(const std::function<void(assembler&)>& asm_function);
|
||||
|
||||
template <typename T> T extract(void* address) {
|
||||
const auto data = static_cast<uint8_t*>(address);
|
||||
const auto offset = *reinterpret_cast<int32_t*>(data);
|
@ -11,7 +11,9 @@ bool move_file(const std::string& src, const std::string& target) {
|
||||
return MoveFileA(src.data(), target.data()) == TRUE;
|
||||
}
|
||||
|
||||
bool file_exists(const std::string& file) { return std::ifstream(file).good(); }
|
||||
bool file_exists(const std::string& file) {
|
||||
return std::filesystem::exists(file);
|
||||
}
|
||||
|
||||
bool write_file(const std::string& file, const std::string& data,
|
||||
const bool append) {
|
||||
@ -54,7 +56,7 @@ bool read_file(const std::string& file, std::string* data) {
|
||||
|
||||
if (size > -1) {
|
||||
data->resize(static_cast<uint32_t>(size));
|
||||
stream.read(const_cast<char*>(data->data()), size);
|
||||
stream.read(data->data(), size);
|
||||
stream.close();
|
||||
return true;
|
||||
}
|
@ -9,7 +9,7 @@ memory::allocator::~allocator() { this->clear(); }
|
||||
void memory::allocator::clear() {
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
for (auto& data : this->pool_) {
|
||||
for (const auto& data : this->pool_) {
|
||||
memory::free(data);
|
||||
}
|
||||
|
@ -18,11 +18,9 @@ public:
|
||||
|
||||
void* allocate(size_t length);
|
||||
|
||||
template <typename T> inline T* allocate() {
|
||||
return this->allocate_array<T>(1);
|
||||
}
|
||||
template <typename T> T* allocate() { return this->allocate_array<T>(1); }
|
||||
|
||||
template <typename T> inline T* allocate_array(const size_t count = 1) {
|
||||
template <typename T> T* allocate_array(const size_t count = 1) {
|
||||
return static_cast<T*>(this->allocate(count * sizeof(T)));
|
||||
}
|
||||
|
||||
@ -37,12 +35,9 @@ public:
|
||||
|
||||
static void* allocate(size_t length);
|
||||
|
||||
template <typename T> static inline T* allocate() {
|
||||
return allocate_array<T>(1);
|
||||
}
|
||||
template <typename T> static T* allocate() { return allocate_array<T>(1); }
|
||||
|
||||
template <typename T>
|
||||
static inline T* allocate_array(const size_t count = 1) {
|
||||
template <typename T> static T* allocate_array(const size_t count = 1) {
|
||||
return static_cast<T*>(allocate(count * sizeof(T)));
|
||||
}
|
||||
|
@ -24,8 +24,7 @@ std::vector<std::string> split(const std::string& s, const char delim) {
|
||||
std::vector<std::string> elems;
|
||||
|
||||
while (std::getline(ss, item, delim)) {
|
||||
elems.push_back(item); // elems.push_back(std::move(item)); // if C++11
|
||||
// (based on comment from @mchiasson)
|
||||
elems.push_back(item); // elems.push_back(std::move(item)); if C++11
|
||||
}
|
||||
|
||||
return elems;
|
||||
@ -92,12 +91,12 @@ std::string get_clipboard_data() {
|
||||
return {};
|
||||
}
|
||||
|
||||
void strip(const char* in, char* out, int max) {
|
||||
void strip(const char* in, char* out, size_t max) {
|
||||
if (!in || !out)
|
||||
return;
|
||||
|
||||
max--;
|
||||
auto current = 0;
|
||||
size_t current = 0;
|
||||
while (*in != 0 && current < max) {
|
||||
const auto color_index = (*(in + 1) - 48) >= 0xC ? 7 : (*(in + 1) - 48);
|
||||
|
||||
@ -111,6 +110,7 @@ void strip(const char* in, char* out, int max) {
|
||||
|
||||
++in;
|
||||
}
|
||||
|
||||
*out = '\0';
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include "memory.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef ARRAYSIZE
|
||||
template <class Type, size_t n> size_t ARRAYSIZE(Type (&)[n]) { return n; }
|
||||
@ -87,7 +86,7 @@ std::string dump_hex(const std::string& data,
|
||||
|
||||
std::string get_clipboard_data();
|
||||
|
||||
void strip(const char* in, char* out, int max);
|
||||
void strip(const char* in, char* out, size_t max);
|
||||
|
||||
std::string convert(const std::wstring& wstr);
|
||||
std::wstring convert(const std::string& str);
|
@ -1,31 +0,0 @@
|
||||
#include <stdinc.hpp>
|
||||
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "utils/hook.hpp"
|
||||
|
||||
namespace gameplay {
|
||||
namespace {
|
||||
const game::dvar_s* player_meleeRange = nullptr;
|
||||
utils::hook::detour fire_weapon_melee_hook;
|
||||
|
||||
void fire_weapon_melee_stub(game::gentity_s* ent, int time) {
|
||||
if (player_meleeRange->current.value == 0.0f)
|
||||
return;
|
||||
|
||||
fire_weapon_melee_hook.invoke<void>(ent, time);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_unpack() override {
|
||||
player_meleeRange =
|
||||
*reinterpret_cast<game::dvar_s**>(SELECT(0xC51990, 0xBCAFE4));
|
||||
|
||||
fire_weapon_melee_hook.create(SELECT(0x401E00, 0x465E40),
|
||||
&fire_weapon_melee_stub);
|
||||
}
|
||||
};
|
||||
} // namespace gameplay
|
||||
|
||||
REGISTER_COMPONENT(gameplay::component)
|
@ -1 +0,0 @@
|
||||
#include <stdinc.hpp>
|
Loading…
x
Reference in New Issue
Block a user