Lessen breakup kicks and more (#625)

This commit is contained in:
maybegreat48 2022-11-21 15:42:12 +00:00 committed by GitHub
parent 6f3e3cc6b3
commit bbd13dd06d
46 changed files with 1109 additions and 146 deletions

View File

@ -3,7 +3,7 @@ include(FetchContent)
FetchContent_Declare(
gtav_classes
GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git
GIT_TAG 1dc66fb37fec895c96c2b1e4944e382a83c5e993
GIT_TAG 16f85bea73691623d1a43c4a912f94f93e509597
GIT_PROGRESS TRUE
CONFIGURE_COMMAND ""
BUILD_COMMAND ""

View File

@ -126,12 +126,9 @@ namespace big
while (g_running)
{
g_player_service->iterate([](const player_entry &entry)
{
looped::player_never_wanted(entry.second);
});
looped::player_good_options();
looped::player_spectate();
looped::player_remote_control_vehicle();
script::get_current()->yield();
}

View File

@ -15,8 +15,9 @@ namespace big
static void tunables_disable_phone();
static void player_never_wanted(const player_ptr &player);
static void player_good_options();
static void player_spectate();
static void player_remote_control_vehicle();
static void protections_replay_interface();

View File

@ -0,0 +1,67 @@
#include "gta/PickupRewards.h"
#include "backend/looped/looped.hpp"
#include "services/players/player_service.hpp"
#include "util/globals.hpp"
#include "util/misc.hpp"
namespace big
{
// rate limit script events to prevent crashes
static int offRadarPlayer = 0;
static int neverWantedPlayer = 0;
void looped::player_good_options()
{
if (!NETWORK::NETWORK_IS_SESSION_STARTED())
return;
offRadarPlayer++;
if (offRadarPlayer > 32)
offRadarPlayer = 0;
neverWantedPlayer++;
if (neverWantedPlayer > 32)
neverWantedPlayer = 0;
g_player_service->iterate([](const player_entry& entry)
{
if ((g->session.off_radar_all || entry.second->off_radar) && offRadarPlayer == entry.second->id())
globals::give_remote_otr(entry.second->id());
});
g_player_service->iterate([](const player_entry& entry)
{
if ((g->session.never_wanted_all || entry.second->never_wanted) && PLAYER::GET_PLAYER_WANTED_LEVEL(entry.second->id()) > 0 && neverWantedPlayer == entry.second->id())
globals::clear_wanted_player(entry.second->id());
});
if (g->session.semi_godmode_all)
{
g_pointers->m_give_pickup_rewards(-1, REWARD_HEALTH);
g_pointers->m_give_pickup_rewards(-1, REWARD_ARMOUR);
}
else
{
g_player_service->iterate([](const player_entry& entry)
{
if (entry.second->semi_godmode)
{
if (CPed* ped = entry.second->get_ped())
{
if (ped->m_maxhealth == 0 || ped->m_health == 0 || misc::has_bit_set((int*)&ped->m_damage_bits, 8))
return;
if (ped->m_health < ped->m_maxhealth)
{
g_pointers->m_give_pickup_rewards(1 << entry.second->id(), REWARD_HEALTH);
}
if (ped->m_armor < 50.0f)
{
g_pointers->m_give_pickup_rewards(1 << entry.second->id(), REWARD_ARMOUR);
}
}
}
});
}
}
}

View File

@ -1,14 +0,0 @@
#include "backend/looped/looped.hpp"
#include "services/players/player_service.hpp"
#include "util/globals.hpp"
namespace big
{
void looped::player_never_wanted(const player_ptr &player)
{
if (player->never_wanted)
{
globals::clear_wanted_player(player->id());
}
}
}

View File

@ -0,0 +1,50 @@
#include "backend/looped/looped.hpp"
#include "pointers.hpp"
#include "natives.hpp"
#include "util/entity.hpp"
#include "fiber_pool.hpp"
namespace big
{
void looped::player_remote_control_vehicle()
{
if (g->m_remote_controller_vehicle == -1)
return;
if (!ENTITY::DOES_ENTITY_EXIST(g->m_remote_controlled_vehicle))
{
g->m_remote_controlled_vehicle = -1;
g->m_remote_controlled_vehicle = -1;
return;
}
if (!ENTITY::DOES_ENTITY_EXIST(g->m_remote_controller_vehicle))
{
g->m_remote_controlled_vehicle = -1;
g->m_remote_controlled_vehicle = -1;
return;
}
if (VEHICLE::IS_VEHICLE_SEAT_FREE(g->m_remote_controller_vehicle, -1, TRUE))
{
auto controlled = g->m_remote_controlled_vehicle;
auto controller = g->m_remote_controller_vehicle;
g_fiber_pool->queue_job([controlled, controller]
{
if (entity::take_control_of(controlled))
{
ENTITY::SET_ENTITY_COLLISION(g->m_remote_controlled_vehicle, TRUE, TRUE);
ENTITY::DETACH_ENTITY(controlled, TRUE, TRUE);
VEHICLE::SET_VEHICLE_DOORS_LOCKED(controlled, 0);
VEHICLE::SET_VEHICLE_DOORS_LOCKED_FOR_ALL_PLAYERS(controlled, FALSE);
ENTITY::SET_ENTITY_INVINCIBLE(controlled, FALSE);
entity::delete_entity(controller);
}
});
g->m_remote_controller_vehicle = -1;
g->m_remote_controlled_vehicle = -1;
}
}
};

View File

@ -13,7 +13,7 @@ namespace big
std::uint64_t host_token;
g_pointers->m_generate_uuid(&host_token);
host_token = g->session.force_session_host ? 1 : host_token;
host_token = g->session.force_session_host ? (rand() % 10000) : host_token;
*g_pointers->m_host_token = host_token;

View File

@ -12,7 +12,8 @@ namespace big
SPOOFED_ROCKSTAR_ID,
TRIGGERED_ANTICHEAT,
TRIED_CRASH_PLAYER,
TRIED_KICK_PLAYER
TRIED_KICK_PLAYER,
BLAME_EXPLOSION_DETECTED
};
inline std::unordered_map<Infraction, const char*> infraction_desc =
@ -23,6 +24,7 @@ namespace big
{Infraction::SPOOFED_ROCKSTAR_ID, "Had spoofed RID"},
{Infraction::TRIGGERED_ANTICHEAT, "Triggered Rockstar's anticheat"},
{Infraction::TRIED_CRASH_PLAYER, "Tried to crash you"},
{Infraction::TRIED_KICK_PLAYER, "Tried to kick you"}
{Infraction::TRIED_KICK_PLAYER, "Tried to kick you"},
{Infraction::BLAME_EXPLOSION_DETECTED, "Tried to blame someone for their explosion"}
};
}

View File

