Files
2026-08-14 15:56:39 -04:00

20 KiB

RuneWaker instance import guide

This guide records how Forsaken Abbey was recovered and packages that work as a repeatable, offline-capable process for the remaining RuneWaker instances. The preserved client/server files are evidence and build inputs. They are not runtime dependencies of HealerMan.

Non-negotiable runtime rule

The shipped game may read only ordinary HealerMan source files and packaged assets. It must never connect to Microsoft SQL Server, read a RuneWaker backup, or depend on D:\Projects\runewaker/rom-pvt-server-backups.

SQL Server is used only by the explicit refresh command. That command:

  1. restores ROM_Global.bak and ObjectEdit.bak under unique temporary names;
  2. marks both restored databases read-only;
  3. exports one zone to JSON;
  4. drops both temporary databases in finally cleanup;
  5. hashes the source backups/catalogs so the snapshot is auditable.

Normal environment, actor, population, mechanics-audit, validation, offline, build, test, and game commands do not connect to SQL.

What Forsaken Abbey established

Forsaken Abbey is the reference import. Its environment recipe is recipes/forsaken-abbey.json; its population recipe is recipes/forsaken-abbey-population.json.

The completed pass recovered:

  • the primary monastery ROS selected from 184 WDB descriptors;
  • textured visual, collision chunks, and a Recast navigation GLB;
  • an authoritative entrance from Resource/luascript/03242.lua;
  • 251 active Zone 102 spawn rows;
  • 142 ordinary combat rows and eight boss rows (150 runtime spawns);
  • 101 audited deferred object/trigger/quest rows;
  • nine unique combat actor ROS models converted to GLB;
  • eight spawn-specific objectives, including four Necromage Keeper spawns;
  • source hashes, coordinate/projection records, and validation reports.

The current actor pass recovers the original GR2 skeletons and sampled ACT/RAS clips for the mesh-convertible combat models. Ghoul Drake, keeper doors, scripted summons, patrols, treasure loot, and other encounter state still need reviewed mechanics passes.

Coordinate conversion

RuneWaker server positions are world coordinates. The environment ROS is placed by one WDB descriptor. Spawns must be moved into that ROS's local frame before the RuneWaker-to-Three coordinate conversion:

sourceLocal = inverse(primaryWdbPlacement) * serverPosition
threeLocal  = (sourceLocal.x, sourceLocal.y, -sourceLocal.z) * metersPerSourceUnit

The current calibrated scale is 0.1 meter per RuneWaker source unit. The authoritative test-dungeon table uses 360 degrees per turn:

yawRadians = normalizedDirection / 360 * 2*pi

Every entrance and spawn is projected to the generated navigation mesh. Combat rows must remain within the recipe's maximum projection distance (currently 4 meters). Deferred objects may be farther away, but the report records them.

Toolchain

The pipeline uses:

  • the preserved/native RuneWaker object factory and ROS bridge;
  • Microsoft DirectXTex texconv for DDS to PNG;
  • Blender for OBJ/MTL cleanup and GLB export;
  • glTF Transform with Meshopt for browser delivery;
  • HealerMan's Recast navigation stage;
  • Khronos glTF validation.

Set these only when their defaults do not match the machine:

$env:RUNEWAKER_ROOT = 'D:\Projects\rom-pvt-server-backups\Runewaker'
$env:BLENDER_BIN = 'C:\\path\\to\\blender.exe'
$env:TEXCONV_BIN = 'C:\\path\\to\\texconv.exe'
$env:RUNEWAKER_SQL_SERVER = '.\\HEALERMAN_RW'

Command overview

The generic entry point is:

node scripts/runewaker-pipeline/cli.mjs help

Commands:

Command SQL Purpose
discover No Index configured dungeons, WDBs, Lua files, and recipe readiness.
inspect-wdb No Read one WDB and rank likely primary environment ROS models.
scaffold No Create environment/population recipes plus a review checklist.
environment No Convert, optimize, navmesh, package, and validate the environment.
refresh Yes Explicit temporary restore and portable population snapshot refresh.
actors No Convert reviewed unique creature ROS models to packaged GLBs.
population No Generate static entities, spawns, bosses, provenance, and report.
mechanics No Extract Lua/AutoPlot/template mechanic evidence and a review queue.
offline No Regenerate population and mechanics evidence from packaged inputs.
build No Environment + actors + population + mechanics + validation.
validate No Verify accounting, hashes, nav projection, portability, and GLBs.
prepare-all No Generate all recipes/snapshots from the portable all-zone export.
build-all No Resumable environment/actors/population/mechanics/validation batch.
register-all No Generate runtime definitions for every green packaged instance.
refresh-all Yes Explicit read-only forensic refresh for every configured zone.
spells:refresh No Rebuild the portable NPC spell catalog from preserved client DB files.
paperdolls:refresh Yes Export exact ImageObjectDB paperdoll component/color rows to a portable snapshot.
audit:animation-readiness No Account for source graphs, native clips, proxies, manifests, and shipping GLBs.
audit:gameplay No Audit source-backed attacks, evidence-only spells, objectives, navigation, and deferred mechanics.

