diff --git a/mods/src/patches/parts/sync.cc b/mods/src/patches/parts/sync.cc index fe1035cfc..6d96a8768 100644 --- a/mods/src/patches/parts/sync.cc +++ b/mods/src/patches/parts/sync.cc @@ -1095,10 +1095,120 @@ static void ship_combat_log_data() namespace processors { +static void queue_battle_ids(const std::vector& battle_ids) +{ + std::vector to_enqueue; + { + using trackers::previously_sent_battlelogs; + using trackers::previously_sent_battlelogs_mtx; + + std::scoped_lock lk(previously_sent_battlelogs_mtx); + + for (const auto id : battle_ids | std::views::reverse) { + if (eastl::find(previously_sent_battlelogs.begin(), previously_sent_battlelogs.end(), id) + == previously_sent_battlelogs.end()) { + previously_sent_battlelogs.push_back(id); + to_enqueue.push_back(id); + } + } + } + + if (!to_enqueue.empty()) { + http::logging::debug("QUEUE", "battle headers", + STR_FORMAT("Queuing {} battles for background processing", to_enqueue.size())); + + { + std::scoped_lock lk(workers::combat_log_data_mtx); + for (const auto id : to_enqueue) { + workers::combat_log_data_queue.push(id); + } + } + + trackers::save_previously_sent_logs(); + workers::combat_log_data_cv.notify_all(); + } +} + static void battle_result_headers(std::unique_ptr&& bytes) { - // TODO: Placeholder for future client support; currently unused by the game client. - spdlog::debug("process_battle_result_headers() was called"); + if (auto response = Digit::PrimeServer::Models::BattleResultHeadersResponse(); response.ParseFromString(*bytes)) { + http::logging::trace("PROCESS", "battle headers", + STR_FORMAT("Processing {} battle headers", response.headers_size())); + + std::vector battle_ids; + battle_ids.reserve(static_cast(response.headers_size())); + + for (const auto& header : response.headers()) { + battle_ids.push_back(static_cast(header.id())); + } + + queue_battle_ids(battle_ids); + } else { + spdlog::error("Failed to parse battle result headers"); + } +} + +struct ShipRecord { + int64_t id; + int64_t hull_id; + int32_t tier; + int32_t level; + double level_percentage; + std::vector components; +}; + +static void sync_ships(const std::vector& ships) +{ + using json = nlohmann::json; + using trackers::types::ShipState; + + static std::unordered_map ship_states; + static std::mutex ship_states_mtx; + static std::atomic_bool is_first_sync{true}; + + auto ship_array = json::array(); + { + std::scoped_lock lk(ship_states_mtx); + + for (const auto& ship : ships) { + const ShipState state{ship.tier, ship.level, ship.level_percentage, ship.components}; + + if (const auto& it = ship_states.find(ship.id); it == ship_states.end() || it->second != state) { + ship_states[ship.id] = state; + ship_array.push_back({{"type", SyncConfig::Type::Ships}, + {"psid", ship.id}, + {"level", ship.level}, + {"level_percentage", ship.level_percentage}, + {"tier", ship.tier}, + {"hull_id", ship.hull_id}, + {"components", ship.components}}); + } + } + } + + if (!ship_array.empty()) { + const bool first_sync = is_first_sync.exchange(false, std::memory_order_acq_rel); + workers::queue_data(SyncConfig::Type::Ships, ship_array, first_sync); + } +} + +static void player_ships(std::unique_ptr&& bytes) +{ + if (auto response = Digit::PrimeServer::Models::PlayerShipsResponse(); response.ParseFromString(*bytes)) { + http::logging::trace("PROCESS", "ships", STR_FORMAT("Processing {} ships", response.ships_size())); + + std::vector ships; + ships.reserve(static_cast(response.ships_size())); + + for (const auto& ship : response.ships() | std::views::values) { + ships.push_back({ship.id(), ship.hullid(), ship.tier(), ship.level(), ship.levelpercentage(), + std::vector(ship.components().begin(), ship.components().end())}); + } + + sync_ships(ships); + } else { + spdlog::error("Failed to parse player ships"); + } } static void battle_report(std::unique_ptr&& bytes) @@ -1927,36 +2037,7 @@ namespace json battle_ids.push_back(id); } - std::vector to_enqueue; - { - using trackers::previously_sent_battlelogs; - using trackers::previously_sent_battlelogs_mtx; - - std::scoped_lock lk(previously_sent_battlelogs_mtx); - - for (const auto id : battle_ids | std::views::reverse) { - if (eastl::find(previously_sent_battlelogs.begin(), previously_sent_battlelogs.end(), id) - == previously_sent_battlelogs.end()) { - previously_sent_battlelogs.push_back(id); - to_enqueue.push_back(id); - } - } - } - - if (!to_enqueue.empty()) { - http::logging::debug("QUEUE", "battle headers", - STR_FORMAT("Queuing {} battles for background processing", to_enqueue.size())); - - { - std::scoped_lock lk(workers::combat_log_data_mtx); - for (const auto id : to_enqueue) { - workers::combat_log_data_queue.push(id); - } - } - - trackers::save_previously_sent_logs(); - workers::combat_log_data_cv.notify_all(); - } + queue_battle_ids(battle_ids); } static void resources(const nlohmann::json& section) @@ -2020,43 +2101,19 @@ namespace json static void ships(const nlohmann::json& section) { using json = nlohmann::json; - using trackers::types::ShipState; - - static std::unordered_map ship_states; - static std::mutex ship_states_mtx; - static std::atomic_bool is_first_sync{true}; http::logging::trace("PROCESS", "ships", STR_FORMAT("Processing {} ships (JSON)", section.size())); - auto ship_array = json::array(); - { - std::scoped_lock lk(ship_states_mtx); - - for (const auto& ship : section.get() | std::views::values) { - const auto id = ship["id"].get(); - const auto tier = ship["tier"].get(); - const auto level = ship["level"].get(); - const auto level_percentage = ship["level_percentage"].get(); - const auto components = ship["components"].get>(); - const ShipState state{tier, level, level_percentage, components}; - - if (const auto& it = ship_states.find(id); it == ship_states.end() || it->second != state) { - ship_states[id] = state; - ship_array.push_back({{"type", SyncConfig::Type::Ships}, - {"psid", id}, - {"level", level}, - {"level_percentage", level_percentage}, - {"tier", tier}, - {"hull_id", ship["hull_id"].get()}, - {"components", components}}); - } - } - } + std::vector records; + records.reserve(section.size()); - if (!ship_array.empty()) { - const bool first_sync = is_first_sync.exchange(false, std::memory_order_acq_rel); - workers::queue_data(SyncConfig::Type::Ships, ship_array, first_sync); + for (const auto& ship : section.get() | std::views::values) { + records.push_back({ship["id"].get(), ship["hull_id"].get(), ship["tier"].get(), + ship["level"].get(), ship["level_percentage"].get(), + ship["components"].get>()}); } + + sync_ships(records); } static void parse(std::unique_ptr&& bytes) @@ -2382,7 +2439,11 @@ static void HandleEntityGroup(EntityGroup* entity_group) break; // ships - // TODO: currently still part of JSON, likely to change in the future + case EntityGroup::Type::Ships: + if (sync_options.ships) { + submit_async(processors::player_ships); + } + break; // slots case EntityGroup::Type::EntitySlots: diff --git a/mods/src/prime/EntityGroup.h b/mods/src/prime/EntityGroup.h index 5214470d9..2356a1890 100644 --- a/mods/src/prime/EntityGroup.h +++ b/mods/src/prime/EntityGroup.h @@ -252,6 +252,18 @@ struct EntityGroup { ResourceProducers = 240, // 0x000000F0 Starbase = 241, // 0x000000F1 + NodeSystem = 242, // 0x000000F2 + DockingPoints = 243, // 0x000000F3 + PlayerContainer = 244, // 0x000000F4 + MarauderQuickScanData = 245, // 0x000000F5 + AllianceContainer = 246, // 0x000000F6 + QuickScanResults = 247, // 0x000000F7 + DeployedFleets = 248, // 0x000000F8 + MyDeployedFleets = 249, // 0x000000F9 + Fleets = 250, // 0x000000FA + Ships = 251, // 0x000000FB + ResourceHarvesters = 252, // 0x000000FC + PlanetaryBaseData = 1213, // 0x000004BD PlanetaryResourceProducers = 1214, // 0x000004BE PlanetaryMapData = 1215, // 0x000004BF diff --git a/mods/src/prime/proto/Digit.PrimeServer.Models.proto b/mods/src/prime/proto/Digit.PrimeServer.Models.proto index e3cb44dd6..39392b81b 100644 --- a/mods/src/prime/proto/Digit.PrimeServer.Models.proto +++ b/mods/src/prime/proto/Digit.PrimeServer.Models.proto @@ -3577,6 +3577,17 @@ enum Type { TYPE_STARBASEMODULES = 239; TYPE_RESOURCEPRODUCERS = 240; TYPE_STARBASE = 241; + TYPE_NODESYSTEM = 242; + TYPE_DOCKINGPOINTS = 243; + TYPE_PLAYERCONTAINER = 244; + TYPE_MARAUDERQUICKSCANDATA = 245; + TYPE_ALLIANCECONTAINER = 246; + TYPE_QUICKSCANRESULTS = 247; + TYPE_DEPLOYEDFLEETS = 248; + TYPE_MYDEPLOYEDFLEETS = 249; + TYPE_FLEETS = 250; + TYPE_SHIPS = 251; + TYPE_RESOURCEHARVESTERS = 252; TYPE_OUTPOSTSTATICDATA = 8401; TYPE_OUTPOSTUPGRADERESPONSE = 8402; TYPE_OUTPOSTSYNC = 8403; @@ -3900,6 +3911,19 @@ message PlayerPartyResponse { PartyResponse response = 1; } +message PlayerShipData { + int64 id = 1; + int32 tier = 2; + int64 hullId = 3; + repeated int64 components = 4; + int32 level = 5; + float levelPercentage = 6; +} + +message PlayerShipsResponse { + map ships = 1; +} + message PlanetaryBaseSpec { int64 id = 1; string buildingName = 2; @@ -5583,3 +5607,11 @@ message WaveDefenseChallengeDataResponse { message ActiveWormholesResponse { repeated Wormhole wormholes = 1; } + +message JournalHeaderProto { + int64 id = 1; +} + +message BattleResultHeadersResponse { + repeated JournalHeaderProto headers = 1; +}