• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] Specific unit death crashes after game is loaded

Status
Not open for further replies.
Level 10
Joined
May 31, 2019
Messages
139
Recently I stumbled on a crash in my project, but in the fortunate position of having saved shortly before it occurred, and so I was able to reliably reproduce it.

It was occurring when a specific Peasant was killed. There's nothing unique about this peasant that the map cares about. As in, it's not pre-placed, and there's no trigger or function in the map that references it. It was simply trained by the AI to do AI things.

An otherwise identical Peasant that will get killed right after the the game is loaded (but before the crashy peasant will die) dies without any crash or other issue.

Yet, in the map instance where this crash happened, every time the game was loaded, the same Peasant will crash the game upon death.

I believe this crash is something that was not going to happen in the game's initial (not 'loaded') state, because
  • I played for a little bit after saving without crashing, and the Peasant in question ran into my army at the same point regardless.
  • I've been able to play through the map many times before without saving/loading and the crash never happened.
  • Previously I remember experiencing other random crashes after saving and loading my game in other missions, but I just chalked it up as a 'random event' at the time without investigating it further.

It's a bit tricky, since this does not happen every game either. I'm not sure under what conditions a 'crash-when-dying' unit will appear, but when there is such a unit, it can be reliably crash the game every time if a game is loaded and you know which unit it is.

This is function in my modified blizzard.j that is called every time a unit dies. It's a series of checks to determine if anything special needs to happen based on what kind of unit it is and what state it's in.

