diff --git a/mods/src/defaultconfig.h b/mods/src/defaultconfig.h index 40b31ae57..7158c8bdc 100644 --- a/mods/src/defaultconfig.h +++ b/mods/src/defaultconfig.h @@ -225,9 +225,10 @@ namespace Sync constexpr const char* nextspocksclub_url = "https://next.spocks.club/sync/ingress/"; constexpr bool away_assignments = true; - constexpr bool battlelogs = true; + constexpr bool battlelogs = false; constexpr bool buffs = true; constexpr bool buildings = true; + constexpr bool haven = true; constexpr bool inventory = true; constexpr bool jobs = true; constexpr bool missions = true; diff --git a/mods/src/patches/parts/sync.cc b/mods/src/patches/parts/sync.cc index f54a90368..0fd2ffcff 100644 --- a/mods/src/patches/parts/sync.cc +++ b/mods/src/patches/parts/sync.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -612,6 +613,23 @@ namespace types std::vector components; }; + struct PlanetaryBaseState { + explicit PlanetaryBaseState(const int32_t l = -1, const int32_t s = -1, const int64_t p = -1) + : level(l) + , status(s) + , position(p) + { + } + + bool operator==(const PlanetaryBaseState& other) const + { return this->level == other.level && this->status == other.status && this->position == other.position; } + + private: + int32_t level = -1; + int32_t status = -1; + int64_t position = -1; + }; + struct pairhash { template std::size_t operator()(const std::pair& x) const { return std::hash()(x.first) ^ std::hash()(x.second); } @@ -799,8 +817,11 @@ static std::mutex slot_states_mtx; static std::unordered_map away_assignment_states; static std::mutex away_assignment_states_mtx; -static std::unordered_map structure_states; -static std::mutex structure_states_mtx; +static std::unordered_map structure_states; +static std::mutex structure_states_mtx; + +static std::unordered_map pieces_states; +static std::mutex pieces_states_mtx; static eastl::ring_buffer previously_sent_battlelogs; static std::mutex previously_sent_battlelogs_mtx; @@ -1467,29 +1488,25 @@ static void starbase_modules(std::unique_ptr&& bytes) static void planetary_base_data(std::unique_ptr&& bytes) { using json = nlohmann::json; - using trackers::structure_states; - using trackers::structure_states_mtx; + using trackers::pieces_states; + using trackers::pieces_states_mtx; if (auto response = Digit::PrimeServer::Models::PlanetaryBase(); response.ParseFromString(*bytes)) { - http::logging::trace("PROCESS", "planetary base data", STR_FORMAT("Processing {} buildings", response.buildings_size())); - - auto structure_array = json::array(); + http::logging::trace("PROCESS", "planetary base data", STR_FORMAT("Processing {} haven building pieces", response.pieces_size())); + auto pieces_array = json::array(); { - std::scoped_lock lk(structure_states_mtx); - - for (const auto& building : response.buildings() | std::views::values) { - if (const auto& it = structure_states.find(building.id()); it == structure_states.end() || it->second != building.level()) { - structure_states[building.id()] = building.level(); - structure_array.push_back({{"type", SyncConfig::Type::Haven}, - {"sid", building.specid()}, - {"id", building.id()}, - {"level", building.level()}}); + std::scoped_lock lk(pieces_states_mtx); + + for (const auto& [piece, count] : response.pieces()) { + if (const auto& it = pieces_states.find(piece); it == pieces_states.end() || it->second != count) { + pieces_states[piece] = count; + pieces_array.push_back({{"type", SyncConfig::Type::Haven + "_pieces"}, {"sid", piece}, {"count", count}}); } } } - if (!structure_array.empty()) { - workers::queue_data(SyncConfig::Type::Haven, structure_array); + if (!pieces_array.empty()) { + workers::queue_data(SyncConfig::Type::Haven, pieces_array); } } } @@ -1498,9 +1515,22 @@ static void planetary_map_building_data(const google::protobuf::Mapsecond != state) { + structure_states[id] = state; + structure_array.push_back({{"type", SyncConfig::Type::Haven}, {"sid", building.specid()}, {"id", id}, {"level", building.level()}, {"status", building.status()}, {"position", building.position()}}); + } + } } if (!structure_array.empty()) { @@ -1526,18 +1556,51 @@ static void planetary_map_prosperity(std::unique_ptr&& bytes) } } -/* static void planetary_map(std::unique_ptr&& bytes) +static void planetary_map(std::unique_ptr&& bytes) { if (auto response = Digit::PrimeServer::Models::PlanetaryMapResponse(); response.ParseFromString(*bytes)) { http::logging::trace("PROCESS", "planetary map", STR_FORMAT("Processing {} haven structures", response.mapdata().buildings_size())); + const auto& owner = response.owneruserid(); + if (!owner.empty()) { + auto profileManager = UserProfileManager::Instance(); + if (profileManager == nullptr) { + spdlog::error("Failed to get UserProfileManager instance"); + return; + } + + static auto& class_helper = profileManager->get_class_helper(); + static auto fn = class_helper.GetMethodInfo("GetLocalUserProfile"); + + if (fn == nullptr ) { + spdlog::error("Failed to get method info for GetLocalUserProfile"); + return; + } + + Il2CppException* exception = nullptr; + auto localProfile = reinterpret_cast(il2cpp_runtime_invoke(fn, profileManager, nullptr, &exception)); + if (exception != nullptr || localProfile == nullptr) { + spdlog::error("Failed to get local user profile"); + return; + } + + const auto userId = localProfile->UserId; + if (userId == nullptr) { + spdlog::error("Local user profile has empty UserId"); + return; + } + + if (owner != userId) { + spdlog::debug("Planetary map data is not for local user, skipping processing"); + return; + } + } - // TODO: If the owner is not the current player, we may want to skip processing the map data, as it may not be relevant to the player's own structures. planetary_map_building_data(response.mapdata().buildings()); } else { spdlog::error("Failed to parse planetary map"); } -} */ +} static void player_inventories(std::unique_ptr&& bytes) { @@ -2518,11 +2581,11 @@ static void HandleEntityGroup(EntityGroup* entity_group) } break; - // case EntityGroup::Type::PlanetaryMapResponse: - // if (sync_options.haven) { - // submit_async(processors::planetary_map); - // } - // break; + case EntityGroup::Type::PlanetaryMapResponse: + if (sync_options.haven) { + submit_async(processors::planetary_map); + } + break; // inventory case EntityGroup::Type::PlayerInventories: diff --git a/mods/src/prime/UserProfile.h b/mods/src/prime/UserProfile.h index d5204ef5c..d202a9592 100644 --- a/mods/src/prime/UserProfile.h +++ b/mods/src/prime/UserProfile.h @@ -6,6 +6,7 @@ struct UserProfile { public: __declspec(property(get = __get_LocaId)) long LocaId; __declspec(property(get = __get_Name)) Il2CppString* Name; + __declspec(property(get = __get_UserId)) Il2CppString* UserId; private: static IL2CppClassHelper& get_class_helper() @@ -28,4 +29,9 @@ struct UserProfile { return *(Il2CppString**)((char*)this + field); } + Il2CppString* __get_UserId() + { + static auto prop = get_class_helper().GetProperty("UserId"); + return prop.GetRaw(this); + } }; diff --git a/mods/src/prime/UserProfileManager.h b/mods/src/prime/UserProfileManager.h new file mode 100644 index 000000000..90da6b935 --- /dev/null +++ b/mods/src/prime/UserProfileManager.h @@ -0,0 +1,22 @@ +#pragma once + +#include "MonoSingleton.h" +#include "UserProfile.h" + +#include + +struct UserProfileManager : MonoSingleton { + + static IL2CppClassHelper& get_class_helper() + { + static auto class_helper = + il2cpp_get_class_helper("Assembly-CSharp", "Digit.Prime.PlayerProfile", "UserProfileManager"); + return class_helper; + } + + [[nodiscard]] UserProfile* GetLocalUserProfile() + { + static auto method = get_class_helper().GetInvokeMethod("GetLocalUserProfile"); + return method.Invoke(this); + } +}; diff --git a/mods/src/str_utils.h b/mods/src/str_utils.h index a9fbfb7df..2c153891d 100644 --- a/mods/src/str_utils.h +++ b/mods/src/str_utils.h @@ -145,6 +145,16 @@ inline std::string to_string(const Il2CppString* str) return result; } +inline bool operator==(const Il2CppString* lhs, const std::string& rhs) +{ + return to_string(lhs) == rhs; +} + +inline bool operator==(const std::string& lhs, const Il2CppString* rhs) +{ + return lhs == to_string(rhs); +} + inline std::optional> parse_timestamp(const std::string& timestamp) { #ifdef _WIN32