Update for b3337 (#1)

* Merge "BE fixes and additions"

* Merge "Add BattlEye server"

* Apply diff

* feat: bypass BE kicks from legit hosts

* fix: remove patched stuff

* Remove duplicate chat spammer notification (#2)

---------

Co-authored-by: Cringe-Arab <evmeschenco@gmail.com>
This commit is contained in:
Mr-X-GTA 2024-09-27 22:47:41 +02:00 committed by GitHub
parent f51e8c2ca5
commit 82b8eaef39
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 512 additions and 100 deletions

View File

@ -33,6 +33,7 @@ IncludeBlocks: Regroup
IndentCaseLabels: 'false' IndentCaseLabels: 'false'
IndentPPDirectives: BeforeHash IndentPPDirectives: BeforeHash
IndentWidth: '4' IndentWidth: '4'
InsertBraces: true
TabWidth: 4 TabWidth: 4
IndentWrappedFunctionNames: 'true' IndentWrappedFunctionNames: 'true'
KeepEmptyLinesAtTheStartOfBlocks: 'false' KeepEmptyLinesAtTheStartOfBlocks: 'false'

View File

@ -14,7 +14,7 @@ concurrency:
jobs: jobs:
ci: ci:
runs-on: [self-hosted, Windows] runs-on: windows-latest
name: CI name: CI
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

View File

@ -29,7 +29,7 @@ jobs:
fi fi
build_nightly: build_nightly:
runs-on: [self-hosted, Windows] runs-on: windows-latest
name: Build Nightly name: Build Nightly
needs: check_recent_commit needs: check_recent_commit
if: needs.check_recent_commit.outputs.should_run == 'true' if: needs.check_recent_commit.outputs.should_run == 'true'

View File

@ -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) ## You haven't seen the last of us
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.

View File

@ -21,13 +21,6 @@ namespace big
{ {
void bypass_battleye() 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<std::uint32_t, 16> valid_hashes = {1410389794, 967, 1523678325, 472, 0, 0, 1323039495, 0, 0, 1731098795, 2256610353, 17956, 414639110, 307143837, 3443181821, 0}; constexpr std::array<std::uint32_t, 16> 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) if (auto hashes = *g_pointers->m_gta.m_game_data_hash)

View File

@ -0,0 +1,35 @@
#include "backend/player_command.hpp"
#include "gta_util.hpp"
#include "pointers.hpp"
#include "services/battleye/battleye_service.hpp"
#include <network/Network.hpp>
#include "packet.hpp"
#include <network/snSession.hpp>
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<command_context> 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);
}

View File

@ -20,7 +20,7 @@ namespace big
else else
{ {
if (player->is_host()) if (player->is_host())
player_command::get("oomkick"_J)->call(player, {}); player_command::get("battleupdate"_J)->call(player, {});
else else
player_command::get("desync"_J)->call(player, {}); player_command::get("desync"_J)->call(player, {});
} }

View File

@ -26,6 +26,7 @@ namespace big
static bool bLastKickHost = false; static bool bLastKickHost = false;
void looped::session_auto_kick_host() 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; 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]] if (kick_host && !bLastKickHost && is_next_in_queue()) [[unlikely]]
{ {
@ -42,5 +43,6 @@ namespace big
}); });
} }
bLastKickHost = kick_host; bLastKickHost = kick_host;
#endif
} }
} }

View File

@ -114,8 +114,9 @@ inline const static constexpr std::pair<const char*, uint32_t> packet_types[] =
{"Msg_0x89", 0x89}, {"Msg_0x89", 0x89},
{"Msg_0x86", 0x86}, {"Msg_0x86", 0x86},
{"MsgDtlsCxnCommand", 0x8A}, {"MsgDtlsCxnCommand", 0x8A},
{"MsgSetKickVote", 0x8E}, {"MsgSetKickVote", 0x8E},
{"MsgTransitionHandshake", 0x8D}, {"MsgTransitionHandshake", 0x8D},
{"MsgDidInvitePlayerRequest", 0x8B}, {"MsgDidInvitePlayerRequest", 0x8B},
{"MsgDidInvitePlayerResponse", 0x8C}, {"MsgDidInvitePlayerResponse", 0x8C},
{"MsgBattlEyeCmd", 0x8F},
}; };

View File

