feat(Logging): Added settings to toggle them (#126)
* feat(Settings): Added notification settings
This commit is contained in:
parent
24a7e7d906
commit
52149aba33
@ -22,6 +22,65 @@ namespace big
|
||||
bool no_idle_kick = false;
|
||||
};
|
||||
|
||||
struct notifications
|
||||
{
|
||||
struct pair
|
||||
{
|
||||
bool log = false;
|
||||
bool notify = false;
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
pair clear_ped_task{};
|
||||
pair report_cash_spawn{};
|
||||
pair modder_detect{};
|
||||
pair request_control_event{};
|
||||
} received_event{};
|
||||
|
||||
struct
|
||||
{
|
||||
pair bounty{};
|
||||
pair ceo_ban{};
|
||||
pair ceo_kick{};
|
||||
pair ceo_money{};
|
||||
pair clear_wanted_level{};
|
||||
pair fake_deposit{};
|
||||
pair force_mission{};
|
||||
pair force_teleport{};
|
||||
pair gta_banner{};
|
||||
pair network_bail{};
|
||||
pair personal_vehicle_destroyed{};
|
||||
pair remote_off_radar{};
|
||||
pair rotate_cam{};
|
||||
pair send_to_cutscene{};
|
||||
pair send_to_island{};
|
||||
pair sound_spam{};
|
||||
pair spectate{};
|
||||
pair transaction_error{};
|
||||
pair vehicle_kick{};
|
||||
} script_event_handler{};
|
||||
|
||||
pair gta_thread_kill{};
|
||||
pair gta_thread_start{};
|
||||
|
||||
pair net_array_error{};
|
||||
pair network_player_mgr_shutdown{};
|
||||
|
||||
struct
|
||||
{
|
||||
bool above_map = true;
|
||||
bool log = false;
|
||||
bool notify = false;
|
||||
} player_join;
|
||||
pair player_leave{};
|
||||
|
||||
pair reports{};
|
||||
|
||||
pair send_net_info_to_lobby{};
|
||||
pair transaction_rate_limit{};
|
||||
};
|
||||
|
||||
struct player {
|
||||
int character_slot = 1;
|
||||
bool player_never_wanted = false;
|
||||
@ -184,6 +243,7 @@ namespace big
|
||||
|
||||
debug debug{};
|
||||
tunables tunables{};
|
||||
notifications notifications{};
|
||||
player player{};
|
||||
protections protections{};
|
||||
self self{};
|
||||
@ -211,25 +271,110 @@ namespace big
|
||||
{
|
||||
this->debug.script_event_logging = j["debug"]["script_event_logging"];
|
||||
|
||||
this->protections.script_events.bounty = j["protections"]["script_events"]["bounty"];
|
||||
this->protections.script_events.ceo_ban = j["protections"]["script_events"]["ceo_ban"];
|
||||
this->protections.script_events.ceo_kick = j["protections"]["script_events"]["ceo_kick"];
|
||||
this->protections.script_events.ceo_money = j["protections"]["script_events"]["ceo_money"];
|
||||
this->protections.script_events.clear_wanted_level = j["protections"]["script_events"]["clear_wanted_level"];
|
||||
this->protections.script_events.fake_deposit = j["protections"]["script_events"]["fake_deposit"];
|
||||
this->protections.script_events.force_mission = j["protections"]["script_events"]["force_mission"];
|
||||
this->protections.script_events.force_teleport = j["protections"]["script_events"]["force_teleport"];
|
||||
this->protections.script_events.gta_banner = j["protections"]["script_events"]["gta_banner"];
|
||||
this->protections.script_events.network_bail = j["protections"]["script_events"]["network_bail"];
|
||||
this->protections.script_events.personal_vehicle_destroyed = j["protections"]["script_events"]["personal_vehicle_destroyed"];
|
||||
this->protections.script_events.remote_off_radar = j["protections"]["script_events"]["remote_off_radar"];
|
||||
this->protections.script_events.rotate_cam = j["protections"]["script_events"]["rotate_cam"];
|
||||
this->protections.script_events.send_to_cutscene = j["protections"]["script_events"]["send_to_cutscene"];
|
||||
this->protections.script_events.send_to_island = j["protections"]["script_events"]["send_to_island"];
|
||||
this->protections.script_events.sound_spam = j["protections"]["script_events"]["sound_spam"];
|
||||
this->protections.script_events.spectate = j["protections"]["script_events"]["spectate"];
|
||||
this->protections.script_events.transaction_error = j["protections"]["script_events"]["transaction_error"];
|
||||
this->protections.script_events.vehicle_kick = j["protections"]["script_events"]["vehicle_kick"];
|
||||
g->notifications.gta_thread_kill.log = j["notifications"]["gta_thread_kill"]["log"];
|
||||
g->notifications.gta_thread_kill.notify = j["notifications"]["gta_thread_kill"]["notify"];
|
||||
g->notifications.gta_thread_start.log = j["notifications"]["gta_thread_start"]["log"];
|
||||
g->notifications.gta_thread_start.notify = j["notifications"]["gta_thread_start"]["notify"];
|
||||
|
||||
g->notifications.net_array_error.log = j["notifications"]["net_array_error"]["log"];
|
||||
g->notifications.net_array_error.notify = j["notifications"]["net_array_error"]["notify"];
|
||||
|
||||
g->notifications.network_player_mgr_shutdown.log = j["notifications"]["network_player_mgr_shutdown"]["log"];
|
||||
g->notifications.network_player_mgr_shutdown.notify = j["notifications"]["network_player_mgr_shutdown"]["notify"];
|
||||
|
||||
g->notifications.player_join.above_map = j["notifications"]["player_join"]["above_map"];
|
||||
g->notifications.player_join.log = j["notifications"]["player_join"]["log"];
|
||||
g->notifications.player_join.notify = j["notifications"]["player_join"]["notify"];
|
||||
|
||||
g->notifications.player_leave.log = j["notifications"]["player_leave"]["log"];
|
||||
g->notifications.player_leave.notify = j["notifications"]["player_leave"]["notify"];
|
||||
|
||||
g->notifications.received_event.clear_ped_task.log = j["notifications"]["received_event"]["clear_ped_task"]["log"];
|
||||
g->notifications.received_event.clear_ped_task.notify = j["notifications"]["received_event"]["clear_ped_task"]["notify"];
|
||||
g->notifications.received_event.modder_detect.log = j["notifications"]["received_event"]["modder_detect"]["log"];
|
||||
g->notifications.received_event.modder_detect.notify = j["notifications"]["received_event"]["modder_detect"]["notify"];
|
||||
g->notifications.received_event.request_control_event.log = j["notifications"]["received_event"]["request_control_event"]["log"];
|
||||
g->notifications.received_event.request_control_event.notify = j["notifications"]["received_event"]["request_control_event"]["notify"];
|
||||
g->notifications.received_event.report_cash_spawn.log = j["notifications"]["received_event"]["report_cash_spawn"]["log"];
|
||||
g->notifications.received_event.report_cash_spawn.notify = j["notifications"]["received_event"]["report_cash_spawn"]["notify"];
|
||||
|
||||
g->notifications.reports.log = j["notifications"]["reports"]["log"];
|
||||
g->notifications.reports.notify = j["notifications"]["reports"]["notify"];
|
||||
|
||||
{
|
||||
const auto& script_handler_j = j["notifications"]["script_event_handler"];
|
||||
auto& script_handler = this->notifications.script_event_handler;
|
||||
|
||||
script_handler.bounty.log = script_handler_j["bounty"]["log"];
|
||||
script_handler.bounty.notify = script_handler_j["bounty"]["notify"];
|
||||
script_handler.ceo_ban.log = script_handler_j["ceo_ban"]["log"];
|
||||
script_handler.ceo_ban.notify = script_handler_j["ceo_ban"]["notify"];
|
||||
script_handler.ceo_kick.log = script_handler_j["ceo_kick"]["log"];
|
||||
script_handler.ceo_kick.notify = script_handler_j["ceo_kick"]["notify"];
|
||||
script_handler.ceo_money.log = script_handler_j["ceo_money"]["log"];
|
||||
script_handler.ceo_money.notify = script_handler_j["ceo_money"]["notify"];
|
||||
script_handler.clear_wanted_level.log = script_handler_j["clear_wanted_level"]["log"];
|
||||
script_handler.clear_wanted_level.notify = script_handler_j["clear_wanted_level"]["notify"];
|
||||
script_handler.fake_deposit.log = script_handler_j["fake_deposit"]["log"];
|
||||
script_handler.fake_deposit.notify = script_handler_j["fake_deposit"]["notify"];
|
||||
script_handler.force_mission.log = script_handler_j["force_mission"]["log"];
|
||||
script_handler.force_mission.notify = script_handler_j["force_mission"]["notify"];
|
||||
script_handler.force_teleport.log = script_handler_j["force_teleport"]["log"];
|
||||
script_handler.force_teleport.notify = script_handler_j["force_teleport"]["notify"];
|
||||
script_handler.gta_banner.log = script_handler_j["gta_banner"]["log"];
|
||||
script_handler.gta_banner.notify = script_handler_j["gta_banner"]["notify"];
|
||||
script_handler.network_bail.log = script_handler_j["network_bail"]["log"];
|
||||
script_handler.network_bail.notify = script_handler_j["network_bail"]["notify"];
|
||||
script_handler.personal_vehicle_destroyed.log = script_handler_j["personal_vehicle_destroyed"]["log"];
|
||||
script_handler.personal_vehicle_destroyed.notify = script_handler_j["personal_vehicle_destroyed"]["notify"];
|
||||
script_handler.remote_off_radar.log = script_handler_j["remote_off_radar"]["log"];
|
||||
script_handler.remote_off_radar.notify = script_handler_j["remote_off_radar"]["notify"];
|
||||
script_handler.rotate_cam.log = script_handler_j["rotate_cam"]["log"];
|
||||
script_handler.rotate_cam.notify = script_handler_j["rotate_cam"]["notify"];
|
||||
script_handler.send_to_cutscene.log = script_handler_j["send_to_cutscene"]["log"];
|
||||
script_handler.send_to_cutscene.notify = script_handler_j["send_to_cutscene"]["notify"];
|
||||
script_handler.send_to_island.log = script_handler_j["send_to_island"]["log"];
|
||||
script_handler.send_to_island.notify = script_handler_j["send_to_island"]["notify"];
|
||||
script_handler.sound_spam.log = script_handler_j["sound_spam"]["log"];
|
||||
script_handler.sound_spam.notify = script_handler_j["sound_spam"]["notify"];
|
||||
script_handler.spectate.log = script_handler_j["spectate"]["log"];
|
||||
script_handler.spectate.notify = script_handler_j["spectate"]["notify"];
|
||||
script_handler.transaction_error.log = script_handler_j["transaction_error"]["log"];
|
||||
script_handler.transaction_error.notify = script_handler_j["transaction_error"]["notify"];
|
||||
script_handler.vehicle_kick.log = script_handler_j["vehicle_kick"]["log"];
|
||||
script_handler.vehicle_kick.notify = script_handler_j["vehicle_kick"]["notify"];
|
||||
}
|
||||
|
||||
g->notifications.send_net_info_to_lobby.log = j["notifications"]["send_net_info_to_lobby"]["log"];
|
||||
g->notifications.send_net_info_to_lobby.notify = j["notifications"]["send_net_info_to_lobby"]["notify"];
|
||||
|
||||
g->notifications.transaction_rate_limit.log = j["notifications"]["transaction_rate_limit"]["log"];
|
||||
g->notifications.transaction_rate_limit.notify = j["notifications"]["transaction_rate_limit"]["notify"];
|
||||
|
||||
{
|
||||
const auto& script_handler_j = j["protections"]["script_events"];
|
||||
auto& script_handler = this->protections.script_events;
|
||||
|
||||
script_handler.bounty = script_handler_j["bounty"];
|
||||
script_handler.ceo_ban = script_handler_j["ceo_ban"];
|
||||
script_handler.ceo_kick = script_handler_j["ceo_kick"];
|
||||
script_handler.ceo_money = script_handler_j["ceo_money"];
|
||||
script_handler.clear_wanted_level = script_handler_j["clear_wanted_level"];
|
||||
script_handler.fake_deposit = script_handler_j["fake_deposit"];
|
||||
script_handler.force_mission = script_handler_j["force_mission"];
|
||||
script_handler.force_teleport = script_handler_j["force_teleport"];
|
||||
script_handler.gta_banner = script_handler_j["gta_banner"];
|
||||
script_handler.network_bail = script_handler_j["network_bail"];
|
||||
script_handler.personal_vehicle_destroyed = script_handler_j["personal_vehicle_destroyed"];
|
||||
script_handler.remote_off_radar = script_handler_j["remote_off_radar"];
|
||||
script_handler.rotate_cam = script_handler_j["rotate_cam"];
|
||||
script_handler.send_to_cutscene = script_handler_j["send_to_cutscene"];
|
||||
script_handler.send_to_island = script_handler_j["send_to_island"];
|
||||
script_handler.sound_spam = script_handler_j["sound_spam"];
|
||||
script_handler.spectate = script_handler_j["spectate"];
|
||||
script_handler.transaction_error = script_handler_j["transaction_error"];
|
||||
script_handler.vehicle_kick = script_handler_j["vehicle_kick"];
|
||||
}
|
||||
|
||||
this->tunables.disable_phone = j["tunables"]["disable_phone"];
|
||||
this->tunables.no_idle_kick = j["tunables"]["no_idle_kick"];
|
||||
@ -302,6 +447,17 @@ namespace big
|
||||
|
||||
nlohmann::json to_json()
|
||||
{
|
||||
constexpr auto return_notify_pair = [](const notifications::pair& notify_pair) -> auto
|
||||
{
|
||||
return nlohmann::json{
|
||||
{ "log", notify_pair.log },
|
||||
{ "notify", notify_pair.notify }
|
||||
};
|
||||
};
|
||||
|
||||
const auto& script_handler_notifications = this->notifications.script_event_handler;
|
||||
const auto& script_handler_protections = this->protections.script_events;
|
||||
|
||||
return nlohmann::json{
|
||||
{
|
||||
"debug",
|
||||
@ -309,30 +465,78 @@ namespace big
|
||||
{ "script_event_logging", this->debug.script_event_logging }
|
||||
}
|
||||
},
|
||||
{
|
||||
"notifications", {
|
||||
{ "gta_thread_kill", return_notify_pair(g->notifications.gta_thread_kill) },
|
||||
{ "gta_thread_start", return_notify_pair(g->notifications.gta_thread_start) },
|
||||
{"net_array_error", return_notify_pair(g->notifications.net_array_error)},
|
||||
{ "network_player_mgr_shutdown", return_notify_pair(g->notifications.network_player_mgr_shutdown) },
|
||||
{ "player_join", {
|
||||
{ "above_map", g->notifications.player_join.above_map },
|
||||
{ "log", g->notifications.player_join.log },
|
||||
{ "notify", g->notifications.player_join.notify }
|
||||
}
|
||||
},
|
||||
{ "player_leave", return_notify_pair(g->notifications.player_leave) },
|
||||
{
|
||||
"received_event", {
|
||||
{ "clear_ped_task", return_notify_pair(g->notifications.received_event.clear_ped_task) },
|
||||
{ "modder_detect", return_notify_pair(g->notifications.received_event.modder_detect) },
|
||||
{ "report_cash_spawn", return_notify_pair(g->notifications.received_event.report_cash_spawn) },
|
||||
{ "request_control_event", return_notify_pair(g->notifications.received_event.request_control_event) }
|
||||
}
|
||||
},
|
||||
{ "reports", return_notify_pair(g->notifications.reports) },
|
||||
{ "script_event_handler", {
|
||||
{ "bounty", return_notify_pair(script_handler_notifications.bounty) },
|
||||
{ "ceo_ban", return_notify_pair(script_handler_notifications.ceo_ban) },
|
||||
{ "ceo_kick", return_notify_pair(script_handler_notifications.ceo_kick) },
|
||||
{ "ceo_money", return_notify_pair(script_handler_notifications.ceo_money) },
|
||||
{ "clear_wanted_level", return_notify_pair(script_handler_notifications.clear_wanted_level) },
|
||||
{ "fake_deposit", return_notify_pair(script_handler_notifications.fake_deposit) },
|
||||
{ "force_mission", return_notify_pair(script_handler_notifications.force_mission) },
|
||||
{ "force_teleport", return_notify_pair(script_handler_notifications.force_teleport) },
|
||||
{ "gta_banner", return_notify_pair(script_handler_notifications.gta_banner) },
|
||||
{ "network_bail", return_notify_pair(script_handler_notifications.network_bail) },
|
||||
{ "personal_vehicle_destroyed", return_notify_pair(script_handler_notifications.personal_vehicle_destroyed) },
|
||||
{ "remote_off_radar", return_notify_pair(script_handler_notifications.remote_off_radar) },
|
||||
{ "rotate_cam", return_notify_pair(script_handler_notifications.rotate_cam) },
|
||||
{ "send_to_cutscene", return_notify_pair(script_handler_notifications.send_to_cutscene) },
|
||||
{ "send_to_island", return_notify_pair(script_handler_notifications.send_to_island) },
|
||||
{ "sound_spam", return_notify_pair(script_handler_notifications.sound_spam) },
|
||||
{ "spectate", return_notify_pair(script_handler_notifications.spectate) },
|
||||
{ "transaction_error", return_notify_pair(script_handler_notifications.transaction_error) },
|
||||
{ "vehicle_kick", return_notify_pair(script_handler_notifications.vehicle_kick) }
|
||||
}
|
||||
},
|
||||
{ "send_net_info_to_lobby", return_notify_pair(g->notifications.send_net_info_to_lobby) },
|
||||
{ "transaction_rate_limit", return_notify_pair(g->notifications.transaction_rate_limit) }
|
||||
}
|
||||
},
|
||||
{
|
||||
"protections",
|
||||
{
|
||||
{
|
||||
"script_events", {
|
||||
{ "bounty", this->protections.script_events.bounty },
|
||||
{ "ceo_ban", this->protections.script_events.ceo_ban },
|
||||
{ "ceo_kick", this->protections.script_events.ceo_kick },
|
||||
{ "ceo_money", this->protections.script_events.ceo_money },
|
||||
{ "clear_wanted_level", this->protections.script_events.clear_wanted_level },
|
||||
{ "fake_deposit", this->protections.script_events.fake_deposit },
|
||||
{ "force_mission", this->protections.script_events.force_mission },
|
||||
{ "force_teleport", this->protections.script_events.force_teleport },
|
||||
{ "gta_banner", this->protections.script_events.gta_banner },
|
||||
{ "network_bail", this->protections.script_events.network_bail },
|
||||
{ "personal_vehicle_destroyed", this->protections.script_events.personal_vehicle_destroyed },
|
||||
{ "remote_off_radar", this->protections.script_events.remote_off_radar },
|
||||
{ "rotate_cam", this->protections.script_events.rotate_cam },
|
||||
{ "send_to_cutscene", this->protections.script_events.send_to_cutscene },
|
||||
{ "send_to_island", this->protections.script_events.send_to_island },
|
||||
{ "sound_spam", this->protections.script_events.sound_spam },
|
||||
{ "spectate", this->protections.script_events.spectate },
|
||||
{ "transaction_error", this->protections.script_events.transaction_error },
|
||||
{ "vehicle_kick", this->protections.script_events.vehicle_kick }
|
||||
{ "bounty", script_handler_protections.bounty },
|
||||
{ "ceo_ban", script_handler_protections.ceo_ban },
|
||||
{ "ceo_kick", script_handler_protections.ceo_kick },
|
||||
{ "ceo_money", script_handler_protections.ceo_money },
|
||||
{ "clear_wanted_level", script_handler_protections.clear_wanted_level },
|
||||
{ "fake_deposit", script_handler_protections.fake_deposit },
|
||||
{ "force_mission", script_handler_protections.force_mission },
|
||||
{ "force_teleport", script_handler_protections.force_teleport },
|
||||
{ "gta_banner", script_handler_protections.gta_banner },
|
||||
{ "network_bail", script_handler_protections.network_bail },
|
||||
{ "personal_vehicle_destroyed", script_handler_protections.personal_vehicle_destroyed },
|
||||
{ "remote_off_radar", script_handler_protections.remote_off_radar },
|
||||
{ "rotate_cam", script_handler_protections.rotate_cam },
|
||||
{ "send_to_cutscene", script_handler_protections.send_to_cutscene },
|
||||
{ "send_to_island", script_handler_protections.send_to_island },
|
||||
{ "sound_spam", script_handler_protections.sound_spam },
|
||||
{ "spectate", script_handler_protections.spectate },
|
||||
{ "transaction_error", script_handler_protections.transaction_error },
|
||||
{ "vehicle_kick", script_handler_protections.vehicle_kick }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,11 @@ namespace big
|
||||
{
|
||||
if (SCRIPT::GET_HASH_OF_THIS_SCRIPT_NAME() == RAGE_JOAAT("shop_controller") && strcmp(entryLine1, "CTALERT_F_2") == 0)
|
||||
{
|
||||
if (g->notifications.transaction_rate_limit.log)
|
||||
LOG(WARNING) << "Received transaction rate limit";
|
||||
if (g->notifications.transaction_rate_limit.notify)
|
||||
g_notification_service->push_warning("Transaction Rate Limit", "You're receiving transaction rate limits, whatever you're doing do it less.");
|
||||
|
||||
// dismisses popup instead of killing it silently
|
||||
*script_global(4529830).as<int*>() = 0;
|
||||
|
||||
|
@ -7,6 +7,11 @@ namespace big
|
||||
{
|
||||
rage::eThreadState result = g_hooking->m_gta_thread_kill_hook.get_original<decltype(>a_thread_kill)>()(thread);
|
||||
|
||||
if (g->notifications.gta_thread_kill.log)
|
||||
LOG(INFO) << "Script Thread '" << thread->m_name << "' terminated.";
|
||||
if (g->notifications.gta_thread_kill.notify)
|
||||
g_notification_service->push("Script Thread Termination", fmt::format("Script Thread '{}' terminated.", thread->m_name));
|
||||
|
||||
g_native_hooks->do_cleanup_for_thread(thread);
|
||||
|
||||
if (thread->m_script_hash == RAGE_JOAAT("freemode"))
|
||||
|
@ -7,6 +7,14 @@ namespace big
|
||||
{
|
||||
GtaThread* new_thread = g_hooking->m_gta_thread_start_hook.get_original<decltype(&hooks::gta_thread_start)>()(a1, a2);
|
||||
|
||||
if (const char* name = new_thread->m_name; strlen(name) > 0)
|
||||
{
|
||||
if (g->notifications.gta_thread_kill.log)
|
||||
LOG(INFO) << "Script Thread '" << name << "' started.";
|
||||
if (g->notifications.gta_thread_kill.notify)
|
||||
g_notification_service->push("Script Thread Startup", fmt::format("Script Thread '{}' started.", name));
|
||||
}
|
||||
|
||||
if (new_thread != nullptr)
|
||||
g_native_hooks->check_for_thread(new_thread);
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "hooking.hpp"
|
||||
#include "natives.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
@ -8,13 +7,17 @@ namespace big
|
||||
switch (net_event->m_stat)
|
||||
{
|
||||
case RAGE_JOAAT("MPPLY_GAME_EXPLOITS"):
|
||||
case RAGE_JOAAT("MPPLY_VC_HATE"):
|
||||
case RAGE_JOAAT("MPPLY_EXPLOITS"):
|
||||
case RAGE_JOAAT("MPPLY_VC_HATE"):
|
||||
case RAGE_JOAAT("MPPLY_TC_ANNOYINGME"):
|
||||
case RAGE_JOAAT("MPPLY_TC_HATE"):
|
||||
std::string report = fmt::format("From: {}", sender->get_name());
|
||||
const std::string report = fmt::format("From: {}", sender->get_name());
|
||||
|
||||
g_notification_service->push_warning("BLOCKED REPORT", report);
|
||||
if (g->notifications.reports.log)
|
||||
LOG(INFO) << "Blocked report; " << report;
|
||||
|
||||
if (g->notifications.reports.notify)
|
||||
g_notification_service->push_warning("BLOCKED REPORT", report);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -9,8 +9,12 @@ namespace big
|
||||
{
|
||||
if (datbitbuffer->m_bitsRead + bytes_to_read > datbitbuffer->m_curBit)
|
||||
{
|
||||
LOG(WARNING) << "Received NET_ARRAY_ERROR crash from " << a2->get_name();
|
||||
if (g->notifications.net_array_error.log)
|
||||
LOG(WARNING) << "Received NET_ARRAY_ERROR crash from " << a2->get_name();
|
||||
|
||||
if (g->notifications.net_array_error.notify)
|
||||
g_notification_service->push_warning("Protections", fmt::format("Detected NET_ARRAY_ERROR crash from {}", a2->get_name()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,11 @@ namespace big
|
||||
{
|
||||
g_player_service->do_cleanup();
|
||||
|
||||
if (g->notifications.network_player_mgr_shutdown.log)
|
||||
LOG(INFO) << "CNetworkPlayerMgr#shutdown got called, we're probably leaving our session.";
|
||||
if (g->notifications.network_player_mgr_shutdown.notify)
|
||||
g_notification_service->push("Network Player Manager", "Leaving session and cleaning up player data.");
|
||||
|
||||
return g_hooking->m_network_player_mgr_shutdown_hook.get_original<decltype(&hooks::network_player_mgr_shutdown)>()(_this);
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#include "hooking.hpp"
|
||||
#include "services/player_service.hpp"
|
||||
#include "util/notify.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
@ -7,6 +8,20 @@ namespace big
|
||||
{
|
||||
g_player_service->player_join(net_player);
|
||||
|
||||
if (const rage::netPlayerData* net_player_data = net_player->get_net_data(); net_player_data)
|
||||
{
|
||||
if (g->notifications.player_join.above_map)
|
||||
notify::player_joined(net_player);
|
||||
|
||||
if (g->notifications.player_join.log)
|
||||
LOG(INFO) << "Player joined '" << net_player_data->m_name
|
||||
<< "' taking slot #" << (int)net_player->m_player_id
|
||||
<< " with Rockstar ID: " << net_player_data->m_rockstar_id2;
|
||||
|
||||
if (g->notifications.player_join.notify)
|
||||
g_notification_service->push("Player Joined", fmt::format("{} taking slot #{} with Rockstar ID: {}", net_player_data->m_name, net_player->m_player_id, net_player_data->m_rockstar_id2));
|
||||
}
|
||||
|
||||
return g_hooking->m_player_has_joined_hook.get_original<decltype(&hooks::player_join)>()(_this, net_player);
|
||||
}
|
||||
}
|
@ -7,6 +7,17 @@ namespace big
|
||||
{
|
||||
g_player_service->player_leave(net_player);
|
||||
|
||||
if (const rage::netPlayerData* net_player_data = net_player->get_net_data(); net_player_data)
|
||||
{
|
||||
if (g->notifications.player_leave.log)
|
||||
LOG(INFO) << "Player left '" << net_player_data->m_name
|
||||
<< "' freeing slot #" << (int)net_player->m_player_id
|
||||
<< " with Rockstar ID: " << net_player_data->m_rockstar_id2;
|
||||
if (g->notifications.player_leave.notify)
|
||||
g_notification_service->push("Player Left", fmt::format("{} freeing slot #{} with Rockstar ID: {}", net_player_data->m_name, net_player->m_player_id, net_player_data->m_rockstar_id2));
|
||||
}
|
||||
|
||||
|
||||
return g_hooking->m_player_has_left_hook.get_original<decltype(&hooks::player_leave)>()(_this, net_player);
|
||||
}
|
||||
}
|
@ -33,10 +33,14 @@ namespace big
|
||||
if (source_player->m_player_id < 32)
|
||||
{
|
||||
g_pointers->m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
|
||||
g_notification_service->push_warning("Protection",
|
||||
fmt::format("{} possible attempt at freezing entity.", source_player->get_name())
|
||||
);
|
||||
|
||||
if (g->notifications.received_event.clear_ped_task.log)
|
||||
LOG(INFO) << "RECEIVED_EVENT_HANDLER : " << source_player->get_name() << " sent CLEAR_PED_TASKS event.";
|
||||
|
||||
if (g->notifications.received_event.clear_ped_task.notify)
|
||||
g_notification_service->push_warning("Protection",
|
||||
fmt::format("{} possible attempt at freezing entity.", source_player->get_name())
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -55,9 +59,13 @@ namespace big
|
||||
|
||||
if (money >= 2000)
|
||||
{
|
||||
g_notification_service->push_warning("Protection",
|
||||
fmt::format("{} is spawning cash.", source_player->get_name())
|
||||
);
|
||||
if (g->notifications.received_event.report_cash_spawn.log)
|
||||
LOG(INFO) << "RECEIVED_EVENT_HANDLER : " << source_player->get_name() << " sent REPORT_CASH_SPAWN event.";
|
||||
|
||||
if (g->notifications.received_event.report_cash_spawn.notify)
|
||||
g_notification_service->push_warning("Protection",
|
||||
fmt::format("{} is spawning cash.", source_player->get_name())
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -66,40 +74,30 @@ namespace big
|
||||
case RockstarEvent::NETWORK_CHECK_CODE_CRCS_EVENT:
|
||||
case RockstarEvent::REPORT_MYSELF_EVENT:
|
||||
{
|
||||
g_notification_service->push_warning("Protection",
|
||||
fmt::format("Detected {} as cheating.", source_player->get_name())
|
||||
);
|
||||
if (g->notifications.received_event.modder_detect.log)
|
||||
LOG(INFO) << "RECEIVED_EVENT_HANDLER : " << source_player->get_name() << " sent modder event.";
|
||||
|
||||
if (g->notifications.received_event.modder_detect.notify)
|
||||
g_notification_service->push_warning("Protection",
|
||||
fmt::format("Detected {} as cheating.", source_player->get_name())
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
case RockstarEvent::REQUEST_CONTROL_EVENT:
|
||||
{
|
||||
g_pointers->m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
|
||||
g_notification_service->push_warning("Protection",
|
||||
fmt::format("Denied player control request from {}", source_player->get_name())
|
||||
);
|
||||
|
||||
if (g->notifications.received_event.request_control_event.log)
|
||||
LOG(INFO) << "RECEIVED_EVENT_HANDLER : " << source_player->get_name() << " sent modder event.";
|
||||
|
||||
if (g->notifications.received_event.request_control_event.notify)
|
||||
g_notification_service->push_warning("Protection",
|
||||
fmt::format("Denied player control request from {}", source_player->get_name())
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
//case RockstarEvent::GIVE_PICKUP_REWARDS_EVENT:
|
||||
//{
|
||||
// uint32_t amount, hash;
|
||||
// buffer->ReadDword(&amount, 3);
|
||||
// buffer->ReadDword(&hash, 32);
|
||||
|
||||
// if (func::is_crash_reward(hash) && func::is_crash_pickup(hash))
|
||||
// {
|
||||
// g_pointers->m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
|
||||
// notify::blocked_event(event_name, source_player->player_id);
|
||||
|
||||
// return false;
|
||||
// }
|
||||
// buffer->Seek(0);
|
||||
|
||||
// return true;
|
||||
//}
|
||||
}
|
||||
|
||||
return g_hooking->m_received_event_hook.get_original<decltype(&received_event)>()(event_manager, source_player, target_player, event_id, event_index, event_handled_bitset, bit_buffer_size, bit_buffer);
|
||||
|
@ -1,125 +1,183 @@
|
||||
#include "hooking.hpp"
|
||||
#include "gta/enums.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
void format_string(std::string_view player_name, std::string_view protection_type, bool should_log, bool should_notify)
|
||||
{
|
||||
if (should_log)
|
||||
LOG(WARNING) << "BLOCKED_SCRIPT_EVENT";
|
||||
|
||||
if (should_notify)
|
||||
g_notification_service->push_warning("Script Event Protection",
|
||||
fmt::format("From: {}\nEvent Type: {}", player_name.data(), protection_type.data())
|
||||
);
|
||||
}
|
||||
|
||||
bool hooks::scripted_game_event(CScriptedGameEvent* scripted_game_event, CNetGamePlayer* player)
|
||||
{
|
||||
auto args = scripted_game_event->m_args;
|
||||
|
||||
eRemoteEvent hash = (eRemoteEvent)args[0];
|
||||
|
||||
char type[32] = "";
|
||||
const eRemoteEvent hash = static_cast<eRemoteEvent>(args[0]);
|
||||
const char* player_name = player->get_name();
|
||||
|
||||
const auto& notify = g->notifications.script_event_handler;
|
||||
|
||||
switch (hash)
|
||||
{
|
||||
case eRemoteEvent::Bounty:
|
||||
if (g->protections.script_events.bounty)
|
||||
strcpy(type, "Bounty");
|
||||
{
|
||||
format_string(player_name, "Bounty", notify.bounty.log, notify.bounty.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::CeoBan:
|
||||
if (g->protections.script_events.ceo_ban)
|
||||
strcpy(type, "Ceo Ban");
|
||||
{
|
||||
format_string(player_name, "Ceo Ban", notify.ceo_ban.log, notify.ceo_ban.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::CeoKick:
|
||||
if (g->protections.script_events.ceo_kick)
|
||||
strcpy(type, "Ceo Kick");
|
||||
{
|
||||
format_string(player_name, "Ceo Kick", notify.ceo_kick.log, notify.ceo_kick.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::CeoMoney:
|
||||
if (g->protections.script_events.ceo_money)
|
||||
strcpy(type, "Ceo Money");
|
||||
{
|
||||
format_string(player_name, "Ceo Money", notify.ceo_money.log, notify.ceo_money.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::ClearWantedLevel:
|
||||
if (g->protections.script_events.clear_wanted_level)
|
||||
strcpy(type, "Clear Wanted Level");
|
||||
{
|
||||
format_string(player_name, "Clear Wanted Level", notify.clear_wanted_level.log, notify.clear_wanted_level.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::FakeDeposit:
|
||||
if (g->protections.script_events.fake_deposit)
|
||||
strcpy(type, "Deposit");
|
||||
{
|
||||
format_string(player_name, "Fake Deposit", notify.fake_deposit.log, notify.fake_deposit.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::ForceMission:
|
||||
if (g->protections.script_events.force_mission)
|
||||
strcpy(type, "Force Mission");
|
||||
{
|
||||
format_string(player_name, "Force Mission", notify.force_mission.log, notify.force_mission.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::GtaBanner:
|
||||
if (g->protections.script_events.gta_banner)
|
||||
strcpy(type, "GTA Banner");
|
||||
{
|
||||
format_string(player_name, "GTA Banner", notify.gta_banner.log, notify.gta_banner.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::NetworkBail:
|
||||
if (g->protections.script_events.network_bail)
|
||||
strcpy(type, "Network Bail");
|
||||
{
|
||||
format_string(player_name, "Network Bail", notify.network_bail.log, notify.network_bail.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::PersonalVehicleDestroyed:
|
||||
if (g->protections.script_events.personal_vehicle_destroyed)
|
||||
strcpy(type, "Personal Vehicle Destroyed");
|
||||
{
|
||||
format_string(player_name, "Personal Vehicle Destroyed", notify.personal_vehicle_destroyed.log, notify.personal_vehicle_destroyed.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::RemoteOffradar:
|
||||
if (g->protections.script_events.remote_off_radar)
|
||||
strcpy(type, "Remote Off Radar");
|
||||
{
|
||||
format_string(player_name, "Off Radar", notify.remote_off_radar.log, notify.remote_off_radar.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::RotateCam:
|
||||
if (g->protections.script_events.rotate_cam)
|
||||
strcpy(type, "Rotate Cam");
|
||||
{
|
||||
format_string(player_name, "Rotate Cam", notify.rotate_cam.log, notify.rotate_cam.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::SendToCutscene:
|
||||
if (g->protections.script_events.send_to_cutscene)
|
||||
strcpy(type, "Send To Cutscene");
|
||||
{
|
||||
format_string(player_name, "Send to Cutscene", notify.send_to_cutscene.log, notify.send_to_cutscene.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::SendToIsland:
|
||||
if (g->protections.script_events.send_to_island)
|
||||
strcpy(type, "Send To Island");
|
||||
{
|
||||
format_string(player_name, "Send to Island", notify.send_to_island.log, notify.send_to_island.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::SoundSpam:
|
||||
if (g->protections.script_events.sound_spam)
|
||||
strcpy(type, "Sound Spam");
|
||||
{
|
||||
format_string(player_name, "Sound Spamn", notify.sound_spam.log, notify.sound_spam.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::Spectate:
|
||||
if (g->protections.script_events.spectate)
|
||||
strcpy(type, "Specate");
|
||||
{
|
||||
format_string(player_name, "Spectate", notify.spectate.log, notify.spectate.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::Teleport:
|
||||
if (g->protections.script_events.force_teleport)
|
||||
strcpy(type, "Force Teleport");
|
||||
{
|
||||
format_string(player_name, "Apartment Invite", notify.force_teleport.log, notify.force_teleport.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::TransactionError:
|
||||
if (g->protections.script_events.transaction_error)
|
||||
strcpy(type, "Transaction Error");
|
||||
{
|
||||
format_string(player_name, "Transaction Error", notify.transaction_error.log, notify.transaction_error.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case eRemoteEvent::VehicleKick:
|
||||
if (g->protections.script_events.vehicle_kick)
|
||||
strcpy(type, "Vehicle Kick");
|
||||
{
|
||||
format_string(player_name, "Vehicle Kick", notify.vehicle_kick.log, notify.vehicle_kick.notify);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (strlen(type) != 0)
|
||||
{
|
||||
g_notification_service->push_warning("Protection",
|
||||
fmt::format("BLOCKED SCRIPT EVENT\nFrom: {}\nEvent Type: {}", player->get_name(), type)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (g->debug.script_event_logging)
|
||||
{
|
||||
LOG(INFO) << "== Begin of Script Event ==";
|
||||
|
@ -7,8 +7,6 @@ namespace big
|
||||
// check so we're 100% sure we modify data only for ourselves
|
||||
if (g_local_player->m_player_info->m_net_player_data.m_rockstar_id == player->m_rockstar_id)
|
||||
{
|
||||
LOG(INFO) << "HOOKS | Sending spoofed values to lobby.";
|
||||
|
||||
if (g->spoofing.spoof_username)
|
||||
memcpy(player->m_name, g->spoofing.username.c_str(), sizeof(player->m_name));
|
||||
|
||||
@ -25,6 +23,11 @@ namespace big
|
||||
player->m_rockstar_id = g->spoofing.rockstar_id;
|
||||
player->m_rockstar_id2 = g->spoofing.rockstar_id;
|
||||
}
|
||||
|
||||
if (g->notifications.send_net_info_to_lobby.log)
|
||||
LOG(INFO) << "Sending spoofed values to session host";
|
||||
if (g->notifications.send_net_info_to_lobby.notify)
|
||||
g_notification_service->push("Player Info Spoofing", "Sent spoofed values to lobby host.");
|
||||
}
|
||||
|
||||
return g_hooking->m_send_net_info_to_lobby.get_original<decltype(&hooks::send_net_info_to_lobby)>()(player, a2, a3, a4);
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "gta_util.hpp"
|
||||
#include "friends_service.hpp"
|
||||
#include "player_service.hpp"
|
||||
#include "util/notify.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
@ -137,7 +136,6 @@ namespace big
|
||||
void player_service::player_join(CNetGamePlayer* net_game_player)
|
||||
{
|
||||
if (net_game_player == nullptr) return;
|
||||
notify::player_joined(net_game_player);
|
||||
|
||||
std::unique_ptr<player> plyr = std::make_unique<player>(net_game_player);
|
||||
plyr->m_is_friend = friends_service::is_friend(plyr);
|
||||
|
@ -3,6 +3,16 @@
|
||||
|
||||
namespace big
|
||||
{
|
||||
void draw_pair_option(const std::string_view name, decltype(g->notifications.gta_thread_kill)& option)
|
||||
{
|
||||
ImGui::Text(name.data());
|
||||
|
||||
ImGui::PushID(name.data());
|
||||
ImGui::Checkbox("Log", &option.log);
|
||||
ImGui::Checkbox("Notify", &option.notify);
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void view::settings() {
|
||||
if (ImGui::TreeNode("Hotkeys"))
|
||||
{
|
||||
@ -37,7 +47,7 @@ namespace big
|
||||
ImGui::Checkbox("Force Teleport", &g->protections.script_events.force_teleport);
|
||||
ImGui::Checkbox("GTA Banner", &g->protections.script_events.gta_banner);
|
||||
ImGui::Checkbox("Network Bail", &g->protections.script_events.network_bail);
|
||||
ImGui::Checkbox("Destroy Personal Vehicle", &g->protections.script_events.personal_vehicle_destroyed);
|
||||
ImGui::Checkbox("Personal Vehicle Destroyed", &g->protections.script_events.personal_vehicle_destroyed);
|
||||
ImGui::Checkbox("Remote Off Radar", &g->protections.script_events.remote_off_radar);
|
||||
ImGui::Checkbox("Rotate Cam", &g->protections.script_events.rotate_cam);
|
||||
ImGui::EndGroup();
|
||||
@ -52,6 +62,99 @@ namespace big
|
||||
ImGui::Checkbox("Transaction Error", &g->protections.script_events.transaction_error);
|
||||
ImGui::Checkbox("Vehicle Kick", &g->protections.script_events.vehicle_kick);
|
||||
ImGui::EndGroup();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Notifications"))
|
||||
{
|
||||
if (ImGui::TreeNode("GTA Threads"))
|
||||
{
|
||||
draw_pair_option("Terminate", g->notifications.gta_thread_kill);
|
||||
draw_pair_option("Start", g->notifications.gta_thread_start);
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Network Player Manager"))
|
||||
{
|
||||
ImGui::Text("Player Join");
|
||||
|
||||
ImGui::Checkbox("Above Map", &g->notifications.player_join.above_map);
|
||||
ImGui::Checkbox("Log", &g->notifications.player_join.log);
|
||||
ImGui::Checkbox("Notify", &g->notifications.player_join.notify);
|
||||
|
||||
draw_pair_option("Player Leave", g->notifications.player_leave);
|
||||
draw_pair_option("Shutdown", g->notifications.network_player_mgr_shutdown);
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Received Event"))
|
||||
{
|
||||
auto& received_event = g->notifications.received_event;
|
||||
|
||||
ImGui::BeginGroup();
|
||||
draw_pair_option("Clear Ped Tasks", received_event.clear_ped_task);
|
||||
draw_pair_option("Modder Detection", received_event.modder_detect);
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
draw_pair_option("Report Cash Spawn", received_event.report_cash_spawn);
|
||||
draw_pair_option("Request Control Event", received_event.request_control_event);
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Script Event Handler"))
|
||||
{
|
||||
auto& script_event_handler = g->notifications.script_event_handler;
|
||||
|
||||
ImGui::BeginGroup();
|
||||
draw_pair_option("Bounty", script_event_handler.bounty);
|
||||
draw_pair_option("CEO Ban", script_event_handler.ceo_ban);
|
||||
draw_pair_option("CEO Kick", script_event_handler.ceo_kick);
|
||||
draw_pair_option("CEO Money", script_event_handler.ceo_money);
|
||||
draw_pair_option("Wanted Level", script_event_handler.clear_wanted_level);
|
||||
draw_pair_option("Fake Deposit", script_event_handler.fake_deposit);
|
||||
draw_pair_option("Force Mission", script_event_handler.force_mission);
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
draw_pair_option("Force Teleport", script_event_handler.force_teleport);
|
||||
draw_pair_option("GTA Banner", script_event_handler.gta_banner);
|
||||
draw_pair_option("Network Bail", script_event_handler.network_bail);
|
||||
draw_pair_option("Destroy Personal Vehicle", script_event_handler.personal_vehicle_destroyed);
|
||||
draw_pair_option("Remote Off Radar", script_event_handler.remote_off_radar);
|
||||
draw_pair_option("Rotate Cam", script_event_handler.rotate_cam);
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
draw_pair_option("Send to Cutscene", script_event_handler.send_to_cutscene);
|
||||
draw_pair_option("Send to Island", script_event_handler.send_to_island);
|
||||
draw_pair_option("Sound Spam", script_event_handler.sound_spam);
|
||||
draw_pair_option("Spectate", script_event_handler.spectate);
|
||||
draw_pair_option("Transaction Error", script_event_handler.transaction_error);
|
||||
draw_pair_option("Vehicle Kick", script_event_handler.vehicle_kick);
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Other"))
|
||||
{
|
||||
draw_pair_option("Net Array Error", g->notifications.net_array_error);
|
||||
draw_pair_option("Reports", g->notifications.reports);
|
||||
draw_pair_option("Transaction Error / Rate Limit", g->notifications.transaction_rate_limit);
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
Reference in New Issue
Block a user