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:
- restores
ROM_Global.bakandObjectEdit.bakunder unique temporary names; - marks both restored databases read-only;
- exports one zone to JSON;
- drops both temporary databases in
finallycleanup; - 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
texconvfor 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
ImageObjectDBhead, 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=3plus a positive-levelmodel/characterdisplay -> boss;sex=3on 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.pngfor 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:
PopulationDefinitionMapentities;- 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_guidsas 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.