• 🏆 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!

Сauses freezing

Level 6
Joined
Aug 26, 2016
Messages
100
After some time after using this code the game crashes.


JASS:
library SPASLib


native UnitAlive takes unit id returns boolean

function GetLocZSPAS takes real x, real y returns real
    call MoveLocation( LFZ, x, y )
    return GetLocationZ( LFZ )
endfunction

struct vectorSPAS
    real x
    real y
    real z
    
    method normalize takes nothing returns nothing
        local real l = SquareRoot( x * x + y * y + z * z )
        
        if l == 0.00 then
            set l = 1.00
        endif
        
        set x = x * ( 1.00 / l )
        set y = y * ( 1.00 / l )
        set z = z * ( 1.00 / l )
    endmethod
    
    static method create takes real x, real y, real z returns thistype
        local thistype this = thistype.allocate( )
        
        set this.x = x
        set this.y = y
        set this.z = z
        
        call this.normalize( )
        
        return this
    endmethod
endstruct

struct bulletSPAS
    unit caster
    unit dummy
    player p
    
    real x
    real y
    real z
    real s // speed
    real d // distance
    real r // radius
    
    vectorSPAS v
endstruct

function MoveSPAS takes nothing returns nothing
    local bullet A = LoadInteger( H, GetHandleId( GetExpiredTimer( ) ), 0 )
    local unit u
    local string BloodEffect = "Objects/Spawnmodels/Undead/UndeadBlood/UndeadBloodAcolyte.mdl"
    set A.x = A.x + A.s * A.v.x * Cos( A.v.z )
    set A.y = A.y + A.s * A.v.y * Cos( A.v.z )
    set A.z = A.z + A.s * A.v.z
    
    set A.d = A.d - A.s
    
    call SetUnitX( A.dummy, A.x )
    call SetUnitY( A.dummy, A.y )
    call SetUnitFlyHeight( A.dummy, A.z - GetLocZSPAS( A.x, A.y ), 0.00 )
    
    call GroupEnumUnitsInRange( TempGroup, A.x, A.y, A.r, null )
    
    loop
        set u = FirstOfGroup( TempGroup )
        exitwhen u == null
        call GroupRemoveUnit( TempGroup, u )
        
        if UnitAlive( u ) and IsUnitEnemy( u, A.p ) and RAbsBJ( GetUnitFlyHeight( u ) - GetUnitFlyHeight( A.dummy ) ) <= 100.00 then
            call GroupClear( TempGroup )
            call UnitDamageTarget( A.caster, u, 40.00, false, false, null, null, null )
            call DestroyEffect( AddSpecialEffectTarget( BloodEffect, u, "origin" ) )
            call RemoveUnit( A.dummy )
            set BloodEffect = null
            call DestroyTimer( GetExpiredTimer( ) )
            call FlushChildHashtable( H, GetHandleId( GetExpiredTimer( ) ) )
            call DestroyTimer( GetExpiredTimer( ) )
            set A.dummy = null
            set A.caster = null
            call A.v.destroy( )
            call A.destroy( )
        endif
    endloop
    
    if A.d <= 0.00 or A.z - GetLocZSPAS( A.x, A.y ) <= 20.00 then
        call PauseTimer( GetExpiredTimer( ) )
        call FlushChildHashtable( H, GetHandleId( GetExpiredTimer( ) ) )
        call DestroyTimer( GetExpiredTimer( ) )
        
        call RemoveUnit( A.dummy )
        
        set A.dummy = null
        set A.caster = null
        call A.v.destroy( )
        call A.destroy( )
    endif
    
endfunction

