Commit graph

3742 commits

Author SHA1 Message Date
Kelsi
0f15d0f3a0 fix(terrain): reject NaN rays in raycastTerrain
Without this, the AABB tests divide by ray.direction components and
NaN propagates through tmin/tmax into the triangle intersection,
returning undefined behavior at the hit position.
2026-05-06 07:58:56 -07:00
Kelsi
493cb68ddc fix(viewport): defensive NaN checks in patrol path ribbon
len < 0.001f returns false for NaN — same short-circuit class of
bug as elsewhere. Reject non-finite endpoints upfront and double-
check the computed length before dividing.
2026-05-06 07:57:54 -07:00
Kelsi
ba017193db fix(viewport): skip NPCs with NaN position in marker update
Same defensive pattern as updateObjectMarkers — non-finite NPC
position would produce NaN vertex positions and Vulkan would drop
the entire NPC marker batch, hiding every NPC marker in the zone.
2026-05-06 07:57:19 -07:00
Kelsi
b1cfef8264 fix(markers): skip objects with NaN position/scale on update
A non-finite object transform would produce NaN vertex positions
in the marker mesh — Vulkan validation flags it and dropping the
entire batch leaves all markers invisible. Skip the bad object
instead so the rest of the markers still render.
2026-05-06 07:56:26 -07:00
Kelsi
45dabaff44 fix(wcp): normalize separators before traversal check on unpack
Older WCP files packed on Windows (before pack-side normalization
was added) carry backslash separators. Normalize to '/' first so
the unpack works on any platform — and so the traversal check sees
a consistent canonical form (no more '\' special case).
2026-05-06 07:54:54 -07:00
Kelsi
439d1381f0 fix(editor): catch NaN-from-normalize when camera flies to a target
When the camera looks straight up/down, projecting forward onto XY
gives a zero vector — glm::normalize then returns NaN. The original
length<0.001 fallback ran AFTER the divide-by-zero, and NaN length
< 0.001 is false (NaN comparisons return false), so the fallback
never fired. Length-check the source before normalizing.
2026-05-06 07:53:41 -07:00
Kelsi
130aa34d73 fix(viewport): hide path preview on zero-length input
start == end would call glm::normalize on a zero vector, producing
NaN dir/perp and NaN ribbon vertex positions. Vulkan would either
drop the draw silently or trip a validation error. Hide the preview
when the segment is degenerate.
2026-05-06 07:53:07 -07:00
Kelsi
4babaebf86 fix(stamp): scrub NaN samples at save time
Symmetric with the load-side scrub. Without this, a stamp captured
on terrain that had a NaN mid-edit would throw on serialize and
abort the whole save.
2026-05-06 07:51:56 -07:00
Kelsi
ebb7e0f831 fix(wcp): normalize path separators on pack for cross-platform reads
WCP packs created on Windows would store paths with backslashes;
unpack on Linux/macOS would either fail the path-traversal check
('\' treated as absolute prefix) or land each file as a single
opaque filename rather than a directory tree. Normalize to '/' on
write so the format is portable in both directions.
2026-05-06 07:50:10 -07:00
Kelsi
ad65b2ad36 test(editor): add 4 quest validateChains tests + rename to test_editor_units
Tests cover:
- non-existent nextQuestId is flagged
- orphan quests (no questgiver, no turn-in) are flagged
- turn-in only quest is accepted (auto-completed quest pattern)
- circular chain is detected

Renamed test_sql_escape → test_editor_units since the file now
houses both SQL escape and quest validation tests.
2026-05-06 07:49:26 -07:00
Kelsi
ea713ae994 test(sql): add escape unit tests
5 tests covering: doubled single quotes (King's Land case),
backslash escaping, ordinary text passthrough, control characters
(NUL drop, CR/LF/tab/Ctrl-Z escape sequences), and combined
escapes. Locks in the recent escape expansion that fixed the
multi-line INSERT bug.
2026-05-06 07:47:58 -07:00
Kelsi
5366c53734 fix(objects): NaN guards on transform deltas
A NaN move/rotate/scale delta would poison every selected object's
transform permanently and produce NaN model matrices in the
renderer. Reject upfront.
2026-05-06 07:45:26 -07:00
Kelsi
2c5710b910 fix(terrain): NaN guards + zero-length checks on river/road/ridge generators
Same defensive pattern as paintAlongPath. carveRiver, flattenRoad,
and createRidge all called glm::normalize on a possibly-zero
direction vector, then divided by lineLen later. NaN endpoints
short-circuited dist comparisons and applied the height delta
to every vertex on every chunk.
2026-05-06 07:43:39 -07:00
Kelsi
869cee70b1 fix(painter): reject NaN endpoints and zero-length lines in paintAlongPath
Two bugs:
1. NaN start/end produced NaN distances that the chunk-skip check
   (dist > width + 40) treated as 'always within range', so every
   chunk got painted.
2. Zero-length line caused glm::normalize to return NaN; same
   downstream effect.

Compute lineDir manually after the length check so we never hit
the divide-by-zero path.
2026-05-06 07:41:58 -07:00
Kelsi
7e48658ab1 fix(terrain): NaN guards on createCrater and createMesa
Same defensive pattern as createHill — NaN center/radius would
short-circuit dist comparisons and apply the height delta to every
vertex on every chunk. Reject upfront.
2026-05-06 07:40:29 -07:00
Kelsi
f484b742db fix(editor): NaN guards on flattenAroundSelected and createHill
Same NaN-comparison short-circuit pattern: dist >= radius is false
when dist is NaN, so the loop body would run for every vertex and
write garbage heights / bend the terrain unbounded.
2026-05-06 07:39:35 -07:00
Kelsi
12bcd0ef8c fix(brush): defensive guards in EditorBrush::getInfluence
Three issues:
1. NaN distance returned 1.0 (full influence) because distance >=
   radius is false for NaN; the inner-radius check then returned 1.
2. Non-positive radius would divide by zero in the t computation.
3. falloff = 0 produces division by zero in the outer falloff path.

Also clamps falloff to [0,1] so a slider extreme can't break the math.
2026-05-06 07:38:50 -07:00
Kelsi
bd6e5fe3de fix(brush): reject NaN/non-positive brush settings before sculpt apply
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.
2026-05-06 07:37:54 -07:00
Kelsi
9207d54f20 fix(painter): reject NaN brush center / non-positive radius
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.
2026-05-06 07:37:11 -07:00
Kelsi
891bb711a0 fix(editor): bound tile coords + NaN-guard baseHeight in createNewTerrain
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).
2026-05-06 07:36:32 -07:00
Kelsi
d9d0797b7f fix(wcp): cap info JSON string lengths at pack time
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.
2026-05-06 07:35:44 -07:00
Kelsi
efd0a6de29 fix(editor): reject out-of-range tile coords in loadADT
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.
2026-05-06 07:34:58 -07:00
Kelsi
237cc67b24 fix(wcp): sanitize zone name before using it as a directory path
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.
2026-05-06 07:34:09 -07:00
Kelsi
6b06bd07f9 feat(quest): detect orphan quests + speed up chain validation
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.
2026-05-06 07:33:31 -07:00
Kelsi
778f4aca3e fix(wob): warn + clamp uint32 indices on WMO conversion
WoB allows uint32 indices but WMO format is uint16. The previous
static_cast would silently wrap a >65k index into a wrong-but-
valid value — producing visible mis-stitched triangles in the
renderer. Now log a warning once per group and clamp to 0
(degenerate triangle) so the bug is visible.
2026-05-06 07:32:07 -07:00
Kelsi
9d944ed2f9 feat(editor): add --list-wcp CLI to print every file in a WCP archive
--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.
2026-05-06 07:30:26 -07:00
Kelsi
9cb8b160ef fix(wob): clamp out-of-range indices at save time
Symmetric with the load-side index clamp.
2026-05-06 07:29:29 -07:00
Kelsi
dbb3be86f2 fix(wom): clamp out-of-range indices at save time
Symmetric with the load-side index clamp. A WoM whose indices
reference past the vertex buffer would crash the GPU vertex shader;
the save side now clamps to 0 (degenerate triangle) so the file
matches what the load guard would produce.
2026-05-06 07:28:31 -07:00
Kelsi
a0895fabdf fix(wom): drop invalid batches at save time
Symmetric with the load-side validation. A WOM3 batch whose
indexStart+indexCount exceeds the index buffer, or whose texture
index points past the texture array, would otherwise emit an
invalid file that the load-time guard then has to drop.

Filter at save instead so the on-disk file stays compact and
self-consistent.
2026-05-06 07:27:33 -07:00
Kelsi
c00bfab1a5 fix(wom): scrub NaN bone pivots and clamp parent indices at save time
Symmetric with the existing load-side guards. A bone with a NaN
pivot poisons its child bones' world matrices; an out-of-range
parent index would walk past the bones array during evaluation.
2026-05-06 07:26:26 -07:00
Kelsi
3b1fad7be9 fix(wob): scrub NaN/inf group vertices at save time
Symmetric scrub on the WoB save path matching the existing load
guard. A manually-constructed WoweeBuilding with NaN vertices
would otherwise persist them and force the load-time scrub to
re-clean the same data on every reload.
2026-05-06 07:24:51 -07:00
Kelsi
6347e78d72 fix(wom): scrub NaN/inf bone keyframes at save time
The load side already scrubs keyframe translation/rotation/scale
floats, but fromM2 → save → load is the typical path: a corrupt
M2 source would write NaN keyframes that the load-time guard would
have to clean up on every subsequent load. Symmetric scrub here
ensures the file is clean from the start.

movingSpeed also defaults to 0 if non-finite (matches load).
2026-05-06 07:23:35 -07:00
Kelsi
7a03011625 fix(sql): expand string escape to handle NUL, CR/LF/tab, Ctrl-Z
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.
2026-05-06 07:22:04 -07:00
Kelsi
19e479a8ff test(open-formats): add WOB doodad NaN scrub test
Verifies the recent WOB doodad-transform sanitize. A WOB with NaN
position/rotation/scale on a doodad should load with the doodad
zeroed out (position/rotation 0, scale 1). Prevents regressing
the GPU crash that NaN model matrices would cause.
2026-05-06 07:21:20 -07:00
Kelsi
a16689f7fa test(open-formats): add hardening tests + fix existing liquidType assertion
The existing WOT round-trip test asserted liquidType==5; the recent
commit clamped >3 to 0, so the test would have failed once rebuilt.
Updated the test data to use type=3 (slime, in valid range).

Adds 5 new hardening test cases:
- WOT clamps OOR tileX/tileY to 32
- WOT clamps OOR water liquidType to 0
- WOC load skips degenerate triangles
- WOC rejects > 2M triangle headers
- WOC clamps OOR tileX/tileY to 32

Catches regressions in the defensive bounds added across recent
commits.
2026-05-06 07:19:40 -07:00
Kelsi
a2a554dff7 feat(editor): add --info-quests CLI for quests.json summary
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.
2026-05-06 07:16:27 -07:00
Kelsi
df2027463a feat(editor): add --info-objects CLI for objects.json summary
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.
2026-05-06 07:15:40 -07:00
Kelsi
93e67ae31b feat(editor): add --info-creatures CLI for creature.json summary
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.
2026-05-06 07:14:42 -07:00
Kelsi
d1f347a9c1 fix(wob): sanitize doodad transform during fromWMO conversion
A WMO with a NaN doodad quaternion would produce NaN euler angles
through glm::eulerAngles() and persist them into the WoB. Identity
quaternion fallback for a non-finite source, plus NaN scrub on
position/rotation/scale separately so the converted WOB is always
load-safe.
2026-05-06 07:13:49 -07:00
Kelsi
5af4bba556 feat(validate): report file counts and per-format invalid totals
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.
2026-05-06 07:12:04 -07:00
Kelsi
7e2dc4ec1d fix(wcp): apply unpack-side header bounds to readInfo + categorize .woc
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').
2026-05-06 07:10:53 -07:00
Kelsi
ed749b9afa fix(wot): clamp liquid type to known range on load
WoW liquid types are 0=water/1=ocean/2=magma/3=slime. A user-edited
WOT could carry an out-of-range value that the editor renderer
silently maps to plain water but the server treats as undefined.
2026-05-06 07:09:48 -07:00
Kelsi
1c1250a37c fix(wot): scrub NaN water height on load
A WOT water entry with non-finite height would push NaN through
the water mesh builder and produce a degenerate Vulkan draw
(invisible water at best, GPU hang at worst).
2026-05-06 07:08:50 -07:00
Kelsi
b8e2d08b17 fix(wob): scrub NaN/inf doodad transforms at save time
Same scrub now applied symmetrically on the save side so a
corrupted in-memory doodad transform can't be persisted into a
WOB and then have to be cleaned up on every subsequent load.
2026-05-06 07:07:21 -07:00
Kelsi
5d78cbb81d fix(wob): scrub NaN/inf doodad position+rotation on load
Already had a guard for scale; extending to position/rotation too.
A WoB with non-finite doodad transforms produces NaN model matrices
that propagate into the M2 instance SSBO and crash the GPU.
2026-05-06 07:06:25 -07:00
Kelsi
1135e499d7 fix(zone): scrub NaN/inf manifest floats at save time
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.).
2026-05-06 07:05:13 -07:00
Kelsi
49feb8b9f6 fix(objects): scrub NaN/inf floats at save time
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.
2026-05-06 07:04:27 -07:00
Kelsi
999388b805 fix(npc): scrub NaN/inf floats at save time
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.
2026-05-06 07:03:51 -07:00
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