fix(native_hooks): fix game crashes resulting from not calling the destructor

This commit is contained in:
maybegreat48
2025-03-18 19:58:08 -04:00
parent d1c1ff53ee
commit 4e6f5587d7
18 changed files with 306 additions and 19 deletions
+4 -2
View File
@@ -12,7 +12,7 @@ namespace YimMenu
m_Program(program),
m_Hook(std::make_unique<VMTHook>("rage::scrProgram", program, 9))
{
m_Hook->Hook(6, ScrProgram_Dtor);
m_Hook->Hook(6, reinterpret_cast<void*>(&ScrProgram_Dtor));
m_Hook->Enable();
m_OrigHandlers = new rage::scrNativeHandler[program->m_NativeCount];
memcpy(m_OrigHandlers, program->m_NativeEntrypoints, program->m_NativeCount * sizeof(rage::scrNativeHandler));
@@ -36,7 +36,9 @@ namespace YimMenu
void NativeHooks::Program::ScrProgram_Dtor(rage::scrProgram* _this, bool free_mem)
{
NativeHooks::UnregisterProgram(_this);
(**reinterpret_cast<decltype(&NativeHooks::Program::ScrProgram_Dtor)**>(_this))(_this, free_mem);
auto vtable = *reinterpret_cast<void***>(_this);
auto destructor = *(vtable + 6);
reinterpret_cast<decltype(&NativeHooks::Program::ScrProgram_Dtor)>(destructor)(_this, free_mem);
}
NativeHooks::NativeHooks() :
+17 -1
View File
@@ -13,9 +13,20 @@ namespace YimMenu
void PlayerAllCommand::OnCall()
{
std::vector<Player> validPlayers;
validPlayers.reserve(Players::GetPlayers().size() - 1);
for (auto& p : Players::GetPlayers())
{
if (p.second.IsValid() && (!Self::GetPlayer().IsValid() || p.second.GetId() != Self::GetPlayer().GetId()))
m_PlayerCommand->Call(p.second);
validPlayers.push_back(p.second);
}
m_PlayerCommand->Call(validPlayers);
}
void PlayerCommand::OnCall(const std::vector<Player>& players)
{
for (auto& p : players)
Call(p);
}
PlayerCommand::PlayerCommand(std::string name, std::string label, std::string description, int num_args, bool all_version) :
@@ -34,4 +45,9 @@ namespace YimMenu
{
OnCall(target);
}
void PlayerCommand::Call(const std::vector<Player>& targets)
{
OnCall(targets);
}
}
+3 -1
View File
@@ -21,9 +21,11 @@ namespace YimMenu
protected:
virtual void OnCall(Player) = 0;
virtual void OnCall(const std::vector<Player>&);
public:
PlayerCommand(std::string name, std::string label, std::string description, int num_args = 0, bool all_version = true);
void Call(Player target); // TODO
void Call(Player target);
void Call(const std::vector<Player>& targets);
};
}
+1 -1
View File
@@ -37,7 +37,7 @@ namespace YimMenu::Features
if (SCRIPT::GET_HASH_OF_THIS_SCRIPT_NAME() == "freemode"_J)
{
GSBD::Get()->FreemodeState = eFreemodeState::RUNNING;
GSBD::Get()->CayoPericoStrandedAnimalChoice = 0; // unbrick cayo just in case if the script host never responds
GSBD::Get()->CayoPericoStrandedAnimalChoice = 1; // unbrick cayo just in case if the script host never responds
}
return ctx->SetReturnValue(TRUE);
}
+7 -5
View File
@@ -17,13 +17,15 @@ namespace YimMenu::Features
{
NETWORK::NETWORK_SET_IN_SPECTATOR_MODE(false, 0);
HUD::SET_MINIMAP_IN_SPECTATOR_MODE(false, 0);
if (selected)
{
NETWORK::NETWORK_SET_IN_SPECTATOR_MODE(true, selected.GetPed().GetHandle());
HUD::SET_MINIMAP_IN_SPECTATOR_MODE(true, selected.GetPed().GetHandle());
}
m_SpectatingPlayer = selected;
}
if (m_SpectatingPlayer)
{
NETWORK::NETWORK_SET_IN_SPECTATOR_MODE(true, m_SpectatingPlayer.GetPed().GetHandle());
HUD::SET_MINIMAP_IN_SPECTATOR_MODE(true, m_SpectatingPlayer.GetPed().GetHandle());
}
}
virtual void OnDisable() override
@@ -0,0 +1,29 @@
#include "game/commands/PlayerCommand.hpp"
#include "types/script/ScriptEvent.hpp"
namespace YimMenu::Features
{
class SendSext : public PlayerCommand
{
using PlayerCommand::PlayerCommand;
static constexpr auto sm_Strippers = std::to_array({"HCH", "INF", "JUL", "NIK", "SAP", "TXI"});
static constexpr auto sm_SextTypes = std::to_array({"1ST", "2ND", "NEED"});
virtual void OnCall(Player player) override
{
OnCall(std::vector{player});
}
virtual void OnCall(const std::vector<Player>& players) override
{
SCRIPT_EVENT_SEND_TEXT_LABEL_MESSAGE message;
strncpy(message.Label.Data, std::format("SXT_{}_{}", sm_Strippers[rand() % sm_Strippers.size()], sm_SextTypes[rand() % sm_SextTypes.size()]).c_str(), 24);
for (auto& player : players)
message.SetPlayer(player.GetId());
message.Send();
}
};
static SendSext _SendSext{"sendsext", "Send Sext", "Sends a random sext to the specified player"};
}
@@ -0,0 +1,31 @@
#include "core/commands/BoolCommand.hpp"
#include "core/commands/IntCommand.hpp"
#include "game/commands/PlayerCommand.hpp"
#include "types/script/ScriptEvent.hpp"
#include "types/script/globals/GSBD_FM_Events.hpp"
namespace YimMenu::Features
{
static IntCommand _BountyAmount{"bountyamount", "Bounty Amount", "The bounty amount to set", 1, 10000, 9000};
static BoolCommand _AnonymousBounty{"anonymousbounty", "Anonymous Bounty", "Sets the bounty as anonymous"};
class SetBounty : public PlayerCommand
{
using PlayerCommand::PlayerCommand;
virtual void OnCall(Player player) override
{
SCRIPT_EVENT_BOUNTY bounty;
bounty.Target = player.GetId();
bounty.FromPed = _AnonymousBounty.GetState();
bounty.Amount = _BountyAmount.GetState();
bounty.BountyFlags = 1;
bounty.AntiReplayValue1 = GSBD_FM_Events::Get()->AntiReplayValue1;
bounty.AntiReplayValue2 = GSBD_FM_Events::Get()->AntiReplayValue2;
bounty.SetAllPlayers();
bounty.Send();
}
};
static SetBounty _SetBounty{"setbounty", "Set Bounty", "Sets the specified bounty to the player"};
}
+11 -1
View File
@@ -8,14 +8,24 @@ namespace YimMenu::Submenus
{
// TODO: this needs a rework
auto session = std::make_shared<Category>("Session");
auto bountyGroup = std::make_shared<Group>("Bounty", 1);
auto toxicGroup = std::make_shared<Group>("Toxic");
auto trollGroup = std::make_shared<Group>("Troll");
auto enhancements = std::make_shared<Group>("Enhancements");
bountyGroup->AddItem(std::make_shared<IntCommandItem>("bountyamount"_J, "Amount"));
bountyGroup->AddItem(std::make_shared<BoolCommandItem>("anonymousbounty"_J, "Anonymous"));
bountyGroup->AddItem(std::make_shared<CommandItem>("setbountyall"_J, "Set Bounties"));
trollGroup->AddItem(std::make_shared<CommandItem>("sendsextall"_J, "Send Sexts"));
toxicGroup->AddItem(std::make_shared<CommandItem>("killall"_J, "Kill All"));
toxicGroup->AddItem(std::make_shared<CommandItem>("explodeall"_J, "Explode All"));
enhancements->AddItem(std::make_shared<BoolCommandItem>("fastjoin"_J));
session->AddItem(bountyGroup);
session->AddItem(trollGroup);
session->AddItem(toxicGroup);
session->AddItem(enhancements);
@@ -0,0 +1,23 @@
#include "Troll.hpp"
namespace YimMenu::Submenus
{
std::shared_ptr<Category> BuildTrollMenu()
{
auto menu = std::make_shared<Category>("Troll");
auto general = std::make_shared<Group>("General");
auto bounty = std::make_shared<Group>("Bounty", 1);
general->AddItem(std::make_shared<PlayerCommandItem>("sendsext"_J));
bounty->AddItem(std::make_shared<IntCommandItem>("bountyamount"_J, "Amount"));
bounty->AddItem(std::make_shared<BoolCommandItem>("anonymousbounty"_J, "Anonymous"));
bounty->AddItem(std::make_shared<PlayerCommandItem>("setbounty"_J));
menu->AddItem(general);
menu->AddItem(bounty);
return menu;
}
}
@@ -0,0 +1,8 @@
#pragma once
#include "core/frontend/manager/Category.hpp"
#include "game/frontend/items/Items.hpp"
namespace YimMenu::Submenus
{
std::shared_ptr<Category> BuildTrollMenu();
}
+2
View File
@@ -2,6 +2,7 @@
#include "Player/Info.hpp"
#include "Player/Toxic.hpp"
#include "Player/Troll.hpp"
#include "core/frontend/widgets/imgui_colors.h"
#include "game/backend/PlayerData.hpp"
#include "game/backend/Players.hpp"
@@ -115,6 +116,7 @@ namespace YimMenu::Submenus
Submenu::Submenu("Players")
{
AddCategory(std::move(BuildInfoMenu()));
AddCategory(std::move(BuildTrollMenu()));
AddCategory(std::move(BuildToxicMenu()));
for (auto& category : m_Categories)
-6
View File
@@ -1,14 +1,8 @@
#include "Self.hpp"
#include "core/util/Joaat.hpp"
#include "core/commands/BoolCommand.hpp"
#include "core/commands/Commands.hpp"
#include "core/commands/IntCommand.hpp"
#include "game/frontend/items/Items.hpp"
#include <map>
namespace YimMenu::Features
{
// BoolCommand _RecoveryEnabled("recoveryenabled", "Recovery Enabled", "Is the recovery feature enabled");
+1 -1
View File
@@ -19,7 +19,7 @@ namespace YimMenu
return m_Handle && m_Handle->IsPhysical() && m_Handle->m_PlayerInfo;
}
int Player::GetId()
int Player::GetId() const
{
if (!IsValid())
{
+1 -1
View File
@@ -35,7 +35,7 @@ namespace YimMenu
// object
bool IsValid() const;
int GetId();
int GetId() const;
const char* GetName();
CNetGamePlayer* GetHandle();
rage::rlGamerInfo* GetGamerInfo();
+10
View File
@@ -0,0 +1,10 @@
#include "ScriptEvent.hpp"
#include "game/gta/Natives.hpp"
void SCRIPT_EVENT::Send()
{
auto size = __SizeOfEvent;
__SizeOfEvent = 0;
SCRIPT::_SEND_TU_SCRIPT_EVENT_NEW(1, this, size / sizeof(std::uint32_t), PlayerBits, EventIndex);
__SizeOfEvent = size;
}
+143
View File
@@ -0,0 +1,143 @@
#pragma once
#include "types.hpp"
#define REGISTER_SCRIPT_EVENT(classType, indexType) \
constexpr static auto EVENT_INDEX = ScriptEventIndex::indexType; \
classType() \
{ \
memset(this, 0, sizeof(classType)); EventIndex = static_cast<int>(EVENT_INDEX); \
__SizeOfEvent = sizeof(classType); \
} \
classType(const classType& other) \
{ \
memcpy(this, &other, sizeof(classType)); \
EventIndex = static_cast<int>(EVENT_INDEX); \
__SizeOfEvent = sizeof(classType); \
}
// I doubt rockstar would cycle this again, but best to keep this, just in case
enum class ScriptEventIndex
{
Bounty = 1517551547, // (137, "FM_TXT_BNTY0", iVar1, PLAYER::GET_PLAYER_NAME(Var2.f_1), "", 5000, Var2.f_6);
CeoBan = 1531565154, // mpply_vipgameplaydisabledtimer
CeoKick = -11681548,
CeoMoney = -337848027, // Goon_Paid_Large
ClearWantedLevel = -1704545346,
ForceMission = 259469385, // ), Var0.f_2, 1))
GiveCollectible = 968269233, // DLC_SUM20_HIDDEN_COLLECTIBLES xref
GtaBanner = -330501227, // NETWORK::NETWORK_IS_SCRIPT_ACTIVE("BUSINESS_BATTLES", -1, true, 0) second one
NetworkBail = -901348601, // NETWORK::NETWORK_BAIL(16, 0, 0); xref func
PersonalVehicleDestroyed = 1655503526, // PLYVEH_INS_DES1
RemoteOffradar = 57493695, // NETWORK::GET_TIME_DIFFERENCE(NETWORK::GET_NETWORK_TIME(), Var0.f_2)
SendToCutscene = -1951335381, // (bVar3, bVar4, 125f, 1)
SendToCayoPerico = 373376135, // CELL_HI_INV
SendToLocation = 1669592503, // &Var222, 11);
SoundSpam = 996099702, // CELL_APTINVYACHT
Spectate = 1840946429, // SPEC_TCK1
Teleport = -1321657966, // Mission_Pass_Notify
TeleportToWarehouse = -1253241415, // .f_4 == 50
TransactionError = -830063381, // NETWORK_RECEIVE_PLAYER_JOBSHARE_CASH
VehicleKick = -503325966, // PIM_RFMOC
MCTeleport = 1103127469, // NETWORK::NETWORK_HASH_FROM_PLAYER_HANDLE(PLAYER::PLAYER_ID()) == (first one)
StartActivity = 1450115979, // (Var0.f_2, -1); first match
MarkPlayerAsBeast = 1649541577, // GB_BOSSBST xref == PLAYER... global (sadly doesn't actually turn people into the beast)
KickFromInterior = -1496371358, // ENTITY::SET_ENTITY_HEALTH(PLAYER::PLAYER_PED_ID(), PED::GET_PED_MAX_HEALTH(PLAYER::PLAYER_PED_ID()), false)
InteriorControl = -1638522928, // eventData.f_3 != _INVALID_PLAYER_INDEX_0()
SendTextLabelSMS = -1773335296,
Crash = -1604421397, // SET_NO_LOADING_SCREEN, xref it
Crash2 = -375628860,
Crash3 = 323285304,
TSECommand = 800157557, // CnCTG_IN_BF
TSECommandRotateCam = 225624744, // != 29) && f
TSECommandSound = 385726943,
Notification = -642704387,
NotificationMoneyBanked = 94410750, // TICK_TC_BANK
NotificationMoneyRemoved = -242911964, // TICK_TC_REMO
NotificationMoneyStolen = -295926414, // TICK_TC_STOL
NotificationCrash1 = -994541138, // AMCH_ATTEMPT2
NotificationCrash2 = 782258655, // GBSAL_COLL
DestroyPersonalVehicle = 109434679, // CnC_PV_THEFT
TriggerCEORaid = -1906536929,
StartScriptBegin = -366707054,
StartScriptProceed = 1757622014
};
struct SCRIPT_EVENT
{
protected:
SCR_HASH EventIndex; // 0x00
Player SenderIndex; // 0x08
int __SizeOfEvent; // 0x0C
SCR_INT PlayerBits; // 0x10
public:
int GetSender() const
{
return SenderIndex;
}
Hash GetEventIndex() const
{
return EventIndex;
}
bool ShouldReceiveEvent(int player) const
{
return PlayerBits & (1 << player);
}
void SetAllPlayers()
{
PlayerBits = -1;
}
void SetAllPlayersExcept(int player_to_exclude)
{
PlayerBits = -1 & ~(1 << player_to_exclude);
}
void SetPlayer(int player)
{
PlayerBits |= (1 << player);
}
// must be called in script context
void Send();
inline void Send(int player)
{
SetPlayer(player);
Send();
}
};
static_assert(sizeof(SCRIPT_EVENT) == 3 * 8);
struct SCRIPT_EVENT_BOUNTY : public SCRIPT_EVENT
{
REGISTER_SCRIPT_EVENT(SCRIPT_EVENT_BOUNTY, Bounty);
PLAYER_INDEX Target;
SCR_INT BountyFlags;
SCR_INT Amount;
SCR_INT PAD_0006;
SCR_BOOL FromPed;
uint64_t PAD_0008[13];
SCR_INT AntiReplayValue1;
SCR_INT AntiReplayValue2;
};
static_assert(sizeof(SCRIPT_EVENT_BOUNTY) == 23 * 8);
struct SCRIPT_EVENT_SEND_TEXT_LABEL_MESSAGE : public SCRIPT_EVENT
{
REGISTER_SCRIPT_EVENT(SCRIPT_EVENT_SEND_TEXT_LABEL_MESSAGE, SendTextLabelSMS);
TEXT_LABEL_23 Label;
};
static_assert(sizeof(SCRIPT_EVENT_SEND_TEXT_LABEL_MESSAGE) == 9 * 8);
#undef REGISTER_SCRIPT_EVENT
@@ -0,0 +1,13 @@
#pragma once
#include "../types.hpp"
// TODO
struct GSBD_FM_Events
{
uint64_t PAD_0000[8];
SCR_INT AntiReplayValue1;
SCR_INT AntiReplayValue2;
static GSBD_FM_Events* Get();
};
//static_assert(sizeof(GSBD) == 1498 * 8);
+2
View File
@@ -6,6 +6,7 @@
#include "GSBD.hpp"
#include "GSBD_BlockB.hpp"
#include "GSBD_FM.hpp"
#include "GSBD_FM_Events.hpp"
#include "GSBD_Kicking.hpp"
#include "GSBD_PropertyInstances.hpp"
#include "g_AMC_playerBD.hpp"
@@ -27,6 +28,7 @@ DEFINE_GLOBAL_ACCESSOR(GPBD_MissionName, 1873815);
DEFINE_GLOBAL_ACCESSOR(GSBD, 2648917);
DEFINE_GLOBAL_ACCESSOR(GSBD_BlockB, 2650415);
DEFINE_GLOBAL_ACCESSOR(GSBD_FM, 1835557);
DEFINE_GLOBAL_ACCESSOR(GSBD_FM_Events, 1917230);
DEFINE_GLOBAL_ACCESSOR(GSBD_Kicking, 1877529);
DEFINE_GLOBAL_ACCESSOR(GSBD_PropertyInstances, 1937699);
DEFINE_GLOBAL_ACCESSOR(g_AMC_playerBD, 2699624);