@ -146,6 +146,7 @@ namespace big
script_events script_events{};
bool script_host_kick = true;
bool rid_join = false;
bool lessen_breakups = false; // disabled by default due to anticheat concerns
};
struct self
@ -200,6 +201,14 @@ namespace big
bool player_magnet_enabled = false;
int player_magnet_count = 32;
bool is_team = false;
bool name_spoof_enabled = false;
bool advertise_menu = false;
std::string spoofed_name = "";
// not to be saved
bool never_wanted_all = false;
bool off_radar_all = false;
bool semi_godmode_all = false;
};
struct settings {
@ -406,6 +415,10 @@ namespace big
int player_count = 0;
CNetGamePlayer* m_syncing_player = nullptr;
std::unordered_map<std::uint64_t, std::uint64_t> m_spoofed_peer_ids;
int m_remote_controller_vehicle = -1;
int m_remote_controlled_vehicle = -1;
debug debug{};
tunables tunables{};
@ -573,6 +586,7 @@ namespace big
this->protections.script_host_kick = j["protections"]["script_host_kick"];
this->protections.rid_join = j["protections"]["rid_join"];
this->protections.lessen_breakups = j["protections"]["lessen_breakups"];
this->tunables.disable_phone = j["tunables"]["disable_phone"];
this->tunables.no_idle_kick = j["tunables"]["no_idle_kick"];
@ -612,6 +626,9 @@ namespace big
this->session.player_magnet_enabled = j["session"]["player_magnet_enabled"];
this->session.player_magnet_count = j["session"]["player_magnet_count"];
this->session.is_team = j["session"]["is_team"];
this->session.name_spoof_enabled = j["session"]["name_spoof_enabled"];
this->session.advertise_menu = j["session"]["advertise_menu"];
this->session.spoofed_name = j["session"]["spoofed_name"];
this->settings.dev_dlc = j["settings"]["dev_dlc"];
this->settings.hotkeys.menu_toggle = j["settings"]["hotkeys"]["menu_toggle"];
@ -861,7 +878,8 @@ namespace big
},
{ "script_host_kick", g->protections.script_host_kick },
{ "rid_join", g->protections.rid_join }
{ "rid_join", g->protections.rid_join },
{ "lessen_breakups", g->protections.lessen_breakups }
}
},
{
@ -931,7 +949,10 @@ namespace big
{ "force_session_host", this->session.force_session_host },
{ "player_magnet_enabled", this->session.player_magnet_enabled },
{ "player_magnet_count", this->session.player_magnet_count },
{ "is_team", this->session.is_team }
{ "is_team", this->session.is_team },
{ "name_spoof_enabled", this->session.name_spoof_enabled },
{ "advertise_menu", this->session.advertise_menu },
{ "spoofed_name", this->session.spoofed_name }
}
},
{

40
src/core/scr_globals.hpp Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include "script_global.hpp"
namespace big::scr_globals
{
namespace size
{
constexpr int globalplayer_bd = 453;
constexpr int gpbd_fm_3 = 599;
constexpr int gpbd_fm_1 = 888;
}
static inline script_global gsbd(2680265);
static inline script_global gsbd_fm(1835502);
static inline script_global gsbd_kicking(1883751);
static inline script_global gsbd_fm_events(1920255);
static inline script_global gsbd_block_c(2683918);
static inline script_global globalplayer_bd(2689235);
static inline script_global gpbd_fm_3(1892703);
static inline script_global gpbd_fm_1(1853348);
static inline script_global launcher_global(2779753);
static inline script_global sp(113386);
static inline script_global mission_definition(91229);
static inline script_global creator_job_metadata(4718592);
static inline script_global terminate_creator(1574607); // NETWORK::NETWORK_BAIL(1, 0, 0); fm_*_creator
static inline script_global switch_struct(1574632);
static inline script_global mission_creator_radar_follows_camera(2621443);
static inline script_global mission_creator_exited(1574530);
static inline script_global in_multiplayer(78319); // g_bInMultiplayer
static inline script_global vehicle_global = script_global(1585857);
static inline script_global mechanic_global = script_global(2815059);
static inline script_global spawn_global = script_global(2725439);
}

View File

@ -59,7 +59,6 @@ class CGameScriptHandlerNetwork;
class CGameScriptHandlerMgr;
class CEntity;
class CDynamicEntity;
class CPhysical;
class CObject;

View File

@ -211,6 +211,49 @@ namespace rage
WriteQWord((uint64_t)data, length);
}
template<typename T>
inline void WriteSigned(int length, T data)
{
int sign = data < 0;
int signEx = (data < 0) ? 0xFFFFFFFF : 0;
int d = (data ^ signEx);
Write<int>(1, sign);
Write<int>(length - 1, d);
}
inline float ReadFloat(int length, float divisor)
{
auto integer = Read<int>(length);
float max = (1 << length) - 1;
return ((float)integer / max) * divisor;
}
inline void WriteFloat(int length, float divisor, float value)
{
float max = (1 << length) - 1;
int integer = (int)((value / divisor) * max);
Write<int>(length, integer);
}
inline float ReadSignedFloat(int length, float divisor)
{
auto integer = ReadSigned<int>(length);
float max = (1 << (length - 1)) - 1;
return ((float)integer / max) * divisor;
}
inline void WriteSignedFloat(int length, float divisor, float value)
{
float max = (1 << (length - 1)) - 1;
int integer = (int)((value / divisor) * max);
WriteSigned<int>(length, integer);
}
public:
void* m_data; //0x0000
uint32_t m_bitOffset; //0x0008
@ -376,7 +419,7 @@ namespace rage
virtual __int64 get_type() = 0;
virtual void unk_0x20() = 0;
virtual void unk_0x28() = 0;
virtual bool get_extra_information(__int64* info_array, int check) = 0;
virtual bool get_extra_information(void* info_array, int check) = 0;
virtual void unk_0x38() = 0;
};

View File

@ -179,6 +179,86 @@ class CGameScriptHandlerMgr : public rage::scriptHandlerMgr
{
};
class CGameScriptHandlerNetComponent
{
public:
virtual ~CGameScriptHandlerNetComponent() = default;
virtual bool _0x08(void*) = 0;
virtual void _0x10(CNetGamePlayer*) = 0; // creates a scriptId?
virtual void* player_left(CNetGamePlayer* player) = 0;
virtual void* send_host_migration_event(CNetGamePlayer* player) = 0;
virtual void* player_joined(void**, void* msg_ctx) = 0;
virtual void* player_joined_ack(void**, void* msg_ctx) = 0;
virtual bool _0x38(void*, void*) = 0; // join_script?
virtual void* _0x40(void*, void*) = 0;
virtual void* _0x48(void*, void*, void*) = 0;
virtual void* _0x50(void*, void*) = 0;
virtual void* _0x58(void*, void*) = 0;
virtual void* _0x60(void*, void*) = 0;
virtual void* _0x68(void*, void*) = 0;
virtual void _0x70(void*, void*) = 0;
virtual void _0x78(void*, void*) = 0;
virtual short _0x80(void*, void*) = 0;
virtual void* _0x88(void*, void*) = 0;
virtual void* _0x90(void*, void*) = 0; // HOST_MIGRATION_FAILED
virtual bool _0x98(void*, void*) = 0;
virtual void* _0xA0(void*, void*) = 0;
virtual void* _0xA8(void*, void*) = 0;
virtual short _0xB0(void*, void*) = 0;
virtual bool register_host_broadcast_data(void* data, int size, char* a3) = 0;
virtual bool register_player_broadcast_data(int a1, int size, bool a3) = 0;
virtual bool _0xC8() = 0; // something to do to joining session
virtual bool _0xD0() = 0;
virtual bool add_player_to_script(CNetGamePlayer* player, short* outParticipantID, short* outSlot, int* outFailReason) = 0;
virtual bool add_player_to_script_internal(CNetGamePlayer* player, short participantID, short slot) = 0; // player aka participant
virtual bool remove_player_from_script(CNetGamePlayer* player) = 0;
virtual void* player_left_impl(CNetGamePlayer*, bool) = 0;
virtual bool do_host_migration(CNetGamePlayer* player, short host_token, bool unk) = 0; // aka _0xF8
virtual void* leave_from_script() = 0; // calls above function with player = nullptr
virtual bool _0x108() = 0;
virtual void* _0x110() = 0;
virtual bool _0x118() = 0; // related to above function
CGameScriptHandler* m_script_handler; //0x0008
char pad_0010[48]; //0x0010
std::uint32_t m_participants; //0x0040
};
static_assert(sizeof(rage::scriptHandler) == 0x60);
static_assert(sizeof(CGameScriptHandler) == 0xA0);
static_assert(sizeof(CGameScriptHandlerNetwork) == 0xB0);

View File

@ -76,6 +76,9 @@ namespace big
detour_hook_helper::add<hooks::sort_session_details>("SSD", g_pointers->m_sort_session_details);
detour_hook_helper::add<hooks::add_player_to_session>("APTS", g_pointers->m_add_player_to_session);
detour_hook_helper::add<hooks::send_chat_net_message>("SCNM", g_pointers->m_send_chat_net_message);
g_hooking = this;
}

View File

@ -16,6 +16,8 @@ class CDynamicEntityGameStateDataNode;
class CVehicleGadgetDataNode;
class CJoinRequestContext;
class SessionSortEntry;
class RemoteGamerInfoMsg;
class CMsgTextMessage;
namespace rage
{
@ -91,6 +93,9 @@ namespace big
static bool handle_join_request(Network* network, rage::snSession* session, rage::rlGamerInfo* player_info, CJoinRequestContext* ctx, BOOL is_transition_session);
static bool sort_session_details(SessionSortEntry* e1, SessionSortEntry* e2);
static bool add_player_to_session(rage::netConnectionManager* mgr, int receiver_msg_id, int* out_command_hndl, RemoteGamerInfoMsg* msg, int flags, void* unk);
static bool send_chat_net_message(rage::netConnectionManager* mgr, int receiver_msg_id, CMsgTextMessage* msg, int flags, void* unk);
};
class minhook_keepalive

View File

