mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-07 17:43:51 +00:00
feat(editor): add --repair-zone to auto-fix manifest/disk drift
When a zone is hand-edited, partially copied, or modified by tools
that don't re-write zone.json, the manifest can fall out of sync
with the on-disk reality. --repair-zone reconciles them:
wowee_editor --repair-zone custom_zones/MyZone --dry-run
would add tile (31, 30) to manifest
would set hasCreatures: false -> true
repair-zone: custom_zones/MyZone (dry-run)
fixes : 2
warnings : 0 (manual decision needed)
re-run without --dry-run to apply
Auto-fixes:
- WHM files on disk matching <mapName>_TX_TY.whm pattern but not
in manifest tiles[] -> add to tiles
- hasCreatures flag mismatched against actual creatures.json
presence + non-empty -> sync
Warns (no auto-fix — needs manual decision):
- Tiles in manifest but no .whm on disk (could be in-progress
work or genuinely deleted; user decides)
--dry-run flag previews changes. Pairs with --strip-zone (cleanup
derived) and --validate (open-format coverage) for the trio of
zone-health-maintenance commands.
Verified: scaffolded zone, hand-copied an extra .whm/.wot pair to
simulate disk-without-manifest drift, added a creature then flipped
hasCreatures=false in zone.json. --repair-zone correctly identifies
2 fixes, dry-run lists them, real run applies them, manifest now
shows correct tiles array + hasCreatures=true.
This commit is contained in:
parent
b82f9827d4
commit
1718c2333f
1 changed files with 114 additions and 0 deletions
|
|
@ -475,6 +475,8 @@ static void printUsage(const char* argv0) {
|
|||
std::printf(" Wipe one or more content files (terrain + manifest preserved)\n");
|
||||
std::printf(" --strip-zone <zoneDir> [--dry-run]\n");
|
||||
std::printf(" Remove derived outputs (.glb/.obj/.stl/.html/.dot/.csv/ZONE.md/DEPS.md)\n");
|
||||
std::printf(" --repair-zone <zoneDir> [--dry-run]\n");
|
||||
std::printf(" Auto-fix manifest/disk drift (missing tiles in manifest, hasCreatures flag)\n");
|
||||
std::printf(" --build-woc <wot-base> Generate a WOC collision mesh from WHM/WOT and exit\n");
|
||||
std::printf(" --regen-collision <zoneDir> Rebuild every WOC under a zone dir and exit\n");
|
||||
std::printf(" --fix-zone <zoneDir> Re-parse + re-save zone JSONs to apply latest scrubs/caps and exit\n");
|
||||
|
|
@ -678,6 +680,7 @@ int main(int argc, char* argv[]) {
|
|||
"--clone-object",
|
||||
"--remove-creature", "--remove-object", "--remove-quest",
|
||||
"--copy-zone", "--rename-zone", "--clear-zone-content", "--strip-zone",
|
||||
"--repair-zone",
|
||||
"--build-woc", "--regen-collision", "--fix-zone",
|
||||
"--export-png", "--export-obj", "--import-obj",
|
||||
"--export-wob-obj", "--import-wob-obj",
|
||||
|
|
@ -9149,6 +9152,117 @@ int main(int argc, char* argv[]) {
|
|||
std::printf(" freed : %.1f KB\n", bytesFreed / 1024.0);
|
||||
}
|
||||
return 0;
|
||||
} else if (std::strcmp(argv[i], "--repair-zone") == 0 && i + 1 < argc) {
|
||||
// Auto-fix the common manifest-vs-disk drift issues that
|
||||
// accumulate when a zone is hand-edited or partially copied:
|
||||
// - WHM/WOT files exist on disk but tile not in manifest
|
||||
// -> add to tiles
|
||||
// - manifest hasCreatures=false but creatures.json exists
|
||||
// and is non-empty -> set true
|
||||
// - manifest hasCreatures=true but no creatures.json or
|
||||
// empty -> clear false
|
||||
//
|
||||
// Tiles in manifest with NO disk files are NOT auto-removed
|
||||
// (they may indicate work-in-progress); they're warned about
|
||||
// so the user can decide.
|
||||
//
|
||||
// --dry-run flag previews changes without writing.
|
||||
std::string zoneDir = argv[++i];
|
||||
bool dryRun = false;
|
||||
if (i + 1 < argc && std::strcmp(argv[i + 1], "--dry-run") == 0) {
|
||||
dryRun = true; i++;
|
||||
}
|
||||
namespace fs = std::filesystem;
|
||||
std::string manifestPath = zoneDir + "/zone.json";
|
||||
if (!fs::exists(manifestPath)) {
|
||||
std::fprintf(stderr,
|
||||
"repair-zone: %s has no zone.json\n", zoneDir.c_str());
|
||||
return 1;
|
||||
}
|
||||
wowee::editor::ZoneManifest zm;
|
||||
if (!zm.load(manifestPath)) {
|
||||
std::fprintf(stderr, "repair-zone: parse failed\n");
|
||||
return 1;
|
||||
}
|
||||
int fixes = 0, warnings = 0;
|
||||
// Pass 1: scan disk for WHM files matching mapName_X_Y.whm
|
||||
// pattern. Match against manifest tiles. Anything on disk
|
||||
// but missing from manifest gets queued for addition.
|
||||
std::set<std::pair<int,int>> manifestTiles(
|
||||
zm.tiles.begin(), zm.tiles.end());
|
||||
std::set<std::pair<int,int>> diskTiles;
|
||||
std::error_code ec;
|
||||
for (const auto& e : fs::directory_iterator(zoneDir, ec)) {
|
||||
if (!e.is_regular_file()) continue;
|
||||
std::string name = e.path().filename().string();
|
||||
if (e.path().extension() != ".whm") continue;
|
||||
// Expect "<mapName>_TX_TY.whm". Parse out the two
|
||||
// integers between the last two underscores.
|
||||
std::string stem = name.substr(0, name.size() - 4);
|
||||
std::string prefix = zm.mapName + "_";
|
||||
if (stem.size() <= prefix.size() ||
|
||||
stem.substr(0, prefix.size()) != prefix) {
|
||||
continue; // doesn't match map slug
|
||||
}
|
||||
std::string coords = stem.substr(prefix.size());
|
||||
auto under = coords.find('_');
|
||||
if (under == std::string::npos) continue;
|
||||
try {
|
||||
int tx = std::stoi(coords.substr(0, under));
|
||||
int ty = std::stoi(coords.substr(under + 1));
|
||||
diskTiles.insert({tx, ty});
|
||||
} catch (...) {}
|
||||
}
|
||||
// Tiles on disk but not in manifest -> add.
|
||||
std::vector<std::pair<int,int>> toAdd;
|
||||
for (const auto& d : diskTiles) {
|
||||
if (manifestTiles.count(d) == 0) toAdd.push_back(d);
|
||||
}
|
||||
for (const auto& [tx, ty] : toAdd) {
|
||||
std::printf(" %s tile (%d, %d) to manifest\n",
|
||||
dryRun ? "would add" : "added", tx, ty);
|
||||
if (!dryRun) zm.tiles.push_back({tx, ty});
|
||||
fixes++;
|
||||
}
|
||||
// Tiles in manifest but no .whm on disk -> warn (not auto-removed).
|
||||
for (const auto& m : manifestTiles) {
|
||||
if (diskTiles.count(m) == 0) {
|
||||
std::printf(" WARN: tile (%d, %d) in manifest but no %s_%d_%d.whm on disk\n",
|
||||
m.first, m.second, zm.mapName.c_str(),
|
||||
m.first, m.second);
|
||||
warnings++;
|
||||
}
|
||||
}
|
||||
// hasCreatures flag sync.
|
||||
bool creaturesPresent = false;
|
||||
wowee::editor::NpcSpawner sp;
|
||||
if (sp.loadFromFile(zoneDir + "/creatures.json") &&
|
||||
sp.spawnCount() > 0) {
|
||||
creaturesPresent = true;
|
||||
}
|
||||
if (zm.hasCreatures != creaturesPresent) {
|
||||
std::printf(" %s hasCreatures: %s -> %s\n",
|
||||
dryRun ? "would set" : "set",
|
||||
zm.hasCreatures ? "true" : "false",
|
||||
creaturesPresent ? "true" : "false");
|
||||
if (!dryRun) zm.hasCreatures = creaturesPresent;
|
||||
fixes++;
|
||||
}
|
||||
if (!dryRun && fixes > 0) {
|
||||
if (!zm.save(manifestPath)) {
|
||||
std::fprintf(stderr,
|
||||
"repair-zone: failed to write %s\n", manifestPath.c_str());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
std::printf("\nrepair-zone: %s%s\n",
|
||||
zoneDir.c_str(), dryRun ? " (dry-run)" : "");
|
||||
std::printf(" fixes : %d\n", fixes);
|
||||
std::printf(" warnings : %d (manual decision needed)\n", warnings);
|
||||
if (dryRun && fixes > 0) {
|
||||
std::printf(" re-run without --dry-run to apply\n");
|
||||
}
|
||||
return 0;
|
||||
} else if (std::strcmp(argv[i], "--pack-wcp") == 0 && i + 1 < argc) {
|
||||
// Pack a zone directory into a .wcp archive.
|
||||
// Usage: --pack-wcp <zoneDirOrName> [destPath]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue