Fix crash

This commit is contained in:
2022-04-18 12:32:56 +02:00
parent 2793afac0e
commit d20cdfbb41
40 changed files with 224 additions and 114 deletions

View File

@ -0,0 +1,46 @@
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include <utils/hook.hpp>
namespace ban {
namespace {
const game::dvar_t* sv_discord = nullptr;
const game::dvar_t* sv_clanWebsite = nullptr;
bool out_of_band_print_hk(game::netsrc_t src, game::netadr_s to,
const char* msg) {
// Proof of concept patch. Please ignore
if (msg != "error\nPATCH_BANNED_FROM_SERVER"s) {
return game::NET_OutOfBandPrint(src, to, msg);
}
const auto error_msg =
std::format("error\nPermanently banned\nDiscord: {}\nWebsite: {}",
sv_discord->current.string, sv_clanWebsite->current.string);
return game::NET_OutOfBandPrint(src, to, error_msg.data());
}
} // namespace
class component final : public component_interface {
public:
void post_unpack() override {
if (game::current == game::gamemode::zombies)
return;
sv_discord = game::Dvar_RegisterString(
"sv_discord", "https://www.discord.gg/", game::DVAR_ARCHIVE,
"Discord invitation link");
sv_clanWebsite =
game::Dvar_RegisterString("sv_clanWebsite", "https://www.google.com/",
game::DVAR_ARCHIVE, "Website link");
utils::hook::call(0x48B7E2, out_of_band_print_hk);
}
};
} // namespace ban
REGISTER_COMPONENT(ban::component)

View File

@ -0,0 +1,96 @@
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include <utils/hook.hpp>
#include <utils/io.hpp>
namespace bots {
namespace {
using bot_entry = std::pair<std::string, std::string>;
std::vector<bot_entry> bot_names;
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;
std::filesystem::current_path(path);
if (!utils::io::file_exists("bots/bots.json")) {
game::Com_Printf(game::CON_CHANNEL_SERVER, "bots.json was not found\n");
return;
}
rapidjson::Document obj;
const rapidjson::ParseResult result =
obj.Parse(utils::io::read_file("bots/bots.json").data());
if (!result || !obj.IsObject()) {
game::Com_Printf(game::CON_CHANNEL_SERVER,
"Failed to parse ban file. Empty?\n");
return;
}
for (rapidjson::Value::ConstMemberIterator itr = obj.MemberBegin();
itr != obj.MemberEnd(); ++itr) {
if (itr->value.GetType() == rapidjson::Type::kStringType) {
bot_names.emplace_back(
std::make_pair(itr->name.GetString(), itr->value.GetString()));
}
}
}
const char* sv_bot_name_random_stub() {
if (bot_names.empty()) {
load_bot_data();
}
if (!bot_names.empty()) {
static size_t bot_id = 0;
bot_id %= bot_names.size();
const auto& entry = bot_names.at(bot_id++);
return entry.first.data();
}
return sv_bot_name_random_hook.invoke<const char*>();
}
int build_connect_string(char* buf, const char* connect_string,
const char* name, const char* xuid, int protocol,
int port) {
// Default
auto clan_tag = "3arc"s;
for (const auto& [bot_name, tag] : bot_names) {
if (bot_name == name) {
clan_tag = tag;
break;
}
}
return _snprintf_s(buf, 0x400, _TRUNCATE, connect_string, name,
clan_tag.data(), xuid, protocol, port);
}
} // namespace
class component final : public component_interface {
public:
void post_unpack() override {
if (game::current == game::gamemode::zombies)
return;
utils::hook::set<const char*>(
0x6B6294,
"connect "
"\"\\cg_predictItems\\1\\cl_punkbuster\\0\\cl_"
"anonymous\\0\\color\\4\\head\\"
"default\\model\\multi\\snaps\\20\\rate\\5000\\name\\%s\\clanAbbrev\\%"
"s\\bdOnlineUserID\\%s\\protocol\\%d\\qport\\%d\"");
sv_bot_name_random_hook.create(0x49ED80, &sv_bot_name_random_stub);
utils::hook::call(0x6B6299, build_connect_string);
}
};
} // namespace bots
REGISTER_COMPONENT(bots::component)

View File