refresh, refresh-all, and paperdolls:refresh are deliberately not part of any normal build. They restore temporary read-only databases only long enough to create portable JSON, then drop those databases in finally cleanup.

Reproducing the completed 30-instance batch

The repository contains a portable export for all 30 configured zones: 8,199 active NPCData rows, authoritative ZoneObjectDB.mapfile mappings, and hashed source evidence. With SQL stopped, regenerate and verify everything with:

npm run runewaker:prepare:all
npm run runewaker:spells:refresh
npm run runewaker:build:all
npm run runewaker:register:all
npm run runewaker:audit:animations
npm run runewaker:audit:gameplay
npm test
npm run build

The batch driver supports focused and resumable work:

node scripts/runewaker-pipeline/cli.mjs build-all --resume
node scripts/runewaker-pipeline/cli.mjs build-all --only kalins-shrine,sand-vortex
node scripts/runewaker-pipeline/cli.mjs build-all --only mystic-altar --phase environment,population,validate --force

It writes runewaker-export-work/all-instances-build-report.json after each dungeon, so a single bad asset does not lose prior work.

The batch handles these source cases explicitly and records them in recipes and package metadata:

  • Direct mesh actors select the largest GR2 body hierarchy, sample original ACT/RAS controls at 30 Hz, remap skin weights across Blender vertex reorder/seams, and ship Khronos-validated native GLBs.
  • Runtime paperdoll containers are assembled from the exact ImageObjectDB head, hair, upper/lower body, hands, feet, and equipment rows requested by combat templates. Each distinct image ID receives a namespaced GLB and provenance entry. Unsupported or genuinely triangle-free displays remain explicit proxy/pose-only exceptions instead of being labelled native.
  • Ystra Zone 109 packages all three authoritative placements of its repeated boss-room ROS and clones the exact room navmesh for each placement.
  • Sand Vortex has props but no terrain ROS, so its arena is derived from WDB bounds. Six modular/primary-collision maps add source-anchor support pads to preserve entrance and combat coordinates; full secondary WDB placement assembly remains a visible review item.

Only an explicit refresh requires SQL. Start the extraction-only instance, then:

npm run runewaker:refresh:all
npm run runewaker:paperdolls:refresh

The first command restores both backups once, sets them read-only, exports all zones, drops the temporary databases, and runs prepare-all. The paperdoll refresh independently restores ObjectEdit.bak read-only, exports only the requested appearance rows, and drops its temporary database. Stop MSSQL$HEALERMAN_RW afterward. The game and all normal build commands consume packaged files only.

Importing another instance

1. Discover the source catalog

node scripts/runewaker-pipeline/cli.mjs discover

The command writes runewaker-export-work/instance-catalog.json. The catalog currently sees 30 server-configured dungeons and more than 60 dungeon WDBs. A name match is only a candidate. Internal client names often differ from the display/server name (Forsaken Abbey is dgn_forlorn_monastary.wdb). Add verified exceptions to discovery-overrides.json.

2. Scaffold recipes

node scripts/runewaker-pipeline/cli.mjs scaffold --id <dungeon_config_id>

If the WDB match is ambiguous:

node scripts/runewaker-pipeline/cli.mjs scaffold --id <id> --wdb <file.wdb>

The scaffolder runs the preserved WDB reader and ranks ROS resources. It chooses the primary environment automatically only when a model/dungeon ROS clearly dominates the next candidate. Otherwise it stops and asks for an explicit:

--model model/dungeon/.../environment.ros

It also searches configured Lua files for authoritative X/Y/Z/DIR constants. If none are found, entryCoordinates.reviewRequired remains true.

The scaffold is not a playable dungeon. All templates begin as unclassified until their SQL/template evidence is recovered and reviewed.

3. Inspect the WDB separately when needed

node scripts/runewaker-pipeline/cli.mjs inspect-wdb --wdb <file.wdb> --slug <slug>

Review runewaker-export-work/<slug>/inspection/wdb-environment-candidates.json. Check model path, bounds, placement count, scale, and a visual export. Largest bounds are a useful signal, not proof.

4. Perform the one-time forensic population refresh

Start the isolated HEALERMAN_RW development instance, then run:

