Same NaN-comparison short-circuit bug as the texture painter — a
brush with a NaN cursor position would mark every vertex in every
'affected' chunk as full influence and silently rewrite huge
swaths of terrain. Reject upfront in applyBrush.
NaN comparisons return false, so the dist >= radius early-out
would never fire and the falloff path would skip its inner check
too — the brush would paint full strength on every texel in the
chunk. Reject upfront.
Same defensive validation as loadADT — out-of-range tile coords
would generate broken save paths. Also guards against a NaN
baseHeight slider (would propagate into every terrain vertex).
A stray gigantic name/description/author field would inflate the
info JSON past the 16MB unpack cap and make the pack unreadable
via readInfo/unpackZone. Caps mirror the zone manifest limits.
A tileX/tileY outside 0..63 would generate ADT paths the asset
manager refuses, then poison the manifest.tiles entries on save.
Reject upfront with a log message.
The unpacker used info.name verbatim as the destination subdirectory.
A malicious WCP could carry a name like '../etc' or '/usr/bin' to
write extracted files outside destDir. Now slugified to alphanumeric
+ underscore/dash, matching the server module slug rule.
validateChains now also flags quests with no questgiver and no
turn-in NPC — those are unreachable in-game and a common authoring
mistake. Also replaced the O(n²) inner lookup with an O(1)
unordered_map of id → nextId so circular detection scales.
--info-wcp gives counts and totals; --list-wcp gives the full file
listing sorted by path. Useful for spotting missing texture/model
entries before unpacking and shipping a zone.
The previous escape only doubled quotes and backslashes. A quest
description containing a literal newline would emit a multi-line
INSERT that breaks per-line execution scripts; a NUL byte could
prematurely terminate the string in non-length-prefixed clients;
Ctrl-Z is the historical MySQL string terminator on Windows.
Now full MySQL/MariaDB string-literal escape: NUL drops, CR/LF/tab
become \r/\n/\t, Ctrl-Z becomes \Z.
Completes the --info-* family. Reports total/chained quest counts,
reward/item counts, total XP awarded, objective-type breakdown
(kill/collect/talk), and any quest-chain validation errors. Lets
zone authors spot broken chains, missing rewards, and lopsided XP
balance from the command line.
Mirrors --info-creatures and the other format inspectors. Reports
total placement count, M2/WMO breakdown, unique source paths, and
scale range. Useful for spotting empty zones, accidental scale
extremes, or duplicated placements before packing.
Mirrors the existing --info-wom/wob/woc/wot/wcp inspectors. Reports
total spawn count, hostile/questgiver/vendor/trainer flag counts,
behavior breakdown (stationary/wander/patrol), and unique displayId
count. Useful for triaging zone NPC content from the command line.
The previous --validate output told you whether *some* file of each
type existed, which was hard to act on for partially-valid zones.
Now reports the per-format file count and how many failed magic
validation, e.g. 'WOM (12 invalid: 2)' so a zone author can spot
missing or corrupted models without grepping through file listings.
readInfo previously trusted fileCount/infoSize blindly, so a malicious
or corrupted WCP could allocate a 4GB string just to print metadata
via --info-wcp. Same 1M file / 16MB info caps now applied. Also
categorizes .woc collision files (was bucketed under 'other').
Mirrors the load-side sanitize. nlohmann throws on NaN/inf, so
a corrupted in-memory baseHeight or volume would abort the manifest
save and lose all zone-level settings (flags, audio config, etc.).
Same json-serialization-safety fix as for NPC and WOT saves.
Object position/rotation NaN would abort the entire objects.json
save and lose all placement work in the session.
nlohmann::json throws on non-finite serialization, which would
abort the entire creatures.json save and lose every spawn change
in the session. Scrub position/orientation/scale/radii/patrol
floats on the way out so a single corrupt NPC can't kill the
batch save.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
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.
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.
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
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.
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)
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.