From 61bb60d1f430c932b3870478ba093d96d2895b6f Mon Sep 17 00:00:00 2001 From: maybegreat48 <96936658+maybegreat48@users.noreply.github.com> Date: Fri, 14 Jul 2023 09:02:47 +0000 Subject: [PATCH] Player database improvements (#1705) * feat(protections): add per-player sync block options * feat(player_database): improve player tracker * fix(rapid_fire): remove unnecessary log statement * fix(player_database): default state should be UNKNOWN, not INVALID --- src/backend/looped/weapons/rapid_fire.cpp | 9 +- src/backend/looped/weapons/weapon_hotkeys.cpp | 15 +- src/core/settings.hpp | 10 +- src/function_types.hpp | 2 +- src/gta/enums.hpp | 16 +++ .../protections/received_clone_create.cpp | 6 + src/hooks/protections/received_clone_sync.cpp | 6 + src/hooks/protections/received_event.cpp | 6 + src/pointers.cpp | 2 +- .../player_database/persistent_player.hpp | 12 +- .../player_database_service.cpp | 136 ++++++++++++++---- .../player_database_service.hpp | 11 +- src/services/players/player.hpp | 7 +- src/util/spam.hpp | 29 ++-- src/views/debug/view_debug_globals.cpp | 1 + src/views/network/view_network.cpp | 6 +- src/views/network/view_player_database.cpp | 79 +++++++--- src/views/players/player/player_info.cpp | 13 +- src/views/self/view_weapons.cpp | 10 +- 19 files changed, 282 insertions(+), 94 deletions(-) diff --git a/src/backend/looped/weapons/rapid_fire.cpp b/src/backend/looped/weapons/rapid_fire.cpp index e3e7208a..1645c749 100644 --- a/src/backend/looped/weapons/rapid_fire.cpp +++ b/src/backend/looped/weapons/rapid_fire.cpp @@ -20,8 +20,6 @@ namespace big if (!weapon_entity) return; - LOG(INFO) << weapon_entity; - Vector3 dim_min; Vector3 dim_max; MISC::GET_MODEL_DIMENSIONS(ENTITY::GET_ENTITY_MODEL(weapon_entity), &dim_min, &dim_max); @@ -36,14 +34,15 @@ namespace big Vector3 end = camera_position + camera_direction * 2000.0; const auto raycast_handle = - SHAPETEST::START_EXPENSIVE_SYNCHRONOUS_SHAPE_TEST_LOS_PROBE( - camera_position.x, + SHAPETEST::START_EXPENSIVE_SYNCHRONOUS_SHAPE_TEST_LOS_PROBE(camera_position.x, camera_position.y, camera_position.z, end.x, end.y, end.z, - -1, 0, 7); + -1, + 0, + 7); int did_raycast_hit = 0; Vector3 raycast_hit_position{}; Vector3 raycast_surface_normal_hit_position{}; diff --git a/src/backend/looped/weapons/weapon_hotkeys.cpp b/src/backend/looped/weapons/weapon_hotkeys.cpp index 3a6a62d2..94bbd711 100644 --- a/src/backend/looped/weapons/weapon_hotkeys.cpp +++ b/src/backend/looped/weapons/weapon_hotkeys.cpp @@ -1,12 +1,12 @@ #include "backend/looped/looped.hpp" -#include "natives.hpp" -#include "services/gta_data/gta_data_service.hpp" #include "gta/enums.hpp" #include "gui.hpp" +#include "natives.hpp" +#include "services/gta_data/gta_data_service.hpp" namespace big { - const int input_array[6] = {(int)ControllerInputs::INPUT_SELECT_WEAPON_UNARMED, (int)ControllerInputs::INPUT_SELECT_WEAPON_MELEE, (int)ControllerInputs::INPUT_SELECT_WEAPON_SHOTGUN, (int)ControllerInputs::INPUT_SELECT_WEAPON_HEAVY, (int)ControllerInputs::INPUT_SELECT_WEAPON_SPECIAL, (int)ControllerInputs::INPUT_SELECT_WEAPON_HANDGUN}; + constexpr int input_array[6] = {(int)ControllerInputs::INPUT_SELECT_WEAPON_UNARMED, (int)ControllerInputs::INPUT_SELECT_WEAPON_MELEE, (int)ControllerInputs::INPUT_SELECT_WEAPON_SHOTGUN, (int)ControllerInputs::INPUT_SELECT_WEAPON_HEAVY, (int)ControllerInputs::INPUT_SELECT_WEAPON_SPECIAL, (int)ControllerInputs::INPUT_SELECT_WEAPON_HANDGUN}; static void resolve_weapon_hotkey(Hash weapon) { @@ -37,7 +37,14 @@ namespace big PAD::DISABLE_CONTROL_ACTION(0, input_array[iterator_keys], FALSE); if (PAD::IS_DISABLED_CONTROL_JUST_PRESSED(0, input_array[iterator_keys])) { - auto hotkey_vector = g.weapons.weapon_hotkeys[iterator_keys]; + if (!g.weapons.weapon_hotkeys.count(iterator_keys)) + continue; + + auto& hotkey_vector = g.weapons.weapon_hotkeys[iterator_keys]; + + if (hotkey_vector.empty()) + continue; + Hash weapon_hash_to_select = hotkey_vector[0]; for (auto vector_iterator = hotkey_vector.begin(); vector_iterator != hotkey_vector.end(); ++vector_iterator) { diff --git a/src/core/settings.hpp b/src/core/settings.hpp index ae65a9fa..f49024dc 100644 --- a/src/core/settings.hpp +++ b/src/core/settings.hpp @@ -230,9 +230,15 @@ namespace big struct player_db { - bool update_player_online_states = false; + bool update_player_online_states = true; + bool notify_when_online = false; + bool notify_when_joinable = true; + bool notify_when_unjoinable = false; + bool notify_when_offline = false; + bool notify_on_session_type_change = false; + bool notify_on_session_change = false; - NLOHMANN_DEFINE_TYPE_INTRUSIVE(player_db, update_player_online_states) + NLOHMANN_DEFINE_TYPE_INTRUSIVE(player_db, update_player_online_states, notify_when_online, notify_when_joinable, notify_when_unjoinable, notify_when_offline, notify_on_session_type_change, notify_on_session_change) } player_db{}; struct protections diff --git a/src/function_types.hpp b/src/function_types.hpp index b3ec1de9..fae225ca 100644 --- a/src/function_types.hpp +++ b/src/function_types.hpp @@ -98,7 +98,7 @@ namespace big::functions using get_gamer_online_state = bool (*)(int profile_index, rage::rlGamerHandle* handles, std::uint32_t count, int* online_state, rage::rlTaskStatus* status); using start_get_session_by_gamer_handle = bool (*)(int profile_index, rage::rlGamerHandle* handles, int count, rage::rlSessionByGamerTaskResult* result, int unk, bool* success, rage::rlTaskStatus* state); using start_matchmaking_find_sessions = bool (*)(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* m_filter, unsigned int max_sessions, rage::rlSessionInfo* result_sessions, int* result_session_count, rage::rlTaskStatus* state); - using start_get_presence_attributes = bool (*)(int profile_index, rage::rlScHandle* handle, rage::rlQueryPresenceAttributesContext* contexts, int count, rage::rlTaskStatus* state); + using start_get_presence_attributes = bool (*)(int profile_index, rage::rlScHandle* handle, int num_handles, rage::rlQueryPresenceAttributesContext** contexts, int count, rage::rlTaskStatus* state); using join_session_by_info = bool (*)(Network* network, rage::rlSessionInfo* info, int unk, int flags, rage::rlGamerHandle* handles, int handlecount); using generate_uuid = bool (*)(std::uint64_t* uuid); diff --git a/src/gta/enums.hpp b/src/gta/enums.hpp index 074186ae..b59e1517 100644 --- a/src/gta/enums.hpp +++ b/src/gta/enums.hpp @@ -1948,3 +1948,19 @@ enum class eCombatAbilityLevel }; NLOHMANN_JSON_SERIALIZE_ENUM(eCombatAbilityLevel, {{eCombatAbilityLevel::POOR, "poor"}, {eCombatAbilityLevel::AVERAGE, "average"}, {eCombatAbilityLevel::PROFESSIONAL, "professional"}}) + +enum class GSType : int32_t +{ + Unknown = -2, + + // actual values start here + Invalid = -1, + InviteOnly, + FriendsOnly, + ClosedCrew, + OpenCrew, + Job, + Public, + Max, + Modder = 69 // stand? +}; \ No newline at end of file diff --git a/src/hooks/protections/received_clone_create.cpp b/src/hooks/protections/received_clone_create.cpp index 57221a56..c32f13e8 100644 --- a/src/hooks/protections/received_clone_create.cpp +++ b/src/hooks/protections/received_clone_create.cpp @@ -1,4 +1,5 @@ #include "hooking.hpp" +#include "services/players/player_service.hpp" #include "util/notify.hpp" namespace big @@ -11,6 +12,11 @@ namespace big return true; } + auto plyr = g_player_service->get_by_id(src->m_player_id); + + if (plyr && plyr->block_clone_create) + return true; + g.m_syncing_player = src; return g_hooking->get_original()(mgr, src, dst, object_type, object_id, object_flag, buffer, timestamp); } diff --git a/src/hooks/protections/received_clone_sync.cpp b/src/hooks/protections/received_clone_sync.cpp index ae04b00a..9bf73c9f 100644 --- a/src/hooks/protections/received_clone_sync.cpp +++ b/src/hooks/protections/received_clone_sync.cpp @@ -1,4 +1,5 @@ #include "hooking.hpp" +#include "services/players/player_service.hpp" #include "util/notify.hpp" namespace big @@ -17,6 +18,11 @@ namespace big return eAckCode::ACKCODE_FAIL; } + auto plyr = g_player_service->get_by_id(src->m_player_id); + + if (plyr && plyr->block_clone_create) + return eAckCode::ACKCODE_FAIL; + g.m_syncing_player = src; return g_hooking->get_original()(mgr, src, dst, object_type, object_id, buffer, unk, timestamp); } diff --git a/src/hooks/protections/received_event.cpp b/src/hooks/protections/received_event.cpp index a80f442b..7fb1337a 100644 --- a/src/hooks/protections/received_event.cpp +++ b/src/hooks/protections/received_event.cpp @@ -368,6 +368,12 @@ namespace big auto plyr = g_player_service->get_by_id(source_player->m_player_id); + if (plyr && plyr->block_net_events) + { + g_pointers->m_gta.m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset); + return; + } + switch (static_cast(event_id)) { case eNetworkEvents::KICK_VOTES_EVENT: diff --git a/src/pointers.cpp b/src/pointers.cpp index 9bedf7f6..5836727a 100644 --- a/src/pointers.cpp +++ b/src/pointers.cpp @@ -1389,7 +1389,7 @@ namespace big // Start Get Presence Attributes { "SGPA", - "48 8B C4 48 89 58 08 48 89 68 10 48 89 70 18 48 89 78 20 41 54 41 56 41 57 48 83 EC 40 33 DB 41", + "48 8B C4 48 89 58 08 48 89 68 10 48 89 70 18 48 89 78 20 41 54 41 56 41 57 48 83 EC 40 33 DB 49", [](memory::handle ptr) { g_pointers->m_sc.m_start_get_presence_attributes = ptr.as(); diff --git a/src/services/player_database/persistent_player.hpp b/src/services/player_database/persistent_player.hpp index f31fd905..bd6fde8c 100644 --- a/src/services/player_database/persistent_player.hpp +++ b/src/services/player_database/persistent_player.hpp @@ -35,15 +35,10 @@ namespace nlohmann }; } +enum class GSType : int32_t; + namespace big { - enum class PlayerOnlineStatus - { - UNKNOWN, - OFFLINE, - ONLINE - }; - struct persistent_player { std::string name; @@ -55,7 +50,8 @@ namespace big std::unordered_set infractions; std::string notes = ""; std::optional command_access_level = std::nullopt; - PlayerOnlineStatus online_state = PlayerOnlineStatus::UNKNOWN; + GSType session_type = GSType(-2); + int64_t session_id = -1; NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(persistent_player, name, rockstar_id, block_join, block_join_reason, is_modder, notify_online, infractions, notes, command_access_level) }; diff --git a/src/services/player_database/player_database_service.cpp b/src/services/player_database/player_database_service.cpp index cd6ec923..4f52fb66 100644 --- a/src/services/player_database/player_database_service.cpp +++ b/src/services/player_database/player_database_service.cpp @@ -2,14 +2,43 @@ #include "backend/bool_command.hpp" #include "file_manager.hpp" +#include "gta/enums.hpp" #include "pointers.hpp" #include "util/session.hpp" namespace big { - bool_command g_player_db_auto_update_online_states("player_db_auto_update_states", "Auto Update Player Online States", "Toggling this feature will automatically update the player online states every 5minutes.", + bool_command g_player_db_auto_update_online_states("player_db_auto_update_states", "Auto Update Tracked Player States", "Toggling this feature will automatically update the tracked players' online states every minute", g.player_db.update_player_online_states); + void player_database_service::handle_session_type_change(persistent_player& player, GSType new_session_type) + { + if (!player.notify_online) + return; + + if (g.player_db.notify_when_joinable && !is_joinable_session(player.session_type) && is_joinable_session(new_session_type)) + { + g_notification_service->push_success("Player DB", std::format("{} is now in a joinable session", player.name)); + } + else if (g.player_db.notify_when_online && (player.session_type == GSType::Invalid || player.session_type == GSType::Unknown) && new_session_type != GSType::Invalid) + { + g_notification_service->push_success("Player DB", std::format("{} is now online", player.name)); + } + else if (g.player_db.notify_when_unjoinable && is_joinable_session(player.session_type) && !is_joinable_session(new_session_type) && new_session_type != GSType::Invalid) + { + g_notification_service->push("Player DB", std::format("{} is no longer in a joinable session", player.name)); + } + else if (g.player_db.notify_when_offline && player.session_type != GSType::Invalid && player.session_type != GSType::Unknown && new_session_type == GSType::Invalid) + { + g_notification_service->push("Player DB", std::format("{} is no longer online", player.name)); + } + + if (g.player_db.notify_on_session_type_change && (int)new_session_type >= (int)GSType::InviteOnly && (int)new_session_type < (int)GSType::Max) + { + g_notification_service->push("Player DB", std::format("{} is now in a{} {} session", player.name, new_session_type == GSType::InviteOnly ? "n" : "", get_session_type_str(new_session_type))); + } + } + player_database_service::player_database_service() : m_file_path(g_file_manager.get_project_file("./players.json").get_path()) { @@ -150,14 +179,14 @@ namespace big return; g_thread_pool->push([this] { - static auto last_update = std::chrono::high_resolution_clock::now() - 5min; + static auto last_update = std::chrono::high_resolution_clock::now() - 45s; while (g_running && g.player_db.update_player_online_states) { const auto cur = std::chrono::high_resolution_clock::now(); - if (cur - last_update > 5min) + if (cur - last_update > 45s) { g_fiber_pool->queue_job([this] { - update_player_states(); + update_player_states(true); }); last_update = cur; } @@ -167,52 +196,105 @@ namespace big }); } - void player_database_service::update_player_states() + void player_database_service::update_player_states(bool tracked_only) { - const auto player_count = m_players.size(); + const auto player_count = m_players.size(); + constexpr auto bucket_size = 100; - std::vector> gamer_handle_buckets; - gamer_handle_buckets.resize(std::ceil(player_count / 32.f)); + std::vector> gamer_handle_buckets; + gamer_handle_buckets.resize(std::ceil(player_count / (float)bucket_size)); - auto it = m_players.begin(); - for (size_t i = 0; i < player_count; ++i) + size_t i = 0; + for (auto& player : m_players) { - gamer_handle_buckets[i / 32].push_back(it->second->rockstar_id); - - it++; + if (!tracked_only || player.second->notify_online) + { + gamer_handle_buckets[i / bucket_size].push_back(player.second->rockstar_id); + i++; + } } + if (i == 0) + return; + for (auto& bucket : gamer_handle_buckets) { - rage::rlTaskStatus status; - std::array online; + rage::rlTaskStatus status{}; + rage::rlQueryPresenceAttributesContext contexts[bucket_size][2]{}; + rage::rlQueryPresenceAttributesContext* contexts_per_player[bucket_size]{}; - if (g_pointers->m_gta.m_get_gamer_online_state(0, bucket.data(), bucket.size(), online.data(), &status)) + for (int i = 0; i < bucket_size; i++) + { + contexts[i][0].m_presence_attibute_type = 3; + strcpy(contexts[i][0].m_presence_attribute_key, "gstype"); + strcpy(contexts[i][0].m_presence_attribute_value, "-1"); + contexts[i][1].m_presence_attibute_type = 3; + strcpy(contexts[i][1].m_presence_attribute_key, "gsinfo"); + contexts_per_player[i] = contexts[i]; + } + + if (g_pointers->m_sc.m_start_get_presence_attributes(0, bucket.data(), bucket.size(), contexts_per_player, 2, &status)) { while (status.status == 1) { script::get_current()->yield(); } - for (size_t i = 0; i < bucket.size(); ++i) + if (status.status == 3) { - if (const auto& it = m_players.find(bucket[i].m_rockstar_id); it != m_players.end()) + for (size_t i = 0; i < bucket.size(); ++i) { - if (online[i] == 1) + if (const auto& it = m_players.find(bucket[i].m_rockstar_id); it != m_players.end()) { - if (it->second->online_state == PlayerOnlineStatus::OFFLINE && it->second->notify_online) - { - g_notification_service->push_success("Player DB", - std::format("{} is now online!", it->second->name)); - } - it->second->online_state = PlayerOnlineStatus::ONLINE; + rage::rlSessionInfo info{}; + info.m_session_token = -1; + GSType gstype = (GSType)atoi(contexts[i][0].m_presence_attribute_value); - continue; + if (!g_pointers->m_gta.m_decode_session_info(&info, contexts[i][1].m_presence_attribute_value, nullptr)) + gstype = GSType::Invalid; + + if (it->second->session_type != gstype) + { + handle_session_type_change(*it->second, gstype); + } + else if (it->second->notify_online && it->second->session_id != info.m_session_token) + { + g_notification_service->push("Player DB", + std::format("{} has joined a new session", it->second->name)); + } + + it->second->session_type = gstype; + it->second->session_id = info.m_session_token; } - it->second->online_state = PlayerOnlineStatus::OFFLINE; } } + else + { + LOG(WARNING) << "Presence attribute endpoint failed"; + } } } } + + bool player_database_service::is_joinable_session(GSType type) + { + return type == GSType::Public || type == GSType::OpenCrew; + } + + const char* player_database_service::get_session_type_str(GSType type) + { + switch (type) + { + case GSType::Invalid: return "Offline"; + case GSType::InviteOnly: return "Invite Only"; + case GSType::FriendsOnly: return "Friends Only"; + case GSType::ClosedCrew: return "Closed Crew"; + case GSType::OpenCrew: return "Crew"; + case GSType::Job: return "In Mission"; + case GSType::Public: return "Public"; + case GSType::Modder: return "Unknown (Concealed By Modder)"; + } + + return "Unknown"; + } } \ No newline at end of file diff --git a/src/services/player_database/player_database_service.hpp b/src/services/player_database/player_database_service.hpp index 0870324e..e248f89d 100644 --- a/src/services/player_database/player_database_service.hpp +++ b/src/services/player_database/player_database_service.hpp @@ -14,7 +14,7 @@ namespace nlohmann static void from_json(const json& j, std::shared_ptr& value) { - value = std::make_shared(); + value = std::make_shared(); *value = j.get(); } }; @@ -28,6 +28,8 @@ namespace big std::map> m_sorted_players; std::shared_ptr m_selected = nullptr; + void handle_session_type_change(persistent_player& player, GSType new_session_type); + public: std::filesystem::path m_file_path; player_database_service(); @@ -46,9 +48,12 @@ namespace big void set_selected(std::shared_ptr selected); std::shared_ptr get_selected(); - + void start_update_loop(); - void update_player_states(); + void update_player_states(bool tracked_only = false); + + static bool is_joinable_session(GSType type); + static const char* get_session_type_str(GSType type); }; inline player_database_service* g_player_database_service; diff --git a/src/services/players/player.hpp b/src/services/players/player.hpp index 1e9ecc7f..97fe2e45 100644 --- a/src/services/players/player.hpp +++ b/src/services/players/player.hpp @@ -83,7 +83,12 @@ namespace big std::optional time_difference; std::uint32_t num_time_syncs_sent = 9999; - bool block_explosions = false; + bool block_explosions = false; + bool block_clone_create = false; + bool block_clone_sync = false; + bool block_net_events = false; + bool log_clones = false; + bool log_network_events = false; int spectating_player = -1; diff --git a/src/util/spam.hpp b/src/util/spam.hpp index 68191020..d5a1e38d 100644 --- a/src/util/spam.hpp +++ b/src/util/spam.hpp @@ -11,6 +11,8 @@ namespace "www.", ".cn", ".CN", + ".cc", + ".CC", ".TOP", ".COM", ".top", @@ -25,8 +27,6 @@ namespace "doit#", "krutka#", "", - // causes false positives for people typing in cyrillic - // "\xD0\xBC\xD0\xB5", // Cyrillic "me" "P888", "gtacash", "\xE6\x89\xA3\xE6\x89\xA3", // no clue what this is @@ -39,21 +39,22 @@ namespace "REP +", "20R$", // Brazil currency? "l55.me", - "\xE5\xBA\x97", //"shop" in Chinese - "\xE9\x92\xB1", //"money" in Chinese - "\xE5\x88\xB7", //"make(money)" in Chinese - // disabled as it's too verbose - // "av", //uknowwhat video - "\xE8\x90\x9D\xE8\x8E\x89", //"cute girl" in Chinese - "\xE5\xA6\x88", //"mother" in Chinese - "\xE7\xBE\x8E\xE5\xA5\xB3", //"sexy girl" in Chinese - "\xE5\xBC\xBA\xE5\xA5\xB8", //"rape" in Chinese - "\xE8\x90\x9D", //"loli" in Chinese - "\xE6\x8C\x82", //"hack" in Chinese - "\xE5\x85\x83", //chinese dollar + "\xE5\xBA\x97", //"shop" in Chinese + "\xE9\x92\xB1", //"money" in Chinese + "\xE5\x88\xB7", //"make(money)" in Chinese + "\xE8\x90\x9D\xE8\x8E\x89", // "cute girl" in Chinese + "\xE5\xA6\x88", // "mother" in Chinese + "\xE7\xBE\x8E\xE5\xA5\xB3", // "sexy girl" in Chinese + "\xE5\xBC\xBA\xE5\xA5\xB8", // "rape" in Chinese + "\xE8\x90\x9D", // "loli" in Chinese + "\xE6\x8C\x82", // "hack" in Chinese + "\xE5\x85\x83", // chinese dollar + "\xE9\x98\xB4\xE4\xBC\xA0\xE5\xAA\x92", // "Yin Media" in Chinese + "\xE7\xBD\x91\xE7\xBA\xA2", // "internet celebrities" in Chinese "TRUSTPILOT", "cashlounge", "Fast Delivery", + "yosativa", }; } diff --git a/src/views/debug/view_debug_globals.cpp b/src/views/debug/view_debug_globals.cpp index 11e57c35..82ca9886 100644 --- a/src/views/debug/view_debug_globals.cpp +++ b/src/views/debug/view_debug_globals.cpp @@ -249,6 +249,7 @@ namespace big global_test.global_appendages.clear(); } ImGui::EndGroup(); + ImGui::EndTabItem(); } } } \ No newline at end of file diff --git a/src/views/network/view_network.cpp b/src/views/network/view_network.cpp index baaf54c9..0c5b9cc2 100644 --- a/src/views/network/view_network.cpp +++ b/src/views/network/view_network.cpp @@ -47,8 +47,10 @@ namespace big ImGui::SameLine(); components::button("JOIN_SESSION_INFO"_T, [] { rage::rlSessionInfo info; - g_pointers->m_gta.m_decode_session_info(&info, base64, nullptr); - session::join_session(info); + if (g_pointers->m_gta.m_decode_session_info(&info, base64, nullptr)) + session::join_session(info); + else + g_notification_service->push_error("Join", "Session info is invalid"); }); components::button("COPY_SESSION_INFO"_T, [] { diff --git a/src/views/network/view_player_database.cpp b/src/views/network/view_player_database.cpp index 125294c7..01fccf0a 100644 --- a/src/views/network/view_player_database.cpp +++ b/src/views/network/view_player_database.cpp @@ -2,6 +2,7 @@ #include "core/data/command_access_levels.hpp" #include "core/data/infractions.hpp" #include "fiber_pool.hpp" +#include "gta/enums.hpp" #include "pointers.hpp" #include "services/api/api_service.hpp" #include "services/player_database/player_database_service.hpp" @@ -17,6 +18,18 @@ namespace big bool notes_dirty = false; std::shared_ptr current_player; + ImVec4 get_player_color(persistent_player& player) + { + if (player.session_type == GSType::Unknown) + return ImVec4(.5f, .5f, .5f, 1.0f); + else if (player.session_type == GSType::Invalid) + return ImVec4(1.f, 0.f, 0.f, 1.f); + else if (!player_database_service::is_joinable_session(player.session_type)) + return ImVec4(1.f, 1.f, 0.f, 1.f); + else + return ImVec4(0.f, 1.f, 0.f, 1.f); + } + void draw_player_db_entry(std::shared_ptr player, const std::string& lower_search) { std::string name = player->name; @@ -28,15 +41,9 @@ namespace big float circle_size = 7.5f; auto cursor_pos = ImGui::GetCursorScreenPos(); - auto plyr_state = player->online_state; //render status circle - ImGui::GetWindowDrawList()->AddCircleFilled(ImVec2(cursor_pos.x + 4.f + circle_size, cursor_pos.y + 4.f + circle_size), - circle_size, - ImColor(plyr_state == PlayerOnlineStatus::ONLINE ? ImVec4(0.f, 1.f, 0.f, 1.f) : - plyr_state == PlayerOnlineStatus::OFFLINE ? ImVec4(1.f, 0.f, 0.f, 1.f) : - plyr_state == PlayerOnlineStatus::UNKNOWN ? ImVec4(.5f, .5f, .5f, 1.0f) : - ImVec4(.5f, .5f, .5f, 1.0f))); + ImGui::GetWindowDrawList()->AddCircleFilled(ImVec2(cursor_pos.x + 4.f + circle_size, cursor_pos.y + 4.f + circle_size), circle_size, ImColor(get_player_color(*player))); //we need some padding ImVec2 cursor = ImGui::GetCursorPos(); @@ -57,6 +64,9 @@ namespace big strncpy(note_buffer, current_player->notes.data(), sizeof(note_buffer)); } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip(player_database_service::get_session_type_str(player->session_type)); + ImGui::PopID(); } } @@ -76,13 +86,20 @@ namespace big for (auto& player : item_arr | std::ranges::views::values) { - if (player->online_state == PlayerOnlineStatus::ONLINE) + if (player_database_service::is_joinable_session(player->session_type)) draw_player_db_entry(player, lower_search); } for (auto& player : item_arr | std::ranges::views::values) { - if (player->online_state != PlayerOnlineStatus::ONLINE) + if (!player_database_service::is_joinable_session(player->session_type) && player->session_type != GSType::Invalid + && player->session_type != GSType::Unknown) + draw_player_db_entry(player, lower_search); + } + + for (auto& player : item_arr | std::ranges::views::values) + { + if (player->session_type == GSType::Invalid || player->session_type == GSType::Unknown) draw_player_db_entry(player, lower_search); } } @@ -107,7 +124,7 @@ namespace big if (ImGui::InputScalar("RID"_T.data(), ImGuiDataType_S64, ¤t_player->rockstar_id) || ImGui::Checkbox("IS_MODDER"_T.data(), ¤t_player->is_modder) || ImGui::Checkbox("BLOCK_JOIN"_T.data(), ¤t_player->block_join) - || ImGui::Checkbox("Notify When Online", ¤t_player->notify_online)) + || ImGui::Checkbox("Track Player", ¤t_player->notify_online)) { if (current_player->rockstar_id != selected->rockstar_id) g_player_database_service->update_rockstar_id(selected->rockstar_id, current_player->rockstar_id); @@ -212,10 +229,28 @@ namespace big if (ImGui::Button("REMOVE_ALL"_T.data())) { - g_player_database_service->set_selected(nullptr); - g_player_database_service->get_players().clear(); - g_player_database_service->get_sorted_players().clear(); - g_player_database_service->save(); + ImGui::OpenPopup("##removeall"); + } + + if (ImGui::BeginPopupModal("##removeall")) + { + ImGui::Text("Are you sure?"); + + if (ImGui::Button("Yes")) + { + g_player_database_service->set_selected(nullptr); + g_player_database_service->get_players().clear(); + g_player_database_service->get_sorted_players().clear(); + g_player_database_service->save(); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("No")) + { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); } ImGui::SameLine(); @@ -224,8 +259,18 @@ namespace big g_player_database_service->update_player_states(); }); - if (components::command_checkbox<"player_db_auto_update_states">()) - g_player_database_service->start_update_loop(); + if (ImGui::TreeNode("Player Tracking")) + { + if (components::command_checkbox<"player_db_auto_update_states">("Enable")) + g_player_database_service->start_update_loop(); + ImGui::Checkbox("Notify When Online", &g.player_db.notify_when_online); + ImGui::Checkbox("Notify When Joinable", &g.player_db.notify_when_joinable); + ImGui::Checkbox("Notify When Unjoinable", &g.player_db.notify_when_unjoinable); + ImGui::Checkbox("Notify When Offline", &g.player_db.notify_when_offline); + ImGui::Checkbox("Notify On Session Type Change", &g.player_db.notify_on_session_type_change); + ImGui::Checkbox("Notify On Session Change", &g.player_db.notify_on_session_change); + ImGui::TreePop(); + } ImGui::Separator(); components::sub_title("NEW_ENTRY"_T); @@ -244,7 +289,7 @@ namespace big ImGui::SameLine(); if (ImGui::Button("SEARCH"_T.data())) { - g_thread_pool->push([]{ + g_thread_pool->push([] { if (!g_api_service->get_rid_from_username(new_name, *(uint64_t*)&new_rockstar_id)) { g_notification_service->push_error("New Player DB Entry", std::format("No user '{}' called could be found.", new_name)); diff --git a/src/views/players/player/player_info.cpp b/src/views/players/player/player_info.cpp index b0791de5..0c555d1e 100644 --- a/src/views/players/player/player_info.cpp +++ b/src/views/players/player/player_info.cpp @@ -40,11 +40,11 @@ namespace big if (id != -1) { - auto& stats = scr_globals::gpbd_fm_1.as()->Entries[id].PlayerStats; - auto& boss_goon = scr_globals::gpbd_fm_3.as()->Entries[id].BossGoon; + auto& stats = scr_globals::gpbd_fm_1.as()->Entries[id].PlayerStats; + auto& boss_goon = scr_globals::gpbd_fm_3.as()->Entries[id].BossGoon; - const auto money = reinterpret_cast(stats.Money); - const auto wallet = reinterpret_cast(stats.WalletBalance); + const auto money = reinterpret_cast(stats.Money); + const auto wallet = reinterpret_cast(stats.WalletBalance); if (boss_goon.Language >= 0 && boss_goon.Language < 13) ImGui::Text("PLAYER_INFO_LANGUAGE"_T.data(), languages[boss_goon.Language].name); @@ -69,6 +69,11 @@ namespace big } ImGui::Checkbox("Block Explosions", &g_player_service->get_selected()->block_explosions); + ImGui::Checkbox("Block Clone Creates", &g_player_service->get_selected()->block_clone_create); + ImGui::Checkbox("Block Clone Syncs", &g_player_service->get_selected()->block_clone_sync); + ImGui::Checkbox("Block Network Events", &g_player_service->get_selected()->block_net_events); + + ImGui::Separator(); if (ImGui::BeginCombo("CHAT_COMMAND_PERMISSIONS"_T.data(), COMMAND_ACCESS_LEVELS[g_player_service->get_selected()->command_access_level.value_or( diff --git a/src/views/self/view_weapons.cpp b/src/views/self/view_weapons.cpp index 150d64d1..6e8a5b8e 100644 --- a/src/views/self/view_weapons.cpp +++ b/src/views/self/view_weapons.cpp @@ -3,12 +3,12 @@ #include "core/data/special_ammo_types.hpp" #include "fiber_pool.hpp" #include "gta/joaat.hpp" +#include "gta/weapons.hpp" #include "natives.hpp" #include "pointers.hpp" #include "services/gta_data/gta_data_service.hpp" -#include "views/view.hpp" #include "services/persist_weapons/persist_weapons.hpp" -#include "gta/weapons.hpp" +#include "views/view.hpp" namespace big { @@ -274,7 +274,7 @@ namespace big } if (ImGui::CollapsingHeader("Persist Weapons")) { - ImGui::Checkbox("Enabled", &g.persist_weapons.enabled); + ImGui::Checkbox("Enabled##persist_weapons", &g.persist_weapons.enabled); static std::string selected_loadout = g.persist_weapons.weapon_loadout_file; ImGui::PushItemWidth(250); @@ -311,14 +311,14 @@ namespace big } if (ImGui::CollapsingHeader("Weapon Hotkeys")) { - ImGui::Checkbox("Enabled", &g.weapons.enable_weapon_hotkeys); + ImGui::Checkbox("Enabled##weapon_hotkeys", &g.weapons.enable_weapon_hotkeys); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("This will select the next weapon in the hotkey list.\r\nThe first weapon in the list is the first weapon it will select, then the second is the one it will select after and so on.\r\nAfter the end of the list, it will wrap back to the first weapon."); } static int selected_key = 0; - const char* const keys[]{ "1", "2", "3", "4", "5", "6" }; + const char* const keys[]{"1", "2", "3", "4", "5", "6"}; ImGui::PushItemWidth(250); ImGui::Combo("Key", &selected_key, keys, IM_ARRAYSIZE(keys));