mirror of
https://github.com/Mr-X-GTA/YimMenu.git
synced 2024-12-22 12:07:46 +08:00
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:
parent
f51e8c2ca5
commit
82b8eaef39
@ -33,6 +33,7 @@ IncludeBlocks: Regroup
|
||||
IndentCaseLabels: 'false'
|
||||
IndentPPDirectives: BeforeHash
|
||||
IndentWidth: '4'
|
||||
InsertBraces: true
|
||||
TabWidth: 4
|
||||
IndentWrappedFunctionNames: 'true'
|
||||
KeepEmptyLinesAtTheStartOfBlocks: 'false'
|
||||
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -14,7 +14,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: [self-hosted, Windows]
|
||||
runs-on: windows-latest
|
||||
name: CI
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
2
.github/workflows/nightly.yml
vendored
2
.github/workflows/nightly.yml
vendored
@ -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'
|
||||
|
60
README.md
60
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
|
@ -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<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)
|
||||
|
35
src/backend/commands/player/kick/battleye.cpp
Normal file
35
src/backend/commands/player/kick/battleye.cpp
Normal 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);
|
||||
}
|
@ -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, {});
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -118,4 +118,5 @@ inline const static constexpr std::pair<const char*, uint32_t> packet_types[] =
|
||||
{"MsgTransitionHandshake", 0x8D},
|
||||
{"MsgDidInvitePlayerRequest", 0x8B},
|
||||
{"MsgDidInvitePlayerResponse", 0x8C},
|
||||
{"MsgBattlEyeCmd", 0x8F},
|
||||
};
|
@ -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
|
||||
|
@ -534,6 +534,9 @@ enum class KickReason : uint8_t
|
||||
NAT_TYPE,
|
||||
SCADMIN,
|
||||
SCADMIN_BLACKLIST,
|
||||
UNK,
|
||||
BATTLEYE_KICK,
|
||||
BATTLEYE_BAN,
|
||||
NUM_REASONS
|
||||
};
|
||||
|
||||
|
@ -443,6 +443,7 @@ namespace rage
|
||||
Msg_0x45 = 0x45,
|
||||
Msg_0x89 = 0x89,
|
||||
Msg_0x86 = 0x86,
|
||||
MsgBattlEyeCmd = 0x8F,
|
||||
};
|
||||
|
||||
enum class eEventNetworkType : int64_t
|
||||
|
@ -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");
|
||||
|
@ -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::send_clone_create>("SCC", g_pointers->m_gta.m_send_clone_create);
|
||||
|
||||
g_hooking = this;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
17
src/hooks/misc/send_clone_create.cpp
Normal file
17
src/hooks/misc/send_clone_create.cpp
Normal 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);
|
||||
}
|
||||
}
|
@ -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<uint8_t>(-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))
|
||||
|
@ -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 <network/CNetworkPlayerMgr.hpp>
|
||||
|
||||
@ -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();
|
||||
|
||||
|
@ -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<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))
|
||||
{
|
||||
@ -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<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:
|
||||
{
|
||||
if ((int)msgType > 0x91) [[unlikely]]
|
||||
|
@ -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);
|
||||
|
@ -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&()
|
||||
{
|
||||
|
@ -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<PVOID>();
|
||||
@ -1986,6 +1986,15 @@ namespace big
|
||||
{
|
||||
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
|
||||
|
||||
|
282
src/services/battleye/battleye_service.cpp
Normal file
282
src/services/battleye/battleye_service.cpp
Normal 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);
|
||||
}
|
||||
}
|
62
src/services/battleye/battleye_service.hpp
Normal file
62
src/services/battleye/battleye_service.hpp
Normal 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;
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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<int>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<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(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);
|
||||
}
|
||||
|
@ -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 <network/snSession.hpp>
|
||||
|
||||
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);
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user