@ -150,8 +150,9 @@ namespace big
bool external_console = true; bool external_console = true;
bool window_hook = false; bool window_hook = false;
bool block_all_metrics = 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{}; } debug{};
struct tunables struct tunables

View File

@ -534,6 +534,9 @@ enum class KickReason : uint8_t
NAT_TYPE, NAT_TYPE,
SCADMIN, SCADMIN,
SCADMIN_BLACKLIST, SCADMIN_BLACKLIST,
UNK,
BATTLEYE_KICK,
BATTLEYE_BAN,
NUM_REASONS NUM_REASONS
}; };

View File

@ -443,6 +443,7 @@ namespace rage
Msg_0x45 = 0x45, Msg_0x45 = 0x45,
Msg_0x89 = 0x89, Msg_0x89 = 0x89,
Msg_0x86 = 0x86, Msg_0x86 = 0x86,
MsgBattlEyeCmd = 0x8F,
}; };
enum class eEventNetworkType : int64_t enum class eEventNetworkType : int64_t

View File

@ -419,6 +419,8 @@ namespace big
functions::get_last_keyboard_state m_get_last_keyboard_state; functions::get_last_keyboard_state m_get_last_keyboard_state;
PVOID m_network_can_access_multiplayer; PVOID m_network_can_access_multiplayer;
PVOID m_send_clone_create;
}; };
#pragma pack(pop) #pragma pack(pop)
static_assert(sizeof(gta_pointers) % 8 == 0, "Pointers are not properly aligned"); static_assert(sizeof(gta_pointers) % 8 == 0, "Pointers are not properly aligned");

View File

@ -158,6 +158,8 @@ namespace big
detour_hook_helper::add<hooks::network_can_access_multiplayer>("NCAM", g_pointers->m_gta.m_network_can_access_multiplayer); detour_hook_helper::add<hooks::network_can_access_multiplayer>("NCAM", g_pointers->m_gta.m_network_can_access_multiplayer);
detour_hook_helper::add<hooks::send_clone_create>("SCC", g_pointers->m_gta.m_send_clone_create);
g_hooking = this; g_hooking = this;
} }

View File

@ -215,6 +215,8 @@ namespace big
static void* create_pool_item(GenericPool* pool); static void* create_pool_item(GenericPool* pool);
static bool network_can_access_multiplayer(void* a1, int* error); 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 class minhook_keepalive

View File

@ -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()) 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([] { 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()); g_notification_service.push_warning("KICKED"_T.data(), "USER_DESYNC_KICKED_REJOIN"_T.data());
} }

View File

@ -0,0 +1,17 @@
#include "hooking/hooking.hpp"
#include "services/players/player_service.hpp"
#include <network/CNetGamePlayer.hpp>
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<hooks::send_clone_create>()(_this, object, player, buffer);
}
}

View File