@ -0,0 +1,135 @@
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include "command.hpp"
namespace chat {
namespace {
std::mutex chat_mutex;
std::unordered_set<std::uint64_t> mute_list{};
void mute_player(const game::client_s* client) {
std::unique_lock<std::mutex> _(chat_mutex);
if (mute_list.contains(client->xuid)) {
game::SV_GameSendServerCommand(
-1, game::SV_CMD_CAN_IGNORE,
utils::string::va("%c \"%s is already muted\"", 0x65, client->name));
return;
}
mute_list.insert(client->xuid);
}
void unmute_player(const game::client_s* client) {
std::unique_lock<std::mutex> _(chat_mutex);
mute_list.erase(client->xuid);
game::SV_GameSendServerCommand(
client->gentity->entnum, game::SV_CMD_CAN_IGNORE,
utils::string::va("%c \"You were unmuted\"", 0x65));
}
void client_command(int client_number) {
char buf[1024] = {0};
if (game::g_entities[client_number].client == nullptr) {
// Not in game
return;
}
game::SV_Cmd_ArgvBuffer(0, buf, sizeof(buf));
std::unique_lock<std::mutex> _(chat_mutex);
if (utils::string::starts_with(buf, "say") &&
mute_list.contains(game::svs_clients[client_number].xuid)) {
game::SV_GameSendServerCommand(
client_number, game::SV_CMD_CAN_IGNORE,
utils::string::va("%c \"You are muted\"", 0x65));
return;
}
game::ClientCommand(client_number);
}
} // namespace
class component final : public component_interface {
public:
void post_unpack() override {
utils::hook::call(SELECT(0x58DA1C, 0x4FB3BD), client_command);
add_chat_commands();
}
private:
static void add_chat_commands() {
command::add("sayAs", [](const command::params& params) {
if (params.size() < 3) {
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
"Usage: sayAs <client number> <message>\n");
return;
}
const auto* client = game::SV_GetPlayerByNum();
if (client == nullptr)
return;
auto* const gentity = client->gentity;
assert(gentity != nullptr);
if (gentity->client == nullptr)
return;
const auto message = params.join(2);
game::G_Say(gentity, nullptr, 0, message.data());
});
command::add("mutePlayer", [](const command::params& params) {
if (params.size() < 2) {
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
"Usage: mutePlayer <client number>\n");
return;
}
const auto* client = game::SV_GetPlayerByNum();
if (client == nullptr)
return;
assert(client->gentity != nullptr);
if (client->gentity->client == nullptr)
return;
mute_player(client);
});
command::add("unmutePlayer", [](const command::params& params) {
if (params.size() < 2) {
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
"Usage: unmutePlayer <client number>\n");
return;
}
const auto* client = game::SV_GetPlayerByNum();
if (client == nullptr)
return;
assert(client->gentity != nullptr);
if (client->gentity->client == nullptr)
return;
unmute_player(client);
});
}
};
} // namespace chat
REGISTER_COMPONENT(chat::component)

View File

@ -0,0 +1,125 @@
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include <utils/string.hpp>
#include <utils/nt.hpp>
#include "command.hpp"
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 = {};
const auto command = utils::string::to_lower(params[0]);
if (const auto got = handlers.find(command); got != handlers.end()) {
got->second(params);
}
}
params::params() : nesting_(game::sv_cmd_args->nesting) {
assert(game::sv_cmd_args->nesting < CMD_MAX_NESTING);
}
int params::size() const { return game::sv_cmd_args->argc[this->nesting_]; }
const char* params::get(const int index) const {
if (index >= this->size()) {
return "";
}
return game::sv_cmd_args->argv[this->nesting_][index];
}
std::string params::join(const int index) const {
std::string result = {};
for (auto i = index; i < this->size(); i++) {
if (i > index)
result.append(" ");
result.append(this->get(i));
}
return result;
}
void add_raw(const char* name, void (*callback)()) {
game::Cmd_AddCommandInternal(
name, callback,
utils::memory::get_allocator()->allocate<game::cmd_function_t>());
}
void add(const char* name, const std::function<void(const params&)>& callback) {
const auto command = utils::string::to_lower(name);
if (!handlers.contains(command)) {
add_raw(name, main_handler);
}
handlers[command] = callback;
}
void execute(std::string command, const bool sync) {
command += "\n";
if (sync) {
game::Cmd_ExecuteSingleCommand(game::LOCAL_CLIENT_0, 0, command.data());
} else {
game::Cbuf_AddText(game::LOCAL_CLIENT_0, command.data());
}
}
class component final : public component_interface {
public:
void post_unpack() override { add_commands_generic(); }
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
REGISTER_COMPONENT(command::component)

View File

@ -0,0 +1,22 @@
#pragma once
namespace command {
class params {
public:
params();
int size() const;
const char* get(int index) const;
std::string join(int index) const;
const char* operator[](const int index) const { return this->get(index); }
private:
int nesting_;
};
void add_raw(const char* name, void (*callback)());
void add(const char* name, const std::function<void(const params&)>& callback);
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)

13
src/client/dllmain.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "std_include.hpp"
#include "loader/component_loader.hpp"
BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD ul_reason_for_call,
LPVOID /*reserved_*/) {
if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
component_loader::post_unpack();
} else if (ul_reason_for_call == DLL_PROCESS_DETACH) {
component_loader::pre_destroy();
}
return TRUE;
}

