w3i format v31 needs patch 1.32 or newer
Lanternfall Defense: 2 (F) to 75 (C) on a copy
A hero defense we built from our own MIT-licensed template to show what a report looks like. The problems in its script are deliberate, and they are ones real maps have. Every number below is the engine's own output.
Verdict
Would not load on 3.0.1. Fixed on a copy.
The script declares IsItemEquipped, a name Warcraft III 3.0.0 gave to a new native, so the game rejects the map. It also leaks a location and a unit group every second and throws away two special effects. Three operations fixed 15 of 23 findings; the rest are listed under "What this did not fix".
The plan that ran
The engine suggested four operations. The experimental one (optimize.scatter) was left out, as a one-click plan would.
- Apply the map doctor's targeted fixes fix.doctor risk: review
- blz-301-rename: 0 site(s), 1 refused
- native-clash-rename: 2 site(s)
- hp-fraction-threshold: 1 site(s)
- local-location-remove: 3 site(s), 1 refused
- Stop handle leaks in code that runs all game leak.hot risk: safe
- OnPickup (line 45): local unit u set to null before 1 exit(s)
- SpawnBoss (line 90): local trigger t set to null before 1 exit(s)
- SpawnWave (line 105): local unit u set to null before 1 exit(s)
- HealAura (line 132): local group g freed with DestroyGroup before 1 exit(s)
- HealAura (line 133): local unit v set to null before 1 exit(s)
- OnCreepDeath (line 147): local unit u set to null before 1 exit(s)
- OnGoalEntered (line 157): local unit u set to null before 1 exit(s)
- Destroy fire-and-forget effects optimize.effects risk: review
- ShowLantern: 1 effect(s) wrapped in DestroyEffect (line 38)
- SpawnWave: 1 effect(s) wrapped in DestroyEffect (line 100)
Runs on
Each build the map was checked against, before and after. "Not tested" means the rule pack has no verdict for that build, not that it passed.
w3i format v31 needs patch 1.32 or newer
Area grades
The score is the lowest weighted area. How each grade is computed is on the scoring page.
- Errors3 found · 3 fixed
- ErrorsThe map declares a function with the same name as a native added in 3.0.0, so it fails to compile or load.FixedBRK-native-name-clash · line 34 in
IsItemEquippedfix.doctor - ErrorsA function, global or local in the map has the name of a native in the target build's prelude.FixedBRK-prelude-name-clash · line 34 in
IsItemEquipped - Errorspjass rejects the script against this build's common.j and Blizzard.j, so the map does not compile.FixedGATE-pjass · line 34
- Severe7 found · 4 fixed
- SevereLeaks in repeating codeStill there, now line 90PERF-L01 · line 126leak.hot
- SevereA location is stored and never removed, so every call leaks one.Still therePERF-L01 · line 102 in
SpawnWavefix.doctor - SevereA location is stored and never removed, so every call leaks one.FixedPERF-L01 · line 127 in
HealAurafix.doctor - SevereA group made by a BJ function is neither destroyed nor flagged with bj_wantDestroyGroup, so it leaks.FixedPERF-L02 · line 128 in
HealAura - SevereA special effect is created and its handle thrown away, so it can never be destroyed.FixedPERF-L04 · line 118 in
SpawnWave - SevereA special effect is created and its handle thrown away, so it can never be destroyed.FixedPERF-L04 · line 40 in
ShowLantern - SevereA handle is created or game state changes inside a GetLocalPlayer branch, which desyncs the other players.Still thereDESYNC-localplayer-handle · line 40 in
ShowLantern - Warnings4 found · 3 fixed
- WarningsUnit orders re-issued many times a secondStill there, now line 100PERF-U01 · line 98optimize.scatter
- WarningsLeaks in per-event codeFixedPERF-L04 · line 38optimize.effects
- WarningsA life threshold below 1 is compared with absolute hit points, so a "25 percent" phase only fires at death.FixedLOGIC-hp-fraction-threshold · line 94 in
SpawnBossfix.doctor - WarningsThe call uses an unprefixed 3.0.0 native whose removal is announced, so the map stops compiling once it goes.FixedBRK-unprefixed-300-call · line 46 in
OnPickupfix.doctor - Info9 found · 5 fixed
- InfoA handle local is not set to null before the function returns.FixedPERF-L05 · line 90 in
SpawnBoss - InfoA handle local is not set to null before the function returns.Still therePERF-L05 · line 102 in
SpawnWave - InfoA handle local is not set to null before the function returns.FixedPERF-L05 · line 127 in
HealAura - InfoA periodic callback creates units or effects inside a loop with no visible per-tick cap.Still therePERF-M02 · line 110 in
SpawnWave - InfoA handle local is not set to null before the function returns.FixedPERF-L05 · line 45 in
OnPickup - InfoA handle local is not set to null before the function returns.FixedPERF-L05 · line 141 in
OnCreepDeath - InfoA handle local is not set to null before the function returns.FixedPERF-L05 · line 151 in
OnGoalEntered - InfoA handle local is not set to null before the function returns.Still therePERF-L05 · line 55 in
SpawnHeroes - InfoA handle local is not set to null before the function returns.Still therePERF-L05 · line 166 in
InitCustomTriggers
What changed
Every line the fix touched in war3map.j (+19 / −5 lines). Nothing else in the archive was modified.
@@ -32,5 +32,5 @@ // Old inventory helper, written before 3.0.0 added a native with the same name.function IsItemEquipped takes unit u, integer itemId returns booleanfunction MD_IsItemEquipped takes unit u, integer itemId returns boolean return GetInventoryIndexOfItemTypeBJ(u, itemId) > 0endfunction@@ -38,5 +38,5 @@function ShowLantern takes player p, real x, real y returns nothing if GetLocalPlayer() == p then call AddSpecialEffect("Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl", x, y) call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl", x, y)) endifendfunction@@ -44,8 +44,9 @@function OnPickup takes nothing returns nothing local unit u = GetTriggerUnit() if IsItemEquipped(u, 'ratf') then if MD_IsItemEquipped(u, 'ratf') then call Say(GetUnitName(u) + " carries the Claws of Attack.") call ShowLantern(GetOwningPlayer(u), GetUnitX(u), GetUnitY(u)) endif set u = nullendfunction @@ -92,6 +93,7 @@ call GroupAddUnit(creeps, boss) call IssuePointOrder(boss, "attack", GOAL_X, GOAL_Y) call TriggerRegisterUnitStateEvent(t, boss, UNIT_STATE_LIFE, LESS_THAN, 0.25) call TriggerRegisterUnitStateEvent(t, boss, UNIT_STATE_LIFE, LESS_THAN, GetUnitState(boss, UNIT_STATE_MAX_LIFE) * 0.25) call TriggerAddAction(t, function OnBossEnraged) set t = nullendfunction @@ -116,5 +118,5 @@ set k = k + 1 endloop call AddSpecialEffectLoc("Abilities\\Spells\\Human\\MassTeleport\\MassTeleportCaster.mdl", spawn) call DestroyEffect(AddSpecialEffectLoc("Abilities\\Spells\\Human\\MassTeleport\\MassTeleportCaster.mdl", spawn)) call Say("Wave " + I2S(wave) + " of " + I2S(WAVES)) if wave >= WAVES then@@ -122,4 +124,9 @@ call DestroyTimer(GetExpiredTimer()) endif call RemoveLocation(spawn) set spawn = null call RemoveLocation(goal) set u = null set goal = nullendfunction @@ -136,4 +143,9 @@ endif endloop call RemoveLocation(c) call DestroyGroup(g) set g = null set v = null set c = nullendfunction @@ -146,4 +158,5 @@ endif endif set u = nullendfunction @@ -161,4 +174,5 @@ endif endif set u = nullendfunction
Proof ladder
What was checked on the fixed copy. A hollow seal means the check was not run for this example, so we do not claim it.
- L0 Your original is untouchedThe input file's SHA-256 is identical before and after the run.
- L1 The copy opens and nothing was lostRe-opened from scratch; only war3map.j changed.
- L2 The script still compilespjass on the output. Errors before and after: 3.0.1 0->0, 1.26.0 0->0
- L3 The re-scan finds nothing newThe fixed copy was scanned again; no finding appeared that was not there before.
- L4 Opens in the World EditorNot run for this example (needs the editor harness on a lab PC).
- L5 Plays in a real gameNot run in our lab for this example.
What this did not fix
- Severe A location is stored and never removed, so every call leaks one.
The leak engine refused this site: the location is assigned inside a loop, where a mechanical fix could free a point that is still in use. - Severe A handle is created or game state changes inside a GetLocalPlayer branch, which desyncs the other players.
Needs a person: the effect must be created for every player and only its model path chosen per player. The desync guide shows the pattern. - Warnings Unit orders re-issued many times a second
optimize.scatter could address it, but it is rated experimental, so it was left out of the plan. - Warnings Leaks in repeating code
This aggregate finding now points at line 90: the boss trigger is created each time the boss spawns, which happens once per game here, so it was left alone. - Info A handle local is not set to null before the function returns.
Locals set once at start-up, or a location the engine would not touch inside a loop. Harmless in this map. - Info A periodic callback creates units or effects inside a loop with no visible per-tick cap.
A wave loop that creates units without a per-tick cap. That is the design of a wave map. - Errors Does not run on 1.26 or 1.31
The map is saved in the w3i format v31, which needs patch 1.32 or newer. We do not down-convert maps.
Map facts
- Map
- Lanternfall Defense (example, ours)
- Players
- 4
- Tileset
- Lordaeron Summer
- Script
- JASS, 243 lines, 19 functions
- Size
- 18,100 bytes before, 18,237 after
- SHA-256
- 93955fd07b649749… → d72f1773e80e8725…
- Terrain
- From the MIT-licensed wc3-ts-template
Want this for your map?
Send it with a request: post it on our Discord with a link to the map's page. The community votes on Discord, and the top request is saved every week.