diff --git a/.clang-format b/.clang-format index a0ec550c..0c7c0a9e 100644 --- a/.clang-format +++ b/.clang-format @@ -33,6 +33,7 @@ IncludeBlocks: Regroup IndentCaseLabels: 'false' IndentPPDirectives: BeforeHash IndentWidth: '4' +InsertBraces: true TabWidth: 4 IndentWrappedFunctionNames: 'true' KeepEmptyLinesAtTheStartOfBlocks: 'false' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3901000b..8439af32 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ concurrency: jobs: ci: - runs-on: [self-hosted, Windows] + runs-on: windows-latest name: CI steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index d8a756eb..939990a4 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -29,7 +29,7 @@ jobs: fi build_nightly: - runs-on: [self-hosted, Windows] + runs-on: windows-latest name: Build Nightly needs: check_recent_commit if: needs.check_recent_commit.outputs.should_run == 'true' diff --git a/README.md b/README.md index 68be46ed..36ca633f 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,3 @@ -# YimMenu +# Message to Rockstar Security Team: -![](https://img.shields.io/badge/dynamic/json?color=ffab00&label=Online%20Version&query=%24.game.online&url=https%3A%2F%2Fraw.githubusercontent.com%2FYimMenu%2FYimMenu%2Fmaster%2Fmetadata.json&style=flat-square&labelColor=000000) ![](https://img.shields.io/badge/dynamic/json?color=ffab00&label=Game%20Build&query=%24.game.build&url=https%3A%2F%2Fraw.githubusercontent.com%2FYimMenu%2FYimMenu%2Fmaster%2Fmetadata.json&style=flat-square&labelColor=000000) - -A mod menu base for Grand Theft Auto V. -Strictly for educational purposes. - -YimMenu is originally based of off [BigBaseV2](https://github.com/Pocakking/BigBaseV2) which was an amazing base at the time but nowadays is a bit dated. -So here I am with an up-to-date menu focusing on protecting the user from toxic modders. - -## Table of contents - - * [How to build](#how-to-build) - * [Make your own flavour of YimMenu](#make-your-own-flavour-of-yimmenu) - * [Staying Up To Date](#staying-up-to-date) - * [Project Structure](#project-structure) - * [Contributing](#contributing) - -## How to compile YimMenu - -Read the [SETUP](https://github.com/YimMenu/YimMenu/wiki/Setup-your-PC-for-YimMenu-Development) guide. - -## Make your own flavour of YimMenu - -Fork YimMenu and git clone that repository instead of https://github.com/YimMenu/YimMenu.git - -Click the below link to make your own fork of YimMenu: -https://github.com/YimMenu/YimMenu/fork - -## Staying Up To Date - -Pull the latest changes from this repository. - -With a command line it is as easy as: - -```bash -git pull -``` - -CMake should be handling removed / added files automatically without any user input. - -If this is not the case for some reason you'll have to redo the steps in the [Making changes to the source code section of the SETUP](https://github.com/YimMenu/YimMenu/wiki/Setup-your-PC-for-YimMenu-Development#making-changes-to-the-source-code). - -If you are doing custom modifications to the codebase and have a fork you are on your own for staying up to date with upstream (this repository), google stuff like "merge from upstream" and learn how to use Git. - -## Project Structure - -- `backend/` all features that should be ran in a loop are in here sorted by category -- `gui/` includes everything related to UI elements -- `hooks/` function hooks -- `native_hooks/` hooks to natives -- `services/` service files to interact and maintain stuff -- `util/` general utility functions to keep code as compact and as much in one place as possible - -## Contributing - -You're free to contribute to YimMenu as long as the features are useful, not overly toxic and do not contain anything money related that might get the menu targeted by Take2. - -Make sure to read the [CONTRIBUTING.md](CONTRIBUTING.md) file. +## You haven't seen the last of us \ No newline at end of file diff --git a/src/backend/backend.cpp b/src/backend/backend.cpp index 6ce9bd0e..f4faddab 100644 --- a/src/backend/backend.cpp +++ b/src/backend/backend.cpp @@ -21,13 +21,6 @@ namespace big { void bypass_battleye() { - auto old = g.session.spoof_host_token_type; - g.session.spoof_host_token_type = std::max(old, 1); - if (old != g.session.spoof_host_token_type) - g.session.spoof_host_token_dirty = true; - g.session.kick_host_when_forcing_host = true; - g.session.exclude_modders_from_kick_host = true; // useful - constexpr std::array valid_hashes = {1410389794, 967, 1523678325, 472, 0, 0, 1323039495, 0, 0, 1731098795, 2256610353, 17956, 414639110, 307143837, 3443181821, 0}; if (auto hashes = *g_pointers->m_gta.m_game_data_hash) diff --git a/src/backend/commands/player/kick/battleye.cpp b/src/backend/commands/player/kick/battleye.cpp new file mode 100644 index 00000000..f7687da0 --- /dev/null +++ b/src/backend/commands/player/kick/battleye.cpp @@ -0,0 +1,35 @@ +#include "backend/player_command.hpp" +#include "gta_util.hpp" +#include "pointers.hpp" +#include "services/battleye/battleye_service.hpp" + +#include + +#include "packet.hpp" +#include + +namespace big +{ + class battleye_update_kick : player_command + { + using player_command::player_command; + + virtual CommandAccessLevel get_access_level() override + { + return CommandAccessLevel::TOXIC; + } + + virtual void execute(player_ptr player, const command_arguments& _args, const std::shared_ptr ctx) override + { + unsigned char data[] = {0x00, 0x50, 0x31, 0x4A, 0xC0, 0x1A, 0x13, 0xFF, 0xFF, 0xFF}; + player->tampered_with_be = true; + for (int i = 0; i < 20; i++) + { + data[0] = i; + g_battleye_service.send_message_to_client(player->get_net_game_player()->get_host_token(), &data, sizeof(data)); + } + } + }; + + battleye_update_kick g_battleye_update_kick("battleupdate", "BATTLEYE_UPDATE_KICK", "BATTLEYE_UPDATE_KICK_DESC", 0); +} \ No newline at end of file diff --git a/src/backend/commands/player/kick/smart_kick.cpp b/src/backend/commands/player/kick/smart_kick.cpp index 12ed2f7d..7c2ecba4 100644 --- a/src/backend/commands/player/kick/smart_kick.cpp +++ b/src/backend/commands/player/kick/smart_kick.cpp @@ -20,7 +20,7 @@ namespace big else { if (player->is_host()) - player_command::get("oomkick"_J)->call(player, {}); + player_command::get("battleupdate"_J)->call(player, {}); else player_command::get("desync"_J)->call(player, {}); } diff --git a/src/backend/looped/session/auto_kick_host.cpp b/src/backend/looped/session/auto_kick_host.cpp index 4bb000bb..183f3d45 100644 --- a/src/backend/looped/session/auto_kick_host.cpp +++ b/src/backend/looped/session/auto_kick_host.cpp @@ -26,6 +26,7 @@ namespace big static bool bLastKickHost = false; void looped::session_auto_kick_host() { + #if 0 bool kick_host = *g_pointers->m_gta.m_is_session_started && g.session.spoof_host_token_type != 0 && g.session.kick_host_when_forcing_host; if (kick_host && !bLastKickHost && is_next_in_queue()) [[unlikely]] { @@ -42,5 +43,6 @@ namespace big }); } bLastKickHost = kick_host; + #endif } } diff --git a/src/core/data/packet_types.hpp b/src/core/data/packet_types.hpp index c7dad731..e90269ba 100644 --- a/src/core/data/packet_types.hpp +++ b/src/core/data/packet_types.hpp @@ -114,8 +114,9 @@ inline const static constexpr std::pair packet_types[] = {"Msg_0x89", 0x89}, {"Msg_0x86", 0x86}, {"MsgDtlsCxnCommand", 0x8A}, - {"MsgSetKickVote", 0x8E}, - {"MsgTransitionHandshake", 0x8D}, + {"MsgSetKickVote", 0x8E}, + {"MsgTransitionHandshake", 0x8D}, {"MsgDidInvitePlayerRequest", 0x8B}, {"MsgDidInvitePlayerResponse", 0x8C}, + {"MsgBattlEyeCmd", 0x8F}, }; \ No newline at end of file diff --git a/src/core/settings.hpp b/src/core/settings.hpp index 7c609f83..087dfa9d 100644 --- a/src/core/settings.hpp +++ b/src/core/settings.hpp @@ -150,8 +150,9 @@ namespace big bool external_console = true; bool window_hook = false; bool block_all_metrics = false; + bool battleye_server = false; - NLOHMANN_DEFINE_TYPE_INTRUSIVE(debug, logs, external_console, window_hook, block_all_metrics) + NLOHMANN_DEFINE_TYPE_INTRUSIVE(debug, logs, external_console, window_hook, block_all_metrics, battleye_server) } debug{}; struct tunables diff --git a/src/gta/enums.hpp b/src/gta/enums.hpp index 62b02591..4884e77e 100644 --- a/src/gta/enums.hpp +++ b/src/gta/enums.hpp @@ -534,6 +534,9 @@ enum class KickReason : uint8_t NAT_TYPE, SCADMIN, SCADMIN_BLACKLIST, + UNK, + BATTLEYE_KICK, + BATTLEYE_BAN, NUM_REASONS }; diff --git a/src/gta/net_game_event.hpp b/src/gta/net_game_event.hpp index 89802763..6b175231 100644 --- a/src/gta/net_game_event.hpp +++ b/src/gta/net_game_event.hpp @@ -443,6 +443,7 @@ namespace rage Msg_0x45 = 0x45, Msg_0x89 = 0x89, Msg_0x86 = 0x86, + MsgBattlEyeCmd = 0x8F, }; enum class eEventNetworkType : int64_t diff --git a/src/gta_pointers.hpp b/src/gta_pointers.hpp index 9fda7522..dddc4b7b 100644 --- a/src/gta_pointers.hpp +++ b/src/gta_pointers.hpp @@ -419,6 +419,8 @@ namespace big functions::get_last_keyboard_state m_get_last_keyboard_state; PVOID m_network_can_access_multiplayer; + + PVOID m_send_clone_create; }; #pragma pack(pop) static_assert(sizeof(gta_pointers) % 8 == 0, "Pointers are not properly aligned"); diff --git a/src/hooking/hooking.cpp b/src/hooking/hooking.cpp index 611cb33b..d7dac7c9 100644 --- a/src/hooking/hooking.cpp +++ b/src/hooking/hooking.cpp @@ -158,6 +158,8 @@ namespace big detour_hook_helper::add("NCAM", g_pointers->m_gta.m_network_can_access_multiplayer); + detour_hook_helper::add("SCC", g_pointers->m_gta.m_send_clone_create); + g_hooking = this; } diff --git a/src/hooking/hooking.hpp b/src/hooking/hooking.hpp index ff2775ce..9cd49ae4 100644 --- a/src/hooking/hooking.hpp +++ b/src/hooking/hooking.hpp @@ -215,6 +215,8 @@ namespace big static void* create_pool_item(GenericPool* pool); static bool network_can_access_multiplayer(void* a1, int* error); + + static void send_clone_create(CNetworkObjectMgr* _this, rage::netObject* object, CNetGamePlayer* player, rage::datBitBuffer* buffer); }; class minhook_keepalive diff --git a/src/hooks/info/get_network_event_data.cpp b/src/hooks/info/get_network_event_data.cpp index 3860c93c..6e12ee8a 100644 --- a/src/hooks/info/get_network_event_data.cpp +++ b/src/hooks/info/get_network_event_data.cpp @@ -61,7 +61,7 @@ namespace big if (g.protections.kick_rejoin && !NETWORK::NETWORK_IS_ACTIVITY_SESSION() && SCRIPT::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH("maintransition"_J) == 0 && !STREAMING::IS_PLAYER_SWITCH_IN_PROGRESS()) { g_fiber_pool->queue_job([] { - session::join_session(gta_util::get_network()->m_last_joined_session.m_session_info); + session::join_session(*(rage::rlSessionInfo*)((uint64_t)gta_util::get_network() + 0x1E448)); // TODO gta_util::get_network()->m_last_joined_session.m_session_info }); g_notification_service.push_warning("KICKED"_T.data(), "USER_DESYNC_KICKED_REJOIN"_T.data()); } diff --git a/src/hooks/misc/send_clone_create.cpp b/src/hooks/misc/send_clone_create.cpp new file mode 100644 index 00000000..347c70a8 --- /dev/null +++ b/src/hooks/misc/send_clone_create.cpp @@ -0,0 +1,17 @@ +#include "hooking/hooking.hpp" +#include "services/players/player_service.hpp" + +#include + +namespace big +{ + void hooks::send_clone_create(CNetworkObjectMgr* _this, rage::netObject* object, CNetGamePlayer* player, rage::datBitBuffer* buffer) + { + if (auto plyr = g_player_service->get_by_id(player->m_player_id); plyr && plyr->bad_host && (eNetObjType)object->m_object_type == eNetObjType::NET_OBJ_TYPE_PLAYER) + { + return; + } + + g_hooking->get_original()(_this, object, player, buffer); + } +} \ No newline at end of file diff --git a/src/hooks/player_management/assign_physical_index.cpp b/src/hooks/player_management/assign_physical_index.cpp index d3e04b2a..29b05c24 100644 --- a/src/hooks/player_management/assign_physical_index.cpp +++ b/src/hooks/player_management/assign_physical_index.cpp @@ -5,10 +5,10 @@ #include "lua/lua_manager.hpp" #include "services/player_database/player_database_service.hpp" #include "services/players/player_service.hpp" +#include "services/battleye/battleye_service.hpp" #include "util/notify.hpp" #include "util/session.hpp" - namespace big { inline bool is_spoofed_host_token(rage::rlGamerInfo* info) @@ -25,6 +25,7 @@ namespace big if (new_index == static_cast(-1)) { + g_battleye_service.remove_player(player->get_host_token()); g_player_service->player_leave(player); if (net_player_data) @@ -50,6 +51,8 @@ namespace big g_player_service->player_join(player); if (net_player_data) { + g_battleye_service.add_player(player->get_host_token(), net_player_data->m_gamer_handle.m_rockstar_id, net_player_data->m_name); + if (g.protections.admin_check) { if (admin_rids.contains(net_player_data->m_gamer_handle.m_rockstar_id)) diff --git a/src/hooks/player_management/network_player_mgr.cpp b/src/hooks/player_management/network_player_mgr.cpp index 34f1d657..eafeeb88 100644 --- a/src/hooks/player_management/network_player_mgr.cpp +++ b/src/hooks/player_management/network_player_mgr.cpp @@ -2,6 +2,7 @@ #include "hooking/hooking.hpp" #include "lua/lua_manager.hpp" #include "services/players/player_service.hpp" +#include "services/battleye/battleye_service.hpp" #include @@ -25,6 +26,14 @@ namespace big void hooks::network_player_mgr_shutdown(CNetworkPlayerMgr* _this) { + for (auto& [_, player] : g_player_service->players()) + { + if (player->is_valid()) + { + g_battleye_service.remove_player(player->get_net_game_player()->get_host_token()); + } + } + g_player_service->do_cleanup(); self::spawned_vehicles.clear(); diff --git a/src/hooks/protections/receive_net_message.cpp b/src/hooks/protections/receive_net_message.cpp index 895e76e5..bc582ef6 100644 --- a/src/hooks/protections/receive_net_message.cpp +++ b/src/hooks/protections/receive_net_message.cpp @@ -8,7 +8,9 @@ #include "lua/lua_manager.hpp" #include "natives.hpp" #include "services/players/player_service.hpp" +#include "services/battleye/battleye_service.hpp" #include "util/chat.hpp" +#include "util/entity.hpp" #include "util/session.hpp" #include "gta/net_object_mgr.hpp" @@ -370,7 +372,7 @@ namespace big } case rage::eNetMessage::MsgKickPlayer: { - KickReason reason = buffer.Read(3); + KickReason reason = buffer.Read(5); if (!is_host_of_session(gta_util::get_network()->m_game_session_ptr, event->m_peer_id)) { @@ -378,13 +380,16 @@ namespace big return true; } + LOGF(stream::net_messages, VERBOSE, "{} sent us a MsgKickPlayer, reason = {}", peer->m_info.name, (int)reason); + if (reason == KickReason::VOTED_OUT) { g_notification_service.push_warning("PROTECTIONS"_T.data(), "YOU_HAVE_BEEN_KICKED"_T.data()); return true; } - LOGF(stream::net_messages, VERBOSE, "{} sent us a MsgKickPlayer, reason = {}", peer->m_info.name, (int)reason); + if (reason == KickReason::BATTLEYE_KICK || reason == KickReason::BATTLEYE_BAN) + return true; break; } case rage::eNetMessage::MsgRadioStationSyncRequest: @@ -706,9 +711,6 @@ namespace big { if (g.session.log_chat_messages) chat::log_chat(message, player, spam_reason, is_team); - g_notification_service.push("PROTECTIONS"_T.data(), - - std::format("{} {}", player->get_name(), "IS_A_SPAMMER"_T.data())); player->is_spammer = true; g.reactions.chat_spam.process(player); return true; @@ -736,6 +738,30 @@ namespace big return true; } + case rage::eNetMessage::MsgBattlEyeCmd: + { + char data[1028]{}; + int size = buffer.Read(11); + bool client = buffer.Read(1); + buffer.SeekForward(4); // normalize before we read + + buffer.ReadArray(&data, size * 8); + + if (client && player) + { + g_battleye_service.receive_message(player->get_net_game_player()->get_host_token(), &data, size); + } + else if (player && !player->bad_host) + { + player->bad_host = true; + g_fiber_pool->queue_job([player] { + entity::force_remove_network_entity(g_local_player, player, false); + }); + g_battleye_service.send_message_to_server(player->get_net_game_player()->get_host_token(), &data, size); + } + + break; + } default: { if ((int)msgType > 0x91) [[unlikely]] diff --git a/src/packet.cpp b/src/packet.cpp index ea45a917..1045367e 100644 --- a/src/packet.cpp +++ b/src/packet.cpp @@ -12,18 +12,17 @@ namespace big { } - void packet::send(uint32_t msg_id) + void packet::send(uint32_t msg_id, bool unk_flag) { - g_pointers->m_gta - .m_queue_packet(gta_util::get_network()->m_game_session_ptr->m_net_connection_mgr, msg_id, m_data, (m_buffer.m_curBit + 7) >> 3, 1, nullptr); + g_pointers->m_gta.m_queue_packet(gta_util::get_network()->m_game_session_ptr->m_net_connection_mgr, msg_id, m_data, (m_buffer.m_curBit + 7) >> 3, unk_flag ? 0x4000001 : 1, nullptr); } void packet::send(player_ptr player, int connection_id) { - send(player->get_session_player()->m_player_data.m_peer_id_2, connection_id); + send_direct(player->get_session_player()->m_player_data.m_peer_id_2, connection_id); } - void packet::send(int peer_id, int connection_id) + void packet::send_direct(int peer_id, int connection_id) { auto mgr = gta_util::get_network()->m_game_session_ptr->m_net_connection_mgr; auto peer = g_pointers->m_gta.m_get_connection_peer(mgr, peer_id); diff --git a/src/packet.hpp b/src/packet.hpp index 1599be7e..be7c57b6 100644 --- a/src/packet.hpp +++ b/src/packet.hpp @@ -8,13 +8,13 @@ namespace big class packet { public: - char m_data[0x400]{}; + char m_data[0x480]{}; rage::datBitBuffer m_buffer; packet(); - void send(uint32_t msg_id); + void send(uint32_t msg_id, bool unk_flag = false); void send(player_ptr player, int connection_id); - void send(int peer_id, int connection_id); + void send_direct(int peer_id, int connection_id); inline operator rage::datBitBuffer&() { diff --git a/src/pointers.cpp b/src/pointers.cpp index f6eec04c..1144cacb 100644 --- a/src/pointers.cpp +++ b/src/pointers.cpp @@ -3,7 +3,7 @@ #include "gta_pointers_layout_info.hpp" #include "sc_pointers_layout_info.hpp" -#define GTA_VERSION_TARGET "1.69-3323" +#define GTA_VERSION_TARGET "1.69-3337" namespace big { @@ -1817,7 +1817,7 @@ namespace big // Session Request Patch { "SRP", - "48 8B BD 80 01 00 00 E9 FF 00 00 00", + "48 8B 9D 70 01 00 00 E9 FF 00 00 00", [](memory::handle ptr) { g_pointers->m_gta.m_session_request_patch = ptr.add(0x13).as(); @@ -1986,6 +1986,15 @@ namespace big { g_pointers->m_gta.m_network_can_access_multiplayer = ptr.add(1).rip().as(); } + }, + // Send Clone Create + { + "SCC", + "48 8B 02 4D 8B F8", + [](memory::handle ptr) + { + g_pointers->m_gta.m_send_clone_create = ptr.sub(0x1C).as(); + } } >(); // don't leave a trailing comma at the end diff --git a/src/services/battleye/battleye_service.cpp b/src/services/battleye/battleye_service.cpp new file mode 100644 index 00000000..e8fa89b1 --- /dev/null +++ b/src/services/battleye/battleye_service.cpp @@ -0,0 +1,282 @@ +#include "battleye_service.hpp" +#include "services/players/player_service.hpp" +#include "packet.hpp" +#include "script_mgr.hpp" +#include "thread_pool.hpp" +#include "util/session.hpp" +#include "backend/looped_command.hpp" + +#include + +namespace +{ + static std::string base64_encode(const std::string& data) + { + static constexpr char sEncodingTable[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; + + size_t in_len = data.size(); + size_t out_len = 4 * ((in_len + 2) / 3); + std::string ret(out_len, '\0'); + size_t i; + char* p = ret.data(); + + for (i = 0; i < in_len - 2; i += 3) + { + *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; + *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)]; + *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int)(data[i + 2] & 0xC0) >> 6)]; + *p++ = sEncodingTable[data[i + 2] & 0x3F]; + } + if (i < in_len) + { + *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; + if (i == (in_len - 1)) + { + *p++ = sEncodingTable[((data[i] & 0x3) << 4)]; + *p++ = '='; + } + else + { + *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)]; + *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)]; + } + *p++ = '='; + } + + return ret; + } + +} + +namespace big +{ + class battleye_server : looped_command + { + using looped_command::looped_command; + + virtual void on_enable() override + { + g_battleye_service.start(); + } + + virtual void on_tick() override + { + } + + virtual void on_disable() override + { + g_battleye_service.stop(); + } + }; + + battleye_server g_battleye_server("battleyeserver", "BATTLEYE_SERVER", "BATTLEYE_SERVER_DESC", g.debug.battleye_server); + + bool battleye_service::is_running() + { + return m_battleye_api.m_shutdown != nullptr; + } + + void battleye_service::send_message_to_client(std::uint64_t token, void* message, int size) + { + packet pkt; + + char header_buf[32]; + rage::datBitBuffer header(header_buf, sizeof(header_buf)); + + header.Write(size, 11); + header.Write(false, 1); // we are not the client + + pkt.write_message(rage::eNetMessage::MsgBattlEyeCmd); + pkt.m_buffer.WriteArray(&header_buf, header.GetDataLength() * 8); + pkt.m_buffer.WriteArray(message, size * 8); + + // send to player + if (auto plyr = g_player_service->get_by_host_token(token); plyr && plyr->get_session_player()) + { + pkt.send(plyr->get_session_player()->m_msg_id, true); + } + } + + void battleye_service::send_message_to_server(std::uint64_t token, void* message, int size) + { + packet pkt; + + char header_buf[32]; + rage::datBitBuffer header(header_buf, sizeof(header_buf)); + + header.Write(size, 11); + header.Write(true, 1); // we are the client + + pkt.write_message(rage::eNetMessage::MsgBattlEyeCmd); + pkt.m_buffer.WriteArray(&header_buf, header.GetDataLength() * 8); + pkt.m_buffer.WriteArray(message, size * 8); + + // send to player + if (auto plyr = g_player_service->get_by_host_token(token); plyr && plyr->get_session_player()) + { + pkt.send(plyr->get_session_player()->m_msg_id, true); + } + } + + void battleye_service::kick_player(std::uint64_t token, const char* reason) + { + if (auto plyr = g_player_service->get_by_host_token(token)) + { + LOGF(WARNING, "BattlEye wants us to kick {} for {}", plyr->get_name(), reason); + if (!plyr->tampered_with_be) + { + session::add_infraction(plyr, Infraction::TRIGGERED_ANTICHEAT); + } + } + } + + void battleye_service::script_func() + { + bool was_host = false; + while (is_running()) + { + if (g_player_service->get_self()->is_valid()) + { + bool is_host = g_player_service->get_self()->is_host(); + + if (is_host != was_host) + { + if (is_host) + { + for (auto& [_, player] : g_player_service->players()) + { + add_player(player->get_net_game_player()->get_host_token(), player->get_rockstar_id(), player->get_name()); + } + } + was_host = is_host; + } + } + + big::script::get_current()->yield(); + } + } + + void battleye_service::thread_func() + { + while (is_running() && g_running) + { + // we need to run this every frame (lockstep with game or otherwise) + { + std::lock_guard lock(m_mutex); + if (is_running() && !m_battleye_api.m_run()) + { + LOG(WARNING) << "BE::Run failed"; + } + } + + std::this_thread::yield(); + } + } + + battleye_service::battleye_service() + { + } + + battleye_service::~battleye_service() + { + stop(); + } + + void battleye_service::start() + { + std::lock_guard lock(m_mutex); + + if (is_running()) + { + return; + } + + auto handle = LoadLibraryA("BattlEye\\BEServer_x64.dll"); + + if (!handle) + { + LOG(WARNING) << "Failed to load BattlEye"; + return; + } + + m_battleye_user_data.m_game_name = "paradise"; + m_battleye_user_data.m_log_func = [](const char* message, int level) { + LOG(INFO) << "BattlEye: " << message; + }; + m_battleye_user_data.m_kick_player = [](std::uint64_t player, const char* reason) { + g_battleye_service.kick_player(player, reason); + }; + m_battleye_user_data.m_send_message = [](std::uint64_t player, const void* pkt_data, int size) { + g_battleye_service.send_message_to_client(player, const_cast(pkt_data), size); + }; + if (reinterpret_cast(GetProcAddress(handle, "Init"))(1, &m_battleye_user_data, &m_battleye_api)) + { + // background thread (not game thread) + g_thread_pool->push([this] { this->thread_func(); }); + + // background script (game thread) + g_script_mgr.add_script(std::make_unique([this] { this->script_func(); }, "BE Background Script", false)); + } + else + { + LOG(WARNING) << "Failed to initialize BattlEye"; + } + } + + void battleye_service::stop() + { + std::lock_guard lock(m_mutex); + + if (!is_running()) + { + return; + } + + m_battleye_api.m_shutdown(); + memset(&m_battleye_api, 0, sizeof(CApi)); + } + + void battleye_service::add_player(std::uint64_t token, std::uint64_t rockstar_id, const char* name) + { + std::lock_guard lock(m_mutex); + + if (!is_running() || !g_player_service->get_self()->is_host()) + { + return; + } + + char string[32]{}; + + snprintf(string, sizeof(string), "%I64d", rockstar_id); + auto guid = base64_encode(string); + + m_battleye_api.m_add_player(token, -1, 0, name, false); + m_battleye_api.m_assign_guid(token, guid.data(), guid.length()); + m_battleye_api.m_assign_guid_verified(token, guid.data(), guid.length()); + m_battleye_api.m_set_player_state(token, 1); + } + + void battleye_service::remove_player(std::uint64_t token) + { + std::lock_guard lock(m_mutex); + + if (!is_running()) + { + return; + } + + m_battleye_api.m_set_player_state(token, -1); + } + + void battleye_service::receive_message(std::uint64_t token, void* message, int size) + { + std::lock_guard lock(m_mutex); + + if (!is_running()) + { + return; + } + + m_battleye_api.m_receive_message(token, const_cast(message), size); + } +} \ No newline at end of file diff --git a/src/services/battleye/battleye_service.hpp b/src/services/battleye/battleye_service.hpp new file mode 100644 index 00000000..1a54758e --- /dev/null +++ b/src/services/battleye/battleye_service.hpp @@ -0,0 +1,62 @@ +#pragma once + +namespace big +{ + class battleye_service + { + using log_func_t = void (*)(const char* msg, int level); + using kick_player_t = void (*)(std::uint64_t id, const char* reason); + using send_message_t = void (*)(std::uint64_t id, const void* msg, int size); + using add_player_t = void (*)(std::uint64_t id, u_long ip_addr, u_short port, const char* name, char unused); + using set_player_state_t = void (*)(std::uint64_t id, int reason); + using assign_guid_t = void (*)(std::uint64_t id, const void* guid, int size); // binary + using receive_message_t = void (*)(std::uint64_t id, const void* packet, int size); + + using shutdown_t = void (*)(); + using run_t = bool (*)(); + using run_command_t = void (*)(const char* command); + + struct CUserData + { + const char* m_game_name; + log_func_t m_log_func; + kick_player_t m_kick_player; + send_message_t m_send_message; + void* m_unk; + } m_battleye_user_data{}; + + struct CApi + { + shutdown_t m_shutdown; + run_t m_run; + run_command_t m_run_command; + add_player_t m_add_player; + set_player_state_t m_set_player_state; + assign_guid_t m_assign_guid; + assign_guid_t m_assign_guid_verified; + receive_message_t m_receive_message; + } m_battleye_api{}; + + using init_t = bool (*)(int api_level, CUserData* data, CApi* api); + + std::mutex m_mutex{}; + + bool is_running(); + public: + battleye_service(); + ~battleye_service(); + + void start(); + void stop(); + void add_player(std::uint64_t token, std::uint64_t rockstar_id, const char* name); + void remove_player(std::uint64_t token); + void receive_message(std::uint64_t token, void* message, int size); + void send_message_to_client(std::uint64_t token, void* message, int size); + void send_message_to_server(std::uint64_t token, void* message, int size); + void kick_player(std::uint64_t token, const char* reason); + void script_func(); + void thread_func(); + }; + + inline battleye_service g_battleye_service; +} \ No newline at end of file diff --git a/src/services/matchmaking/matchmaking_service.cpp b/src/services/matchmaking/matchmaking_service.cpp index b605100c..29cb2e42 100644 --- a/src/services/matchmaking/matchmaking_service.cpp +++ b/src/services/matchmaking/matchmaking_service.cpp @@ -35,8 +35,6 @@ namespace big // TODO: the logic is incorrect - attributes->m_param_values[0] = -0x22F37A9E; - if (g.spoofing.spoof_session_bad_sport_status == 1) attributes->m_param_values[0] |= (1 << 14); // Bad Sport diff --git a/src/services/players/player.hpp b/src/services/players/player.hpp index 78dc4dec..280223c8 100644 --- a/src/services/players/player.hpp +++ b/src/services/players/player.hpp @@ -96,6 +96,8 @@ namespace big bool log_clones = false; bool log_network_events = false; + bool bad_host = false; + bool trigger_desync_kick = false; bool trigger_end_session_kick = false; @@ -103,6 +105,8 @@ namespace big int spectating_player = -1; + bool tampered_with_be = false; + menu_settings::script_block_opts script_block_opts{}; protected: bool equals(const CNetGamePlayer* net_game_player) const; diff --git a/src/util/entity.cpp b/src/util/entity.cpp index 7fc4b5f1..8e337f8d 100644 --- a/src/util/entity.cpp +++ b/src/util/entity.cpp @@ -60,7 +60,7 @@ namespace big::entity { if (ptr->m_net_object) { - force_remove_network_entity(ptr, true); + force_remove_network_entity(ptr); return; } } @@ -379,15 +379,15 @@ namespace big::entity return closest_entity; } - void force_remove_network_entity(rage::CDynamicEntity* entity, bool delete_locally) + void force_remove_network_entity(rage::CDynamicEntity* entity, player_ptr for_player, bool delete_locally) { if (!entity->m_net_object) return; - force_remove_network_entity(entity->m_net_object->m_object_id, entity->m_net_object->m_ownership_token, delete_locally); + force_remove_network_entity(entity->m_net_object->m_object_id, entity->m_net_object->m_ownership_token, for_player, delete_locally); } - void force_remove_network_entity(std::uint16_t net_id, int ownership_token, bool delete_locally) + void force_remove_network_entity(std::uint16_t net_id, int ownership_token, player_ptr for_player, bool delete_locally) { char buf[0x200]{}; rage::datBitBuffer remove_buf(buf, sizeof(buf)); @@ -417,13 +417,20 @@ namespace big::entity pack.write(remove_buf.GetPosition(), 13); pack.m_buffer.WriteArray(&buf, remove_buf.GetPosition()); - for (auto& player : g_player_service->players()) + if (for_player) { - if (player.second->get_net_game_player()) + pack.send(for_player->get_net_game_player()->m_msg_id); + } + else + { + for (auto& player : g_player_service->players()) { - if (!player.second->get_ped() || player.second->get_ped()->m_net_object->m_object_id != net_id) // would crash the player otherwise + if (player.second->get_net_game_player()) { - pack.send(player.second->get_net_game_player()->m_msg_id); + if (!player.second->get_ped() || player.second->get_ped()->m_net_object->m_object_id != net_id) // would crash the player otherwise + { + pack.send(player.second->get_net_game_player()->m_msg_id); + } } } } diff --git a/src/util/entity.hpp b/src/util/entity.hpp index a2228f05..8f45f372 100644 --- a/src/util/entity.hpp +++ b/src/util/entity.hpp @@ -22,6 +22,6 @@ namespace big::entity bool request_model(rage::joaat_t hash); double distance_to_middle_of_screen(const rage::fvector2& screen_pos); Entity get_entity_closest_to_middle_of_screen(rage::fwEntity** pointer = nullptr, std::vector ignore_entities = {}, bool include_veh = true, bool include_ped = true, bool include_prop = true, bool include_players = true); - void force_remove_network_entity(rage::CDynamicEntity* entity, bool delete_locally = true); - void force_remove_network_entity(std::uint16_t net_id, int ownership_token = -1, bool delete_locally = true); + void force_remove_network_entity(rage::CDynamicEntity* entity, player_ptr for_player = nullptr, bool delete_locally = true); + void force_remove_network_entity(std::uint16_t net_id, int ownership_token = -1, player_ptr for_player = nullptr, bool delete_locally = true); } diff --git a/src/views/debug/view_debug_misc.cpp b/src/views/debug/view_debug_misc.cpp index fed05ed4..0b45fa75 100644 --- a/src/views/debug/view_debug_misc.cpp +++ b/src/views/debug/view_debug_misc.cpp @@ -5,6 +5,12 @@ #include "util/system.hpp" #include "view_debug.hpp" #include "network/CNetworkPlayerMgr.hpp" +#include "thread_pool.hpp" +#include "packet.hpp" +#include "script_mgr.hpp" +#include "util/session.hpp" + +#include namespace big { @@ -12,6 +18,7 @@ namespace big { if (ImGui::BeginTabItem("DEBUG_TAB_MISC"_T.data())) { + components::command_checkbox<"battleyeserver">(); components::command_checkbox<"external_console">(); components::command_checkbox<"windowhook">("VIEW_DEBUG_MISC_DISABLE_GTA_WINDOW_HOOK"_T); diff --git a/src/views/players/player/player_kick.cpp b/src/views/players/player/player_kick.cpp index dfad6a30..31363e46 100644 --- a/src/views/players/player/player_kick.cpp +++ b/src/views/players/player/player_kick.cpp @@ -24,5 +24,7 @@ namespace big components::player_command_button<"endkick">(g_player_service->get_selected()); ImGui::SameLine(); components::player_command_button<"desync">(g_player_service->get_selected()); + ImGui::SameLine(); + components::player_command_button<"battleupdate">(g_player_service->get_selected()); } }