Commit graph

380 commits

Author SHA1 Message Date
Kelsi
9ef04414d1 fix(wot): scrub NaN/inf in doodad/WMO placements at save time
nlohmann::json serialization throws on NaN/inf floats, which would
abort the entire WOT save and leave the user with an unsaved zone
state. Scrub on the way out so a single corrupted placement can't
take down the whole save.
2026-05-06 07:03:02 -07:00
Kelsi
826d218226 fix(zone): cap manifest strings at AzerothCore SQL limits
map_dbc.MapName / area_table_dbc.AreaName are varchar(100). Edited
zone.json could carry longer strings that would either fail the
INSERT or silently truncate. Cap mapName/displayName at 100,
biome at 64, description at 4096.
2026-05-06 07:02:09 -07:00
Kelsi
2119546a7a fix(npc): truncate over-long NPC name/modelPath at SQL limits
creature_template.name is varchar(100) in AzerothCore. Edited
creature JSON could carry longer names that would either fail
the INSERT or silently truncate. Cap name at 100 and modelPath
at 1024 on load.
2026-05-06 07:01:25 -07:00
Kelsi
280fe1e6e8 fix(quest): truncate over-long title/desc/completion to SQL limits
quest_template.LogTitle is varchar(200) in AzerothCore. Edited
quest JSON could carry longer strings that would either fail the
INSERT or silently truncate at the server. Cap title at 200 chars
and the longer text fields at 8KB on load.
2026-05-06 07:01:00 -07:00
Kelsi
6651eccf3b feat(editor): add --info-wot CLI for inspecting terrain metadata
Mirrors the existing --info-wom/--info-wob/--info-woc/--info-wcp
inspectors. Reports the tile coord, populated chunks, layer count,
water count, texture/doodad/WMO counts, and computed height range
across all chunks. Useful for triaging zones from the command line
without opening the GUI.
2026-05-06 06:58:34 -07:00
Kelsi
d127053e21 fix(objects): bound populateBiome density and asset scale ranges
Same defensive guards as scatter: cap per-asset count at 50k to
prevent editor freeze under a high-density biome, and ensure scale
range preconditions (a<b, both positive) so distScale construction
doesn't undermine validity.
2026-05-06 06:56:32 -07:00
Kelsi
c1af157587 fix(objects): guard ObjectPlacer::scatter against bad inputs
Same defensive guards now applied to NPC scatter: reject NaN/inf
center/radius, cap count at 100k (prevents editor freeze on huge
inputs), and ensure minScale < maxScale so uniform_real_distribution
preconditions hold.
2026-05-06 06:55:43 -07:00
Kelsi
10e77f1c2e fix(npc): guard NpcSpawner::scatter against zero radius and bad inputs
A radius of 0 would either throw from the uniform distribution
constructor (uniform_real_distribution requires a < b) or divide
by zero in the sqrt-based area-uniform sampler. Also reject NaN
center, non-positive radius, and absurdly large counts (>10k)
which would freeze the editor on placement.
2026-05-06 06:54:48 -07:00
Kelsi
d96d040831 fix(sql): substitute generic displayId for NPCs with displayId=0
A creature_template row with modelid1=0 spawns an invisible NPC
in-game. Fall back to 11707 (a generic humanoid) on export so
"Creature"-named placeholder spawns are at least usable; the user
can edit the displayId after.
2026-05-06 06:53:47 -07:00
Kelsi
aa9ef6f2ca fix(adt): scrub NaN/inf floats at write time
ADTWriter::writeFloat now coerces non-finite values to 0 before
emitting bits, so a stray NaN in a chunk position, MDDF rotation,
or MODF extent can't leak into the saved ADT and produce invisible
terrain or off-map placements after reload.
2026-05-06 06:49:51 -07:00
Kelsi
bc1839d9ab fix(zone): drop out-of-range tile coords on manifest load
WoW's tile grid is 64x64. A tile (200,200) entry would generate an
ADT filename the loader rejects and silently leave the zone with no
terrain. Drop bad entries instead.
2026-05-06 06:48:28 -07:00
Kelsi
4a98dd7a2c fix(editor): slugify mapName for module dir + conf keys
A zone named with spaces or punctuation (e.g. "My Zone!") used to
produce a module directory path "mod_wowee_My Zone!/" and conf
keys like "Wowee.My Zone!.Enabled" which AzerothCore's config
parser rejects. The slug is now stripped to [A-Za-z0-9_-], with
spaces/slashes mapped to underscores. SQL VALUES still use the
raw display text via SQLExporter::escape.
2026-05-06 06:47:43 -07:00
Kelsi
a8464fc367 fix(editor): escape user strings in server module map/zone/tele SQL
The map_dbc, area_table_dbc, and game_tele INSERTs previously
embedded mapName/displayName/manifest.mapName as raw strings — a
zone called "King's Land" or anything containing a single quote
would emit malformed SQL that AzerothCore would reject. Promotes
the existing escapeSql helper to a public SQLExporter::escape and
uses it in all three INSERTs.
2026-05-06 06:46:58 -07:00
Kelsi
cc1e1cb7fa fix(editor): cap stamp vertex count and skip NaN samples on load
A malformed stamp JSON could carry millions of entries (would OOM)
or NaN dx/dy/height (would propagate through brush blends and leave
permanent holes in the heightmap).
2026-05-06 06:45:41 -07:00
Kelsi
0a5583310c fix(editor): clamp project mapId and zone tile coords on load
A hand-edited project.json could carry a 0 or massively-OOR mapId
(would break DBC indexing) or tile coords outside 0..63 (would
produce garbage ADT filenames). Defaults restore safe values.
2026-05-06 06:45:09 -07:00
Kelsi
721efb2ecb fix(zone): clamp manifest audio volumes and reject NaN baseHeight on load
A user-edited zone.json could carry NaN/inf or out-of-range volumes
which would silently corrupt terrain elevation or produce silent /
clipping audio. Now baseHeight defaults back to 100 if non-finite,
and music/ambience volumes clamp to [0,1] with NaN fallbacks.
2026-05-06 06:44:30 -07:00
Kelsi
71378c20ff fix(whm): exportOpen sanitizes heights + caps alphaSize at 64KB on save
Sanitize at write time too, not just on load. A mid-edit NaN spike
(e.g. brush operation that produced NaN before being committed) would
otherwise be persisted into the WHM and require the load-time guard
to clean it up forever after. AlphaSize is also capped at 64KB to
match the loader cap.
2026-05-06 06:32:24 -07:00
Kelsi
2df49c725f fix(editor): texture exporter validates BLP image before passing to stbi
Three new sanity checks before stbi_write_png:
  - dimensions <=0 or >8K rejected (matches PNG override loader cap)
  - data buffer must be >= width * height * 4 bytes (corrupt BLP could
    have mismatched dimensions vs data length, and stbi reads off the
    end of the buffer otherwise)