function Trig_ShootSPAS_Actions takes nothing returns nothing
    local timer t = CreateTimer( )
    local unit Target = udg_Camera[GetConvertedPlayerId(GetTriggerPlayer())]
    local bullet A = bullet.create( )
    local real x = GetUnitX(Target)
    local real y = GetUnitY(Target)
    local real z = GetLocZSPAS( x, y ) + 50.00 
    local string WeaponEffect = "RifleImpact.mdx"
    local string WeaponEffect2 = "war3mapImported/RiflemanSpas.mdx"
    local integer i = 10
    
    call SetItemCharges( GetItemOfTypeFromUnitBJ(udg_MainHero[GetConvertedPlayerId(GetTriggerPlayer())], 'I00K'), ( GetItemCharges(GetItemOfTypeFromUnitBJ(udg_MainHero[GetConvertedPlayerId(GetTriggerPlayer())], 'I00K')) - 1 ) )
    
    loop
    set t = CreateTimer( )
        set A = bullet.create( )
        set A.caster = udg_MainHero_2[GetConvertedPlayerId(GetTriggerPlayer())]
        set A.p = GetOwningPlayer( A.caster )
        
        // for offset
        set A.x = GetUnitX( A.caster )
        set A.y = GetUnitY( A.caster )
        set A.z = GetUnitFlyHeight( A.caster ) + GetLocZSPAS( A.x, A.y ) + 110.00
        
        set A.v = vector.create( x - A.x, y - A.y, z - A.z )
        
        set A.x = A.x + 76.00 * A.v.x * Cos( A.v.z )
        set A.y = A.y + 76.00 * A.v.y * Cos( A.v.z )
        set A.z = GetUnitFlyHeight( A.caster ) + GetLocZSPAS( A.x, A.y ) + 110.00
        
        // for move
        set A.v.x = x - A.x
        set A.v.y = y - A.y
        set A.v.z = z - A.z
        
        call A.v.normalize( )
        
        set A.v.x = A.v.x + GetRandomReal( -0.15, 0.15 )
        set A.v.y = A.v.y + GetRandomReal( -0.15, 0.15 )
        set A.v.z = A.v.z 
        
        set A.d = 5200.00
        set A.s = 1500.00 * 0.03125
        set A.r = 70.00
        
        
        set A.dummy = CreateUnit( A.p, 'u000', A.x, A.y, Atan2( A.v.y, A.v.x ) * bj_RADTODEG )
        call SetUnitPathing( A.dummy, false )
        call UnitAddAbility( A.dummy, 'Arav' )
        call SetUnitX( A.dummy, A.x )
        call SetUnitY( A.dummy, A.y )
        call SetUnitFlyHeight( A.dummy, A.z - GetLocZSPAS( A.x, A.y ), 0.00 )
        call BlzStartUnitAbilityCooldown( udg_MainHero[GetConvertedPlayerId(GetTriggerPlayer())], 'A01Z', 0.22 )
        call SaveInteger( H, GetHandleId( t ), 0, A )
        
        call TimerStart( t, 0.02, true, function MoveSPAS )
    set i = i - 1
        exitwhen i <= 0
    endloop
     call DestroyEffect( AddSpecialEffectTarget( WeaponEffect, A.caster, "weapon" ) )
    call DestroyEffect( AddSpecialEffectTarget( WeaponEffect2, A.caster, "weapon" ) )
    set t = null
    set WeaponEffect = null
    set WeaponEffect2 = null
    set Target = null
   
    
    call IssueImmediateOrderBJ( udg_MainHero[GetConvertedPlayerId(GetTriggerPlayer())], "stop" )
    
endfunction

//===========================================================================
function ShootSPAS_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A01Z' ) ) then
        return false
    endif
    if ( not ( GetItemCharges(GetItemOfTypeFromUnitBJ(udg_MainHero[GetConvertedPlayerId(GetTriggerPlayer())], 'I00K')) != 0 ) ) then
        return false
    endif
    return true
endfunction


function InitTrig_ShootSPAS takes nothing returns nothing
    local trigger t11 = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t11, EVENT_PLAYER_UNIT_SPELL_CAST )
    call TriggerAddCondition( t11, Condition( function ShootSPAS_Conditions ) )
    call TriggerAddAction( t11, function Trig_ShootSPAS_Actions )
    set t11 = null
endfunction
endlibrary
 