13
src/client/game/game.cpp Normal file
View File

@ -0,0 +1,13 @@
#include <std_include.hpp>
namespace game {
gamemode current = reinterpret_cast<const char*>(0xA6840C) == "multiplayer"s
? gamemode::multiplayer
: gamemode::zombies;
namespace environment {
bool t5mp() { return current == gamemode::multiplayer; }
bool t5zm() { return current == gamemode::zombies; }
} // namespace environment
} // namespace game

40
src/client/game/game.hpp Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#define SELECT(mp, zm) (game::environment::t5mp() ? mp : zm)
namespace game {
enum gamemode { none, multiplayer, zombies };
extern gamemode current;
namespace environment {
bool t5mp();
bool t5zm();
} // namespace environment
template <typename T> class symbol {
public:
symbol(const size_t t5mp, const size_t t5zm)
: t5mp_(reinterpret_cast<T*>(t5mp)), t5zm_(reinterpret_cast<T*>(t5zm)) {}
T* get() const {
if (environment::t5mp()) {
return t5mp_;
}
return t5zm_;
}
operator T*() const { return this->get(); }
T* operator->() const { return this->get(); }
private:
T* t5mp_;
T* t5zm_;
};
} // namespace game
#include "symbols.hpp"

498
src/client/game/structs.hpp Normal file
View File