Skips with warning rather than crashing the exporter mid-zone.
2026-05-06 06:29:26 -07:00
Kelsi
d3a85776f8 fix(content-pack): packZone truncates path length + skips files >4GB
Two write-side guards mirroring the unpack-side ones:
  - Path length truncated to 1KB (matches unpack cap; long paths would
    silently wrap u16 and corrupt the pack)
  - Files >4GB on disk skipped with a zero-length entry rather than
    silently producing a truncated dataSize that overflows uint32
2026-05-06 06:27:04 -07:00
Kelsi
bbfc364119 fix(editor): texture exporter rejects path-traversal in source M2/WMO texture paths
Texture paths come from M2/WMO files which a malicious zone author
could craft to include '..' or absolute paths. Without this check,
exporting such a zone would write PNGs outside outputDir/textures/
and clobber sibling export files.
2026-05-06 06:12:11 -07:00
Kelsi
15bf77c616 fix(editor): NPC stat field bounds across the board
Across-the-board NPC stat sanity on JSON load:
  - level: 0 -> 1, >255 -> 255
  - health: 0 -> 1
  - maxDmg < minDmg -> bumped to minDmg
  - behavior enum: clamp to 0..3 range
  - wander/aggro/leash radius: NaN/inf -> default; wander capped at 1000
  - respawnTimeMs: <1s -> 1s, >24h -> 24h