@ -1,4 +1,6 @@
#include "hooking.hpp"
#include "gta/net_game_event.hpp"
#include <entities/CDynamicEntity.hpp>
namespace big
{

View File

@ -0,0 +1,26 @@
#include "hooking.hpp"
#include "gta_util.hpp"
#include "services/players/player_service.hpp"
#include <network/Network.hpp>
#include <network/CMsgTextMessage.hpp>
namespace big
{
bool hooks::send_chat_net_message(rage::netConnectionManager* mgr, int receiver_msg_id, CMsgTextMessage* msg, int flags, void* unk)
{
std::uint64_t host_token = -1;
for (auto& [_, plyr] : g_player_service->players())
{
if (plyr->get_net_game_player()->m_msg_id == receiver_msg_id)
{
host_token = plyr->get_net_data()->m_host_token;
break;
}
}
if (g->m_spoofed_peer_ids.contains(host_token))
msg->m_peer_id = g->m_spoofed_peer_ids[host_token];
return g_hooking->get_original<hooks::send_chat_net_message>()(mgr, receiver_msg_id, msg, flags, unk);
}
}

View File

@ -11,11 +11,11 @@ namespace big
{
void* hooks::assign_physical_index(CNetworkPlayerMgr* netPlayerMgr, CNetGamePlayer* player, uint8_t new_index)
{
const auto result = g_hooking->get_original<hooks::assign_physical_index>()(netPlayerMgr, player, new_index);
const auto* net_player_data = player->get_net_data();
if (new_index == static_cast<uint8_t>(-1))
{
g->m_spoofed_peer_ids.erase(player->get_net_data()->m_host_token);
g_player_service->player_leave(player);
if (net_player_data)
@ -29,9 +29,10 @@ namespace big
g_notification_service->push("Player Left", std::format("{} freeing slot #{} with Rockstar ID: {}", net_player_data->m_name, player->m_player_id, net_player_data->m_gamer_handle_2.m_rockstar_id));
}
return result;
return g_hooking->get_original<hooks::assign_physical_index>()(netPlayerMgr, player, new_index);
}
const auto result = g_hooking->get_original<hooks::assign_physical_index>()(netPlayerMgr, player, new_index);
g_player_service->player_join(player);
if (net_player_data)
{

View File

@ -18,6 +18,7 @@ namespace big
void hooks::network_player_mgr_shutdown(CNetworkPlayerMgr* _this)
{
g->m_spoofed_peer_ids.clear();
g_player_service->do_cleanup();
if (g->notifications.network_player_mgr_shutdown.log)

View File

@ -0,0 +1,55 @@
#include "hooking.hpp"
#include "gta_util.hpp"
#include <network/Network.hpp>
#include <network/RemoteGamerInfoMsg.hpp>
// https://stackoverflow.com/questions/8120062/generate-random-64-bit-integer
unsigned
static rand256()
{
static unsigned const limit = RAND_MAX - RAND_MAX % 256;
unsigned result = rand();
while (result >= limit)
{
result = rand();
}
return result % 256;
}
unsigned long long
static rand64bits()
{
unsigned long long results = 0ULL;
for (int count = 8; count > 0; --count)
{
results = 256U * results + rand256();
}
return results;
}
namespace big
{
bool hooks::add_player_to_session(rage::netConnectionManager* mgr, int receiver_msg_id, int* out_command_hndl, RemoteGamerInfoMsg* msg, int flags, void* unk)
{
if (msg->m_gamer_info.m_gamer_handle_2.m_rockstar_id == g_local_player->m_player_info->m_net_player_data.m_gamer_handle_2.m_rockstar_id && gta_util::get_network()->m_game_session_ptr->is_host() && g->protections.lessen_breakups)
{
std::uint64_t host_token = -1;
auto session = gta_util::get_network()->m_game_session_ptr;
for (int i = 0; i < session->m_player_count; i++)
{
if (session->m_players[i]->m_msg_id == receiver_msg_id)
{
host_token = session->m_players[i]->m_player_data.m_host_token;
break;
}
}
std::uint64_t peer_id = rand64bits();
g->m_spoofed_peer_ids.emplace(host_token, peer_id);
msg->m_gamer_info.m_peer_id_2 = peer_id;
}
return g_hooking->get_original<hooks::add_player_to_session>()(mgr, receiver_msg_id, out_command_hndl, msg, flags, unk);
}
}

View File

@ -3,6 +3,7 @@
#include "natives.hpp"
#include "gta_util.hpp"
#include "util/session.hpp"
#include "util/spam.hpp"
#include <network/Network.hpp>
namespace big
@ -56,47 +57,39 @@ namespace big
switch (msgType)
{
case rage::eNetMessage::MsgTextMessage:
{
if (g->session.log_chat_messages)
{
char message[256];
uint64_t unk;
bool is_team;
buffer.ReadString(message, 256);
buffer.ReadQWord(&unk, 64);
buffer.ReadBool(&is_team);
LOG(INFO) << "[CHAT] from " << player->get_name() << ": " << message << (is_team ? " [TEAM]" : " [ALL]");
}
break;
}
case rage::eNetMessage::MsgTextMessage2:
{
if (g->session.log_text_messages)
char message[256];
buffer.ReadString(message, 256);
if (player->is_spammer)
return true;
if (spam::is_text_spam(message))
{
char message[256];
uint64_t unk;
buffer.ReadString(message, 256);
buffer.ReadQWord(&unk, 64);
LOG(INFO) << "[TEXT] from " << player->get_name() << ": " << message;
if (g->session.log_chat_messages)
spam::log_chat(message, player, true);
player->is_spammer = true;
return true;
}
else
{
if (g->session.log_chat_messages)
spam::log_chat(message, player, false);
}
break;
}
case rage::eNetMessage::MsgScriptMigrateHost:
{
if (std::chrono::system_clock::now() - player->m_last_transition_msg_sent < 200ms)
if (player->m_host_migration_rate_limit.process())
{
if (player->m_num_failed_transition_attempts++ == 20)
if (player->m_host_migration_rate_limit.exceeded_last_process())
{
session::add_infraction(player, Infraction::TRIED_KICK_PLAYER);
g_notification_service->push_error("Protections", std::format("{} tried to OOM kick you!", player->get_name()));
}
return true;
}
else
{
player->m_last_transition_msg_sent = std::chrono::system_clock::now();
player->m_num_failed_transition_attempts = 0;
}
break;
}
case rage::eNetMessage::MsgRemoveGamersFromSessionCmd:
@ -181,8 +174,11 @@ namespace big
uint32_t num_of_tokens{};
buffer.ReadDword(&num_of_tokens, 32);
if (player && host_token != player->get_net_data()->m_host_token)
if (player && host_token != player->get_net_data()->m_host_token && !player->exposed_desync_protection)
{
session::add_infraction(player, Infraction::DESYNC_PROTECTION);
player->exposed_desync_protection = true;
}
return true; // block desync kicks as host
}

View File

@ -4,6 +4,7 @@
#include <network/CNetGamePlayer.hpp>
#include "gta/script_id.hpp"
#include "util/notify.hpp"
#include <base/CObject.hpp>
namespace big
{
@ -19,6 +20,121 @@ namespace big
id.m_instance_id = buffer.Read<int32_t>(8);
}
void scan_explosion_event(CNetGamePlayer* player, rage::datBitBuffer* buffer)
{
uint16_t f186;
uint16_t f208;
int ownerNetId;
uint16_t f214;
eExplosionTag explosionType;
float damageScale;
float posX;
float posY;
float posZ;
bool f242;
uint16_t f104;
float cameraShake;
bool isAudible;
bool f189;
bool isInvisible;
bool f126;
bool f241;
bool f243;
uint16_t f210;
float unkX;
float unkY;
float unkZ;
bool f190;
bool f191;
uint32_t f164;
float posX224;
float posY224;
float posZ224;
bool f240;
uint16_t f218;
bool f216;
f186 = buffer->Read<uint16_t>(16);
f208 = buffer->Read<uint16_t>(13);
ownerNetId = buffer->Read<uint16_t>(13);
f214 = buffer->Read<uint16_t>(13); // 1604+
explosionType = (eExplosionTag)buffer->ReadSigned<int>(8); // 1604+ bit size
damageScale = buffer->Read<int>(8) / 255.0f;
posX = buffer->ReadSignedFloat(22, 27648.0f);
posY = buffer->ReadSignedFloat(22, 27648.0f);
posZ = buffer->ReadFloat(22, 4416.0f) - 1700.0f;
f242 = buffer->Read<uint8_t>(1);
f104 = buffer->Read<uint16_t>(16);
cameraShake = buffer->Read<int>(8) / 127.0f;
isAudible = buffer->Read<uint8_t>(1);
f189 = buffer->Read<uint8_t>(1);
isInvisible = buffer->Read<uint8_t>(1);
f126 = buffer->Read<uint8_t>(1);
f241 = buffer->Read<uint8_t>(1);
f243 = buffer->Read<uint8_t>(1); // 1604+
f210 = buffer->Read<uint16_t>(13);
unkX = buffer->ReadSignedFloat(16, 1.1f);
unkY = buffer->ReadSignedFloat(16, 1.1f);
unkZ = buffer->ReadSignedFloat(16, 1.1f);
f190 = buffer->Read<uint8_t>(1);
f191 = buffer->Read<uint8_t>(1);
f164 = buffer->Read<uint32_t>(32);
if (f242)
{
posX224 = buffer->ReadSignedFloat(31, 27648.0f);
posY224 = buffer->ReadSignedFloat(31, 27648.0f);
posZ224 = buffer->ReadFloat(31, 4416.0f) - 1700.0f;
}
else
{
posX224 = 0;
posY224 = 0;
posZ224 = 0;
}
auto f168 = buffer->Read<uint32_t>(32); // >= 1868: f_168
f240 = buffer->Read<uint8_t>(1);
if (f240)
{
f218 = buffer->Read<uint16_t>(16);
if (f191)
{
f216 = buffer->Read<uint8_t>(8);
}
}
buffer->Seek(0);
auto object = g_pointers->m_get_net_object(*g_pointers->m_network_object_mgr, ownerNetId, true);
auto entity = object ? object->GetGameObject() : nullptr;
if (f208 == 0 && entity && entity->gap28 == 4 && reinterpret_cast<CPed*>(entity)->m_player_info && player->m_player_info->m_ped && player->m_player_info->m_ped->m_net_object && ownerNetId != player->m_player_info->m_ped->m_net_object->m_object_id)
{
g_notification_service->push_error("Warning!", std::format("{} blamed {} for explosion", player->get_name(), reinterpret_cast<CPed*>(entity)->m_player_info->m_net_player_data.m_name));
session::add_infraction(g_player_service->get_by_id(player->m_player_id), Infraction::BLAME_EXPLOSION_DETECTED);
}
}
void hooks::received_event(
rage::netEventMgr* event_manager,
CNetGamePlayer* source_player,
@ -306,6 +422,25 @@ namespace big
g->m_syncing_player = source_player;
break;
}
case eNetworkEvents::NETWORK_PLAY_SOUND_EVENT:
{
auto plyr = g_player_service->get_by_id(source_player->m_player_id);
if (plyr->m_play_sound_rate_limit.process())
{
if (plyr->m_play_sound_rate_limit.exceeded_last_process())
{
notify::crash_blocked(source_player, "sound spam");
}
g_pointers->m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
return;
}
break;
}
case eNetworkEvents::EXPLOSION_EVENT:
{
scan_explosion_event(source_player, buffer);
break;
}
default:
break;
}

View File

@ -1,5 +1,15 @@
#include "hooking.hpp"
constexpr static auto advertisments = std::to_array(
{
"~h~~r~YimMenu",
"~h~~g~YimMenu",
"~h~~b~YimMenu",
"~h~~y~YimMenu",
"~h~~o~YimMenu",
"~h~~p~YimMenu"
});
namespace big
{
bool hooks::send_net_info_to_lobby(rage::rlGamerInfo* player, int64_t a2, int64_t a3, DWORD* a4)
@ -31,6 +41,20 @@ namespace big
if (g->notifications.send_net_info_to_lobby.notify)
g_notification_service->push("Player Info Spoofing", "Sent spoofed values to lobby host.");
}
else
{
if (g->session.name_spoof_enabled)
{
if (g->session.advertise_menu)
{
memcpy(player->m_name, advertisments[rand() % advertisments.size()], sizeof(player->m_name));
}
else
{
memcpy(player->m_name, g->session.spoofed_name.c_str(), sizeof(player->m_name));
}
}
}
const auto result = g_hooking->get_original<hooks::send_net_info_to_lobby>()(player, a2, a3, a4);

View File

@ -1,6 +1,7 @@
#pragma once
#include "native_hooks.hpp"
#include "natives.hpp"
#include "core/scr_globals.hpp"
namespace big
{
@ -16,5 +17,11 @@ namespace big
src->set_return_value(return_value);
}
void NETWORK_HAS_RECEIVED_HOST_BROADCAST_DATA(rage::scrNativeCallContext* src)
{
*scr_globals::gsbd.as<int*>() = 4;
src->set_return_value<BOOL>(TRUE);
}
}
}

View File

@ -0,0 +1,44 @@
#pragma once
namespace big
{
namespace am_launcher
{
inline void START_NEW_SCRIPT_WITH_ARGS(rage::scrNativeCallContext* src)
{
const char* name = src->get_arg<const char*>(0);
uint64_t* args = src->get_arg<uint64_t*>(1);
int argc = src->get_arg<int>(2);
int stackSize = src->get_arg<int>(3);
Hash name_hash = rage::joaat(name);
if (name_hash == RAGE_JOAAT("ggsm_arcade") ||
name_hash == RAGE_JOAAT("camhedz_arcade") ||
name_hash == RAGE_JOAAT("wizard_arcade") ||
name_hash == RAGE_JOAAT("puzzle") ||
name_hash == RAGE_JOAAT("fm_intro") ||
name_hash == RAGE_JOAAT("pilot_school_mp") ||
name_hash == RAGE_JOAAT("golf_mp") ||
name_hash == RAGE_JOAAT("tennis_network_mp") ||
name_hash == RAGE_JOAAT("fm_race_controler") ||
name_hash == RAGE_JOAAT("fm_horde_controler") ||
name_hash == RAGE_JOAAT("fm_mission_controller") ||
name_hash == RAGE_JOAAT("fm_mission_controller_2020") ||
name_hash == RAGE_JOAAT("fm_impromptu_dm_controler") ||
name_hash == RAGE_JOAAT("fm_deathmatch_controler") ||
name_hash == RAGE_JOAAT("fm_bj_race_controler") ||
name_hash == RAGE_JOAAT("fm_survival_controller") ||
name_hash == RAGE_JOAAT("tennis_network_mp") ||
name_hash == RAGE_JOAAT("sctv") ||
name_hash == RAGE_JOAAT("am_pi_menu")
)
{
src->set_return_value<int>(0);
return;
}
src->set_return_value<int>(SYSTEM::START_NEW_SCRIPT_WITH_ARGS(name, (Any*)args, argc, stackSize));
};
}
}

View File

@ -17,5 +17,17 @@ namespace big
else
src->set_return_value<BOOL>(PLAYER::IS_PLAYER_PLAYING(src->get_arg<Player>(0)));
};
inline void SET_ENTITY_VISIBLE(rage::scrNativeCallContext* src)
{
auto entity = src->get_arg<Entity>(0);
auto toggle = src->get_arg<bool>(1);
auto outfit = src->get_arg<bool>(2);
if (g->self.invisibility && entity == self::ped && toggle)
return;
else
ENTITY::SET_ENTITY_VISIBLE(entity, toggle, outfit);
}
}
}

