mirror of
https://github.com/Mr-X-GTA/YimMenu.git
synced 2026-10-12 02:08:05 +08:00
Improve protections again and player database (#606)
This commit is contained in:
1 parent
ba3ab119d5
commit
d9d53c8e42
48 files changed
+1129
-803
No files matched your search
@@ -1,4 +1,5 @@
|
||||
#include "hooking.hpp"
|
||||
#include <rage/rlMetric.hpp>
|
||||
|
||||
namespace rage
|
||||
{
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "hooking.hpp"
|
||||
#include <network/Network.hpp>
|
||||
|
||||
namespace big
|
||||
{
|
||||
// true => e1 > e2
|
||||
// false => e1 < e2
|
||||
bool hooks::sort_session_details(SessionSortEntry* e1, SessionSortEntry* e2)
|
||||
{
|
||||
if (g->session.player_magnet_enabled)
|
||||
{
|
||||
return std::abs((int)e1->m_session_detail->m_player_count - g->session.player_magnet_count) < std::abs((int)e2->m_session_detail->m_player_count - g->session.player_magnet_count);
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_hooking->get_original<hooks::sort_session_details>()(e1, e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
#include "hooking.hpp"
|
||||
#include "fiber_pool.hpp"
|
||||
#include "services/players/player_service.hpp"
|
||||
#include "services/player_database/player_database_service.hpp"
|
||||
#include "util/notify.hpp"
|
||||
#include "packet.hpp"
|
||||
#include "gta_util.hpp"
|
||||
#include <network/Network.hpp>
|
||||
|
||||
namespace big
|
||||
{
|
||||
@@ -40,6 +45,35 @@ namespace big
|
||||
|
||||
if (g->notifications.player_join.notify)
|
||||
g_notification_service->push("Player Joined", std::format("{} taking slot #{} with Rockstar ID: {}", net_player_data->m_name, player->m_player_id, net_player_data->m_gamer_handle_2.m_rockstar_id));
|
||||
|
||||
auto id = player->m_player_id;
|
||||
g_fiber_pool->queue_job([id]
|
||||
{
|
||||
if (auto plyr = g_player_service->get_by_id(id))
|
||||
{
|
||||
if (auto entry = g_player_database_service->get_player_by_rockstar_id(plyr->get_net_data()->m_gamer_handle_2.m_rockstar_id))
|
||||
{
|
||||
plyr->is_modder = entry->is_modder;
|
||||
plyr->block_join = entry->block_join;
|
||||
plyr->block_join_reason = plyr->block_join_reason;
|
||||
|
||||
if (strcmp(plyr->get_name(), entry->name.data()))
|
||||
{
|
||||
g_notification_service->push("Players", std::format("{} changed their name to {}", entry->name, plyr->get_name()));
|
||||
entry->name = plyr->get_name();
|
||||
g_player_database_service->save();
|
||||
}
|
||||
}
|
||||
|
||||
if (auto snplyr = plyr->get_session_player())
|
||||
{
|
||||
packet msg{};
|
||||
msg.write_message(rage::eNetMessage::MsgSessionEstablishedRequest);
|
||||
msg.write<uint64_t>(gta_util::get_network()->m_game_session_ptr->m_rline_session.m_session_id, 64);
|
||||
msg.send(snplyr->m_msg_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "hooking.hpp"
|
||||
#include <network/snSession.hpp>
|
||||
#include <network/CMsgJoinResponse.hpp>
|
||||
#include <network/CJoinRequestContext.hpp>
|
||||
#include "services/player_database/player_database_service.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
bool hooks::handle_join_request(Network* network, rage::snSession* session, rage::rlGamerInfo* player_info, CJoinRequestContext* ctx, BOOL is_transition_session)
|
||||
{
|
||||
if (auto player = g_player_database_service->get_player_by_rockstar_id(player_info->m_gamer_handle_2.m_rockstar_id); player && player->block_join)
|
||||
{
|
||||
CMsgJoinResponse response{};
|
||||
response.m_status_code = player->block_join_reason;
|
||||
g_pointers->m_write_join_response_data(&response, ctx->m_join_response_data, 512, &ctx->m_join_response_size);
|
||||
g_notification_service->push("Block Join", std::format("Blocked {} from joining", player->name));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_hooking->get_original<hooks::handle_join_request>()(network, session, player_info, ctx, is_transition_session);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "services/players/player_service.hpp"
|
||||
#include "natives.hpp"
|
||||
#include "gta_util.hpp"
|
||||
#include "util/session.hpp"
|
||||
#include <network/Network.hpp>
|
||||
|
||||
namespace big
|
||||
@@ -86,6 +87,7 @@ namespace big
|
||||
{
|
||||
if (player->m_num_failed_transition_attempts++ == 20)
|
||||
{
|
||||
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;
|
||||
@@ -120,6 +122,7 @@ namespace big
|
||||
if (player && pl && player->id() != pl->id() && count == 1 && frame->m_msg_id == -1)
|
||||
{
|
||||
g_notification_service->push_error("Warning!", std::format("{} breakup kicked {}!", player->get_name(), pl->get_name()));
|
||||
session::add_infraction(player, Infraction::BREAKUP_KICK_DETECTED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -133,6 +136,7 @@ namespace big
|
||||
auto self = g_player_service->get_self();
|
||||
if (self->get_net_data() && self->get_net_data()->m_gamer_handle_2.m_rockstar_id == handle.m_rockstar_id)
|
||||
{
|
||||
session::add_infraction(player, Infraction::TRIED_KICK_PLAYER);
|
||||
g_notification_service->push_error("Protections", std::format("{} tried to lost connection kick you!", player->get_name()));
|
||||
return true;
|
||||
}
|
||||
@@ -141,6 +145,7 @@ namespace big
|
||||
{
|
||||
if (plyr->get_net_data() && plyr != player && player->get_net_data()->m_gamer_handle_2.m_rockstar_id == handle.m_rockstar_id)
|
||||
{
|
||||
session::add_infraction(player, Infraction::LOST_CONNECTION_KICK_DETECTED);
|
||||
g_notification_service->push_error("Protections", std::format("{} tried to lost connection kick {}!", player->get_name(), plyr->get_name()));
|
||||
return true;
|
||||
}
|
||||
@@ -148,6 +153,47 @@ namespace big
|
||||
|
||||
break;
|
||||
}
|
||||
case rage::eNetMessage::MsgSessionEstablished:
|
||||
{
|
||||
rage::rlGamerHandle handle{ 0 };
|
||||
if (player && player->get_net_data())
|
||||
{
|
||||
uint64_t session_id;
|
||||
buffer.ReadQWord(&session_id, 64);
|
||||
gamer_handle_deserialize(handle, buffer);
|
||||
if (session_id == gta_util::get_network()->m_game_session_ptr->m_rline_session.m_session_id)
|
||||
{
|
||||
if (handle.m_rockstar_id != player->get_net_data()->m_gamer_handle_2.m_rockstar_id)
|
||||
{
|
||||
session::add_infraction(player, Infraction::SPOOFED_ROCKSTAR_ID); // TODO: store this RID
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case rage::eNetMessage::MsgNetComplaint:
|
||||
{
|
||||
uint64_t host_token{};
|
||||
buffer.ReadQWord(&host_token, 64);
|
||||
|
||||
std::vector<CNetGamePlayer*> players;
|
||||
|
||||
uint32_t num_of_tokens{};
|
||||
buffer.ReadDword(&num_of_tokens, 32);
|
||||
|
||||
if (player && host_token != player->get_net_data()->m_host_token)
|
||||
session::add_infraction(player, Infraction::DESYNC_PROTECTION);
|
||||
|
||||
return true; // block desync kicks as host
|
||||
}
|
||||
case rage::eNetMessage::MsgRequestObjectIds:
|
||||
{
|
||||
if (player && player->block_join)
|
||||
{
|
||||
g_notification_service->push("Join Blocker", std::format("Trying to prevent {} from joining...", player->get_name()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "hooking.hpp"
|
||||
#include "util/notify.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
bool hooks::received_clone_create(CNetworkObjectMgr* mgr, CNetGamePlayer* src, CNetGamePlayer* dst, eNetObjType object_type, int32_t object_id, int32_t object_flag, rage::datBitBuffer* buffer, int32_t timestamp)
|
||||
{
|
||||
if (object_type < eNetObjType::NET_OBJ_TYPE_AUTOMOBILE || object_type > eNetObjType::NET_OBJ_TYPE_TRAIN)
|
||||
{
|
||||
notify::crash_blocked(src, "out of bounds object type");
|
||||
return true;
|
||||
}
|
||||
|
||||
g->m_syncing_player = src;
|
||||
return g_hooking->get_original<hooks::received_clone_create>()(mgr, src, dst, object_type, object_id, object_flag, buffer, timestamp);
|
||||
}
|
||||
}
|
||||
@@ -1,406 +1,23 @@
|
||||
#include "hooking.hpp"
|
||||
#include "core/globals.hpp"
|
||||
#include "base/CObject.hpp"
|
||||
#include "entities/fwEntity.hpp"
|
||||
#include "rage/netSyncDataNodeBase.hpp"
|
||||
#include "rage/netSyncTree.hpp"
|
||||
#include "gta/net_object_mgr.hpp"
|
||||
#include "datanodes/door/CDoorCreationDataNode.hpp"
|
||||
#include "datanodes/dynamic_entity/CDynamicEntityGameStateDataNode.hpp"
|
||||
#include "datanodes/object/CObjectCreationDataNode.hpp"
|
||||
#include "datanodes/ped/CPedAttachDataNode.hpp"
|
||||
#include "datanodes/ped/CPedCreationDataNode.hpp"
|
||||
#include "datanodes/ped/CPedInventoryDataNode.hpp"
|
||||
#include "datanodes/pickup/CPickupCreationDataNode.hpp"
|
||||
#include "datanodes/physical/CPhysicalAttachDataNode.hpp"
|
||||
#include "datanodes/player/CPlayerAppearanceDataNode.hpp"
|
||||
#include "datanodes/proximity_migrateable/CSectorDataNode.hpp"
|
||||
#include "datanodes/vehicle/CVehicleCreationDataNode.hpp"
|
||||
#include "datanodes/vehicle/CVehicleGadgetDataNode.hpp"
|
||||
#include "network/netObject.hpp"
|
||||
#include "base/CBaseModelInfo.hpp"
|
||||
#include "vehicle/CVehicleModelInfo.hpp"
|
||||
#include "util/model_info.hpp"
|
||||
#include "network/CNetGamePlayer.hpp"
|
||||
#define CLASS_TO_MANGLED_NAME(c) "?AV"#c"@@"
|
||||
#include "util/notify.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
constexpr uint64_t operator ""_fnv1a(char const* str, std::size_t len)
|
||||
eAckCode hooks::received_clone_sync(CNetworkObjectMgr* mgr, CNetGamePlayer* src, CNetGamePlayer* dst, eNetObjType object_type, uint16_t object_id, rage::datBitBuffer* buffer, uint16_t unk, uint32_t timestamp)
|
||||
{
|
||||
auto const fnv_offset_basis = 14695981039346656037ULL;
|
||||
auto const fnv_prime = 1099511628211ULL;
|
||||
|
||||
auto value = fnv_offset_basis;
|
||||
for (auto i = 0; i < len; i++) {
|
||||
value ^= static_cast<size_t>(str[i]);
|
||||
value *= fnv_prime;
|
||||
}
|
||||
value ^= value >> 32;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
constexpr uint32_t crash_models[] = { RAGE_JOAAT("prop_dummy_01"), RAGE_JOAAT("prop_dummy_car"), RAGE_JOAAT("prop_dummy_light"), RAGE_JOAAT("prop_dummy_plane"), RAGE_JOAAT("slod_human"),
|
||||
RAGE_JOAAT("slod_small_quadped"), RAGE_JOAAT("slod_large_quadped"), RAGE_JOAAT("prop_distantcar_night"), RAGE_JOAAT("prop_distantcar_day"), RAGE_JOAAT("hei_bh1_08_details4_em_night"),
|
||||
RAGE_JOAAT("dt1_18_sq_night_slod"), RAGE_JOAAT("ss1_12_night_slod"), -1288391198, RAGE_JOAAT("h4_prop_bush_bgnvla_med_01"), RAGE_JOAAT("h4_prop_bush_bgnvla_lrg_01"),
|
||||
RAGE_JOAAT("h4_prop_bush_buddleia_low_01"), RAGE_JOAAT("h4_prop_bush_ear_aa"), RAGE_JOAAT("h4_prop_bush_ear_ab"), RAGE_JOAAT("h4_prop_bush_fern_low_01"),
|
||||
RAGE_JOAAT("h4_prop_bush_fern_tall_cc"), RAGE_JOAAT("h4_prop_bush_mang_ad"), RAGE_JOAAT("h4_prop_bush_mang_low_aa"), RAGE_JOAAT("h4_prop_bush_mang_low_ab"),
|
||||
RAGE_JOAAT("h4_prop_bush_seagrape_low_01"), RAGE_JOAAT("prop_h4_ground_cover"), RAGE_JOAAT("h4_prop_weed_groundcover_01"), RAGE_JOAAT("h4_prop_grass_med_01"),
|
||||
RAGE_JOAAT("h4_prop_grass_tropical_lush_01"), RAGE_JOAAT("h4_prop_grass_wiregrass_01"), RAGE_JOAAT("h4_prop_weed_01_plant"), RAGE_JOAAT("h4_prop_weed_01_row"),
|
||||
RAGE_JOAAT("urbanweeds02_l1"), RAGE_JOAAT("proc_forest_grass01"), RAGE_JOAAT("prop_small_bushyba"), RAGE_JOAAT("arbitergt"), RAGE_JOAAT("astron2"), RAGE_JOAAT("cyclone2"),
|
||||
RAGE_JOAAT("ignus2"), RAGE_JOAAT("s95"), RAGE_JOAAT("hc_gunman"), RAGE_JOAAT("v_res_d_dildo_a"), RAGE_JOAAT("v_res_d_dildo_b"), RAGE_JOAAT("v_res_d_dildo_c"),
|
||||
RAGE_JOAAT("v_res_d_dildo_d"), RAGE_JOAAT("v_res_d_dildo_e"), RAGE_JOAAT("v_res_d_dildo_f"), RAGE_JOAAT("v_res_skateboard"), RAGE_JOAAT("prop_battery_01"), RAGE_JOAAT("prop_barbell_01"),
|
||||
RAGE_JOAAT("prop_barbell_02"), RAGE_JOAAT("prop_bandsaw_01"), RAGE_JOAAT("prop_bbq_3"), RAGE_JOAAT("v_med_curtainsnewcloth2"), RAGE_JOAAT("bh1_07_flagpoles"),
|
||||
92962485, RAGE_JOAAT("ig_wade") };
|
||||
|
||||
constexpr uint32_t cage_models[] = { RAGE_JOAAT("prop_rub_cage01a"), RAGE_JOAAT("prop_fnclink_05crnr1"), RAGE_JOAAT("prop_gold_cont_01"), RAGE_JOAAT("prop_gold_cont_01b"),
|
||||
RAGE_JOAAT("prop_feeder1"), RAGE_JOAAT("stt_prop_stunt_tube_s"), RAGE_JOAAT("prop_feeder1_cr"), RAGE_JOAAT("p_cablecar_s") };
|
||||
|
||||
inline bool is_model_a_crash_model(uint32_t model) {
|
||||
for (auto iterator : crash_models)
|
||||
if (object_type < eNetObjType::NET_OBJ_TYPE_AUTOMOBILE || object_type > eNetObjType::NET_OBJ_TYPE_TRAIN)
|
||||
{
|
||||
if (iterator == model) return true;
|
||||
notify::crash_blocked(src, "out of bounds object type");
|
||||
return eAckCode::ACKCODE_FAIL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool is_model_a_cage_model(uint32_t model) {
|
||||
for (auto iterator : cage_models)
|
||||
if (auto net_obj = g_pointers->m_get_net_object(mgr, object_id, true); net_obj && net_obj->m_object_type != (int16_t)object_type)
|
||||
{
|
||||
if (iterator == model) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* get_node_from_object(rage::netSyncNodeBase* node)
|
||||
{
|
||||
constexpr uint64_t hash = CLASS_TO_MANGLED_NAME(T)""_fnv1a;
|
||||
if (node->IsParentNode())
|
||||
{
|
||||
for (auto child = node->m_first_child; child; child = child->m_next_sibling) {
|
||||
T* attach_node = get_node_from_object<T>(child);
|
||||
if (attach_node != nullptr)
|
||||
return attach_node;
|
||||
}
|
||||
}
|
||||
else if (node->IsDataNode())
|
||||
{
|
||||
if (typeid(*node).hash_code() == hash)
|
||||
return dynamic_cast<T*>(node);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool is_attachment_infinite(CPhysicalAttachDataNode* node, uint16_t object_id) {
|
||||
if (rage::netObject* attached_object = (*g_pointers->m_network_object_mgr)->find_object_by_id(node->m_attached_to, false); attached_object)
|
||||
{
|
||||
if (rage::netSyncTree* tree = attached_object->GetSyncTree(); tree)
|
||||
{
|
||||
if (rage::netSyncNodeBase* base_node = tree->m_sync_node; base_node) {
|
||||
const auto attached_attach_node = get_node_from_object<CPhysicalAttachDataNode>(base_node);
|
||||
if (attached_attach_node && attached_attach_node->m_attached)
|
||||
{
|
||||
if (attached_attach_node->m_attached_to == object_id) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return is_attachment_infinite(attached_attach_node, object_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
notify::crash_blocked(src, "incorrect object type");
|
||||
return eAckCode::ACKCODE_FAIL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_ped_attachment_infinite(CPedAttachDataNode* node, uint16_t object_id) {
|
||||
if (rage::netObject* attached_object = (*g_pointers->m_network_object_mgr)->find_object_by_id(node->m_attached_to, false); attached_object)
|
||||
{
|
||||
if (rage::netSyncTree* tree = attached_object->GetSyncTree(); tree)
|
||||
{
|
||||
if (rage::netSyncNodeBase* base_node = tree->m_sync_node; base_node) {
|
||||
const auto attached_attach_node = get_node_from_object<CPedAttachDataNode>(base_node);
|
||||
if (attached_attach_node && attached_attach_node->m_attached)
|
||||
{
|
||||
if (attached_attach_node->m_attached_to == object_id) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return is_ped_attachment_infinite(attached_attach_node, object_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool check_node(rage::netSyncNodeBase* node, CNetGamePlayer* sender, uint16_t object_id)
|
||||
{
|
||||
if (node->IsParentNode())
|
||||
{
|
||||
for (auto child = node->m_first_child; child; child = child->m_next_sibling) {
|
||||
if (check_node(child, sender, object_id))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (node->IsDataNode())
|
||||
{
|
||||
//LOG(INFO) << typeid(*node).name() << ": " << HEX_TO_UPPER(typeid(*node).hash_code()); //Use this to get hashes for each node
|
||||
switch (typeid(*node).hash_code()) {
|
||||
case "?AVCDoorCreationDataNode@@"_fnv1a: //CDoorCreationDataNode
|
||||
{
|
||||
const auto creation_node = dynamic_cast<CDoorCreationDataNode*>(node);
|
||||
if (is_model_a_crash_model(creation_node->m_model))
|
||||
{
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Invalid door model: Model: " << HEX_TO_UPPER(creation_node->m_model) << " From: " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Invalid door model from {}", sender->get_name()), std::format("Model: 0x{:x}", creation_node->m_model));
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "?AVCPickupCreationDataNode@@"_fnv1a: //CPickupCreationDataNode
|
||||
{
|
||||
const auto creation_node = dynamic_cast<CPickupCreationDataNode*>(node);
|
||||
if (is_model_a_crash_model(creation_node->m_custom_model))
|
||||
{
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Invalid pickup model: Model: " << HEX_TO_UPPER(creation_node->m_custom_model) << " From : " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Invalid pickup model from {}", sender->get_name()), std::format("Model: 0x{:x}", creation_node->m_custom_model));
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "?AVCPhysicalAttachDataNode@@"_fnv1a: //CPhysicalAttachDataNode
|
||||
{
|
||||
const auto attach_node = dynamic_cast<CPhysicalAttachDataNode*>(node);
|
||||
if (attach_node->m_attached && attach_node->m_attached_to == object_id)
|
||||
{
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Infinite attachment: Node: " << typeid(*node).name() << " From : " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Infinite attachment from {}", sender->get_name()), std::format("Node: {}", typeid(*node).name()));
|
||||
return true;
|
||||
}
|
||||
else if (attach_node->m_attached && is_attachment_infinite(attach_node, object_id))
|
||||
{
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Infinite attachment v2: Node: " << typeid(*node).name() << " From : " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Infinite attachment v2 from {}", sender->get_name()), std::format("Node: {}", typeid(*node).name()));
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "?AVCPedCreationDataNode@@"_fnv1a: //CPedCreationDataNode
|
||||
{
|
||||
const auto creation_node = dynamic_cast<CPedCreationDataNode*>(node);
|
||||
if (is_model_a_crash_model(creation_node->m_model))
|
||||
{
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Invalid ped model: Model: " << HEX_TO_UPPER(creation_node->m_model) << " From : " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Invalid ped model from {}", sender->get_name()), std::format("Model: 0x{:x}", creation_node->m_model));
|
||||
return true;
|
||||
}
|
||||
else if (creation_node->m_has_prop && is_model_a_crash_model(creation_node->m_prop_model)) {
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Invalid ped prop model: Model: " << HEX_TO_UPPER(creation_node->m_prop_model) << " From : " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Invalid ped prop model from {}", sender->get_name()), std::format("Model: 0x{:x}", creation_node->m_prop_model));
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "?AVCPedAttachDataNode@@"_fnv1a: //CPedAttachDataNode
|
||||
{
|
||||
const auto attach_node = dynamic_cast<CPedAttachDataNode*>(node);
|
||||
if (attach_node->m_attached && attach_node->m_attached_to == object_id)
|
||||
{
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Infinite ped attachment: Node: " << typeid(*node).name() << " From : " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Infinite ped attachment from {}", sender->get_name()), std::format("Node: {}", typeid(*node).name()));
|
||||
return true;
|
||||
}
|
||||
else if (attach_node->m_attached && is_ped_attachment_infinite(attach_node, object_id))
|
||||
{
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Infinite ped attachment v2: Node: " << typeid(*node).name() << " From : " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Infinite ped attachment v2 from {}", sender->get_name()), std::format("Node: {}", typeid(*node).name()));
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "?AVCPedInventoryDataNode@@"_fnv1a://CPedInventoryDataNode
|
||||
{
|
||||
const auto inventory_node = dynamic_cast<CPedInventoryDataNode*>(node);
|
||||
if (inventory_node->m_num_items > 105 || inventory_node->m_num_ammos > 65)
|
||||
{
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Invalid inventory data from: " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Invalid inventory data from {}", sender->get_name()), "Invalid inventory data.");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "?AVCVehicleCreationDataNode@@"_fnv1a: //CVehicleCreationDataNode
|
||||
{
|
||||
const auto vehicle_creation_node = dynamic_cast<CVehicleCreationDataNode*>(node);
|
||||
if (is_model_a_crash_model(vehicle_creation_node->m_model)) {
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Invalid vehicle model: Model: " << HEX_TO_UPPER(vehicle_creation_node->m_model) << " From : " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Invalid vehicle model from {}", sender->get_name()), std::format("Model: 0x{:x}", vehicle_creation_node->m_model));
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "?AVCVehicleGadgetDataNode@@"_fnv1a: //CVehicleGadgetDataNode
|
||||
{
|
||||
const auto vehicle_gadget_node = dynamic_cast<CVehicleGadgetDataNode*>(node);
|
||||
if (vehicle_gadget_node->m_gadget_count > 2)
|
||||
{
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Invalid vehicle gadget count from: " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Invalid vehicle gadget count from {}", sender->get_name()), "Invalid vehicle gadget count.");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "?AVCObjectCreationDataNode@@"_fnv1a: //CObjectCreationDataNode
|
||||
{
|
||||
const auto creation_node = dynamic_cast<CObjectCreationDataNode*>(node);
|
||||
if (is_model_a_crash_model(creation_node->m_model)) {
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Invalid object model: Model: " << HEX_TO_UPPER(creation_node->m_model) << " From : " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Invalid object model from {}", sender->get_name()), std::format("Model: 0x{:x}", creation_node->m_model));
|
||||
return true;
|
||||
}
|
||||
else if (is_model_a_cage_model(creation_node->m_model)) {
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Cage model: Model: " << HEX_TO_UPPER(creation_node->m_model) << " From : " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Cage model from {}", sender->get_name()), std::format("Model: 0x{:x}", creation_node->m_model));
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "?AVCPlayerAppearanceDataNode@@"_fnv1a: //CPlayerAppearanceDataNode
|
||||
{
|
||||
const auto player_appearance_node = dynamic_cast<CPlayerAppearanceDataNode*>(node);
|
||||
if (is_model_a_crash_model(player_appearance_node->m_model_hash)) {
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Invalid player model: Model:" << HEX_TO_UPPER(player_appearance_node->m_model_hash) << " From : " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Invalid player model from {}", sender->get_name()), std::format("Model: 0x{:x}", player_appearance_node->m_model_hash));
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "?AVCSectorDataNode@@"_fnv1a: //CSectorDataNode
|
||||
{
|
||||
const auto sector_node = dynamic_cast<CSectorDataNode*>(node);
|
||||
if (sector_node->m_pos_x == 712 || sector_node->m_pos_y == 712 || sector_node->m_pos_z == 712)
|
||||
{
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Invalid sector position from: " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Invalid sector position from {}", sender->get_name()), "Invalid sector position.");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "?AVCDynamicEntityGameStateDataNode@@"_fnv1a: //CDynamicEntityGameStateDataNode
|
||||
{
|
||||
const auto game_state_node = dynamic_cast<CDynamicEntityGameStateDataNode*>(node);
|
||||
if (game_state_node->m_decor_count > 11)
|
||||
{
|
||||
if (g->notifications.invalid_sync.log)
|
||||
LOG(WARNING) << "Invalid decorator count from: " << sender->get_name();
|
||||
if (g->notifications.invalid_sync.notify)
|
||||
g_notification_service->push_warning(std::format("Invalid decorator count from {}", sender->get_name()), "Invalid decorator count.");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t hooks::received_clone_sync(CNetworkObjectMgr* mgr, CNetGamePlayer* src, CNetGamePlayer* dst, eObjType sync_type, uint16_t obj_id, rage::datBitBuffer* buffer, uint16_t unk, uint32_t timestamp)
|
||||
{
|
||||
if (auto sync_tree = g_pointers->m_get_sync_tree_for_type(mgr, sync_type); sync_tree && *g_pointers->m_is_session_started)
|
||||
{
|
||||
if (auto net_obj = g_pointers->m_get_net_object(mgr, obj_id, true); net_obj)
|
||||
{
|
||||
auto tree_name = g_pointers->m_get_sync_type_info(sync_type, 0);
|
||||
|
||||
if (sync_type < eObjType::carObjType || sync_type > eObjType::unkObjType14)
|
||||
{
|
||||
if (g->notifications.out_of_allowed_range_sync_type.log)
|
||||
LOG(WARNING) << "Out of Bounds sync: Type: " << sync_type << " Tree name: " << tree_name << " From: " << src->get_name();
|
||||
if (g->notifications.out_of_allowed_range_sync_type.notify)
|
||||
g_notification_service->push_warning(std::format("Out Of Allowed Sync Range from {}", src->get_name()), std::format("Type {} in sync tree {}", std::uint16_t(sync_type), tree_name));
|
||||
|
||||
return g_hooking->get_original<received_clone_sync>()(mgr, src, dst, sync_type, obj_id, buffer, unk, timestamp);
|
||||
}
|
||||
|
||||
if (net_obj->m_object_type != sync_type)
|
||||
{
|
||||
if (g->notifications.mismatch_sync_type.log)
|
||||
LOG(WARNING) << "Mismatch sync: Type: " << sync_type << " Tree name: " << tree_name << " From: " << src->get_name();
|
||||
if (g->notifications.mismatch_sync_type.notify)
|
||||
g_notification_service->push_warning(std::format("Mismatch Sync from {}", src->get_name()), std::format("Type {} in sync tree {}", std::uint16_t(sync_type), tree_name));
|
||||
|
||||
return eSyncReply::WrongOwner;
|
||||
}
|
||||
|
||||
if (auto game_obj = net_obj->GetGameObject(); game_obj)
|
||||
{
|
||||
if (const auto model_info = game_obj->m_model_info; model_info)
|
||||
{
|
||||
// sync_type is telling us it's a vehicle
|
||||
// let's check if it's actually a vehicle according to our game...
|
||||
if (((sync_type >= eObjType::bikeObjType && sync_type <= eObjType::heliObjType)
|
||||
|| (sync_type >= eObjType::planeObjType && sync_type <= eObjType::submarineObjType)
|
||||
|| (sync_type >= eObjType::trailerObjType && sync_type <= eObjType::trainObjType))
|
||||
&& model_info->m_model_type != eModelType::Vehicle)
|
||||
{
|
||||
return eSyncReply::CantApplyData;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fall through? Should not happen but let's try to be safe
|
||||
return eSyncReply::CantApplyData;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t pos = buffer->m_bitsRead;
|
||||
g_pointers->m_read_bitbuffer_into_sync_tree(sync_tree, 2, 0, buffer, 0);
|
||||
buffer->Seek(pos);
|
||||
|
||||
//LOG(INFO) << typeid(*tree).name() << ": " << HEX_TO_UPPER(typeid(*tree).hash_code()); //Use this to get hashes for each tree
|
||||
|
||||
if (sync_tree->m_child_node_count && check_node(sync_tree->m_sync_node, src, obj_id))
|
||||
{
|
||||
return eSyncReply::CantApplyData;
|
||||
}
|
||||
}
|
||||
}
|
||||
return g_hooking->get_original<received_clone_sync>()(mgr, src, dst, sync_type, obj_id, buffer, unk, timestamp);
|
||||
g->m_syncing_player = src;
|
||||
return g_hooking->get_original<received_clone_sync>()(mgr, src, dst, object_type, object_id, buffer, unk, timestamp);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "hooking.hpp"
|
||||
#include <network/CNetGamePlayer.hpp>
|
||||
#include "gta/script_id.hpp"
|
||||
#include "util/notify.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
@@ -32,7 +33,6 @@ namespace big
|
||||
if (event_id > 91u)
|
||||
{
|
||||
g_pointers->m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -81,15 +81,10 @@ namespace big
|
||||
uint16_t unk2 = buffer->Read<uint16_t>(13);
|
||||
uint32_t action = buffer->Read<uint32_t>(8);
|
||||
|
||||
if (action >= 15 && action <= 18 || action == 33)
|
||||
if ((action >= 15 && action <= 18) || action == 33)
|
||||
{
|
||||
g_pointers->m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
|
||||
if (g->notifications.received_event.vehicle_temp_action.log)
|
||||
LOG(INFO) << "RECEIVED_EVENT_HANDLER : " << source_player->get_name() << " sent TASK_VEHICLE_TEMP_ACTION crash.";
|
||||
if (g->notifications.received_event.vehicle_temp_action.notify)
|
||||
g_notification_service->push_error("Protections", std::format("{} sent TASK_VEHICLE_TEMP_ACTION crash.", source_player->get_name()));
|
||||
|
||||
notify::crash_blocked(source_player, "vehicle temp action");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -172,7 +167,6 @@ namespace big
|
||||
break;
|
||||
}
|
||||
// player sending this event is a modder
|
||||
case eNetworkEvents::NETWORK_CHECK_CODE_CRCS_EVENT:
|
||||
case eNetworkEvents::REPORT_MYSELF_EVENT:
|
||||
{
|
||||
if (g->notifications.received_event.modder_detect.log)
|
||||
@@ -180,6 +174,9 @@ namespace big
|
||||
if (g->notifications.received_event.modder_detect.notify)
|
||||
g_notification_service->push_warning("Protections", std::format("{} sent out a modder event.", source_player->get_name()));
|
||||
|
||||
if (auto plyr = g_player_service->get_by_id(source_player->m_player_id))
|
||||
session::add_infraction(plyr, Infraction::TRIGGERED_ANTICHEAT);
|
||||
|
||||
break;
|
||||
}
|
||||
case eNetworkEvents::REQUEST_CONTROL_EVENT:
|
||||
@@ -202,7 +199,7 @@ namespace big
|
||||
case eNetworkEvents::SCRIPT_WORLD_STATE_EVENT:
|
||||
{
|
||||
auto type = buffer->Read<WorldStateDataType>(4);
|
||||
(void)buffer->Read<bool>(1);
|
||||
buffer->Read<bool>(1);
|
||||
CGameScriptId id;
|
||||
script_id_deserialize(id, *buffer);
|
||||
|
||||
@@ -221,7 +218,8 @@ namespace big
|
||||
if (type != 7)
|
||||
{
|
||||
// most definitely a crash
|
||||
g_notification_service->push_error("Protections", std::format("{} sent rope crash.", source_player->get_name()));
|
||||
LOG(INFO) << std::hex << std::uppercase << "0x" << id.m_hash;
|
||||
notify::crash_blocked(source_player, "rope");
|
||||
g_pointers->m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
return;
|
||||
}
|
||||
@@ -234,7 +232,7 @@ namespace big
|
||||
|
||||
if (unk2 == 0 && (unk3 == 0 || unk3 == 103))
|
||||
{
|
||||
g_notification_service->push_error("Protections", std::format("{} sent SCRIPT_WORLD_STATE_EVENT crash.", source_player->get_name()));
|
||||
notify::crash_blocked(source_player, "pop group override");
|
||||
g_pointers->m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
return;
|
||||
}
|
||||
@@ -250,6 +248,7 @@ namespace big
|
||||
|
||||
if (hash == RAGE_JOAAT("WEAPON_UNARMED"))
|
||||
{
|
||||
notify::crash_blocked(source_player, "remove unarmed");
|
||||
g_pointers->m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
return;
|
||||
}
|
||||
@@ -278,6 +277,35 @@ namespace big
|
||||
buffer->Seek(0);
|
||||
break;
|
||||
}
|
||||
case eNetworkEvents::GIVE_CONTROL_EVENT:
|
||||
{
|
||||
uint32_t timestamp = buffer->Read<uint32_t>(32);
|
||||
int count = buffer->Read<int>(2);
|
||||
bool unk = buffer->Read<bool>(1);
|
||||
|
||||
if (count > 3)
|
||||
{
|
||||
count = 3;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int net_id = buffer->Read<int>(13);
|
||||
eNetObjType object_type = buffer->Read<eNetObjType>(4);
|
||||
int unk = buffer->Read<int>(3);
|
||||
|
||||
if (object_type < eNetObjType::NET_OBJ_TYPE_AUTOMOBILE || object_type > eNetObjType::NET_OBJ_TYPE_TRAIN)
|
||||
{
|
||||
notify::crash_blocked(source_player, "out of bounds give control type");
|
||||
g_pointers->m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
buffer->Seek(0);
|
||||
g->m_syncing_player = source_player;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "hooking.hpp"
|
||||
#include "gta_util.hpp"
|
||||
#include "util/session.hpp"
|
||||
#include <network/CNetGamePlayer.hpp>
|
||||
|
||||
namespace big
|
||||
@@ -276,8 +277,9 @@ namespace big
|
||||
case eRemoteEvent::SHKick:
|
||||
if (g->protections.script_events.network_bail)
|
||||
{
|
||||
if (auto plyr = g_player_service->get_by_id(player->m_player_id))
|
||||
session::add_infraction(plyr, Infraction::TRIED_KICK_PLAYER);
|
||||
format_string(player_name, "Network Bail", notify.network_bail.log, notify.network_bail.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "hooking.hpp"
|
||||
#include "gta/net_game_event.hpp"
|
||||
#include "util/notify.hpp"
|
||||
#include <datanodes/dynamic_entity/CDynamicEntityGameStateDataNode.hpp>
|
||||
|
||||
namespace big
|
||||
{
|
||||
constexpr uint32_t NULL_INTERIOR = 0x0FFFCFFFF;
|
||||
|
||||
void hooks::serialize_dynamic_entity_game_state_data_node(CDynamicEntityGameStateDataNode* node, rage::CSyncDataBase* data)
|
||||
{
|
||||
data->SerializeBool(&node->unk_00C5);
|
||||
if (node->unk_00C5 && !data->IsSizeCalculator())
|
||||
{
|
||||
data->SerializeDwordAlt(&node->m_interior_index, 32);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool is_null_interior = node->m_interior_index == NULL_INTERIOR;
|
||||
data->SerializeBool(&is_null_interior);
|
||||
|
||||
if (!is_null_interior || data->IsSizeCalculator())
|
||||
{
|
||||
data->SerializeDwordAlt(&node->m_interior_index, 32);
|
||||
}
|
||||
else
|
||||
{
|
||||
node->m_interior_index = NULL_INTERIOR;
|
||||
}
|
||||
}
|
||||
|
||||
data->SerializeBool(&node->unk_00C4);
|
||||
|
||||
bool has_decors = node->m_decor_count != 0;
|
||||
data->SerializeBool(&has_decors);
|
||||
|
||||
if (has_decors || data->IsSizeCalculator())
|
||||
{
|
||||
data->SerializeDwordAlt(&node->m_decor_count, 4);
|
||||
|
||||
if (data->IsSizeCalculator())
|
||||
node->m_decor_count = 11;
|
||||
|
||||
if (node->m_decor_count > 11)
|
||||
{
|
||||
notify::crash_blocked(g->m_syncing_player, "out of bounds decorator count");
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < node->m_decor_count; i++)
|
||||
{
|
||||
data->SerializeDwordAlt(&node->m_decors[i].m_type, 3);
|
||||
data->SerializeDwordAlt(&node->m_decors[i].m_value, 32);
|
||||
data->SerializeDwordAlt(&node->m_decors[i].m_name_hash, 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "hooking.hpp"
|
||||
#include "gta/net_game_event.hpp"
|
||||
#include "util/notify.hpp"
|
||||
#include <datanodes/ped/CPedInventoryDataNode.hpp>
|
||||
|
||||
namespace big
|
||||
{
|
||||
void hooks::serialize_ped_inventory_data_node(CPedInventoryDataNode* node, rage::CSyncDataBase* data)
|
||||
{
|
||||
if (data->IsSizeCalculator())
|
||||
{
|
||||
node->m_num_items = 105;
|
||||
node->m_num_ammos = 65;
|
||||
}
|
||||
|
||||
data->SerializeDwordAlt(&node->m_num_items, 7);
|
||||
if (node->m_num_items > 105)
|
||||
{
|
||||
notify::crash_blocked(g->m_syncing_player, "out of bounds inventory item count");
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < node->m_num_items; i++)
|
||||
{
|
||||
data->SerializeDwordAlt(&node->m_items[i], 9);
|
||||
node->unk_1680[i] = 0;
|
||||
node->unk_16E9[i] = 0;
|
||||
}
|
||||
|
||||
data->SerializeDwordAlt(&node->m_num_ammos, 7);
|
||||
data->SerializeBool(&node->m_ammo_all_infinite);
|
||||
if (node->m_num_ammos > 65)
|
||||
{
|
||||
notify::crash_blocked(g->m_syncing_player, "out of bounds inventory ammo count");
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < node->m_num_ammos; i++)
|
||||
{
|
||||
data->SerializeDwordAlt(&node->m_ammos[i], 9);
|
||||
if (node->m_ammo_all_infinite && !data->IsSizeCalculator())
|
||||
continue;
|
||||
data->SerializeBool(&node->m_infinite_ammos[i]);
|
||||
if (node->m_infinite_ammos[i] && !data->IsSizeCalculator())
|
||||
continue;
|
||||
data->SerializeDwordAlt(&node->m_ammo_quantities[i], 14);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "hooking.hpp"
|
||||
#include "gta/net_game_event.hpp"
|
||||
#include "util/notify.hpp"
|
||||
#include <datanodes/vehicle/CVehicleGadgetDataNode.hpp>
|
||||
|
||||
namespace big
|
||||
{
|
||||
void hooks::serialize_vehicle_gadget_data_node(CVehicleGadgetDataNode* node, rage::CSyncDataBase* data)
|
||||
{
|
||||
data->SerializeBool(&node->m_has_parent_offset);
|
||||
if (node->m_has_parent_offset || data->IsSizeCalculator())
|
||||
{
|
||||
data->SerializeSignedFloat((float*)&node->m_parent_offset_x, 24.0f, 14);
|
||||
data->SerializeSignedFloat((float*)&node->m_parent_offset_y, 24.0f, 14);
|
||||
data->SerializeSignedFloat((float*)&node->m_parent_offset_z, 24.0f, 14);
|
||||
}
|
||||
else
|
||||
{
|
||||
node->m_parent_offset_x = 0;
|
||||
node->m_parent_offset_y = 0;
|
||||
node->m_parent_offset_z = 0;
|
||||
node->m_parent_offset_w = 0;
|
||||
}
|
||||
|
||||
data->SerializeDwordAlt(&node->m_gadget_count, 2);
|
||||
|
||||
if (data->IsSizeCalculator())
|
||||
node->m_gadget_count = 2;
|
||||
|
||||
if (node->m_gadget_count > 2)
|
||||
{
|
||||
notify::crash_blocked(g->m_syncing_player, "out of bounds vehicle gadget count");
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < node->m_gadget_count; i++)
|
||||
{
|
||||
data->SerializeDwordAlt((uint32_t*)&node->m_gadget_data[i].m_gadget_type, 3);
|
||||
|
||||
int size;
|
||||
if (data->IsSizeCalculator())
|
||||
size = 94;
|
||||
else
|
||||
size = g_pointers->m_get_vehicle_gadget_array_size(node->m_gadget_data[i].m_gadget_type);
|
||||
|
||||
data->SerializeArray(&node->m_gadget_data[i].m_data, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "hooking.hpp"
|
||||
#include <datanodes/player/CPlayerGamerDataNode.hpp>
|
||||
|
||||
namespace big
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user