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
This commit is contained in:
maybegreat48 2023-07-14 09:02:47 +00:00 committed by GitHub
parent 06cf2a579e
commit 71db1ca1fa
19 changed files with 282 additions and 94 deletions

View File

@ -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{};

View File

@ -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)
{

View File

@ -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

View File

@ -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);

View File

@ -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?
};

View File

@ -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<hooks::received_clone_create>()(mgr, src, dst, object_type, object_id, object_flag, buffer, timestamp);
}

View File

@ -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<received_clone_sync>()(mgr, src, dst, object_type, object_id, buffer, unk, timestamp);
}

View File

@ -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<eNetworkEvents>(event_id))
{
case eNetworkEvents::KICK_VOTES_EVENT:

View File

@ -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<functions::start_get_presence_attributes>();

View File

@ -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<int> infractions;
std::string notes = "";
std::optional<CommandAccessLevel> 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)
};

View File

@ -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<std::vector<rage::rlGamerHandle>> gamer_handle_buckets;
gamer_handle_buckets.resize(std::ceil(player_count / 32.f));
std::vector<std::vector<rage::rlScHandle>> 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<int, 32> 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";
}
}

View File

@ -14,7 +14,7 @@ namespace nlohmann
static void from_json(const json& j, std::shared_ptr<T>& value)
{
value = std::make_shared<T>();
value = std::make_shared<T>();
*value = j.get<T>();
}
};
@ -28,6 +28,8 @@ namespace big
std::map<std::string, std::shared_ptr<persistent_player>> m_sorted_players;
std::shared_ptr<persistent_player> 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<persistent_player> selected);
std::shared_ptr<persistent_player> 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;

View File

@ -83,7 +83,12 @@ namespace big
std::optional<std::uint32_t> 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;

View File

@ -11,6 +11,8 @@ namespace
"www.",
".cn",
".CN",
".cc",
".CC",
".TOP",
".COM",
".top",
@ -25,8 +27,6 @@ namespace
"doit#",
"krutka#",
"<b>",
// 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",
};
}

View File

@ -249,6 +249,7 @@ namespace big
global_test.global_appendages.clear();
}
ImGui::EndGroup();
ImGui::EndTabItem();
}
}
}

View File

@ -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, [] {

View File

@ -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<persistent_player> 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<persistent_player> 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, &current_player->rockstar_id)
|| ImGui::Checkbox("IS_MODDER"_T.data(), &current_player->is_modder)
|| ImGui::Checkbox("BLOCK_JOIN"_T.data(), &current_player->block_join)
|| ImGui::Checkbox("Notify When Online", &current_player->notify_online))
|| ImGui::Checkbox("Track Player", &current_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));

View File

@ -40,11 +40,11 @@ namespace big
if (id != -1)
{
auto& stats = scr_globals::gpbd_fm_1.as<GPBD_FM*>()->Entries[id].PlayerStats;
auto& boss_goon = scr_globals::gpbd_fm_3.as<GPBD_FM_3*>()->Entries[id].BossGoon;
auto& stats = scr_globals::gpbd_fm_1.as<GPBD_FM*>()->Entries[id].PlayerStats;
auto& boss_goon = scr_globals::gpbd_fm_3.as<GPBD_FM_3*>()->Entries[id].BossGoon;
const auto money = reinterpret_cast<uint64_t&>(stats.Money);
const auto wallet = reinterpret_cast<uint64_t&>(stats.WalletBalance);
const auto money = reinterpret_cast<uint64_t&>(stats.Money);
const auto wallet = reinterpret_cast<uint64_t&>(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(

View File

@ -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));