@ -5,10 +5,10 @@
#include "lua/lua_manager.hpp" #include "lua/lua_manager.hpp"
#include "services/player_database/player_database_service.hpp" #include "services/player_database/player_database_service.hpp"
#include "services/players/player_service.hpp" #include "services/players/player_service.hpp"
#include "services/battleye/battleye_service.hpp"
#include "util/notify.hpp" #include "util/notify.hpp"
#include "util/session.hpp" #include "util/session.hpp"
namespace big namespace big
{ {
inline bool is_spoofed_host_token(rage::rlGamerInfo* info) inline bool is_spoofed_host_token(rage::rlGamerInfo* info)
@ -25,6 +25,7 @@ namespace big
if (new_index == static_cast<uint8_t>(-1)) if (new_index == static_cast<uint8_t>(-1))
{ {
g_battleye_service.remove_player(player->get_host_token());
g_player_service->player_leave(player); g_player_service->player_leave(player);
if (net_player_data) if (net_player_data)
@ -50,6 +51,8 @@ namespace big
g_player_service->player_join(player); g_player_service->player_join(player);
if (net_player_data) 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 (g.protections.admin_check)
{ {
if (admin_rids.contains(net_player_data->m_gamer_handle.m_rockstar_id)) if (admin_rids.contains(net_player_data->m_gamer_handle.m_rockstar_id))

View File

@ -2,6 +2,7 @@
#include "hooking/hooking.hpp" #include "hooking/hooking.hpp"
#include "lua/lua_manager.hpp" #include "lua/lua_manager.hpp"
#include "services/players/player_service.hpp" #include "services/players/player_service.hpp"
#include "services/battleye/battleye_service.hpp"
#include <network/CNetworkPlayerMgr.hpp> #include <network/CNetworkPlayerMgr.hpp>
@ -25,6 +26,14 @@ namespace big
void hooks::network_player_mgr_shutdown(CNetworkPlayerMgr* _this) 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(); g_player_service->do_cleanup();
self::spawned_vehicles.clear(); self::spawned_vehicles.clear();

View File

@ -8,7 +8,9 @@
#include "lua/lua_manager.hpp" #include "lua/lua_manager.hpp"
#include "natives.hpp" #include "natives.hpp"
#include "services/players/player_service.hpp" #include "services/players/player_service.hpp"
#include "services/battleye/battleye_service.hpp"
#include "util/chat.hpp" #include "util/chat.hpp"
#include "util/entity.hpp"
#include "util/session.hpp" #include "util/session.hpp"
#include "gta/net_object_mgr.hpp" #include "gta/net_object_mgr.hpp"
@ -370,7 +372,7 @@ namespace big
} }
case rage::eNetMessage::MsgKickPlayer: case rage::eNetMessage::MsgKickPlayer:
{ {
KickReason reason = buffer.Read<KickReason>(3); KickReason reason = buffer.Read<KickReason>(5);
if (!is_host_of_session(gta_util::get_network()->m_game_session_ptr, event->m_peer_id)) 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; return true;
} }
LOGF(stream::net_messages, VERBOSE, "{} sent us a MsgKickPlayer, reason = {}", peer->m_info.name, (int)reason);
if (reason == KickReason::VOTED_OUT) if (reason == KickReason::VOTED_OUT)
{ {
g_notification_service.push_warning("PROTECTIONS"_T.data(), "YOU_HAVE_BEEN_KICKED"_T.data()); g_notification_service.push_warning("PROTECTIONS"_T.data(), "YOU_HAVE_BEEN_KICKED"_T.data());
return true; 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; break;
} }
case rage::eNetMessage::MsgRadioStationSyncRequest: case rage::eNetMessage::MsgRadioStationSyncRequest:
@ -706,9 +711,6 @@ namespace big
{ {
if (g.session.log_chat_messages) if (g.session.log_chat_messages)
chat::log_chat(message, player, spam_reason, is_team); 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; player->is_spammer = true;
g.reactions.chat_spam.process(player); g.reactions.chat_spam.process(player);
return true; return true;
@ -736,6 +738,30 @@ namespace big
return true; return true;
} }
case rage::eNetMessage::MsgBattlEyeCmd:
{
char data[1028]{};
int size = buffer.Read<int>(11);
bool client = buffer.Read<bool>(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: default:
{ {
if ((int)msgType > 0x91) [[unlikely]] if ((int)msgType > 0x91) [[unlikely]]

View File

@ -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 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);
.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);
} }
void packet::send(player_ptr player, int connection_id) 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 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); auto peer = g_pointers->m_gta.m_get_connection_peer(mgr, peer_id);

View File

@ -8,13 +8,13 @@ namespace big
class packet class packet
{ {
public: public:
char m_data[0x400]{}; char m_data[0x480]{};
rage::datBitBuffer m_buffer; rage::datBitBuffer m_buffer;
packet(); 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(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&() inline operator rage::datBitBuffer&()
{ {

View File

@ -3,7 +3,7 @@
#include "gta_pointers_layout_info.hpp" #include "gta_pointers_layout_info.hpp"
#include "sc_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 namespace big
{ {
@ -1817,7 +1817,7 @@ namespace big
// Session Request Patch // Session Request Patch
{ {
"SRP", "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) [](memory::handle ptr)
{ {
g_pointers->m_gta.m_session_request_patch = ptr.add(0x13).as<PVOID>(); g_pointers->m_gta.m_session_request_patch = ptr.add(0x13).as<PVOID>();
@ -1986,6 +1986,15 @@ namespace big
{ {
g_pointers->m_gta.m_network_can_access_multiplayer = ptr.add(1).rip().as<PVOID>(); g_pointers->m_gta.m_network_can_access_multiplayer = ptr.add(1).rip().as<PVOID>();
} }
},
// 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<PVOID>();
}
} }
>(); // don't leave a trailing comma at the end >(); // don't leave a trailing comma at the end

View File

@ -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 <network/snSession.hpp>
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<int>(size, 11);
header.Write<bool>(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<int>(size, 11);
header.Write<bool>(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<void*>(pkt_data), size);
};
if (reinterpret_cast<init_t>(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<big::script>([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<const void*>(message), size);
}
}

View File

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

View File

@ -35,8 +35,6 @@ namespace big
// TODO: the logic is incorrect // TODO: the logic is incorrect
attributes->m_param_values[0] = -0x22F37A9E;
if (g.spoofing.spoof_session_bad_sport_status == 1) if (g.spoofing.spoof_session_bad_sport_status == 1)
attributes->m_param_values[0] |= (1 << 14); // Bad Sport attributes->m_param_values[0] |= (1 << 14); // Bad Sport

View File

@ -96,6 +96,8 @@ namespace big
bool log_clones = false; bool log_clones = false;
bool log_network_events = false; bool log_network_events = false;
bool bad_host = false;
bool trigger_desync_kick = false; bool trigger_desync_kick = false;
bool trigger_end_session_kick = false; bool trigger_end_session_kick = false;
@ -103,6 +105,8 @@ namespace big
int spectating_player = -1; int spectating_player = -1;
bool tampered_with_be = false;
menu_settings::script_block_opts script_block_opts{}; menu_settings::script_block_opts script_block_opts{};
protected: protected:
bool equals(const CNetGamePlayer* net_game_player) const; bool equals(const CNetGamePlayer* net_game_player) const;

View File

@ -60,7 +60,7 @@ namespace big::entity
{ {
if (ptr->m_net_object) if (ptr->m_net_object)
{ {
force_remove_network_entity(ptr, true); force_remove_network_entity(ptr);
return; return;
} }
} }
@ -379,15 +379,15 @@ namespace big::entity
return closest_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) if (!entity->m_net_object)
return; 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]{}; char buf[0x200]{};
rage::datBitBuffer remove_buf(buf, sizeof(buf)); rage::datBitBuffer remove_buf(buf, sizeof(buf));
@ -417,13 +417,20 @@ namespace big::entity
pack.write<int>(remove_buf.GetPosition(), 13); pack.write<int>(remove_buf.GetPosition(), 13);
pack.m_buffer.WriteArray(&buf, remove_buf.GetPosition()); 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);
}
} }
} }
} }

View File

@ -22,6 +22,6 @@ namespace big::entity
bool request_model(rage::joaat_t hash); bool request_model(rage::joaat_t hash);
double distance_to_middle_of_screen(const rage::fvector2& screen_pos); 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<Entity> ignore_entities = {}, bool include_veh = true, bool include_ped = true, bool include_prop = true, bool include_players = true); Entity get_entity_closest_to_middle_of_screen(rage::fwEntity** pointer = nullptr, std::vector<Entity> 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(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, 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);
} }