@ -0,0 +1,498 @@
#pragma once
namespace game {
typedef float vec_t;
typedef vec_t vec2_t[2];
typedef vec_t vec3_t[3];
typedef vec_t vec4_t[4];
enum FsListBehavior_e { FS_LIST_PURE_ONLY = 0x0, FS_LIST_ALL = 0x1 };
enum svscmd_type { SV_CMD_CAN_IGNORE, SV_CMD_RELIABLE };
typedef enum {
ERR_FATAL = 0x0,
ERR_DROP = 0x1,
ERR_SERVERDISCONNECT = 0x2,
ERR_DISCONNECT = 0x3,
ERR_SCRIPT = 0x4,
ERR_SCRIPT_DROP = 0x5,
ERR_LOCALIZATION = 0x6
} errorParm_t;
typedef enum {
LOCAL_CLIENT_INVALID = -1,
LOCAL_CLIENT_0 = 0,
LOCAL_CLIENT_1 = 1,
LOCAL_CLIENT_2 = 2,
LOCAL_CLIENT_3 = 3,
LOCAL_CLIENT_LAST = 3,
LOCAL_CLIENT_COUNT = 4
} LocalClientNum_t;
typedef enum {
CON_CHANNEL_DONT_FILTER = 0x0,
CON_CHANNEL_ERROR = 0x1,
CON_CHANNEL_GAMENOTIFY = 0x2,
CON_CHANNEL_BOLDGAME = 0x3,
CON_CHANNEL_SUBTITLE = 0x4,
CON_CHANNEL_OBITUARY = 0x5,
CON_CHANNEL_LOGFILEONLY = 0x6,
CON_CHANNEL_CONSOLEONLY = 0x7,
CON_CHANNEL_GFX = 0x8,
CON_CHANNEL_SOUND = 0x9,
CON_CHANNEL_FILES = 0xA,
CON_CHANNEL_DEVGUI = 0xB,
CON_CHANNEL_PROFILE = 0xC,
CON_CHANNEL_UI = 0xD,
CON_CHANNEL_CLIENT = 0xE,
CON_CHANNEL_SERVER = 0xF,
CON_CHANNEL_SYSTEM = 0x10,
CON_CHANNEL_PLAYERWEAP = 0x11,
CON_CHANNEL_AI = 0x12,
CON_CHANNEL_ANIM = 0x13,
CON_CHANNEL_PHYS = 0x14,
CON_CHANNEL_FX = 0x15,
CON_CHANNEL_LEADERBOARDS = 0x16,
CON_CHANNEL_LIVE = 0x17,
CON_CHANNEL_PARSERSCRIPT = 0x18,
CON_CHANNEL_SCRIPT = 0x19,
CON_CHANNEL_SPAWNSYSTEM = 0x1A,
CON_CHANNEL_COOPINFO = 0x1B,
CON_CHANNEL_SERVERDEMO = 0x1C,
CON_CHANNEL_DDL = 0x1D,
CON_CHANNEL_NETWORK = 0x1E,
CON_CHANNEL_SCHEDULER = 0x1F,
CON_FIRST_DEBUG_CHANNEL = 0x1F,
CON_CHANNEL_TASK = 0x20,
CON_CHANNEL_SPU = 0x21,
CON_BUILTIN_CHANNEL_COUNT = 0x22
} conChannel_t;
typedef enum {
CON_DEST_CONSOLE = 0,
CON_DEST_MINICON = 1,
CON_DEST_ERROR = 2,
CON_DEST_GAME_FIRST = 3,
CON_DEST_GAME1 = 3,
CON_DEST_GAME2 = 4,
CON_DEST_GAME3 = 5,
CON_DEST_GAME_LAST = 5,
CON_DEST_COUNT = 6
} print_msg_dest_t;
typedef enum {
NS_CLIENT1 = 0x0,
NS_SERVER = 0x1,
NS_MAXCLIENTS = 0x1,
NS_PACKET = 0x2
} netsrc_t;
typedef enum { CS_MOTD = 11, CS_TIMESCALE = 14 } ConfigString;
typedef enum {
FS_READ = 0,
FS_WRITE = 1,
FS_APPEND = 2,
FS_APPEND_SYNC = 3
} fsMode_t;
struct PrintChannel {
char name[32];
bool allowScript;
};
static_assert(sizeof(PrintChannel) == 33);
struct PrintChannelGlob {
PrintChannel openChannels[256];
unsigned int filters[6][8];
};
struct usercmd_s {
int serverTime;
int button_bits[2];
int angles[3];
unsigned __int16 weapon;
unsigned __int16 offHandIndex;
unsigned __int16 lastWeaponAltModeSwitch;
char forwardmove;
char rightmove;
char upmove;
char pitchmove;
char yawmove;
float meleeChargeYaw;
unsigned char meleeChargeDist;
float rollmove;
char selectedLocation[2];
unsigned char selectedYaw;
};
static_assert(sizeof(usercmd_s) == 52);
struct cmd_function_t {
cmd_function_t* next;
const char* name;
const char* autoCompleteDir;
const char* autoCompleteExt;
void(__cdecl* function)();
bool consoleAccess;
};
static_assert(sizeof(cmd_function_t) == 24);
struct CmdArgs {
int nesting;
int localClientNum[8];
int controllerIndex[8];
void* itemDef[8];
int argshift[8];
int argc[8];
const char** argv[8];
char textPool[8192];
const char* argvPool[512];
int usedTextPool[8];
int totalUsedArgvPool;
int totalUsedTextPool;
};
static_assert(sizeof(CmdArgs) == 10476);
typedef enum {
NA_BOT = 0x0,
NA_BAD = 0x1,
NA_LOOPBACK = 0x2,
NA_BROADCAST = 0x3,
NA_IP = 0x4
} netadrtype_t;
struct netadr_s // Confirm nigga
{
netadrtype_t type;
unsigned char ip[4];
unsigned __int16 port;
int addrHandleIndex;
};
static_assert(sizeof(netadr_s) == 16);
struct msg_t {
int overflowed;
int readOnly;
unsigned char* data;
unsigned char* splitData;
int maxsize;
int cursize;
int splitSize;
int readcount;
int bit;
int lastEntityRef;
int flush;
netsrc_t targetLocalNetID;
};
static_assert(sizeof(msg_t) == 48);
typedef enum {
SCRIPTINSTANCE_SERVER,
SCRIPTINSTANCE_CLIENT,
SCRIPT_INSTANCE_MAX
} scriptInstance_t;
enum dvar_flags : unsigned __int16 {
DVAR_ARCHIVE = 0x1,
DVAR_WRITEPROTECTED = 0x10,
DVAR_READONLY = 0x40,
DVAR_CHEAT = 0x80,
DVAR_CODINFO = 0x100,
};
typedef enum : char {
DVAR_TYPE_BOOL = 0x0,
DVAR_TYPE_FLOAT = 0x1,
DVAR_TYPE_FLOAT_2 = 0x2,
DVAR_TYPE_FLOAT_3 = 0x3,
DVAR_TYPE_FLOAT_4 = 0x4,
DVAR_TYPE_INT = 0x5,
DVAR_TYPE_ENUM = 0x6,
DVAR_TYPE_STRING = 0x7,
DVAR_TYPE_COLOR = 0x8,
DVAR_TYPE_INT64 = 0x9,
DVAR_TYPE_LINEAR_COLOR_RGB = 0xA,
DVAR_TYPE_COLOR_XYZ = 0xB,
DVAR_TYPE_COUNT = 0xC,
} dvarType_t;
union DvarValue {
bool enabled;
int integer;
unsigned int unsignedInt;
__int64 integer64;
unsigned __int64 unsignedInt64;
float value;
float vector[4];
const char* string;
unsigned char color[4];
};
struct enum_limit {
int stringCount;
const char** strings;
};
struct int_limit {
int min;
int max;
};
struct int64_limit {
__int64 min;
__int64 max;
};
struct float_limit {
float min;
float max;
};
union DvarLimits {
enum_limit enumeration;
int_limit integer;
float_limit value;
float_limit vector;
};
typedef struct dvar_s {
const char* name;
const char* description;
long hash;
unsigned int flags;
dvarType_t type;
bool modified;
bool loadedFromSaveGame;
DvarValue current;
DvarValue latched;
DvarValue reset;
DvarValue saved;
DvarLimits domain;
dvar_s* hashNext;
} dvar_t;
static_assert(sizeof(dvar_s) == 104);
enum playerFlag {
PLAYER_FLAG_NOCLIP = 1 << 0,
PLAYER_FLAG_UFO = 1 << 1,
PLAYER_FLAG_FROZEN = 1 << 2,
};
struct playerState_s {
int commandTime;
int pm_type;
int bobCycle;
int pm_flags;
int weapFlags;
int otherFlags;
int pm_time;
};
struct pmove_t {
playerState_s* ps;
};
struct gclient_s {
char __pad0[10396];
int flags;
char __pad1[320];
};
static_assert(sizeof(gclient_s) == 10720);
enum entityFlag {
FL_GODMODE = 1,
FL_DEMI_GODMODE = 2,
FL_NOTARGET = 4,
FL_NO_KNOCKBACK = 8,
FL_DROPPED_ITEM = 16,
FL_NO_BOTS = 32,
FL_NO_HUMANS = 64,
FL_TOGGLE = 128,
FL_SOFTACTIVATE = 256,
FL_LOW_PRIORITY_USEABLE = 512,
FL_NO_HEADCHECK = 1024,
FL_DYNAMICPATH = 2048,
FL_SUPPORTS_LINKTO = 4096,
FL_NO_AUTO_ANIM_UPDATE = 8192,
FL_GRENADE_TOUCH_DAMAGE = 16384,
FL_GRENADE_MARTYRDOM = 32768,
FL_MISSILE_DESTABILIZED = 65536,
FL_STABLE_MISSILES = 131072,
FL_REPEAT_ANIM_UPDATE = 262144,
FL_VEHICLE_TARGET = 524288,
FL_GROUND_ENT = 1048576,
FL_CURSOR_HINT = 2097152,
FL_USE_TURRET = 4194304,
FL_MISSILE_ATTRACTOR = 8388608,
FL_TARGET = 16777216,
FL_WEAPON_BEING_GRABBED = 33554432,
FL_OBSTACLE = 67108864,
FL_DODGE_LEFT = 134217728,
FL_DODGE_RIGHT = 268435456,
FL_BADPLACE_VOLUME = 536870912,
FL_AUTO_BLOCKPATHS = 1073741824
};
enum playerStateFlag { PMF_PRONE = 0x1, PMF_DUCKED = 0x2, PMF_MANTLE = 0x4 };
struct gentity_s {
int entnum;
char __pad0[320];
gclient_s* client;
char __pad1[44];
int flags;
char __pad2[384];
};
static_assert(sizeof(gentity_s) == 760);
struct netProfilePacket_t {
int iTime;
int iSize;
int bFragment;
};
struct netProfileStream_t {
netProfilePacket_t packets[60];
int iCurrPacket;
int iBytesPerSecond;
int iLastBPSCalcTime;
int iCountedPackets;
int iCountedFragments;
int iFragmentPercentage;
int iLargestPacket;
int iSmallestPacket;
};
struct netProfileInfo_t {
netProfileStream_t send;
netProfileStream_t recieve;
};
static_assert(sizeof(netProfileInfo_t) == 1504);
struct netchan_t {
int outgoingSequence;
netsrc_t sock;
int dropped;
int incomingSequence;
netadr_s remoteAddress;
int qport;
int fragmentSequence;
int fragmentLength;
unsigned char* fragmentBuffer;
int fragmentBufferSize;
int unsentFragments;
int unsentFragmentStart;
int unsentLength;
unsigned char* unsentBuffer;
int unsentBufferSize;
int reliable_fragments;
unsigned char fragment_send_count[128];
unsigned int fragment_ack[4];
int lowest_send_count;
netProfileInfo_t prof;
};
static_assert(sizeof(netchan_t) == 1728);
struct MantleState {
float yaw;
int timer;
int transIndex;
int flags;
};
enum ViewLockTypes {
PLAYERVIEWLOCK_NONE = 0,
PLAYERVIEWLOCK_FULL = 1,
PLAYERVIEWLOCK_WEAPONJITTER = 2,
PLAYERVIEWLOCKCOUNT = 3
};
typedef enum {
PM_NORMAL = 0,
PM_NORMAL_LINKED = 1,
PM_NOCLIP = 2,
PM_UFO = 3,
PM_SPECTATOR = 4,
PM_INTERMISSION = 5,
PM_LASTSTAND = 6,
PM_REVIVEE = 7,
PM_LASTSTAND_TRANSITION = 8,
PM_DEAD = 9,
PM_DEAD_LINKED = 10
} pmtype_t;
enum clientState_t {
CS_FREE = 0,
CS_ZOMBIE = 1,
CS_RECONNECTING = 2,
CS_CONNECTED = 3,
CS_CLIENTLOADING = 4,
CS_ACTIVE = 5
};
struct PredictedVehicleInfo {
bool inVehicle;
vec3_t origin;
vec3_t angles;
vec3_t tVel;
vec3_t aVel;
};
static_assert(sizeof(PredictedVehicleInfo) == 52);
struct clientHeader_t {
clientState_t state;
int sendAsActive;
int deltaMessage;
int rateDealyed;
netchan_t netchan;
vec3_t predictedOrigin;
int predictedOriginServerTime;
PredictedVehicleInfo vehicle;
};
static_assert(sizeof(clientHeader_t) == 1812);
struct svscmd_info_t {
const char* cmd;
int time;
int type;
};
struct client_s {
clientHeader_t header;
const char* dropReason; // 1812
char userinfo[1024]; // 1816
char reliableCommandBuffer[16384]; // 2840
int reliableCommandBufferNext; // 19224
svscmd_info_t reliableCommandInfo[128]; // 19228
int reliableSequence; // 20764
int reliableAcknowledge; // 20768
int reliableSent; // 20772
int messageAcknowledge; // 20776
int gamestateMessageNum; // 20780
int challenge; // 20784
usercmd_s lastUsercmd; // 20788
int lastClientCommand; // 20840
char lastClientCommandString[1024]; // 20844
gentity_s* gentity; // 21868
char name[32]; // 21872
char clanAbbrev[5]; // 21904
unsigned __int64 xuid; // 21912
unsigned char __pad0[0x76D48];
};
static_assert(sizeof(client_s) == 0x7C2E8);
} // namespace game