2026-05-06 06:02:10 -07:00
Kelsi
f5fc23e003 fix(editor): quest level + reward sanity bounds + item slot cap
Rounds out the quest load guards:
  - requiredLevel: 0 -> 1 (no level-0 quests), >255 -> 80 (typo guard)
  - reward.xp: cap 1M
  - reward.gold: cap 10000
  - reward.silver/copper: cap 99 (server overflows otherwise)
  - reward.itemRewards: cap 6 entries (matches WoW quest_template
    RewardItemId[1..6] slot capacity)
2026-05-06 05:59:05 -07:00
Kelsi
c4c8d9e7ed fix(editor): quest objective load clamps type, count, and per-quest size
Three guards on quest objective loading from JSON:
  - type out of QuestObjectiveType range (0..5) -> defaults to 0 (Kill)
  - targetCount of 0 -> 1 (no-op objectives are nonsense)
  - targetCount > 1000 -> 1000 (typo guard, biggest legit WoW quest is ~100)
  - >10 objectives per quest -> dropped (matches SQL slot capacity, also
    bounds per-quest memory)
2026-05-06 05:56:11 -07:00
Kelsi
62b668e898 fix(content-pack): cap WCP per-entry path length + catch backslash/drive traversal
Path length cap (1KB) — uint16 can hold 64KB but no real zone path
should exceed 256 chars. Path traversal check extended to also catch:
  - Windows backslash absolute paths ('\' at start)
  - Windows drive-prefixed paths ('C:\...')
A WCP downloaded from a forum and unpacked on Windows would otherwise
have these vectors open.
2026-05-06 05:54:31 -07:00
Kelsi
c05d421c29 fix(content-pack): reject malicious WCP headers (oversize counts + path traversal)
Three security/robustness guards on unpackZone:
1. fileCount > 1M or infoSize > 16MB rejected upfront — would OOM on
   the next allocation.
2. Per-file dataSize > 256MB rejected — single malicious entry could
   exhaust memory mid-extraction.
3. Path traversal ('..' or absolute paths) rejected — would write
   outside destDir/<zoneName>/ and clobber system files.
WCPs are user-shareable archives, so a hostile pack downloaded from a
forum should not be able to OOM the editor or write to /etc.
2026-05-06 05:38:53 -07:00
Kelsi
2b02ca6b58 fix(editor): patrol waypoint NaN guard + 10-minute wait-time cap
NaN positions in waypoints would teleport the creature to chaos coords
mid-patrol. Cap wait time at 600000ms (10 min) — prevents obvious typos
(e.g. 24h = 86400000) from producing a creature that effectively never
moves.
2026-05-06 05:05:39 -07:00
Kelsi
604d29d375 fix(editor): NPC + object position/rotation NaN guards on JSON load
Mirrors the recent scale guards. NaN/inf positions or rotations make
the M2 renderer's matrix math produce chaos-shaped instances or crash
during normal computation. Now both fields are reset to zero on load
when any component is non-finite.
2026-05-06 05:04:25 -07:00
Kelsi
d525318e9c fix(editor): normalise NPC orientation to [0,360) and guard scale against NaN
Orientation values from edited JSON could be negative or wrap multiple
revolutions; now normalised once at load. Scale was already clamped
on small-positive but didn't reject NaN/inf — now rejects both with the
same defensive check object_placer just got.
2026-05-06 05:02:24 -07:00
Kelsi
1979d921a7 fix(editor): clamp PlacedObject scale to 1.0 when JSON value is invalid
Same defensive check the WoB doodad load just got — guards against
corrupted/partial-write JSON where scale ends up 0/NaN/inf. Without
this an invisible (scale=0) or crashed (NaN matrix) placement could
silently bork a loaded zone.
2026-05-06 04:59:28 -07:00
Kelsi
70e48640d8 feat(editor): surface quest-chain validation issues to user via toast
validateChains warnings were only going to the log file. Most users
don't tail the log while editing, so a broken chain (quest pointing at
a deleted nextQuestId, circular dependency) would only be discovered
when testing in-game. Now also shows a 5s toast with the issue count.
2026-05-06 04:54:51 -07:00
Kelsi
df1eed1c42 fix(editor): preserve CreatureSpawn.id across JSON save/load
Quest links (questGiverNpcId, turnInNpcId, KillCreature targetName) all
key off CreatureSpawn.id, but loadFromFile always assigned new ids via
nextId(). So saving and reloading would silently break every quest hook
in the zone. Now JSON stores id, the loader reads it back when present
(legacy files fall back to nextId()), and idCounter_ is bumped past
loaded values to prevent future collisions. Same fix as the recent
PlacedObject.uniqueId one.
2026-05-06 04:53:09 -07:00
Kelsi
00717543a8 feat(editor): preset selection auto-fills sensible AzerothCore faction
When a user picks a creature preset, faction stays 0 (server treats this
as 'use template') unless the user already typed a value. Now defaults
based on the preset category:
  Critter        -> 250 (critter, indifferent to all)
  hostile preset -> 14  (monster, hostile to all)
  friendly preset -> 35  (friendly to all)
