From d10d962e31c06cca1ea9ff198e44a30b32c4f97e Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 5 May 2026 10:01:05 -0700 Subject: [PATCH] feat: custom zone discovery system for client auto-detection - CustomZoneDiscovery scans directories for zone.json manifest files - Discovers custom zones in custom_zones/ and output/ directories - Reports: name, author, description, creature/quest availability - Client can list all available custom expansions at startup - Foundation for a zone selection menu in the client UI --- CMakeLists.txt | 1 + include/pipeline/custom_zone_discovery.hpp | 31 +++++++++++ src/pipeline/custom_zone_discovery.cpp | 63 ++++++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 include/pipeline/custom_zone_discovery.hpp create mode 100644 src/pipeline/custom_zone_discovery.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a64e4945..6c69f558 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -590,6 +590,7 @@ set(WOWEE_SOURCES src/pipeline/adt_loader.cpp src/pipeline/wdt_loader.cpp src/pipeline/wowee_terrain_loader.cpp + src/pipeline/custom_zone_discovery.cpp src/pipeline/dbc_layout.cpp src/pipeline/terrain_mesh.cpp diff --git a/include/pipeline/custom_zone_discovery.hpp b/include/pipeline/custom_zone_discovery.hpp new file mode 100644 index 00000000..8880c4c8 --- /dev/null +++ b/include/pipeline/custom_zone_discovery.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +namespace wowee { +namespace pipeline { + +struct CustomZoneInfo { + std::string name; + std::string author; + std::string description; + std::string directory; + uint32_t mapId = 9000; + std::vector> tiles; + bool hasCreatures = false; + bool hasQuests = false; +}; + +class CustomZoneDiscovery { +public: + // Scan directories for custom zones (checks for zone.json files) + static std::vector scan(const std::vector& searchPaths); + + // Scan a single directory + static std::vector scanDirectory(const std::string& path); +}; + +} // namespace pipeline +} // namespace wowee diff --git a/src/pipeline/custom_zone_discovery.cpp b/src/pipeline/custom_zone_discovery.cpp new file mode 100644 index 00000000..1345a3c5 --- /dev/null +++ b/src/pipeline/custom_zone_discovery.cpp @@ -0,0 +1,63 @@ +#include "pipeline/custom_zone_discovery.hpp" +#include "core/logger.hpp" +#include +#include + +namespace wowee { +namespace pipeline { + +std::vector CustomZoneDiscovery::scan(const std::vector& searchPaths) { + std::vector results; + for (const auto& path : searchPaths) { + auto found = scanDirectory(path); + results.insert(results.end(), found.begin(), found.end()); + } + return results; +} + +std::vector CustomZoneDiscovery::scanDirectory(const std::string& path) { + namespace fs = std::filesystem; + std::vector results; + + if (!fs::exists(path)) return results; + + for (auto& entry : fs::directory_iterator(path)) { + if (!entry.is_directory()) continue; + + std::string zoneJson = entry.path().string() + "/zone.json"; + if (!fs::exists(zoneJson)) continue; + + std::ifstream f(zoneJson); + if (!f) continue; + std::string content((std::istreambuf_iterator(f)), + std::istreambuf_iterator()); + + auto findStr = [&](const std::string& key) -> std::string { + auto pos = content.find("\"" + key + "\""); + if (pos == std::string::npos) return ""; + pos = content.find('"', content.find(':', pos) + 1); + if (pos == std::string::npos) return ""; + auto end = content.find('"', pos + 1); + return content.substr(pos + 1, end - pos - 1); + }; + + CustomZoneInfo info; + info.name = findStr("mapName"); + if (info.name.empty()) info.name = findStr("name"); + info.author = findStr("author"); + info.description = findStr("description"); + info.directory = entry.path().string(); + info.hasCreatures = fs::exists(entry.path().string() + "/creatures.json"); + info.hasQuests = fs::exists(entry.path().string() + "/quests.json"); + + if (!info.name.empty()) { + results.push_back(info); + LOG_INFO("Discovered custom zone: ", info.name, " in ", info.directory); + } + } + + return results; +} + +} // namespace pipeline +} // namespace wowee