View File

@ -5,6 +5,7 @@
#include "gta_util.hpp"
#include "shop_controller.hpp"
#include "network_session_host.hpp"
#include "am_launcher.hpp"
#include <script/scrProgram.hpp>
#include <script/scrProgramTable.hpp>
@ -16,14 +17,17 @@ namespace big
native_hooks::native_hooks()
{
add_native_detour(0x812595A0644CE1DE, all_scripts::IS_DLC_PRESENT);
add_native_detour(0x5D10B3795F3FC886, all_scripts::NETWORK_HAS_RECEIVED_HOST_BROADCAST_DATA);
add_native_detour(RAGE_JOAAT("carmod_shop"), 0x06843DA7060A026B, carmod_shop::SET_ENTITY_COORDS);
add_native_detour(RAGE_JOAAT("carmod_shop"), 0x8E2530AA8ADA980E, carmod_shop::SET_ENTITY_HEADING);
add_native_detour(RAGE_JOAAT("carmod_shop"), 0x34E710FF01247C5A, carmod_shop::SET_VEHICLE_LIGHTS);
add_native_detour(RAGE_JOAAT("carmod_shop"), 0x767FBC2AC802EF3D, carmod_shop::STAT_GET_INT);
add_native_detour(RAGE_JOAAT("freemode"), 0x95914459A87EBA28, freemode::NETWORK_BAIL);
add_native_detour(RAGE_JOAAT("freemode"), 0x5E9564D8246B909A, freemode::IS_PLAYER_PLAYING);
add_native_detour(RAGE_JOAAT("freemode"), 0xEA1C610A04DB6BBB, freemode::SET_ENTITY_VISIBLE);
add_native_detour(RAGE_JOAAT("shop_controller"), 0xDC38CC1E35B6A5D7, shop_controller::SET_WARNING_MESSAGE_WITH_HEADER);
add_native_detour(RAGE_JOAAT("maintransition"), 0x6F3D4ED9BEE4E61D, network::NETWORK_SESSION_HOST);
add_native_detour(RAGE_JOAAT("am_launcher"), 0xB8BA7F44DF1575E1, am_launcher::START_NEW_SCRIPT_WITH_ARGS);
for (auto& entry : *g_pointers->m_script_program_table)
if (entry.m_program)

View File

@ -542,6 +542,18 @@ namespace big
m_sort_session_details = ptr.sub(0x10).as<PVOID>();
});
// Add Player To Session
main_batch.add("APTS", "E8 ? ? ? ? 48 8D 8D A0 01 00 00 8A D8", [this](memory::handle ptr)
{
m_add_player_to_session = ptr.add(1).rip().as<PVOID>();
});
// Send Chat Net Message
main_batch.add("SCNM", "E8 ? ? ? ? 41 FF C4 48 83 C5 08", [this](memory::handle ptr)
{
m_send_chat_net_message = ptr.add(1).rip().as<PVOID>();
});
auto mem_region = memory::module("GTA5.exe");
main_batch.run(mem_region);

