From c7cf60ffcb0a47386a8a759e6fdbf5392533883d Mon Sep 17 00:00:00 2001 From: tupoy-ya Date: Thu, 25 Dec 2025 01:14:44 +0500 Subject: [PATCH 1/2] feat: Improve save times. According to my tests this saves about 13 times faster with save_unmodified_stats. --- src/gta/stat.hpp | 6 ++ src/services/stats/stats_service.cpp | 95 ++++++++++++++-------------- src/services/stats/stats_service.hpp | 35 +++++----- 3 files changed, 71 insertions(+), 65 deletions(-) diff --git a/src/gta/stat.hpp b/src/gta/stat.hpp index b31e8e8..e8653af 100644 --- a/src/gta/stat.hpp +++ b/src/gta/stat.hpp @@ -54,6 +54,12 @@ class sStatData virtual void _29() = 0; virtual bool IsZero() = 0; + uint8_t GetCharIndex() + { + uint8_t index = m_flags >> 0x14 & 7; + return index == 6 || index == 7 ? 0 : index; + } + uint64_t m_flags; }; diff --git a/src/services/stats/stats_service.cpp b/src/services/stats/stats_service.cpp index 5e235db..238518d 100644 --- a/src/services/stats/stats_service.cpp +++ b/src/services/stats/stats_service.cpp @@ -5,6 +5,7 @@ #include "gta/stat.hpp" #include "pointers.hpp" +#include #include namespace big @@ -141,33 +142,22 @@ namespace big pso_file.read(buf, size); } - inline uint8_t get_char_index_from_stat(sStatData* stat) - { - if (stat == nullptr) - { - LOG(FATAL) << "stat is nullptr wtf?"; - return 0; - } - uint8_t index = stat->m_flags >> 0x14 & 7; - return index == 6 || index == 7 ? 0 : index; - } - template void stats_service::save_stat_map_to_json(nlohmann::json& json, T& map, bool use_stat_names, uint8_t char_index) { auto stats = nlohmann::json::object(); - for (auto stat : map) + for (const auto& stat : map) { - if (get_char_index_from_stat(get_stat_by_hash(stat.first)) != char_index) + if (stat.second.m_character_index != char_index) continue; if (use_stat_names) { - stats[m_stat_hash_to_string[stat.first]] = stat.second; + stats[m_stat_hash_to_string[stat.first]] = stat.second.data; } else { - stats[std::to_string(stat.first)] = stat.second; + stats[std::to_string(stat.first)] = stat.second.data; } } json = stats; @@ -192,9 +182,10 @@ namespace big { Hash stat_hash = use_stat_names ? rage::joaat(stat.key()) : std::stoul(stat.key()); - if (get_stat_by_hash(stat_hash)) + sStatData* stat_ptr = get_stat_by_hash(stat_hash); + if (stat_ptr != nullptr) { - map[stat_hash] = stat.value(); + map[stat_hash] = {stat.value(), stat_ptr->GetCharIndex()}; } else { @@ -209,9 +200,10 @@ namespace big { Hash stat_hash = use_stat_names ? rage::joaat(stat[0]) : stat[0].get(); - if (get_stat_by_hash(stat_hash)) + sStatData* stat_ptr = get_stat_by_hash(stat_hash); + if (stat_ptr != nullptr) { - map[stat_hash] = stat[1]; + map[stat_hash] = {stat[1], stat_ptr->GetCharIndex()}; } else { @@ -457,6 +449,8 @@ namespace big void stats_service::save_stats() { + std::chrono::time_point start_time = std::chrono::high_resolution_clock::now(); + const auto& stats = *g_pointers->m_stats; for (const auto& stat : stats) { @@ -471,72 +465,72 @@ namespace big { case eStatType::INT: { - m_int_stats[stat.m_hash] = stat.m_stat->GetIntData(); + m_int_stats[stat.m_hash] = {stat.m_stat->GetIntData(), stat.m_stat->GetCharIndex()}; break; } case eStatType::FLOAT: { - m_float_stats[stat.m_hash] = stat.m_stat->GetFloatData(); + m_float_stats[stat.m_hash] = {stat.m_stat->GetFloatData(), stat.m_stat->GetCharIndex()}; break; } case eStatType::STRING: { - m_string_stats[stat.m_hash] = std::string(stat.m_stat->GetStringData()); + m_string_stats[stat.m_hash] = {std::string(stat.m_stat->GetStringData()), stat.m_stat->GetCharIndex()}; break; } case eStatType::BOOL_: { - m_bool_stats[stat.m_hash] = stat.m_stat->GetBoolData(); + m_bool_stats[stat.m_hash] = {stat.m_stat->GetBoolData(), stat.m_stat->GetCharIndex()}; break; } case eStatType::UINT8: { - m_uint8_stats[stat.m_hash] = stat.m_stat->GetUint8Data(); + m_uint8_stats[stat.m_hash] = {stat.m_stat->GetUint8Data(), stat.m_stat->GetCharIndex()}; break; } case eStatType::UINT16: { - m_uint16_stats[stat.m_hash] = stat.m_stat->GetUint16Data(); + m_uint16_stats[stat.m_hash] = {stat.m_stat->GetUint16Data(), stat.m_stat->GetCharIndex()}; break; } case eStatType::UINT32: { - m_uint32_stats[stat.m_hash] = stat.m_stat->GetUint32Data(); + m_uint32_stats[stat.m_hash] = {stat.m_stat->GetUint32Data(), stat.m_stat->GetCharIndex()}; break; } case eStatType::UINT64: { - m_uint64_stats[stat.m_hash] = stat.m_stat->GetUint64Data(); + m_uint64_stats[stat.m_hash] = {stat.m_stat->GetUint64Data(), stat.m_stat->GetCharIndex()}; break; } case eStatType::INT64: { - m_int64_stats[stat.m_hash] = stat.m_stat->GetInt64Data(); + m_int64_stats[stat.m_hash] = {stat.m_stat->GetInt64Data(), stat.m_stat->GetCharIndex()}; break; } case eStatType::DATE: { - m_date_stats[stat.m_hash] = stat.m_stat->GetUint64Data(); + m_date_stats[stat.m_hash] = {stat.m_stat->GetUint64Data(), stat.m_stat->GetCharIndex()}; break; } case eStatType::POS: { - m_pos_stats[stat.m_hash] = stat.m_stat->GetUint64Data(); + m_pos_stats[stat.m_hash] = {stat.m_stat->GetUint64Data(), stat.m_stat->GetCharIndex()}; break; } case eStatType::TEXTLABEL: { - m_textlabel_stats[stat.m_hash] = stat.m_stat->GetIntData(); + m_textlabel_stats[stat.m_hash] = {stat.m_stat->GetIntData(), stat.m_stat->GetCharIndex()}; break; } case eStatType::PACKED: { - m_packed_stats[stat.m_hash] = stat.m_stat->GetUint64Data(); + m_packed_stats[stat.m_hash] = {stat.m_stat->GetUint64Data(), stat.m_stat->GetCharIndex()}; break; } case eStatType::USERID: { - m_userid_stats[stat.m_hash] = stat.m_stat->GetUint64Data(); + m_userid_stats[stat.m_hash] = {stat.m_stat->GetUint64Data(), stat.m_stat->GetCharIndex()}; break; } case eStatType::PROFILE_SETTING: break; @@ -553,10 +547,14 @@ namespace big save_internal_stats_to_json(2); save_script_data_to_json(); + + std::chrono::time_point end_time = std::chrono::high_resolution_clock::now(); + LOGIF(VERBOSE, g.enable_debug_logs, "Saving took {:%S}", std::chrono::duration_cast(end_time-start_time)); } bool stats_service::load_stats() { + std::chrono::time_point start_time = std::chrono::high_resolution_clock::now(); if (!load_internal_stats_from_json(0)) { return false; @@ -579,85 +577,85 @@ namespace big case eStatType::INT: { if (m_int_stats.contains(stat.m_hash)) - stat.m_stat->SetIntData(m_int_stats[stat.m_hash]); + stat.m_stat->SetIntData(m_int_stats[stat.m_hash].data); break; } case eStatType::FLOAT: { if (m_float_stats.contains(stat.m_hash)) - stat.m_stat->SetFloatData(m_float_stats[stat.m_hash]); + stat.m_stat->SetFloatData(m_float_stats[stat.m_hash].data); break; } case eStatType::STRING: { if (m_string_stats.contains(stat.m_hash)) - strncpy(((sSubStatData*)stat.m_stat)->m_data, m_string_stats[stat.m_hash].data(), 32); + strncpy(((sSubStatData*)stat.m_stat)->m_data, m_string_stats[stat.m_hash].data.data(), 32); break; } case eStatType::BOOL_: { if (m_bool_stats.contains(stat.m_hash)) - stat.m_stat->SetBoolData(m_bool_stats[stat.m_hash]); + stat.m_stat->SetBoolData(m_bool_stats[stat.m_hash].data); break; } case eStatType::UINT8: { if (m_uint8_stats.contains(stat.m_hash)) - stat.m_stat->SetUint8Data(m_uint8_stats[stat.m_hash]); + stat.m_stat->SetUint8Data(m_uint8_stats[stat.m_hash].data); break; } case eStatType::UINT16: { if (m_uint16_stats.contains(stat.m_hash)) - stat.m_stat->SetUint16Data(m_uint16_stats[stat.m_hash]); + stat.m_stat->SetUint16Data(m_uint16_stats[stat.m_hash].data); break; } case eStatType::UINT32: { if (m_uint32_stats.contains(stat.m_hash)) - stat.m_stat->SetUint32Data(m_uint32_stats[stat.m_hash]); + stat.m_stat->SetUint32Data(m_uint32_stats[stat.m_hash].data); break; } case eStatType::UINT64: { if (m_uint64_stats.contains(stat.m_hash)) - stat.m_stat->SetUint64Data(m_uint64_stats[stat.m_hash]); + stat.m_stat->SetUint64Data(m_uint64_stats[stat.m_hash].data); break; } case eStatType::INT64: { if (m_int64_stats.contains(stat.m_hash)) - stat.m_stat->SetInt64Data(m_int64_stats[stat.m_hash]); + stat.m_stat->SetInt64Data(m_int64_stats[stat.m_hash].data); break; } case eStatType::DATE: { if (m_date_stats.contains(stat.m_hash)) - stat.m_stat->SetUint64Data(m_date_stats[stat.m_hash]); + stat.m_stat->SetUint64Data(m_date_stats[stat.m_hash].data); break; } case eStatType::POS: { if (m_pos_stats.contains(stat.m_hash)) - stat.m_stat->SetUint64Data(m_pos_stats[stat.m_hash]); + stat.m_stat->SetUint64Data(m_pos_stats[stat.m_hash].data); break; } case eStatType::TEXTLABEL: { if (m_textlabel_stats.contains(stat.m_hash)) - stat.m_stat->SetIntData(m_textlabel_stats[stat.m_hash]); + stat.m_stat->SetIntData(m_textlabel_stats[stat.m_hash].data); break; } case eStatType::PACKED: { if (m_packed_stats.contains(stat.m_hash)) - stat.m_stat->SetUint64Data(m_packed_stats[stat.m_hash]); + stat.m_stat->SetUint64Data(m_packed_stats[stat.m_hash].data); break; } case eStatType::USERID: { if (m_userid_stats.contains(stat.m_hash)) - stat.m_stat->SetUint64Data(m_userid_stats[stat.m_hash]); + stat.m_stat->SetUint64Data(m_userid_stats[stat.m_hash].data); break; } case eStatType::PROFILE_SETTING: break; @@ -674,6 +672,9 @@ namespace big stat.m_stat->SetIntData(1); } } + + std::chrono::time_point end_time = std::chrono::high_resolution_clock::now(); + LOGIF(VERBOSE, g.enable_debug_logs, "Loading took {:%S}", std::chrono::duration_cast(end_time-start_time)); return true; } } \ No newline at end of file diff --git a/src/services/stats/stats_service.hpp b/src/services/stats/stats_service.hpp index c9a7f9a..d89cd26 100644 --- a/src/services/stats/stats_service.hpp +++ b/src/services/stats/stats_service.hpp @@ -114,13 +114,12 @@ namespace big void do_visit(const Ptr& ptr, const Fnc& fnc); }; - class sCustomStat + template + class sSmallStat { public: - sStatData* m_stat; - bool m_is_character; + T data; uint8_t m_character_index; - bool m_is_online; }; class stats_service @@ -166,20 +165,20 @@ namespace big void load_script_data_from_json(const nlohmann::json& json); void save_script_data_to_json(); - std::unordered_map m_int_stats; - std::unordered_map m_float_stats; - std::unordered_map m_string_stats; - std::unordered_map m_bool_stats; - std::unordered_map m_uint8_stats; - std::unordered_map m_uint16_stats; - std::unordered_map m_uint32_stats; - std::unordered_map m_uint64_stats; - std::unordered_map m_int64_stats; - std::unordered_map m_date_stats; - std::unordered_map m_pos_stats; - std::unordered_map m_textlabel_stats; - std::unordered_map m_packed_stats; - std::unordered_map m_userid_stats; + std::unordered_map> m_int_stats; + std::unordered_map> m_float_stats; + std::unordered_map> m_string_stats; + std::unordered_map> m_bool_stats; + std::unordered_map> m_uint8_stats; + std::unordered_map> m_uint16_stats; + std::unordered_map> m_uint32_stats; + std::unordered_map> m_uint64_stats; + std::unordered_map> m_int64_stats; + std::unordered_map> m_date_stats; + std::unordered_map> m_pos_stats; + std::unordered_map> m_textlabel_stats; + std::unordered_map> m_packed_stats; + std::unordered_map> m_userid_stats; std::unordered_map m_stat_hash_to_string; From e561c031e4c1fc270ce9ef4428af8f558cc35155 Mon Sep 17 00:00:00 2001 From: tupoy-ya Date: Thu, 25 Dec 2025 03:37:50 +0500 Subject: [PATCH 2/2] refactor: Rename load_internal_script_data_from_json to load_script_data --- src/services/stats/stats_service.cpp | 7 ++----- src/services/stats/stats_service.hpp | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/services/stats/stats_service.cpp b/src/services/stats/stats_service.cpp index 238518d..b686c62 100644 --- a/src/services/stats/stats_service.cpp +++ b/src/services/stats/stats_service.cpp @@ -394,7 +394,6 @@ namespace big try { - // Ignore comments for save_overwrite.json const nlohmann::json& json = nlohmann::json::parse(file, nullptr, true, /*ignore_comments*/ true); bool uses_human_readable_stat_names = json["uses_human_readable_stat_names"]; load_stat_map_from_json(json["INT"], m_int_stats, uses_human_readable_stat_names); @@ -422,7 +421,7 @@ namespace big return false; } - bool stats_service::load_internal_script_data_from_json() + bool stats_service::load_script_data() { if (!m_save_file_script.exists()) { @@ -560,14 +559,12 @@ namespace big return false; } - // Load character stats if they exist. load_internal_stats_from_json(1); load_internal_stats_from_json(2); - // Load stat overrides last. load_internal_stats_from_json(SAVE_OVERWRITE_INDEX); - load_internal_script_data_from_json(); + load_script_data(); const auto& stats = *g_pointers->m_stats; for (const auto& stat : stats) diff --git a/src/services/stats/stats_service.hpp b/src/services/stats/stats_service.hpp index d89cd26..83175f3 100644 --- a/src/services/stats/stats_service.hpp +++ b/src/services/stats/stats_service.hpp @@ -150,7 +150,7 @@ namespace big void save_internal_stats_to_json(uint8_t char_index = 0); bool load_internal_stats_from_json(uint8_t char_index = 0); - bool load_internal_script_data_from_json(); + bool load_script_data(); template void save_stat_map_to_json(nlohmann::json& json, T& map, bool use_stat_names, uint8_t char_index);