diff --git a/src/component/cheats.cpp b/src/component/cheats.cpp index 237093f..a3b91b7 100644 --- a/src/component/cheats.cpp +++ b/src/component/cheats.cpp @@ -91,6 +91,11 @@ namespace cheats { command::execute(utils::string::va("cmd mr %i 2 allies", *game::serverId), true); }); + + key_catcher::on_key_press("8", [](const game::LocalClientNum_t&) + { + command::execute(utils::string::va("cmd mr %i -1 endround", *game::serverId), true); + }); } }; } diff --git a/src/component/scheduler.cpp b/src/component/scheduler.cpp new file mode 100644 index 0000000..119b0dd --- /dev/null +++ b/src/component/scheduler.cpp @@ -0,0 +1,170 @@ +#include +#include "loader/component_loader.hpp" + +#include "utils/concurrency.hpp" +#include "utils/hook.hpp" + +#include "scheduler.hpp" + +namespace scheduler +{ + std::thread::id async_thread_id; + + namespace + { + struct task + { + std::function handler{}; + std::chrono::milliseconds interval{}; + std::chrono::high_resolution_clock::time_point last_call{}; + }; + + using task_list = std::vector; + + class task_pipeline + { + public: + void add(task&& task) + { + new_callbacks_.access([&task, this](task_list& tasks) + { + tasks.emplace_back(std::move(task)); + }); + } + + void clear() + { + callbacks_.access([&](task_list& tasks) + { + this->merge_callbacks(); + tasks.clear(); + }); + } + + void execute() + { + callbacks_.access([&](task_list& tasks) + { + this->merge_callbacks(); + + for (auto i = tasks.begin(); i != tasks.end();) + { + const auto now = std::chrono::high_resolution_clock::now(); + const auto diff = now - i->last_call; + + if (diff < i->interval) + { + ++i; + continue; + } + + i->last_call = now; + + const auto res = i->handler(); + if (res == cond_end) + { + i = tasks.erase(i); + } + else + { + ++i; + } + } + }); + } + + private: + utils::concurrency::container new_callbacks_; + utils::concurrency::container callbacks_; + + void merge_callbacks() + { + callbacks_.access([&](task_list& tasks) + { + new_callbacks_.access([&](task_list& new_tasks) + { + tasks.insert(tasks.end(), std::move_iterator(new_tasks.begin()), std::move_iterator(new_tasks.end())); + new_tasks = {}; + }); + }); + } + }; + + std::thread thread; + task_pipeline pipelines[pipeline::count]; + + void execute(const pipeline type) + { + assert(type >= 0 && type < pipeline::count); + pipelines[type].execute(); + } + + void cl_frame_stub(game::LocalClientNum_t local) + { + reinterpret_cast(0x041C9B0)(local); + execute(pipeline::client); + } + } + + void clear_tasks(const pipeline type) + { + return pipelines[type].clear(); + } + + void schedule(const std::function& callback, const pipeline type, + const std::chrono::milliseconds delay) + { + assert(type >= 0 && type < pipeline::count); + + task task; + task.handler = callback; + task.interval = delay; + task.last_call = std::chrono::high_resolution_clock::now(); + + pipelines[type].add(std::move(task)); + } + + void loop(const std::function& callback, const pipeline type, + const std::chrono::milliseconds delay) + { + schedule([callback]() + { + callback(); + return cond_continue; + }, type, delay); + } + + void once(const std::function& callback, const pipeline type, + const std::chrono::milliseconds delay) + { + schedule([callback]() + { + callback(); + return cond_end; + }, type, delay); + } + + unsigned int thread_id; + + class component final : public component_interface + { + public: + void post_unpack() override + { + thread = std::thread([]() + { + while (true) + { + execute(pipeline::async); + std::this_thread::sleep_for(10ms); + } + }); + + async_thread_id = thread.get_id(); + + utils::hook::call(0x04E4A0D, cl_frame_stub); + } + }; +} + +REGISTER_COMPONENT(scheduler::component) diff --git a/src/component/scheduler.hpp b/src/component/scheduler.hpp new file mode 100644 index 0000000..09336b9 --- /dev/null +++ b/src/component/scheduler.hpp @@ -0,0 +1,25 @@ +#pragma once + +namespace scheduler +{ + extern std::thread::id async_thread_id; + + enum pipeline + { + client, + async, + count, + }; + + static const bool cond_continue = false; + static const bool cond_end = true; + + void clear_tasks(const pipeline type); + + void schedule(const std::function& callback, pipeline type = pipeline::client, + std::chrono::milliseconds delay = 0ms); + void loop(const std::function& callback, pipeline type = pipeline::client, + std::chrono::milliseconds delay = 0ms); + void once(const std::function& callback, pipeline type = pipeline::client, + std::chrono::milliseconds delay = 0ms); +} diff --git a/src/component/user_info.cpp b/src/component/user_info.cpp new file mode 100644 index 0000000..1b8a276 --- /dev/null +++ b/src/component/user_info.cpp @@ -0,0 +1,81 @@ +#include + +#include +#include "utils/hook.hpp" +#include "utils/info_string.hpp" +#include "utils/string.hpp" + +#include "scheduler.hpp" + +namespace user_info +{ + namespace + { + int a1 = 0; + void cl_check_user_info(int _a1, int force) + { + a1 = _a1; + + if (*game::connectionState <= game::connstate_t::CA_CHALLENGING) + return; + + if (game::cl_paused->current.enabled && !force) + return; + + const std::string infoString = game::Dvar_InfoString(_a1, 0x200); + utils::info_string info(infoString); + + const auto colorCode = rand() % 10; + + char numbers[_MAX_U64TOSTR_BASE2_COUNT]; + char name[16]; + + _itoa_s(rand() % 10000, numbers, sizeof(numbers), 10); + _snprintf_s(name, sizeof(name), _TRUNCATE, "^%d%s", colorCode, numbers); + + info.set("name", name); + + info.set("ec_usingTag", "1"); + info.set("ec_TagText", utils::string::va("^%dGG", colorCode)); + + char bigTitle[_MAX_U64TOSTR_BASE2_COUNT]; + _itoa_s(rand() % 512, bigTitle, sizeof(bigTitle), 10); + + info.set("ec_TitleBg", bigTitle); + + game::CL_AddReliableCommand(_a1, utils::string::va("userinfo \"%s\"", info.build().data())); + } + + __declspec(naked) void cl_check_user_info_stub() + { + __asm + { + pushad + + push 0 + push esi + call cl_check_user_info + add esp, 8 + + popad + ret + } + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + utils::hook::call(0x41CA53, cl_check_user_info_stub); + + scheduler::loop([] + { + cl_check_user_info(a1, TRUE); + }, scheduler::pipeline::client, 4s); + } + }; +} + +REGISTER_COMPONENT(user_info::component) diff --git a/src/dllmain.cpp b/src/dllmain.cpp index 1fd97d5..fc48d19 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -8,6 +8,7 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, { if (ul_reason_for_call == DLL_PROCESS_ATTACH) { + srand(uint32_t(time(nullptr))); component_loader::post_unpack(); } diff --git a/src/game/structs.hpp b/src/game/structs.hpp index 9b80551..c9e1a1b 100644 --- a/src/game/structs.hpp +++ b/src/game/structs.hpp @@ -204,9 +204,8 @@ namespace game netadrtype_t type; unsigned char ip[4]; unsigned __int16 port; - netsrc_t localNetID; - char __pad0[4]; - unsigned int index; + unsigned char ipx[10]; + unsigned int addrHandleIndex; }; static_assert(sizeof(netadr_s) == 24); @@ -278,6 +277,21 @@ namespace game LOCAL_CLIENT_COUNT = 4 }; + typedef enum + { + CA_DISCONNECTED = 0, + CA_CINEMATIC = 1, + CA_LOGO = 2, + CA_CONNECTING = 3, + CA_CHALLENGING = 4, + CA_CONNECTED = 5, + CA_SENDINGSTATS = 6, + CA_REQUESTING_MATCH_RULES = 7, + CA_LOADING = 8, + CA_PRIMED = 9, + CA_ACTIVE = 10 + } connstate_t; + struct msg_t { int overflowed; @@ -981,45 +995,6 @@ namespace game int hintForcedString; }; - struct trace_t - { - float fraction; - float normal[3]; - int surfaceFlags; - int contents; - char material[4]; - TraceHitType hitType; - unsigned __int16 hitId; - float fractionForHitType; - unsigned __int16 modelIndex; - unsigned __int16 partName; - unsigned __int16 partGroup; - bool allsolid; - bool startsolid; - bool walkable; - }; - - static_assert(sizeof(trace_t) == 52); - - struct pml_t - { - float forward[3]; - float right[3]; - float up[3]; - float frametime; - int msec; - int walking; - int groundPlane; - int almostGroundPlane; - trace_t groundTrace; - float impactSpeed; - float previous_origin[3]; - float previous_velocity[3]; - int holdrand; - }; - - static_assert(sizeof(pml_t) == 140); - struct clientConnection_t { int qport; // 0 diff --git a/src/game/symbols.hpp b/src/game/symbols.hpp index a71bb7b..7c37493 100644 --- a/src/game/symbols.hpp +++ b/src/game/symbols.hpp @@ -29,6 +29,7 @@ namespace game Dvar_RegisterFloat{0x4A5CF0}; WEAK symbol Dvar_SetBool{0x46DD70}; WEAK symbol Dvar_SetBoolByName{0x48C7D0}; + WEAK symbol Dvar_InfoString{0x4028C0}; WEAK symbol Key_GetBindingForCmd{0x47D300}; WEAK symbol Key_StringToKeynum{0x50A710}; // Virtual-Key Code @@ -49,6 +50,7 @@ namespace game WEAK symbol MSG_WriteInt64{0x4906B0}; WEAK symbol MSG_WriteShort{0x4ACD80}; WEAK symbol MSG_WriteData{0x4F8C20}; + WEAK symbol CL_AddReliableCommand{0x4EE3A0}; WEAK symbol LiveSteam_GetUid{0x4A4050}; WEAK symbol LiveSteam_Client_ConnectToSteamServer{0x4D6980}; @@ -60,4 +62,6 @@ namespace game WEAK symbol g_wv_hWnd{0x5A86AF0}; WEAK symbol s_wcd_hWnd{0x5A86330}; WEAK symbol serverId{0xFF5058}; + WEAK symbol connectionState{0x1060214}; + WEAK symbol cl_paused{0x1CE6190}; } diff --git a/src/stdinc.hpp b/src/stdinc.hpp index 6b3947d..434a8d7 100644 --- a/src/stdinc.hpp +++ b/src/stdinc.hpp @@ -1,7 +1,11 @@ #pragma once #define DLL_EXPORT extern "C" __declspec(dllexport) + +#define WIN32_LEAN_AND_MEAN + #include +#include #include #include @@ -10,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/src/utils/concurrency.hpp b/src/utils/concurrency.hpp new file mode 100644 index 0000000..05c5d3a --- /dev/null +++ b/src/utils/concurrency.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +namespace utils::concurrency +{ + template + class container + { + public: + template + R access(F&& accessor) const + { + std::lock_guard _{mutex_}; + return accessor(object_); + } + + template + R access(F&& accessor) + { + std::lock_guard _{mutex_}; + return accessor(object_); + } + + template + R access_with_lock(F&& accessor) const + { + std::unique_lock lock{mutex_}; + return accessor(object_, lock); + } + + template + R access_with_lock(F&& accessor) + { + std::unique_lock lock{mutex_}; + return accessor(object_, lock); + } + + T& get_raw() { return object_; } + const T& get_raw() const { return object_; } + + private: + mutable MutexType mutex_{}; + T object_{}; + }; +} diff --git a/src/utils/info_string.cpp b/src/utils/info_string.cpp new file mode 100644 index 0000000..0a9415f --- /dev/null +++ b/src/utils/info_string.cpp @@ -0,0 +1,66 @@ +#include + +#include "info_string.hpp" +#include "string.hpp" + +namespace utils +{ + info_string::info_string(const std::string& buffer) + { + this->parse(buffer); + } + + info_string::info_string(const std::string_view& buffer) + : info_string(std::string{buffer}) + { + } + + void info_string::set(const std::string& key, const std::string& value) + { + this->key_value_pairs_[key] = value; + } + + std::string info_string::get(const std::string& key) const + { + const auto value = this->key_value_pairs_.find(key); + if (value != this->key_value_pairs_.end()) + { + return value->second; + } + + return ""; + } + + void info_string::parse(std::string buffer) + { + if (buffer[0] == '\\') + { + buffer = buffer.substr(1); + } + + auto key_values = string::split(buffer, '\\'); + for (size_t i = 0; !key_values.empty() && i < (key_values.size() - 1); i += 2) + { + const auto& key = key_values[i]; + const auto& value = key_values[i + 1]; + this->key_value_pairs_[key] = value; + } + } + + std::string info_string::build() const + { + //auto first = true; + std::string info_string; + for (auto i = this->key_value_pairs_.begin(); i != this->key_value_pairs_.end(); ++i) + { + //if (first) first = false; + /*else*/ info_string.append("\\"); + + info_string.append(i->first); // Key + info_string.append("\\"); + info_string.append(i->second); // Value + } + + return info_string; + } +} diff --git a/src/utils/info_string.hpp b/src/utils/info_string.hpp new file mode 100644 index 0000000..7391041 --- /dev/null +++ b/src/utils/info_string.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +namespace utils +{ + class info_string + { + public: + info_string() = default; + info_string(const std::string& buffer); + info_string(const std::string_view& buffer); + + void set(const std::string& key, const std::string& value); + std::string get(const std::string& key) const; + std::string build() const; + + private: + std::unordered_map key_value_pairs_{}; + + void parse(std::string buffer); + }; +}