View File

@ -5,6 +5,12 @@
#include "util/system.hpp" #include "util/system.hpp"
#include "view_debug.hpp" #include "view_debug.hpp"
#include "network/CNetworkPlayerMgr.hpp" #include "network/CNetworkPlayerMgr.hpp"
#include "thread_pool.hpp"
#include "packet.hpp"
#include "script_mgr.hpp"
#include "util/session.hpp"
#include <network/snSession.hpp>
namespace big namespace big
{ {
@ -12,6 +18,7 @@ namespace big
{ {
if (ImGui::BeginTabItem("DEBUG_TAB_MISC"_T.data())) if (ImGui::BeginTabItem("DEBUG_TAB_MISC"_T.data()))
{ {
components::command_checkbox<"battleyeserver">();
components::command_checkbox<"external_console">(); components::command_checkbox<"external_console">();
components::command_checkbox<"windowhook">("VIEW_DEBUG_MISC_DISABLE_GTA_WINDOW_HOOK"_T); components::command_checkbox<"windowhook">("VIEW_DEBUG_MISC_DISABLE_GTA_WINDOW_HOOK"_T);

View File

@ -24,5 +24,7 @@ namespace big
components::player_command_button<"endkick">(g_player_service->get_selected()); components::player_command_button<"endkick">(g_player_service->get_selected());
ImGui::SameLine(); ImGui::SameLine();
components::player_command_button<"desync">(g_player_service->get_selected()); components::player_command_button<"desync">(g_player_service->get_selected());
ImGui::SameLine();
components::player_command_button<"battleupdate">(g_player_service->get_selected());
} }
} }