node scripts/runewaker-pipeline/cli.mjs refresh `
  --recipe scripts/runewaker-pipeline/recipes/<slug>-population.json `
  --update-recipe

--update-recipe pins the observed row/boss counts and fills initial template and actor candidates from authoritative fields:

  • NPCData: spawn DBID, template ID, room, position, direction, AutoPlot data;
  • NPCObjectDB: level, boss crown (sex=3), image ID, spells, Lua events;
  • ImageObjectDB: actor model (actworld);
  • StringDB: localized name.

The automatic classification is conservative:

  • sex=3 plus a positive-level model/character display -> boss;
  • sex=3 on an FX, door, or other non-character model -> deferred mechanics object (the source uses the same crown flag on scripted controllers);
  • positive level plus model/character/... -> combat candidate;
  • level-zero or object-model rows -> deferred object candidate;
  • missing/ambiguous evidence -> unclassified and blocked.

Review every changed template. A configured boss_guid is not authoritative by itself: Forsaken Abbey's old configuration included a normal zombie and a chest, while the original data identified different keeper/boss rows. Likewise, NPCObjectDB.sex=3 alone is insufficient: the all-dungeon audit found six invisible soul-ball controllers and two dungeon doors that had been incorrectly promoted to boss objectives before the model-family gate was added.

After refresh, stop SQL Server. All remaining steps must succeed while stopped.

5. Build and review the environment

node scripts/runewaker-pipeline/cli.mjs environment `
  --recipe scripts/runewaker-pipeline/recipes/<slug>.json

Review:

  • <slug>-preview.png for orientation, missing materials, scale, and obvious gaps;
  • visual GLB material/blending behavior;
  • collision chunks for decorative blockers;
  • Recast report and entrance projection;
  • WDB anchor in import-metadata.json;
  • Khronos validation output.

Secondary WDB doodads/effects remain audited. Repeated primary room placements and documented source-coordinate support/fallback geometry are automated; a complete arbitrary multi-ROS WDB scene still requires explicit placement, material, collision, and performance review.

6. Review and build creature actors

In the population recipe, confirm every combat/boss template maps to the correct unique actor model. Deduplicate actors by source ROS; templates can share one GLB.

node scripts/runewaker-pipeline/cli.mjs actors `
  --recipe scripts/runewaker-pipeline/recipes/<slug>-population.json

The shipping contract is GLB under public/assets/creatures/<slug>. Actor pivots, ground offsets, scale, label height, material reuse, and orientation must be reviewed. A direct actor is labelled native only when its source graph, sampled clips, skin, manifest, runtime binding, and validated GLB all agree. Paperdoll actors additionally require an exact image-specific row in snapshots/runewaker-paperdoll-appearances.json; normal actor/population builds never query SQL.

Structural validation is necessary but not a visual animation test. After any native exporter, skeleton-selection, coordinate-basis, or rig-injection change, render representative uncompressed <actor>.animated.raw.glb files with blender/render-animated-actor.py. Review the bind pose and the first, middle, and last frames of the exact idle, run, attack/cast, wound, and death clips used by the runtime. This gate caught the Pasper goatman quaternion-basis bug even though the affected files had valid skins, finite tracks, complete clip families, and zero Khronos warnings. The derivation and an executable example are in ORIGINAL_ANIMATION_IMPORT.md.

The source renderer can layer runtime colors through component masks. The appearance snapshot records those layers and requiresColorLayerBake in provenance. Until a corresponding color-mask bake is implemented and visually checked, retaining the resolved base texture is an explicit color-fidelity limit, not a reason to downgrade a valid skeleton or animation set.

7. Generate the offline population

node scripts/runewaker-pipeline/cli.mjs population `
  --recipe scripts/runewaker-pipeline/recipes/<slug>-population.json

This command reads only the portable snapshot, environment metadata/navmesh, actor manifest, and recipe. It emits:

  • PopulationDefinitionMap entities;
  • namespaced static spawn IDs;
  • spawn-specific boss objectives;
  • source-to-local and navmesh-projection records;
  • portable provenance and accounting reports.

RuneWaker numeric IDs are never assigned to AzerothCore serverEntry.

Use recipe attacks for reviewed data-driven abilities. The generator also joins template spell assignments to the portable source catalog and emits only positively classified direct-HP-damage families. Buffs, heals, summons, plot hooks, crowd-control-only rows, and unknown effects remain evidence-only; they must never become executable attacks by inference. When no reviewed/source-safe attack exists, the generator uses HealerMan's basic combat fallback. Boss health/damage, leash, speed, spawn masks, visual scale, and repeated-objective names can be overridden per template. For example:

{
  "id": 100070,
  "classification": "boss",
  "objectiveNamePattern": "Necromage Keeper {index}",
  "attacks": [
    {
      "id": "rw-fa-shadow-bolt",
      "name": "Shadow Bolt",
      "delivery": "projectile",
      "target": "primary",
      "school": "shadow",
      "animation": "cast",
      "range": 18,
      "cooldownMs": 4200,
      "damageMultiplier": 1.08
    }
  ]
}

8. Extract and implement mechanics

node scripts/runewaker-pipeline/cli.mjs mechanics `
  --recipe scripts/runewaker-pipeline/recipes/<slug>-population.json

