Fix crash

This commit is contained in:
6arelyFuture 2022-04-18 12:32:56 +02:00
parent 2793afac0e
commit d20cdfbb41
Signed by: Future
GPG Key ID: FA77F074E98D98A5
40 changed files with 224 additions and 114 deletions

3
.gitmodules vendored
View File

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

View File

@ -1,7 +1,7 @@
[![build](https://github.com/diamante0018/BlackOpsPlugin/workflows/Build/badge.svg)](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

@ -0,0 +1 @@
Subproject commit a4cb51b532af0f8137c4182914244c3b05d7745f

34
deps/premake/asmjit.lua vendored Normal file
View 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)

View File

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

View File

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

View File

@ -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")) {

View File

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

View File

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

View File

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

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

View File

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

View File

@ -1,4 +1,4 @@
#include <stdinc.hpp>
#include <std_include.hpp>
namespace game {
gamemode current = reinterpret_cast<const char*>(0xA6840C) == "multiplayer"s

View File

@ -19,6 +19,7 @@ public:
T* get() const {
if (environment::t5mp()) {
return t5mp_;
}

View File

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

View File

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

View File

@ -1,4 +1,4 @@
#include <stdinc.hpp>
#include <std_include.hpp>
#include "component_loader.hpp"
void component_loader::register_component(

View File

@ -0,0 +1 @@
#include "std_include.hpp"

View File

@ -7,7 +7,7 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Windows.h>
#include <algorithm>
#include <cassert>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
#include <stdinc.hpp>