View File

@ -0,0 +1,77 @@
#pragma once
#define WEAK __declspec(selectany)
namespace game {
WEAK symbol<void(errorParm_t, const char*, ...)> Com_Error{0x627380, 0x651D90};
WEAK symbol<void(conChannel_t, const char*, ...)> Com_Printf{0x4126C0,
0x43BF30};
WEAK symbol<void(conChannel_t, const char*, ...)> Com_DPrintf{0x4E3FA0,
0x60F7F0};
WEAK symbol<void(conChannel_t, const char*, ...)> Com_PrintError{0x568B90,
0x5DFC40};
WEAK symbol<void(LocalClientNum_t, const char* text)> Cbuf_AddText{0x56EF70,
0x49B930};
WEAK symbol<void(LocalClientNum_t, const char* text)> Cbuf_InsertText{0x695E10,
0x5B3EB0};
WEAK symbol<void(LocalClientNum_t, int controllerIndex, const char* text)>
Cmd_ExecuteSingleCommand{0x50B470, 0x829AD0};
WEAK symbol<void(client_s*, svscmd_type, const char*, ...)>
SV_SendServerCommand{0x588B10, 0x6106E0};
WEAK symbol<void(int, svscmd_type, const char*)> SV_GameSendServerCommand{
0x6B8730, 0x543CF0};
WEAK symbol<void(int, char*, int)> SV_Cmd_ArgvBuffer{0x462CB0, 0x4DF5A0};
WEAK symbol<void(client_s*, const char*)> SV_DelayDropClient{0x4A8DC0,
0x4A2EB0};
WEAK symbol<client_s*()> SV_GetPlayerByName{0x875180, 0x87C350};
WEAK symbol<client_s*()> SV_GetPlayerByNum{0x875260, 0x87C430};
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<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,
0x675850};
WEAK symbol<const dvar_s*(const char*, const char*, unsigned __int16,
const char*)>
Dvar_RegisterString{0x4E3410, 0x59B3B0};
WEAK symbol<const dvar_s*(const char*, float, float, float, unsigned __int16,
const char*)>
Dvar_RegisterFloat{0x670020, 0x679020};
WEAK symbol<const dvar_s*(const char*, bool, unsigned __int16, const char*)>
Dvar_RegisterBool{0x5A5350, 0x45BB20};
WEAK symbol<const dvar_s*(const char*, int, int, int, unsigned __int16,
const char*)>
Dvar_RegisterInt{0x58D900, 0x651910};
WEAK symbol<void(const char*, void(), cmd_function_t*)> Cmd_AddCommandInternal{
0x6AD580, 0x661400};
WEAK symbol<void(const char* cmdName)> Cmd_RemoveCommand{0x527EA0, 0x5F1A90};
WEAK symbol<cmd_function_t*(const char*)> Cmd_FindCommand{0x445B60, 0x479DD0};
WEAK symbol<char*(char*)> I_CleanStr{0x4B0700, 0x0};
WEAK symbol<char*(int)> ConcatArgs{0x5D5F10, 0x4FB210};
WEAK symbol<void(int)> ClientCommand{0x63DB70, 0x4AF770};
WEAK symbol<void(gentity_s*, gentity_s*, int, const char*)> G_Say{0x51BBD0,
0x49A790};
WEAK symbol<void(gentity_s*, unsigned __int16, unsigned int)> Scr_Notify{
0x458D30, 0x0};
WEAK symbol<void(int, scriptInstance_t)> Scr_AddInt{0x49F830, 0x0};
WEAK symbol<void(const float*, scriptInstance_t)> Scr_AddVector{0x532EF0, 0x0};
WEAK symbol<int(const playerState_s*)> PM_GetEffectiveStance{0x659590, 0x0};
WEAK symbol<CmdArgs> sv_cmd_args{0x355BD88, 0x243D208};
WEAK symbol<int> dvarCount{0x385BE74, 0x261CBD4};
WEAK symbol<dvar_t*> sortedDvars{0x385BE88, 0x261CBE8};
WEAK symbol<client_s> svs_clients{0x372D11C, 0x286D01C};
WEAK symbol<gentity_s> g_entities{0x32E5640, 0x1A796F8};
WEAK symbol<int> level_time{0x3443F4C, 0xC2078C};
} // namespace game