View File

@ -166,6 +166,9 @@ namespace big
functions::queue_packet m_queue_packet;
PVOID m_sort_session_details;
PVOID m_add_player_to_session;
PVOID m_send_chat_net_message;
};
inline pointers* g_pointers{};

View File

@ -52,7 +52,9 @@ namespace big
if (m_vmt_hook)
{
m_vmt_hook->disable();
s_map.erase(m_program);
if (m_program)
s_map.erase(m_program);
}
}

View File

@ -73,13 +73,13 @@ namespace big
void mobile_service::register_vehicles()
{
const auto array_size = *mobile::vehicle_global.as<int*>();
const auto array_size = *scr_globals::vehicle_global.as<int*>();
for (int i = 0; i < array_size; i++)
{
if (i % 100 == 0)
script::get_current()->yield();
auto veh_idx_global = mobile::vehicle_global.at(i, 142);
auto veh_idx_global = scr_globals::vehicle_global.at(i, 142);
const auto hash = *veh_idx_global.at(66).as<Hash*>();
const auto& it = m_pv_lookup.find(i);

View File

@ -41,7 +41,7 @@ namespace big
for (auto& p : json.items())
{
m_players[std::stoi(p.key())] = p.value().get<persistent_player>();
m_players[std::stoll(p.key())] = p.value().get<persistent_player>();
}
}
}

View File

@ -2,6 +2,7 @@
#include "player_service.hpp"
#include "vehicle/CVehicle.hpp"
#include "network/snSession.hpp"
#include "rate_limiter.hpp"
namespace big
{
@ -33,21 +34,25 @@ namespace big
[[nodiscard]] CPlayerInfo* get_player_info() const;
[[nodiscard]] class rage::snPlayer* get_session_player();
[[nodiscard]] class rage::snPeer* get_session_peer();
[[nodiscard]] uint8_t id() const;
[[nodiscard]] bool is_friend() const;
[[nodiscard]] bool is_host() const;
[[nodiscard]] bool is_valid() const;
bool off_radar = false;
bool never_wanted = false;
bool semi_godmode = false;
std::chrono::system_clock::time_point m_last_transition_msg_sent{};
int m_num_failed_transition_attempts = 0;
rate_limiter m_host_migration_rate_limit{ 1s, 20 };
rate_limiter m_play_sound_rate_limit{ 1s, 10 };
bool exposed_desync_protection = false;
bool is_modder = false;
bool block_join = false;
int block_join_reason = 0;
bool is_spammer = false;
protected:
bool equals(const CNetGamePlayer* net_game_player) const;

View File

@ -30,15 +30,6 @@ namespace big
m_players.clear();
}
player_ptr player_service::get_by_name(std::string name)
{
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
if (const auto it = m_players.find(name); it != m_players.end())
return it->second;
return nullptr;
}
player_ptr player_service::get_by_msg_id(uint32_t msg_id) const
{
for (const auto& [_, player] : m_players)
@ -49,7 +40,7 @@ namespace big
player_ptr player_service::get_by_id(uint32_t id) const
{
for (const auto& [name, player] : m_players)
for (const auto& [_, player] : m_players)
if (player->id() == id)
return player;
return nullptr;
@ -57,7 +48,7 @@ namespace big
player_ptr player_service::get_by_host_token(uint64_t token) const
{
for (const auto& [name, player] : m_players)
for (const auto& [_, player] : m_players)
if (player->get_net_data()->m_host_token == token)
return player;
return nullptr;
@ -83,10 +74,10 @@ namespace big
if (net_game_player == nullptr || net_game_player == *m_self) return;
auto plyr = std::make_shared<player>(net_game_player);
m_players.emplace(
plyr->to_lowercase_identifier(),
m_players.insert({
plyr->get_name(),
std::move(plyr)
);
});
}
void player_service::player_leave(CNetGamePlayer* net_game_player)
@ -95,8 +86,10 @@ namespace big
if (m_selected_player && m_selected_player->equals(net_game_player))
m_selected_player = m_dummy;
auto plyr = std::make_unique<player>(net_game_player);
m_players.erase(plyr->to_lowercase_identifier());
if (auto it = std::find_if(m_players.begin(), m_players.end(), [net_game_player](const auto& p) { return p.second->id() == net_game_player->m_player_id; }); it != m_players.end())
{
m_players.erase(it);
}
}
void player_service::set_selected(player_ptr plyr)

View File

@ -7,7 +7,7 @@ namespace big
using player_ptr = std::shared_ptr<player>;
using player_entry = std::pair<std::string, player_ptr>;
using players = std::map<std::string, player_ptr>;
using players = std::multimap<std::string, player_ptr>;
class player_service final
{
@ -33,7 +33,6 @@ namespace big
[[nodiscard]] player_ptr get_self();
[[nodiscard]] player_ptr get_by_name(std::string name);
[[nodiscard]] player_ptr get_by_msg_id(uint32_t msg_id) const;
[[nodiscard]] player_ptr get_by_id(uint32_t id) const;
[[nodiscard]] player_ptr get_by_host_token(uint64_t token) const;
@ -45,7 +44,7 @@ namespace big
players& players()
{ return m_players; }
void iterate(const std::function< void(const player_entry &entry) > func)
void iterate(const std::function< void(const player_entry &entry)> func)
{ for (const auto &iter : m_players) func(iter); }
void set_selected(player_ptr plyr);

View File

@ -0,0 +1,40 @@
#pragma once
namespace big
{
class rate_limiter
{
std::uint32_t m_attempts_allowed_in_time_period;
std::chrono::milliseconds m_time_period;
std::chrono::system_clock::time_point m_last_event_time{};
std::uint32_t m_num_attempts_allowed = 0;
public:
rate_limiter(std::chrono::milliseconds time_period, std::uint32_t num_allowed_attempts) :
m_attempts_allowed_in_time_period(num_allowed_attempts),
m_time_period(time_period)
{
}
// Returns true if the rate limit has been exceeded
bool process()
{
if (std::chrono::system_clock::now() - m_last_event_time < m_time_period)
{
if (++m_num_attempts_allowed > m_attempts_allowed_in_time_period)
return true;
}
else
{
m_last_event_time = std::chrono::system_clock::now();
m_num_attempts_allowed = 1;
}
return false;
}
// Check if the rate limit was exceeded by the last process() call. Use this to prevent the player from being flooded with notifications
bool exceeded_last_process()
{
return (m_num_attempts_allowed - 1) == m_attempts_allowed_in_time_period;
}
};
}

View File

@ -32,7 +32,6 @@ namespace big::entity
NETWORK::NETWORK_SET_ENTITY_ONLY_EXISTS_FOR_PARTICIPANTS(ent, true);
ENTITY::SET_ENTITY_COORDS_NO_OFFSET(ent, 0, 0, 0, 0, 0, 0);
ENTITY::SET_ENTITY_AS_MISSION_ENTITY(ent, 1, 1);
ENTITY::SET_ENTITY_AS_NO_LONGER_NEEDED(&ent);
ENTITY::DELETE_ENTITY(&ent);
}
@ -57,19 +56,23 @@ namespace big::entity
return (bool)hit;
}
inline bool take_control_of(Entity ent)
inline bool take_control_of(Entity ent, int timeout = 1000)
{
if (NETWORK::NETWORK_HAS_CONTROL_OF_ENTITY(ent)) return true;
for (uint8_t i = 0; !NETWORK::NETWORK_HAS_CONTROL_OF_ENTITY(ent) && i < 10; i++)
if (NETWORK::NETWORK_HAS_CONTROL_OF_ENTITY(ent))
return true;
for (uint8_t i = 0; !NETWORK::NETWORK_HAS_CONTROL_OF_ENTITY(ent) && i < timeout; i++)
{
NETWORK::NETWORK_REQUEST_CONTROL_OF_ENTITY(ent);
script::get_current()->yield(5ms);
script::get_current()->yield();
}
if (!NETWORK::NETWORK_HAS_CONTROL_OF_ENTITY(ent)) return false;
if (!NETWORK::NETWORK_HAS_CONTROL_OF_ENTITY(ent))
return false;
int netHandle = NETWORK::NETWORK_GET_NETWORK_ID_FROM_ENTITY(ent);
NETWORK::SET_NETWORK_ID_CAN_MIGRATE(netHandle, true);
NETWORK::SET_NETWORK_ID_CAN_MIGRATE(netHandle, false);
NETWORK::NETWORK_DISABLE_PROXIMITY_MIGRATION(netHandle);
return true;
}

