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
This commit is contained in:
Kelsi 2026-05-05 10:01:05 -07:00
parent 954894460e
commit d10d962e31
3 changed files with 95 additions and 0 deletions

View file

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

View file

@ -0,0 +1,31 @@
#pragma once
#include <string>
#include <vector>
#include <cstdint>
namespace wowee {
namespace pipeline {
struct CustomZoneInfo {
std::string name;
std::string author;
std::string description;
std::string directory;
uint32_t mapId = 9000;
std::vector<std::pair<int, int>> tiles;
bool hasCreatures = false;
bool hasQuests = false;
};
class CustomZoneDiscovery {
public:
// Scan directories for custom zones (checks for zone.json files)
static std::vector<CustomZoneInfo> scan(const std::vector<std::string>& searchPaths);
// Scan a single directory
static std::vector<CustomZoneInfo> scanDirectory(const std::string& path);
};
} // namespace pipeline
} // namespace wowee

View file

@ -0,0 +1,63 @@
#include "pipeline/custom_zone_discovery.hpp"
#include "core/logger.hpp"
#include <fstream>
#include <filesystem>
namespace wowee {
namespace pipeline {
std::vector<CustomZoneInfo> CustomZoneDiscovery::scan(const std::vector<std::string>& searchPaths) {
std::vector<CustomZoneInfo> results;
for (const auto& path : searchPaths) {
auto found = scanDirectory(path);
results.insert(results.end(), found.begin(), found.end());
}
return results;
}
std::vector<CustomZoneInfo> CustomZoneDiscovery::scanDirectory(const std::string& path) {
namespace fs = std::filesystem;
std::vector<CustomZoneInfo> 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<char>(f)),
std::istreambuf_iterator<char>());
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