View File

@ -0,0 +1,21 @@
#pragma once
class component_interface {
public:
virtual ~component_interface() {}
virtual void post_start() {}
virtual void post_load() {}
virtual void pre_destroy() {}
virtual void post_unpack() {}
virtual void* load_import([[maybe_unused]] const std::string& library,
[[maybe_unused]] const std::string& function) {
return nullptr;
}
virtual bool is_supported() { return true; }
};

View File

@ -0,0 +1,111 @@
#include <std_include.hpp>
#include "component_loader.hpp"
void component_loader::register_component(
std::unique_ptr<component_interface>&& component_) {
get_components().push_back(std::move(component_));
}
bool component_loader::post_start() {
static auto handled = false;
if (handled)
return true;
handled = true;
try {
for (const auto& component_ : get_components()) {
component_->post_start();
}
} catch (premature_shutdown_trigger&) {
return false;
}
return true;
}
bool component_loader::post_load() {
static auto handled = false;
if (handled)
return true;
handled = true;
clean();
try {
for (const auto& component_ : get_components()) {
component_->post_load();
}
} catch (premature_shutdown_trigger&) {
return false;
}
return true;
}
void component_loader::post_unpack() {
static auto handled = false;
if (handled)
return;
handled = true;
for (const auto& component_ : get_components()) {
component_->post_unpack();
}
}
void component_loader::pre_destroy() {
static auto handled = false;
if (handled)
return;
handled = true;
for (const auto& component_ : get_components()) {
component_->pre_destroy();
}
}
void component_loader::clean() {
auto& components = get_components();
for (auto i = components.begin(); i != components.end();) {
if (!(*i)->is_supported()) {
(*i)->pre_destroy();
i = components.erase(i);
} else {
++i;
}
}
}
void* component_loader::load_import(const std::string& library,
const std::string& function) {
void* function_ptr = nullptr;
for (const auto& component_ : get_components()) {
auto* const component_function_ptr =
component_->load_import(library, function);
if (component_function_ptr) {
function_ptr = component_function_ptr;
}
}
return function_ptr;
}
void component_loader::trigger_premature_shutdown() {
throw premature_shutdown_trigger();
}
std::vector<std::unique_ptr<component_interface>>&
component_loader::get_components() {
using component_vector = std::vector<std::unique_ptr<component_interface>>;
using component_vector_container =
std::unique_ptr<component_vector, std::function<void(component_vector*)>>;
static component_vector_container components(
new component_vector, [](component_vector* component_vector) {
pre_destroy();
delete component_vector;
});
return *components;
}