JASS:
function SC_RouteUnitDies takes nothing returns nothing
    local unit dyingUnit            = GetTriggerUnit()
    local integer unitId            = GetUnitUserData(dyingUnit)
    local integer dyingUnitType     = GetUnitTypeId(dyingUnit)
    local unit killingUnit          = GetKillingUnit()
    local integer i                 = 0
    local boolean debugThis         = false
 
    // Common - StarCrat Mines
    if SC_Cond_UnitIsStarcraftGoldMine(dyingUnit) then
        call SC_Mine_Death(dyingUnit)
    endif
 
    // Common - Death Replacement
    if GetUnitAbilityLevel(dyingUnit, sc_ABIL_DEATH_REPLACE) > 0 then
        call SC_DeathReplace(dyingUnit, dyingUnitType)
    endif
 
    // Common - Model Attachment Ability
    loop
        exitwhen i > sc_ARRAYMAX_MODEL_ATTACHMENT_ABIL
        if GetUnitAbilityLevel(dyingUnit, sc_ARRAY_MODEL_ATTACHMENT_ABIL[i]) > 0 then
            call UnitRemoveAbility(dyingUnit, sc_ARRAY_MODEL_ATTACHMENT_ABIL[i])
        endif
        set i = i + 1
    endloop
 
    // Protoss
    if IsUnitInGroup(dyingUnit, sc_array_carrier_ints[unitId]) then
        call SC_Carrier_InterceptorDeath(dyingUnit, unitId)
    endif
 
    // Protoss Dummy Units
    if dyingUnitType == sc_TIMER_INTERCEPTOR_LAUNCH then
        call SC_Carrier_LaunchTimerExpire(unitId)
    endif
    if dyingUnitType == sc_TIMER_INTERCEPTOR_FORMATION then
        call SC_Carrier_FormationTimerExpire(unitId)
    endif
    if dyingUnitType == sc_TIMER_CARRIER_COMBAT then
        call SC_Carrier_CombatTimerExpire(unitId)
    endif
 
    // Zerg - Lurker
    if SC_Cond_UnitIsLurker(dyingUnit) then
        call SC_Lurker_Death(unitId)
    endif
    if dyingUnitType == sc_TIMER_LURKER_SPINE then
        call SC_Lurker_SpawnSpine(dyingUnit)
    endif
    if dyingUnitType == sc_TIMER_LURKER_SPINE_DAMAGE then
        call SC_Lurker_SpineDamage(unitId, dyingUnit)
    endif
 
    // Zerg - Queen
    if GetUnitTypeId(killingUnit) == sc_DUMMY_SPAWN_BROODLING then
        call SC_SpawnBroodling_End(GetOwningPlayer(killingUnit), dyingUnit)
    endif
    if dyingUnit == sc_array_infest_timer[unitId] then
        call SC_Infest_End(unitId)
    endif
 
    // Zerg - Defiler
    if dyingUnit == sc_array_consume_timer[unitId] then
        call SC_Consume_End(unitId)
    endif
 
    // Zerg - Building
    if SC_Cond_UnitIsZergBuilding(dyingUnit) then
        if SC_Cond_UnitIsZergBuildingUnderConstruction(dyingUnit) then
            //call DisplayTextToPlayer(Player(0), 0, 0, "Dying Unit is a zerg building under construction")
            call SC_ZergBuilding_RemoveBirthFx(dyingUnit, true)
        else
            //call DisplayTextToPlayer(Player(0), 0, 0, "Dying Unit is NOT a zerg building under construction")
            //call DisplayTextToPlayer(Player(0), 0, 0, "it's prop window is: " + R2S(GetUnitPropWindow(dyingUnit)) + ".")
            call SC_ZergBuilding_Death(dyingUnit)
        endif
    endif
    if GetUnitAbilityLevel(dyingUnit, sc_ABIL_ZERG_BUILDING_DEATH_REPLACE) > 0 then
        call SC_ZergBuilding_DeathReplace(dyingUnit)
    endif
    // Remove Creep Generator (if it has one. if it doesn't, this function below knows to do nothing)
    call SC_Creep_RemoveGenerator(dyingUnit)
 
    // Zerg - Larva
    if SC_Cond_UnitIsLarva(dyingUnit) then
        call SC_Larva_Death(unitId, dyingUnit)
    endif
 
    // Zerg - Larva Egg
    if SC_Cond_UnitIsZergEgg(dyingUnit) then
        call SC_Larva_EggDeath(dyingUnit, unitId)
    endif
 
    // Zerg - Aspect Egg
    if SC_Cond_UnitIsAspectEgg(dyingUnit) then
        call SC_AspectEgg_Death(unitId)
    endif
 
    // Zerg - Hatchery
    if SC_Cond_UnitIsHatchery(dyingUnit) then
        call SC_Hatchery_Death(unitId, dyingUnit)
    endif
 
    // Zerg - Nydus
    if dyingUnitType == sc_ZERG_NYDUS_PAIRED then
        call SC_Nydus_Death(unitId)
    endif
 
    // WC2 Orc Dummy Units
    if dyingUnit == sc_array_unholyArmor_timer[unitId] then
        call SC_UnholyArmor_End(unitId)
    endif
    if dyingUnit == bribe_UDexUnits[unitId] and sc_array_unholyArmor_timer[unitId] != null then
        call SC_UnholyArmor_End(unitId)
    endif
 
    // Clear leaks
    set dyingUnit = null
    set killingUnit = null
endfunction



The peasant meets none of these checks, so we can safely assume that it is one or more of the checks themselves that are causing the crash.

Also worth noting, i have a debug trigger that that kills units selected by a player entering the 'die' command. The crash also occurs if the peasant dies from this trigger.

Since I recently stumbled on another issue with a function not behaving correctly after a game load that turned out to be known by the community, I'm wondering if there are any other functions that have issues after loading a game.

I'll attach the entire Blizzard.j here. If you want a 'development build' of my mod so you can try to replicate the issue yourself, let me know.
 

Attachments

  • Blizzard.j
    743.1 KB · Views: 16
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
i'm not sure about what tools are available nowadays, especially if we can log every warcraft errors before it crash.
i'm not sure either why you're editing directly blizzard.j
however, i think you can't use the index 8191 of an array variable on a loaded game (really not sure, just something vague in my mind)
Also, does it crash silentely or with an error box message ?
if it crash silently it might be an infinite loop, like the unit death event trigger and you kill an unit inside this trigger.
 
Status
Not open for further replies.
Top