The mechanics stage hashes and scans configured Lua files, template event scripts, spell IDs, AutoPlot/PlotClass values, boss positions, and spawn rows. It groups evidence into summons, doors, timers, movement, spells, death hooks, phases, interactions, and loot.

It intentionally generates evidence, not executable guesses. Review the audit and map mechanics into HealerMan's existing data/runtime boundaries:

  • spells and timed boss actions -> reviewed attack definitions;
  • summons/waves -> encounter-owned spawned entities with reset cleanup;
  • doors/gates -> explicit encounter state plus collision/navigation behavior;
  • patrols/AutoPlot -> navmesh-projected paths;
  • phases/death hooks -> a tested simulation state machine outside React render;
  • chests/interactions/loot -> noncombat object state and packaged loot data.

Simulation state stays outside React scene components. R3F components render the current world state; they are not the encounter-rule source of truth.

Each mechanic must specify trigger, targets, timing, reset behavior, persistence, offline behavior, and multiplayer synchronization boundary before being marked implemented.

9. Register the dungeon

Use the generated environment metadata and population constants to create the reviewed DungeonDefinition. Keep environment, population, and encounter state as separate concerns. Add the definition to the dungeon catalog only after:

  • entry and bounds are correct;
  • every runtime spawn has a unique ID;
  • boss/objective order is reviewed;
  • all asset URLs resolve offline;
  • validation status is green (or warnings are explicit and acceptable).

10. Validate offline

node scripts/runewaker-pipeline/cli.mjs offline `
  --recipe scripts/runewaker-pipeline/recipes/<slug>-population.json

node scripts/runewaker-pipeline/cli.mjs validate `
  --recipe scripts/runewaker-pipeline/recipes/<slug>-population.json

npm test
npm run build

Stop SQL Server and disconnect the network before the final browser test. Verify environment, all actor models, clustering, aggro, combat, healing, deaths/reset, objective progress, doors/summons/phases, party routing, asset 404s, console errors, WebGL errors, and mobile/desktop performance.

Outputs and provenance

Output Purpose
runewaker-export-work/instance-catalog.json Discovery inventory.
runewaker-export-work/<slug>/inspection/* WDB candidates and raw report.
scripts/runewaker-pipeline/recipes/*.json Reviewed import contracts.
scripts/runewaker-pipeline/snapshots/*.json Portable, hashed spawn/template evidence.
src/assets/game/dungeons/<slug>/* Environment, collision, navigation, reports.
public/assets/creatures/<slug>/* Browser-shipping actor GLBs/manifest.
src/game/generated/*Population.generated.ts Runtime entities/spawns/objectives.
src/game/generated/*Mechanics.generated.ts Non-executable mechanics evidence summary.

Generated runtime data must be reproducible from recipes plus portable snapshots without SQL or network access.

Common failure modes

  • Choosing a large prop instead of the primary environment ROS.
  • Applying the axis/scale conversion before removing the WDB placement.
  • Accepting a spawn far from navigation without understanding why.
  • Treating all level-zero rows as disposable; many are doors or script triggers.
  • Treating dungeon_config.boss_guids as authoritative classification.
  • Passing RuneWaker IDs into AzerothCore serverEntry.
  • Shipping raw OBJ/DDS/ROS or unvalidated DCC exports.
  • Letting React components own encounter simulation state.
  • Translating Lua by keyword alone and silently changing timing/reset semantics.
  • Leaving SQL, backup paths, or source-root reads in runtime code.

Current deliberate limits

The generic scripts automate source discovery, primary environment conversion, portable population recovery, direct GR2 actor conversion, exact paperdoll assembly, source-safe direct-damage attacks, spawn generation, repeated-room assembly, source-anchor navigation, mechanics evidence extraction, runtime registration, and validation. They do not pretend that arbitrary Lua/AutoPlot code can be safely translated without review. Disconnected navmesh islands, doors, phases, summons, patrols, timers, interactions, loot semantics, arbitrary multi-ROS placement assembly, source-exact damage formulas, and multiplayer encounter synchronization remain explicit per-instance engineering work until implemented and playtested.