View File

@ -0,0 +1,51 @@
#pragma once
#include "component_interface.hpp"
class component_loader final {
public:
class premature_shutdown_trigger final : public std::exception {
[[nodiscard]] const char* what() const noexcept override {
return "Premature shutdown requested";
}
};
template <typename T> class installer final {
static_assert(std::is_base_of<component_interface, T>::value,
"component has invalid base class");
public:
installer() { register_component(std::make_unique<T>()); }
};
template <typename T> static T* get() {
for (const auto& component_ : get_components()) {
if (typeid(*component_.get()) == typeid(T)) {
return reinterpret_cast<T*>(component_.get());
}
}
return nullptr;
}
static void
register_component(std::unique_ptr<component_interface>&& component);
static bool post_start();
static bool post_load();
static void post_unpack();
static void pre_destroy();
static void clean();
static void* load_import(const std::string& library,
const std::string& function);
static void trigger_premature_shutdown();
private:
static std::vector<std::unique_ptr<component_interface>>& get_components();
};
#define REGISTER_COMPONENT(name) \
namespace { \
static component_loader::installer<name> __component; \
}

View File

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

View File

@ -0,0 +1,35 @@
#pragma once
#define BINARY_PAYLOAD_SIZE 0x0A000000
#define TLS_PAYLOAD_SIZE 0x2000
#define DLL_EXPORT extern "C" __declspec(dllexport)
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <algorithm>
#include <cassert>
#include <functional>
#include <iostream>
#include <map>
#include <mutex>
#include <string>
#include <unordered_set>
#include <vector>
#pragma warning(disable : 26812)
#include <rapidjson/document.h>
#include <rapidjson/prettywriter.h>
#include <rapidjson/stringbuffer.h>
#pragma comment(lib, "ntdll.lib")
using namespace std::literals;
// clang-format off
#include "game/structs.hpp"
#include "game/game.hpp"
// clang-format on