View File

@ -1,26 +1,34 @@
#pragma once
#include "pointers.hpp"
#include "script_global.hpp"
#include "natives.hpp"
#include "core/scr_globals.hpp"
namespace big::globals
{
namespace size
{
constexpr int globalplayer_bd = 453;
constexpr int gpbd_fm_3 = 599;
constexpr int gpbd_fm_1 = 888;
}
static inline script_global gpbd_fm_3(1892703);
static inline script_global gsbd_fm_events(1920255);
inline void clear_wanted_player(Player target)
{
constexpr size_t arg_count = 3;
int64_t args[arg_count] = {
static_cast<int64_t>(eRemoteEvent::ClearWantedLevel),
self::id,
*script_global(1892703).at(target, 599).at(510).as<int*>()
*scr_globals::gpbd_fm_3.at(target, scr_globals::size::gpbd_fm_3).at(510).as<int*>()
};
g_pointers->m_trigger_script_event(1, args, arg_count, 1 << target);
}
inline void give_remote_otr(Player target)
{
constexpr size_t arg_count = 7;
int64_t args[arg_count] = {
static_cast<int64_t>(eRemoteEvent::RemoteOffradar),
(int64_t)self::id,
(int64_t)(NETWORK::GET_NETWORK_TIME() + 1),
0,
true,
false,
*scr_globals::gpbd_fm_3.at(target, scr_globals::size::gpbd_fm_3).at(510).as<int64_t*>()
};
g_pointers->m_trigger_script_event(1, args, arg_count, 1 << target);

View File

@ -1,5 +1,6 @@
#pragma once
#include "core/enums.hpp"
#include "core/scr_globals.hpp"
#include "globals.hpp"
#include "gta_util.hpp"
#include "misc.hpp"
@ -13,8 +14,6 @@
namespace big::mobile
{
inline auto player_global = script_global(2689235);
inline auto mechanic_global = script_global(2815059);
inline auto vehicle_global = script_global(1585857);
namespace util
{
@ -22,7 +21,7 @@ namespace big::mobile
inline void despawn_current_personal_vehicle()
{
misc::clear_bits(
vehicle_global.at(get_current_personal_vehicle(), 142).at(103).as<int*>(),
scr_globals::vehicle_global.at(get_current_personal_vehicle(), 142).at(103).as<int*>(),
eVehicleFlags::TRIGGER_SPAWN_TOGGLE
);
}
@ -37,7 +36,7 @@ namespace big::mobile
{
inline void off_radar(bool toggle)
{
*player_global.at(PLAYER::GET_PLAYER_INDEX(), 453).at(208).as<int*>() = toggle;
*scr_globals::globalplayer_bd.at(PLAYER::GET_PLAYER_INDEX(), scr_globals::size::globalplayer_bd).at(208).as<int*>() = toggle;
*script_global(2703735).at(56).as<int*>() = NETWORK::GET_NETWORK_TIME() + 1;
}
}
@ -46,12 +45,12 @@ namespace big::mobile
{
inline void request_ammo_drop()
{
*script_global(mechanic_global).at(874).as<int*>() = 1;
*script_global(scr_globals::mechanic_global).at(874).as<int*>() = 1;
}
inline void request_helicopter_pickup()
{
*script_global(mechanic_global).at(876).as<int*>() = 1;
*script_global(scr_globals::mechanic_global).at(876).as<int*>() = 1;
}
}
@ -60,21 +59,21 @@ namespace big::mobile
inline bool fix_index(int veh_idx, bool spawn_veh = false)
{
bool can_be_fixed = misc::has_bits_set(
vehicle_global.at(veh_idx, 142).at(103).as<int*>(),
scr_globals::vehicle_global.at(veh_idx, 142).at(103).as<int*>(),
eVehicleFlags::DESTROYED | eVehicleFlags::HAS_INSURANCE
);
if (can_be_fixed)
{
misc::clear_bits(
vehicle_global.at(veh_idx, 142).at(103).as<int*>(),
scr_globals::vehicle_global.at(veh_idx, 142).at(103).as<int*>(),
eVehicleFlags::DESTROYED | eVehicleFlags::IMPOUNDED | eVehicleFlags::UNK2
);
if (spawn_veh)
{
misc::set_bits(
vehicle_global.at(veh_idx, 142).at(103).as<int*>(),
scr_globals::vehicle_global.at(veh_idx, 142).at(103).as<int*>(),
eVehicleFlags::TRIGGER_SPAWN_TOGGLE | eVehicleFlags::SPAWN_AT_MORS_MUTUAL
);
}
@ -86,7 +85,7 @@ namespace big::mobile
{
int fixed_count = 0;
const int arr_size = *vehicle_global.as<int*>();
const int arr_size = *scr_globals::vehicle_global.as<int*>();
for (int i = 0; i < arr_size; i++)
if (fix_index(i, true))
fixed_count++;
@ -99,12 +98,12 @@ namespace big::mobile
{
inline Vehicle get_personal_vehicle()
{
return *mechanic_global.at(298).as<Vehicle*>();
return *scr_globals::mechanic_global.at(298).as<Vehicle*>();
}
inline void summon_vehicle_by_index(int veh_idx)
{
if (*mechanic_global.at(958).as<int*>() != -1)
if (*scr_globals::mechanic_global.at(958).as<int*>() != -1)
return g_notification_service->push_warning("Vehicle", "Mechanic is not ready to deliver a vehicle right now.");
if (g->clone_pv.spawn_inside && self::veh)
@ -116,10 +115,10 @@ namespace big::mobile
script::get_current()->yield(100ms);
*mechanic_global.at(924).as<int*>() = 1; // disable vehicle node distance check
*mechanic_global.at(911).as<int*>() = 1; // tell freemode to spawn our vehicle
*mechanic_global.at(961).as<int*>() = 0; // required
*mechanic_global.at(958).as<int*>() = veh_idx;
*scr_globals::mechanic_global.at(924).as<int*>() = 1; // disable vehicle node distance check
*scr_globals::mechanic_global.at(911).as<int*>() = 1; // tell freemode to spawn our vehicle
*scr_globals::mechanic_global.at(961).as<int*>() = 0; // required
*scr_globals::mechanic_global.at(958).as<int*>() = veh_idx;
script::get_current()->yield(100ms);
@ -128,7 +127,7 @@ namespace big::mobile
*script_local(freemode_thread, 18399).at(176).as<int*>() = 0; // spawn vehicle instantly
// blocking call till vehicle is delivered
notify::busy_spinner("Delivering vehicle...", mechanic_global.at(958).as<int*>(), -1);
notify::busy_spinner("Delivering vehicle...", scr_globals::mechanic_global.at(958).as<int*>(), -1);
if (g->clone_pv.spawn_inside)
{

View File

@ -2,6 +2,11 @@
#include "fiber_pool.hpp"
#include "natives.hpp"
#include "script.hpp"
#include "gta_util.hpp"
#include "misc.hpp"
#include "gta/script_handler.hpp"
#include "script_local.hpp"
#include "core/scr_globals.hpp"
namespace big::scripts
{
@ -12,7 +17,7 @@ namespace big::scripts
inline bool is_running(int hash)
{
return SCRIPT::DOES_SCRIPT_WITH_NAME_HASH_EXIST(hash);
return SCRIPT::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(hash) > 0;
}
inline void request_script(int hash)
@ -44,4 +49,105 @@ namespace big::scripts
if (is_running(hash)) return true;
return false;
}
// force launcher script over the lobby, take two
// try to get am_launcher in a consistent state before trying to start the script taking account of all participants
inline void start_launcher_script(int script_id)
{
static auto check_players_in_state = [](GtaThread* launcher, int state) -> bool
{
bool set = false;
gta_util::execute_as_script(RAGE_JOAAT("am_launcher"), [launcher, state, &set]
{
for (auto& [_, plyr] : g_player_service->players())
{
if (NETWORK::NETWORK_IS_PLAYER_A_PARTICIPANT(plyr->id()))
{
if (*script_local(launcher->m_stack, 230).at(plyr->id(), 3).at(2).as<int*>() == state)
{
set = true;
break;
}
}
}
});
return set;
};
// 1) Get launcher
if (auto launcher = gta_util::find_script_thread(RAGE_JOAAT("am_launcher")))
{
// 2) Force host of launcher
for (int i = 0; NETWORK::NETWORK_GET_HOST_OF_SCRIPT("am_launcher", -1, 0) != self::id; i++)
{
if (i > 3600)
{
// 2F) Failed to force host of launcher
g_notification_service->push_error("Script", "Cannot force script host of am_launcher");
return;
}
((CGameScriptHandlerNetComponent*)launcher->m_net_component)->send_host_migration_event(g_player_service->get_self()->get_net_game_player());
script::get_current()->yield();
}
launcher->m_context.m_state = rage::eThreadState::unk_3; // prevent bad things from happening to the thread in the meantime
// 3) Remove players from that annoying waiting stage
if (check_players_in_state(launcher, 5))
{
for (int i = 0; check_players_in_state(launcher, 5); i++)
{
if (i > 200)
break; // 3F) Timeout
*scr_globals::launcher_global.at(3).at(1).as<int*>() = 0;
*scr_globals::launcher_global.at(2).as<int*>() = 6;
script::get_current()->yield(10ms);
}
} // State should now be 6 or 0
// 4) Check if a script is already being executed, and unstuck from that state if so
if (check_players_in_state(launcher, 6))
{
for (int i = 0; check_players_in_state(launcher, 6); i++)
{
if (i > 200)
break; // 4F) Timeout
*scr_globals::launcher_global.at(3).at(1).as<int*>() = 0;
*scr_globals::launcher_global.at(2).as<int*>() = 7;
script::get_current()->yield(10ms);
}
} // State should now be 7 or 0
// 5) Get everyone out of state 7
if (check_players_in_state(launcher, 7))
{
for (int i = 0; check_players_in_state(launcher, 7); i++)
{
if (i > 200)
break; // 5F) Timeout
*scr_globals::launcher_global.at(2).as<int*>() = 0;
script::get_current()->yield(10ms);
}
} // State should now be 0
// 6) Actually get the script to start
misc::set_bit(scr_globals::launcher_global.at(1).as<int*>(), 1); // run immediately
*scr_globals::launcher_global.at(2).as<int*>() = 6; // will change to 7 shortly but that's fine as players are guaranteed not to be in the waiting stage
*script_local(launcher->m_stack, 230).at(self::id, 3).at(2).as<int*>() = 6;
*scr_globals::launcher_global.at(3).at(1).as<int*>() = script_id;
launcher->m_context.m_state = rage::eThreadState::running;
}
else
{
// 1F) Cannot find launcher
g_notification_service->push_error("Script", "Cannot start script, am_launcher not running locally");
}
}
}

View File

@ -45,9 +45,9 @@ namespace big::session
{
int idx = index / 32;
int bit = index % 32;
misc::set_bit(globals::gsbd_fm_events.at(11).at(341).at(idx, 1).as<int*>(), bit);
misc::set_bit(globals::gsbd_fm_events.at(11).at(348).at(idx, 1).as<int*>(), bit);
misc::set_bit(globals::gpbd_fm_3.at(self::id, globals::size::gpbd_fm_3).at(10).at(205).at(idx, 1).as<int*>(), bit);
misc::set_bit(scr_globals::gsbd_fm_events.at(11).at(341).at(idx, 1).as<int*>(), bit);
misc::set_bit(scr_globals::gsbd_fm_events.at(11).at(348).at(idx, 1).as<int*>(), bit);
misc::set_bit(scr_globals::gpbd_fm_3.at(self::id, scr_globals::size::gpbd_fm_3).at(10).at(205).at(idx, 1).as<int*>(), bit);
}
inline void force_thunder()
@ -89,15 +89,18 @@ namespace big::session
}
}
g_notification_service->push_error("RID Joiner", "Target Player is offline?");
g_notification_service->push_error("RID Joiner", "Target player is offline?");
}
inline void add_infraction(player_ptr player, Infraction infraction)
{
auto plyr = g_player_database_service->get_or_create_player(player);
plyr->is_modder = true;
player->is_modder = true;
plyr->infractions.insert((int)infraction);
g_player_database_service->save();
if (!plyr->infractions.contains((int)infraction))
{
plyr->is_modder = true;
player->is_modder = true;
plyr->infractions.insert((int)infraction);
g_player_database_service->save();
}
}
}

52
src/util/spam.hpp Normal file
View File

@ -0,0 +1,52 @@
#pragma once
#include "services/players/player_service.hpp"
#include "file_manager/file.hpp"
namespace
{
static const char* spam_texts[] =
{
"QQ",
"WWW.",
"www.",
".cn",
".CN",
".TOP",
".COM",
"\xE3\x80\x90",
"/Menu",
"Money/",
"Money\\\\",
"Money\\",
".gg",
"--->",
"shopgta5",
"doit#"
};
}
namespace big::spam
{
inline bool is_text_spam(const char* text)
{
for (auto e : spam_texts)
if (strstr(text, e) != 0)
return true;
return false;
}
inline void log_chat(char* msg, player_ptr player, bool is_spam)
{
std::ofstream spam_log(g_file_manager->get_project_file(is_spam ? "./spam.log" : "./chat.log").get_path(), std::ios::app);
auto& plData = *player->get_net_data();
spam_log << player->get_name() << " (" << plData.m_gamer_handle_2.m_rockstar_id << ") <"
<< (int)plData.m_external_ip.m_field1 << "." << (int)plData.m_external_ip.m_field2 << "." << (int)plData.m_external_ip.m_field3 << "." << (int)plData.m_external_ip.m_field4 <<
">: " << msg <<
std::endl;
spam_log.close();
}
}

View File

@ -7,13 +7,12 @@
#include "script.hpp"
#include "teleport.hpp"
#include "script_global.hpp"
#include "gta\VehicleValues.h"
#include "gta/VehicleValues.h"
#include "services/vehicle_helper/vehicle_helper.hpp"
#include "core/scr_globals.hpp"
namespace big::vehicle
{
inline auto spawn_global = script_global(2725439);
inline void go_into_personal_vehicle()
{
*script_global(2671449).at(8).as<int*>() = 1;
@ -202,8 +201,6 @@ namespace big::vehicle
auto veh = VEHICLE::CREATE_VEHICLE(hash, location.x, location.y, location.z, heading, is_networked, false, false);
*(unsigned short*)g_pointers->m_model_spawn_bypass = 0x0574;
script::get_current()->yield();
STREAMING::SET_MODEL_AS_NO_LONGER_NEEDED(hash);
if (*g_pointers->m_is_session_started)
@ -227,31 +224,31 @@ namespace big::vehicle
{
if (idx >= 0 && idx < 142)
{
*spawn_global.at(27).at(idx).as<int32_t*>() = val;
*scr_globals::spawn_global.at(27).at(idx).as<int32_t*>() = val;
}
}
// permission fix
*spawn_global.at(27).at(1).as<int32_t*>() = 0;
*scr_globals::spawn_global.at(27).at(1).as<int32_t*>() = 0;
// personal car flag
*spawn_global.at(27).at(94).as<int32_t*>() = 14;
*spawn_global.at(27).at(95).as<int32_t*>() = 2;
*scr_globals::spawn_global.at(27).at(94).as<int32_t*>() = 14;
*scr_globals::spawn_global.at(27).at(95).as<int32_t*>() = 2;
// mmi
*spawn_global.at(27).at(103).as<int32_t*>() = 0;
*scr_globals::spawn_global.at(27).at(103).as<int32_t*>() = 0;
// spawn location
*spawn_global.at(7).at(0).as<float*>() = tmpLocation.x;
*spawn_global.at(7).at(1).as<float*>() = tmpLocation.y;
*spawn_global.at(7).at(2).as<float*>() = tmpLocation.z;
*scr_globals::spawn_global.at(7).at(0).as<float*>() = tmpLocation.x;
*scr_globals::spawn_global.at(7).at(1).as<float*>() = tmpLocation.y;
*scr_globals::spawn_global.at(7).at(2).as<float*>() = tmpLocation.z;
// spawn non pegasus
*spawn_global.at(3).as<int*>() = 0;
*scr_globals::spawn_global.at(3).as<int*>() = 0;
// spawn signal
int* spawn_signal = spawn_global.at(2).as<int32_t*>();
*spawn_global.at(5).as<int32_t*>() = 1;
int* spawn_signal = scr_globals::spawn_global.at(2).as<int32_t*>();
*scr_globals::spawn_global.at(5).as<int32_t*>() = 1;
*spawn_signal = 1;
// wait until the vehicle is spawned
@ -666,4 +663,52 @@ namespace big::vehicle
else
return g_notification_service->push_warning("Vehicle", "Please be in a car first then try again.");
}
inline bool remote_control_vehicle(Vehicle veh)
{
if (!entity::take_control_of(veh, 4000))
{
g_notification_service->push_warning("Remote Control", "Failed to take control of remote vehicle");
return false;
}
if (g->m_remote_controlled_vehicle == veh)
{
return false;
}
Hash model = ENTITY::GET_ENTITY_MODEL(veh);
Vehicle spawned = vehicle::spawn(model, self::pos, 0.0f);
ENTITY::SET_ENTITY_ALPHA(spawned, 0, FALSE);
if (!VEHICLE::IS_THIS_MODEL_A_BIKE(model))
ENTITY::SET_ENTITY_VISIBLE(spawned, FALSE, FALSE);
ENTITY::SET_ENTITY_INVINCIBLE(spawned, TRUE);
float heading = ENTITY::GET_ENTITY_HEADING(veh);
Vector3 rotation = ENTITY::GET_ENTITY_ROTATION(veh, 2);
Vector3 coords = ENTITY::GET_ENTITY_COORDS(veh, FALSE);
Vector3 velocity = ENTITY::GET_ENTITY_VELOCITY(veh);
ENTITY::SET_ENTITY_COORDS_NO_OFFSET(spawned, coords.x, coords.y, coords.z, FALSE, FALSE, FALSE);
ENTITY::SET_ENTITY_HEADING(spawned, heading);
ENTITY::SET_ENTITY_ROTATION(spawned, rotation.x, rotation.y, rotation.z, 2, TRUE);
ENTITY::SET_ENTITY_VISIBLE(veh, TRUE, FALSE);
ENTITY::SET_ENTITY_COLLISION(veh, FALSE, FALSE);
ENTITY::SET_ENTITY_INVINCIBLE(veh, TRUE);
VEHICLE::SET_VEHICLE_DOORS_LOCKED(veh, 4);
VEHICLE::SET_VEHICLE_MAX_SPEED(veh, 0.0001f);
ENTITY::ATTACH_ENTITY_TO_ENTITY(veh, spawned, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FALSE, FALSE, FALSE, FALSE, 0, TRUE, FALSE);
PED::SET_PED_INTO_VEHICLE(PLAYER::PLAYER_PED_ID(), spawned, -1);
VEHICLE::SET_VEHICLE_ENGINE_ON(spawned, TRUE, TRUE, FALSE);
ENTITY::SET_ENTITY_VELOCITY(spawned, velocity.x, velocity.y, velocity.z);
VEHICLE::COPY_VEHICLE_DAMAGES(veh, spawned);
g->m_remote_controller_vehicle = spawned;
g->m_remote_controlled_vehicle = veh;
return true;
}
}

View File

@ -4,6 +4,7 @@
#include "core/data/region_codes.hpp"
#include "gta_util.hpp"
#include "util/notify.hpp"
#include "util/scripts.hpp"
namespace big
{
@ -74,5 +75,55 @@ namespace big
ImGui::Checkbox("Force Session Host", &g->session.force_session_host);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("Join another session to apply changes. The original host of the session must leave or be kicked. This feature is easily detectable by other mod menus, use with caution");
components::sub_title("Remote Name Spoofing");
ImGui::Checkbox("Spoof Other Players' Names", &g->session.name_spoof_enabled);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("Requires session host. Spoofed names will not visible locally nor to the player that had their name spoofed. Requires players to join after becoming host");
if (g->session.name_spoof_enabled)
{
ImGui::Checkbox("Advertise YimMenu", &g->session.advertise_menu);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("Advertise YimMenu by spoofing player names to differently colored variants of 'YimMenu'. You will not be able to customize the name with this option enabled");
if (!g->session.advertise_menu)
{
constexpr size_t name_size = RTL_FIELD_SIZE(rage::rlGamerInfo, m_name);
static char name[name_size];
strcpy_s(name, sizeof(name), g->session.spoofed_name.c_str());
ImGui::Text("Name: ");
ImGui::InputText("##username_input", name, sizeof(name));
if (name != g->session.spoofed_name)
g->session.spoofed_name = std::string(name);
}
}
components::sub_title("All Players");
ImGui::Checkbox("Off The Radar", &g->session.off_radar_all);
ImGui::Checkbox("Never Wanted", &g->session.never_wanted_all);
ImGui::Checkbox("Semi Godmode", &g->session.semi_godmode_all);
components::sub_title("Event Starter");
ImGui::BeginGroup();
components::button("Hot Target", [] { scripts::start_launcher_script(36); });
components::button("Kill List", [] { scripts::start_launcher_script(37); });
components::button("Checkpoints", [] { scripts::start_launcher_script(39); });
components::button("Challenges", [] { scripts::start_launcher_script(40); });
components::button("Penned In", [] { scripts::start_launcher_script(41); });
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
components::button("Hot Property", [] { scripts::start_launcher_script(43); });
components::button("King Of The Castle", [] { scripts::start_launcher_script(45); });
components::button("Criminal Damage", [] { scripts::start_launcher_script(46); });
components::button("Hunt The Beast", [] { scripts::start_launcher_script(47); });
components::button("Business Battles", [] { scripts::start_launcher_script(114); });
ImGui::EndGroup();
}
}

View File

@ -7,6 +7,7 @@
#include "util/ped.hpp"
#include "util/teleport.hpp"
#include "views/view.hpp"
#include "util/vehicle.hpp"
namespace big
{
@ -108,6 +109,11 @@ namespace big
if (auto net_player_data = g_player_service->get_selected()->get_net_data(); net_player_data != nullptr)
{
ImGui::Text("Rockstar ID: %d", net_player_data->m_gamer_handle_2.m_rockstar_id);
ImGui::SameLine();
if (ImGui::Button("Copy")) ImGui::SetClipboardText(std::to_string(net_player_data->m_gamer_handle_2.m_rockstar_id).data());
ImGui::Text(
"IP Address: %d.%d.%d.%d:%d",
net_player_data->m_external_ip.m_field1,
@ -116,6 +122,14 @@ namespace big
net_player_data->m_external_ip.m_field4,
net_player_data->m_external_port
);
ImGui::SameLine();
if (ImGui::Button("Copy")) ImGui::SetClipboardText(std::format("{}.{}.{}.{}:{}", net_player_data->m_external_ip.m_field1,
net_player_data->m_external_ip.m_field2,
net_player_data->m_external_ip.m_field3,
net_player_data->m_external_ip.m_field4,
net_player_data->m_external_port).data());
}
if (ImGui::Button("Add To Database"))
@ -149,6 +163,12 @@ namespace big
if (ImGui::TreeNode("Misc"))
{
components::button("Join CEO/MC", []
{
*scr_globals::gpbd_fm_3.at(self::id, scr_globals::size::gpbd_fm_3).at(10).as<int*>() = g_player_service->get_selected()->id();
*scr_globals::gpbd_fm_3.at(self::id, scr_globals::size::gpbd_fm_3).at(10).at(26).as<int*>() = g_player_service->get_selected()->id();
});
components::button("Steal Outfit", []
{
ped::steal_outfit(
@ -172,8 +192,6 @@ namespace big
ImGui::SameLine();
ImGui::Checkbox("Never Wanted", &g_player_service->get_selected()->never_wanted);
components::button("Give Health", []
{
g_pickup_service->give_player_health(g_player_service->get_selected()->id());
@ -198,6 +216,26 @@ namespace big
g_pickup_service->give_player_weapons(g_player_service->get_selected()->id());
});
ImGui::Checkbox("Off The Radar", &g_player_service->get_selected()->off_radar);
ImGui::Checkbox("Never Wanted", &g_player_service->get_selected()->never_wanted);
ImGui::Checkbox("Semi Godmode", &g_player_service->get_selected()->semi_godmode);
components::button("Remote Control Vehicle", []
{
Vehicle veh = PED::GET_VEHICLE_PED_IS_IN(PLAYER::GET_PLAYER_PED_SCRIPT_INDEX(g_player_service->get_selected()->id()), FALSE);
if (veh == 0)
{
if (g->player.spectating)
g_notification_service->push_warning("Remote Control", "Player not in a vehicle");
else
g_notification_service->push_warning("Remote Control", "Player not in a vehicle, try spectating the player");
return;
}
vehicle::remote_control_vehicle(veh);
g->player.spectating = false;
});
ImGui::TreePop();
}
}

View File

@ -47,6 +47,9 @@ namespace big
ImGui::Checkbox("RID Join", &g->protections.rid_join);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("This will block anyone trying to join you through Rockstar ID, including your friends");
ImGui::Checkbox("Lessen Breakup Kicks As Host", &g->protections.lessen_breakups);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("Attacker must join after you have become host for this to work. There are anti-cheat concerns with this feature");
ImGui::EndGroup();
}