Means picking 'Wolf' from the preset list immediately produces a hostile
NPC that actually attacks players in-game without further configuration.
2026-05-06 04:48:30 -07:00
Kelsi
70366dc5f6 fix(sql): wander_distance is 0 for non-Wander NPCs
Stationary and Patrol creatures should have wander_distance=0; only
Wander behaviour uses it. Previously the editor's wanderRadius template
default of 10.0 was being written for every spawn, making stationary
guards drift around in-game.
2026-05-06 04:46:57 -07:00
Kelsi
2d41e560f2 docs(format-spec): add quick-reference table mapping formats to replaced Blizzard formats 2026-05-06 04:44:24 -07:00
Kelsi
b1c89823d4 docs(format-spec): tighten WHM + WOM layouts with exact field sizes
WHM: clarify chunkCount=256, vertsPerChunk=145 are fixed, baseHeight is
a float, heights are 145 floats. WOM: spell out boundRadius+min+max as
separate fields (was 'bounds(28)'), nameLen prefix, indices uint32, and
texPath length-prefixing. WOM2 trailing block reorganized into a single
list with bone+anim sequence shown as one block. Reading the spec now
yields a working parser without source-diving.
2026-05-06 04:42:04 -07:00
Kelsi
15630f7723 docs(format-spec): tighten WOB layout description with exact field sizes/types
The previous WOB section was loose ('bounds(4)' was actually boundRadius;
group size annotations missed the prefixed string lengths). Updated to
show every byte: 4-byte field sizes, 12-byte vec3s, 2-byte length
prefixes, 48-byte interleaved vertices. Reverse-engineering a WOB from
the spec is now possible without reading the source.
2026-05-06 04:38:15 -07:00
Kelsi
079ff5bfb5 feat(editor): --info-wcp CLI prints content pack metadata
Reports name/author/description/version/format/mapId, total file count,
per-category breakdown (terrain/model/building/texture/data), and total
on-disk bytes. Useful for inspecting third-party WCPs before importing
or for sanity-checking your own exports.
2026-05-06 04:36:40 -07:00
Kelsi
8d78b5f8c6 fix(content-pack): unpackZone now creates the zone subdirectory
packZone stores files relative to the zone subdirectory (e.g. just
'MyZone_32_32.adt'), so unpacking to 'custom_zones/' produced files at
'custom_zones/MyZone_32_32.adt' — without the zone subdir the loader
expects. Now reads the info JSON to extract the zone name and unpacks
to 'custom_zones/<zoneName>/' so imported zones load correctly.
2026-05-06 04:32:28 -07:00
Kelsi
a0e363f706 feat(editor): WCP export toast reports file size in MB 2026-05-06 04:30:13 -07:00
Kelsi
c0ae924fc7 fix(editor): NPC default scale 1.0 (was 3.0) to match AzerothCore defaults
The CreatureSpawn struct default of 3.0 made every newly placed NPC
appear as an oversized 3x-scale creature, very obviously not what users
wanted. Existing JSON spawn files load their stored scale unchanged
(only impacts newly placed templates).
2026-05-06 04:27:22 -07:00
Kelsi
4d11949048 fix(editor): preserve PlacedObject uniqueId across JSON save/load
uniqueId was always regenerated on load, so re-saving the same zone
produced a different uniqueId per placement each time. Since
syncToTerrain copies obj.uniqueId into MDDF/MODF, the ADT also rotated
uniqueIds across cycles. Now JSON stores uniqueId, the loader reads it
back when present (falling back to nextUniqueId() for legacy files),
and uniqueIdCounter_ is bumped past any loaded value so future
placements never collide.
2026-05-06 04:23:39 -07:00
Kelsi
882321863a feat(editor): NPC template + selected-NPC editor expose Respawn (s) input
The respawnTimeMs field was loaded/saved/exported but never editable
through the UI. Added a DragFloat showing seconds (range 5-86400) in
both the template and the selected-NPC editors. SQL export already
divides by 1000 for AzerothCore's spawntimesecs column.
2026-05-06 04:21:42 -07:00
Kelsi
8d006b6b86 feat(editor): selected-NPC editor gains Mana / Min Dmg / Max Dmg / Armor inputs
Last batch of stat fields missing from the selected-NPC editor. Now any
property a user could set on the template can also be edited on an
already-placed NPC, without removing and re-placing.
2026-05-06 04:18:25 -07:00
Kelsi
a7ab2756d6 feat(editor): selected-NPC editor gains role flag checkboxes
Adds Hostile/Quest/Vendor/Inn/Train/Bank/Auc/Repair/Flight checkboxes
to the selected-NPC editor matching the template editor's set. Lets
users toggle these on already-placed NPCs without removing and
re-placing them.
2026-05-06 04:17:31 -07:00
Kelsi
9625201952 feat(editor): selected-NPC editor gains Faction input (parity with template) 2026-05-06 04:15:39 -07:00
Kelsi
4e01dd5553 feat(editor): NPC template gains Faction ID input with common-value tooltip
The CreatureSpawn struct has a faction field that was already exported
to creature_template.faction but wasn't editable. Added an InputInt with
a tooltip listing the common AzerothCore FactionTemplate IDs (Stormwind,
Monster, Beast, Friendly, Critter, etc.) so users can pick the right
hostility/disposition without referencing the DBC manually.
2026-05-06 04:14:33 -07:00
Kelsi
da2e7a4133 feat(editor): viewport WOM/WOB lookups also probe per-zone roots
EditorViewport gains setActiveMapName() so rebuildObjects can pass
per-zone prefixes (output/<map>/models|buildings/, custom_zones/<map>/...)
to tryLoadByGamePath. EditorApp wires it from loadADT, loadWMOInstance,
and createNewTerrain. Now the editor's preview mirrors the main game's
priority: per-zone WOM/WOB beats global custom_zones/, beats game data.
2026-05-06 04:13:03 -07:00
Kelsi
db068d480b feat(wob): tryLoadByGamePath helper, used by editor + terrain_manager
Mirrors the WOM tryLoadByGamePath API: probes custom_zones/buildings/ +
output/buildings/ by default, with optional extraPrefixes (e.g. per-zone
output/<map>/buildings/) checked first. Both the editor and the main
game's terrain_manager now use the helper, removing duplicate inline
lookup loops in two more places.
2026-05-06 04:10:12 -07:00
Kelsi
99aaab3aa8 feat(editor): add Trainer/Banker/Auctioneer/Repair NPC flags + SQL export
CreatureSpawn struct gains four AzerothCore-standard NPC flag bits:
  trainer    -> npcflag 0x10
  repair     -> npcflag 0x1000
  banker     -> npcflag 0x20000
  auctioneer -> npcflag 0x200000
Saved/loaded via the JSON spawn file, exported to creature_template.npcflag,
exposed as checkboxes in the NPC template panel. Lets users build full
city NPCs (city auctioneer, weapon trainer, etc.) without dropping to SQL.
2026-05-06 04:03:23 -07:00
Kelsi
bc6e60c6e9 polish(editor): placement scale slider matches selected-object range (0.1-50) 2026-05-06 04:00:07 -07:00