Level 39
Joined
Feb 27, 2007
Messages
5,019
How certain are you it's this specific code snippet that is causing the crash? I don't see anything in it that seems likely to cause a crash or any similar issue. My intuition is that there's a divide by 0 error that occurs in a specific scenario: I realize you are normalizing the velocity vector and have accounted for a v of 0 as a special case; while the overall magnitude is thus never 0, it's possible that up to 2 of the components of v could be 0, so if you are ever dividing anything by those components (in other code I can't see) you could potentially div0 the game.

The only thing that seems potentially suspect is that in MoveSPAS you are destroying the timer twice, flushing the hashtable with the timer's handle after it has already been destroyed, and then (in a very rare circumstance where the bullet hits a target and is also at the end of its travel or is close enough to the ground on the exact same timer tick) potentially destroying the timer a third time and flushing it again. I would consolidate this logic so the full cleanup only ever happens once.
JASS:
call DestroyTimer( GetExpiredTimer( ) ) //I think this one is meant to be PauseTimer() ?
call FlushChildHashtable( H, GetHandleId( GetExpiredTimer( ) ) )
call DestroyTimer( GetExpiredTimer( ) )

//then below inside the next if-block you have:
call PauseTimer( GetExpiredTimer( ) )
call FlushChildHashtable( H, GetHandleId( GetExpiredTimer( ) ) )
call DestroyTimer( GetExpiredTimer( ) )

I see some other things that could be improved or changed, but these should have no relevance to the crash. Just suggestions:
JASS:
//the * 1.00 part here is totally unnecessary
set x = x * ( 1.00 / l )
set y = y * ( 1.00 / l )
set z = z * ( 1.00 / l )

//The unit should already have Locust, no? In that case there's no reason to turn off it's pathing since it already has none.
//After adding 'Arav' you can remove it and the unit can still have its fly height manipulated; you don't need to keep 'Arav' on the unit.
//Doesn't matter here but in the case where the unit was intended to be controllable/selectable by the player you wouldn't want to see 'Arav' on it.
call SetUnitPathing( A.dummy, false )
call UnitAddAbility( A.dummy, 'Arav' )

//Strings do not need to be nulled, unless I'm way behind on developments with the string data type. I have a small feeling that
//this could be related to the crash by doing something funky to the string table but I don't know why or have any bigger suggestions.
set WeaponEffect = null
set WeaponEffect2 = null

//Since you're using SPELL_CAST instead of SPELL_EFFECT this event will fire before cooldown/mana cost are used and before any animations finish.
//You start a cooldown so you likely want one, and my guess is that you also want this to happen instantly without delay? There are a few abilities
//in the game that won't interrupt current orders, play an animation, or cause the unit to stop moving which you could use instead.
//The classic one is Berserk.
call BlzStartUnitAbilityCooldown( udg_MainHero[GetConvertedPlayerId(GetTriggerPlayer())], 'A01Z', 0.22 )
call TriggerRegisterAnyUnitEventBJ( t11, EVENT_PLAYER_UNIT_SPELL_CAST )
 
Level 6
Joined
Aug 26, 2016
Messages
100
I made the corrections that were recommended to me but the problem did not disappear, I made the unit shoot for about 15 minutes and the game freezes while about 1000 shots are fired.

JASS:
library M249Lib


native UnitAlive takes unit id returns boolean

function GetLocZM249 takes real x, real y returns real
    call MoveLocation( LFZ, x, y )
    return GetLocationZ( LFZ )
endfunction

struct vectorM249
    real x
    real y
    real z
    
    method normalize takes nothing returns nothing
        local real l = SquareRoot( x * x + y * y + z * z )
        
        if l == 0.00 then
            set l = 1.00
        endif
        
        set x = x * ( 1.00 / l )
        set y = y * ( 1.00 / l )
        set z = z * ( 1.00 / l )
    endmethod
    
    static method create takes real x, real y, real z returns thistype
        local thistype this = thistype.allocate( )
        
        set this.x = x
        set this.y = y
        set this.z = z
        
        call this.normalize( )
        
        return this
    endmethod
endstruct

struct bulletM249
    unit caster
    unit dummy
    player p
    
    real x
    real y
    real z
    real s // speed
    real d // distance
    real r // radius
    
    vectorM249 v
endstruct

function MoveM249 takes nothing returns nothing
    local bullet A = LoadInteger( H, GetHandleId( GetExpiredTimer( ) ), 0 )
    local unit u
    local string BloodEffect = "Objects/Spawnmodels/Undead/UndeadBlood/UndeadBloodAcolyte.mdl"
    call DisplayTimedTextToForce( GetPlayersAll(), 2.90, "On" )
    
    set A.x = A.x + A.s * A.v.x * Cos( A.v.z )
    set A.y = A.y + A.s * A.v.y * Cos( A.v.z )
    set A.z = A.z + A.s * A.v.z
    
    set A.d = A.d - A.s
    
    call SetUnitX( A.dummy, A.x )
    call SetUnitY( A.dummy, A.y )
    call SetUnitFlyHeight( A.dummy, A.z - GetLocZM249( A.x, A.y ), 0.00 )
    
    call GroupEnumUnitsInRange( TempGroup, A.x, A.y, A.r, null )
    
    loop
        set u = FirstOfGroup( TempGroup )
        exitwhen u == null
        call GroupRemoveUnit( TempGroup, u )
        
        if UnitAlive( u ) and IsUnitEnemy( u, A.p ) and RAbsBJ( GetUnitFlyHeight( u ) - GetUnitFlyHeight( A.dummy ) ) <= 100.00 then
             
            
            call GroupClear( TempGroup )
            call UnitDamageTarget( A.caster, u, 20.00, false, false, null, null, null )
            call DisplayTimedTextToForce( GetPlayersAll(), 0.90, "Damage" )
            call DestroyEffect( AddSpecialEffectTarget( BloodEffect, u, "origin" ) )
            call RemoveUnit( A.dummy )
            
            call FlushChildHashtable( H, GetHandleId( GetExpiredTimer( ) ) )
            call DestroyTimer( GetExpiredTimer( ) )
            set A.dummy = null
            set A.caster = null
            call A.v.destroy( )
            call A.destroy( )
        endif
    endloop
    
    if A.d <= 0.00 or A.z - GetLocZM249( A.x, A.y ) <= 20.00 then
        
        call FlushChildHashtable( H, GetHandleId( GetExpiredTimer( ) ) )
        call DestroyTimer( GetExpiredTimer( ) )
        call DisplayTimedTextToForce( GetPlayersAll(), 2.90, "Off" )
        call RemoveUnit( A.dummy )
        
        set A.dummy = null
        set A.caster = null
        
        call A.v.destroy( )
        call A.destroy( )
    endif
    
endfunction

function Trig_Shoot2M249_Actions takes nothing returns nothing
    local timer t = CreateTimer( )
    local unit Target = udg_Camera[GetConvertedPlayerId(GetTriggerPlayer())]
    local bullet A = bullet.create( )
    local real x = GetUnitX(Target)
    local real y = GetUnitY(Target)
    local real z = GetLocZM249( x, y ) + 50.00 
    local string WeaponEffect = "RifleImpact.mdx"
    local string WeaponEffect2 = "war3mapImported/RiflemanM249.mdx"
    set A.caster = udg_MainHero_2[GetConvertedPlayerId(GetTriggerPlayer())]
    set A.p = GetOwningPlayer( A.caster )
    call CameraSetTargetNoiseForPlayer( GetOwningPlayer(udg_MainHero_2[GetConvertedPlayerId(GetTriggerPlayer())]), 16.60, 4.70 )
    // for offset
    set A.x = GetUnitX( A.caster )
    set A.y = GetUnitY( A.caster )
    set A.z = GetUnitFlyHeight( A.caster ) + GetLocZM249( A.x, A.y ) + 110.00 
    
    set A.v = vector.create( x - A.x, y - A.y, z - A.z )
    
    set A.x = A.x + 90.00 * A.v.x * Cos( A.v.z )
    set A.y = A.y + 90.00 * A.v.y * Cos( A.v.z )
    set A.z = GetUnitFlyHeight( A.caster ) + GetLocZM249( A.x, A.y ) + 110.00 
    
    // for move
        set A.v.x = x - A.x + GetRandomReal(-80.0, 80.0)
        set A.v.y = y - A.y + GetRandomReal(-80.0, 80.0)
        set A.v.z = z - A.z  

   
     call A.v.normalize( )
    
    set A.d = 4200.00
    set A.s = 1500.00 * 0.03125
    set A.r = 90.00
    
    
    set A.dummy = CreateUnit( A.p, 'u000', A.x, A.y, Atan2( A.v.y, A.v.x )  * bj_RADTODEG  )
    call SetUnitPathing( A.dummy, false )
    
    call SetUnitX( A.dummy, A.x )
    call SetUnitY( A.dummy, A.y )
    call SetUnitFlyHeight( A.dummy, A.z - GetLocZM249( A.x, A.y ), 0.00 )
    
    call SaveInteger( H, GetHandleId( t ), 0, A )
    call TimerStart( t, 0.02, true, function MoveM249 )
    call DestroyEffect( AddSpecialEffectTarget( WeaponEffect, A.caster, "weapon" ) )
    call DestroyEffect( AddSpecialEffectTarget( WeaponEffect2, A.caster, "chest" ) )
    call SetItemCharges( GetItemOfTypeFromUnitBJ(udg_MainHero[GetConvertedPlayerId(GetTriggerPlayer())], 'I00E'), ( GetItemCharges(GetItemOfTypeFromUnitBJ(udg_MainHero[GetConvertedPlayerId(GetTriggerPlayer())], 'I00E')) - 0 ) )
    
    set t = null
    set Target = null
    
    
    
endfunction

//===========================================================================
function Shoot2M249_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A02H' ) ) then
        return false
    endif
    if ( not ( GetItemCharges(GetItemOfTypeFromUnitBJ(udg_MainHero[GetConvertedPlayerId(GetTriggerPlayer())], 'I00E')) != 0 ) ) then
        return false
    endif
    return true
endfunction

function InitTrig_Shoot2M249 takes nothing returns nothing
    local trigger t6 = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t6, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t6, Condition( function Shoot2M249_Conditions ) )
    call TriggerAddAction( t6, function Trig_Shoot2M249_Actions )
    set t6 = null
endfunction
endlibrary
 
Level 20
Joined
Feb 27, 2019
Messages
592
Which ability is 'A02H'? Some abilities with a low interval can cause freezing after ~8 minutes of game time such as Cluster Rockets or Shadow Strike.
 
Last edited:
Level 20
Joined
Feb 27, 2019
Messages
592
It seems to me that the problem is in the timer because the movement function cannot determine the timer that needs to be turned off.
Why do you suspect that the movement function cant determine the timer that needs to be turned off?

Does the crash occur faster or slower if the shots occur faster or slower? Are the crashes consistent in some way?
 
Top