• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[vJASS] TimerUtils error

Status
Not open for further replies.
Level 22
Joined
Feb 3, 2009
Messages
3,292
Hello, whenever in my map I cast a spell named Water Cyclone it displays an error message:

"NewTimer: Unable to allocate a timer, you should probably switch to the blue flavor or fix timer leaks."

Afterwards when looking in the editor I found this line in the TimerUtils script code. Is anyone familiar on how to fix it?
 
Level 22
Joined
Feb 3, 2009
Messages
3,292
Here is the entire code of Water Cyclone:

JASS:
//+--------------------------------------------
//|     Water Cyclone  v1.00c by BlackRose             
//+--------------------------------------------
//| Requires:
//+----------
//| TimerUtils by Vexorian
//| AutoFly    by Azlier
//| xefx       by Vexorian
//| xepreload  by Vexorian
//|
//| Description
//+------------
//| Becomes the core of a vicious cyclone, waves of water swirl in, 
//| sweeping in enemy units. Upon reaching the eye, the water violently
//| explodes, releasing enemy units swept in a violent nova outwards.
//|
//| Other:
//+--------
//| Uses a modified model of the NagaDeath effect. For performance issues. 
//+---------------------------------------------------------------------------+

scope WaterCyclone initializer onInit // requires TimerUtils, AutoFly, xefx, xepreload

    globals
        private constant integer SPELL_ID = 'A04J'
        
        private constant boolean DAMAGE_OVERTIME = false
        private constant boolean SETXY           = false
        // Use Position or XY. Position stops orders.
                
        private constant real TIMER_INTERVAL = 0.03125  // Each x do stuff.
                
        private constant integer FIREWORKS_NUMBER = 35  // How many firework effects are made when done spinning in.
        private constant string  FIREWORKS_EFFECT = "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdx"  // Main model of the effect.
        private constant string  FIREWORKS_FLASH  = ""  // Constant flash on effect.
        private constant real    FIREWORKS_SCALE  = 1.00
        private constant string  ON_LANDING_EFFECT = "war3mapImported\\NagaDeathSpare.mdx"//""
        private constant boolean CREATE_ON_DUMMY   = false
                
        private constant real AOE = 1000         // Well, not really AoE. How far the spirals start from the caster.
        private constant real ENUM_AOE = 300    // Radius enemy units can be pulled into the washing machine (Cyclone).
        private constant real ENUM_HEIGHT = 90  // Max height units can be to get pulled.
        
        private constant integer SPIRAL_NUMBER = 5  // How many spirals lines there are.
        private constant real SPININ_DURATION = 1.5 // How long it take to spiral inwards.
        private constant real ANGLE_INCREMENT = -5  // Positive for clockwise, negative for anti-clockwise. This spell is indeed a toilet.
        
        private constant real HEIGHT = 45.          // Height of the effect moving in. If you set one.
        private constant string INWARDS_EFFECT = "" // Missile of the spiral. In this case it's none and just constant water flashing.
        private constant string FLASH_EFFECT = "war3mapImported\\NagaDeathSpare.mdx"
        private constant real   INWARDS_SCALE = 1.00 // Missile (INWARDS_EFFECT) size?
        
        private constant attacktype ATK_TYPE = ATTACK_TYPE_NORMAL
        private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
        private constant weapontype WPN_TYPE = null
    endglobals

    // First half is dealt over time during the spin.
    // Second half is dealt upon the landing.
    // If DAMAGE_OVERTIME is true.
    private function DAMAGE takes integer Level returns real
        return 250. + (Level * 250.)
    endfunction
    
    // =========================================================
    //                     NOT CONFIGURABLE
    // =========================================================
    globals
        private constant group ENUM_GROUP = CreateGroup()
        private constant group INGROUP = CreateGroup()
        private unit TEMP_UNIT
        private unit TEMP_CASTER
        private player TEMP_PLAYER
        private boolexpr UNIT_FILTER
        
        private real TX
        private real TY
        private real TEMP_REAL
    endglobals
    
    native UnitAlive takes unit u returns boolean
    
    // ================================================================
    // Which units can be added to the swirl.
    // Currently it is: E
    // -Enemy units of caster, 
    // -Alive units
    // -Non structures.
    // -Units under the height limit.
    // -And units not already in a swirl group.
    // ================================================================
    
    private function UnitFilter takes nothing returns boolean
            set TEMP_UNIT = GetFilterUnit()
            return IsUnitEnemy( TEMP_UNIT, TEMP_PLAYER ) and /*
            */ UnitAlive( TEMP_UNIT ) and /*
            */ IsUnitType( TEMP_UNIT, UNIT_TYPE_STRUCTURE ) == false and/*
            */ GetUnitFlyHeight( TEMP_UNIT ) < ENUM_HEIGHT and /*
            */ IsUnitInGroup( TEMP_UNIT, INGROUP ) == false
    endfunction
    
// Credit to AceHart.
private function GetParabolaZ takes real x,real d,real h returns real
    return 4 * h * x * (d - x) / (d * d)
endfunction

    private struct FireworksData
        boolean NotDummy = false
        real Distance
        real Height
        real Duration
        unit Dummy
        unit Source
        
        real DamageDealt = 0
        real DamageMax
        
        real DPS
        real x
        real y
        real z
        real Speed
        real Cosine
        real Sine
        real d = 0
        
        timer t
        
        xefx myfx
        method onDestroy takes nothing returns nothing
            if not DAMAGE_OVERTIME then
                call UnitDamageTarget( .Source, .Dummy, .DPS, true, true, ATK_TYPE, /*
                */ DMG_TYPE, WPN_TYPE )
            elseif .NotDummy then
                set .DPS = .DamageMax - .DamageDealt
                call UnitDamageTarget( .Source, .Dummy, .DPS, true, true, ATK_TYPE, /*
                */ DMG_TYPE, WPN_TYPE )
            endif
            if not .NotDummy then
                if CREATE_ON_DUMMY then
                    call DestroyEffect( AddSpecialEffect( ON_LANDING_EFFECT, .x, .y ) )
                endif
                call .myfx.destroy()
            else
                call DestroyEffect( AddSpecialEffect( ON_LANDING_EFFECT, .x, .y ) )
                call SetUnitPathing( .Dummy, true )
                call GroupRemoveUnit( INGROUP, .Dummy )
                set .Dummy = null
                set .Source = null
            endif
            call ReleaseTimer( .t )
        endmethod
        
        static method move takes nothing returns nothing
            local thistype d = GetTimerData( GetExpiredTimer() )
            set d.d = d.d + d.Speed
            set d.x = d.x + d.Speed * d.Cosine 
            set d.y = d.y + d.Speed * d.Sine 
            set d.z = GetParabolaZ( d.d, d.Distance, d.Height )

            if d.NotDummy then
                if SETXY then
                    call SetUnitX( d.Dummy, d.x )
                    call SetUnitY( d.Dummy, d.y )
                else
                    call SetUnitPosition( d.Dummy, d.x, d.y )
                endif
                call SetUnitFlyHeight( d.Dummy, d.z, 0 )
            else
                set d.myfx.x = d.x
                set d.myfx.y = d.y
                set d.myfx.z = d.z
                if FIREWORKS_FLASH != "" then
                    call d.myfx.flash( FIREWORKS_FLASH )
                endif
            endif

            if DAMAGE_OVERTIME and d.NotDummy then
                call UnitDamageTarget( d.Source, d.Dummy, d.DPS, true, false, ATK_TYPE, /*
                */ DMG_TYPE, WPN_TYPE )
                set d.DamageDealt = d.DamageDealt + d.DPS
            endif

            if d.d >= d.Distance then
                call d.destroy()
            endif
        endmethod
    
        static method create takes real x, real y, unit Target, unit Source, real DPS returns thistype
            local thistype d = thistype.allocate()
            local real angle = GetRandomReal( 0, 359 ) * bj_DEGTORAD
    
            set d.Distance = GetRandomReal( 200, 600 )
            set d.Duration = d.Distance / 280
            set d.Height = d.Distance * 1.1
            
            set d.x = x
            set d.y = y
            set d.z = 0
            set d.Speed = d.Distance / ( d.Duration / TIMER_INTERVAL )
            set d.Cosine = Cos( angle )
            set d.Sine = Sin( angle )
            
            if Target == null then
                set d.myfx = xefx.create( x, y, angle )
                set d.myfx.fxpath = FIREWORKS_EFFECT
            else
                set d.NotDummy = true
                set d.Dummy = Target
                call SetUnitPathing( d.Dummy, false )
                set d.Source = Source
                set d.DPS = DPS
                if DAMAGE_OVERTIME then
                    set d.DamageMax = d.DPS * (SPININ_DURATION / TIMER_INTERVAL )
                endif
            endif
            
            set d.t = NewTimer()
            call SetTimerData( d.t, d )
            call TimerStart( d.t, TIMER_INTERVAL, true, function thistype.move )
            return 0
        endmethod
    endstruct

    private struct WCData
        unit Caster
        player Owner
        xefx array myfx[SPIRAL_NUMBER]
        real array Cosine[SPIRAL_NUMBER]
        real array Sine[SPIRAL_NUMBER]
        group array Group[SPIRAL_NUMBER]
        group DamagedGroup
        real Radius = AOE
        real RadianAdder
        real RadiusDecrement
        real cx
        real cy
        real DPS
        
        timer t
        static method GroupCallback takes nothing returns nothing
            local unit u = GetEnumUnit()
            local FireworksData f = FireworksData.create( TX, TY, u, TEMP_UNIT, TEMP_REAL )
            if not DAMAGE_OVERTIME then
                call UnitDamageTarget( TEMP_UNIT, u, TEMP_REAL, true, true, /*
                */ ATK_TYPE, DMG_TYPE, WPN_TYPE )
            endif
        endmethod
        
        method onDestroy takes nothing returns nothing
            local integer i = 0
            local FireworksData f
            call ReleaseTimer( .t )
            if INWARDS_EFFECT != "" then
                loop
                    exitwhen i == SPIRAL_NUMBER
                    call .myfx[i].destroy()
                    set i = i + 1
                endloop
            endif
            set i = 0
            loop
                exitwhen i == FIREWORKS_NUMBER
                set f = FireworksData.create( .cx, .cy, null, null, 0 )
                set i = i + 1
            endloop
            set TX = .cx
            set TY = .cy
            set TEMP_UNIT = .Caster
            set TEMP_REAL = .DPS
            call ForGroup( .DamagedGroup, function thistype.GroupCallback )
            call GroupClear( .DamagedGroup )
            set .Caster = null
            set .Owner = null
        endmethod
        
        static method create takes nothing returns thistype
            local thistype d = thistype.allocate()
            local real x
            local real y
            local integer i = 0
            local real angle = 360 / SPIRAL_NUMBER
                        
            set d.t = NewTimer()

            set d.Caster = GetTriggerUnit()
            set d.Owner = GetOwningPlayer( d.Caster )
            set d.cx = GetUnitX(d.Caster)
            set d.cy = GetUnitY(d.Caster)
            
            set d.RadiusDecrement = ( AOE / ( SPININ_DURATION/TIMER_INTERVAL) )
            set d.RadianAdder     = ANGLE_INCREMENT * bj_DEGTORAD
            
            set d.DPS = DAMAGE( GetUnitAbilityLevel( d.Caster, SPELL_ID ) ) / 2
            if DAMAGE_OVERTIME then
                set d.DPS = d.DPS / ( SPININ_DURATION / TIMER_INTERVAL )
            endif
            
            loop
                exitwhen i == SPIRAL_NUMBER
                set d.Cosine[i] = ( bj_DEGTORAD * (angle*i) )
                set d.Sine[i] = ( bj_DEGTORAD * (angle*i) )
                set x = d.cx + AOE * Cos(d.Cosine[i])
                set y = d.cy + AOE * Sin(d.Sine[i])
                if INWARDS_EFFECT != "" then
                    set d.myfx[i] = xefx.create( x, y, HEIGHT )
                    set d.myfx[i].fxpath = INWARDS_EFFECT
                endif

                if d.Group[i] == null then
                    set d.Group[i] = CreateGroup()
                else
                    call GroupClear( d.Group[i] )
                endif
                set i = i + 1
            endloop
            
            if d.DamagedGroup == null then
                set d.DamagedGroup = CreateGroup()
            else
                call GroupClear( d.DamagedGroup )
            endif
            
            return d
        endmethod
                
        static method MoveXY takes nothing returns nothing
            local unit u = GetEnumUnit()
            call SetUnitX( u, TX )
            call SetUnitY( u, TY )
            if DAMAGE_OVERTIME then
                call UnitDamageTarget( TEMP_UNIT, u, TEMP_REAL, true, true, /*
                */ ATK_TYPE, DMG_TYPE, WPN_TYPE )
            endif
        endmethod
        
        static method update takes nothing returns nothing
            local thistype d = GetTimerData(GetExpiredTimer())
            local real x
            local real y
            local integer i = 0
            local unit u

            loop
                exitwhen i == SPIRAL_NUMBER
                set d.Cosine[i] = d.Cosine[i] + d.RadianAdder
                set d.Sine[i] = d.Sine[i] + d.RadianAdder

                set x = d.cx + d.Radius * Cos(d.Cosine[i])
                set y = d.cy + d.Radius * Sin(d.Sine[i])
                if INWARDS_EFFECT != "" then
                    set d.myfx[i].x = x
                    set d.myfx[i].y = y
                endif
                if FLASH_EFFECT != "" then
                    call DestroyEffect( AddSpecialEffect( FLASH_EFFECT, x, y ) )
                endif
                set TEMP_PLAYER = d.Owner
                call GroupEnumUnitsInRange( ENUM_GROUP, x, y, ENUM_AOE, UNIT_FILTER )
                loop
                    set u = FirstOfGroup( ENUM_GROUP )
                    exitwhen u == null
                    call GroupAddUnit( d.DamagedGroup, u )
                    call GroupAddUnit( d.Group[i], u )
                    call GroupAddUnit( INGROUP, u )
                    call GroupRemoveUnit( ENUM_GROUP, u )
                endloop
                set TX = x
                set TY = y
                set TEMP_UNIT = d.Caster
                set TEMP_REAL = d.DPS
                call ForGroup( d.Group[i], function thistype.MoveXY )
                set i = i + 1
            endloop

            set d.Radius = d.Radius - d.RadiusDecrement

            if d.Radius <= 0 then
                call d.destroy()
            endif
        endmethod
    endstruct

    private function CheckSpell takes nothing returns boolean
        local WCData d
        if GetSpellAbilityId() == SPELL_ID then
            set d = WCData.create()
            call SetTimerData( d.t, d )
            call TimerStart( d.t, TIMER_INTERVAL, true, function WCData.update )
        endif
        return false
    endfunction

    private function onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        
        call XE_PreloadAbility( SPELL_ID )

        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( t, Condition( function CheckSpell ) )

        set UNIT_FILTER = Condition( function UnitFilter ) 
        set t = null
    endfunction


endscope
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
"NewTimer: Unable to allocate a timer, you should probably switch to the blue flavor or fix timer leaks."

This means that you ran out of the initial quantity and it couldn't allocate any new ones-
private constant integer QUANTITY = 256

That only creates 256 timers at the start. When you go over that number, it tries to create new timers. Creating new timers can be a problem if you have made lots of handles.

Either you have almost 7935 handles active in your map at a time (handles you create yourself), or you have leaks.

For example, here is one leak just in your spell
JASS:
            local unit u = GetEnumUnit()
            call SetUnitX( u, TX )
            call SetUnitY( u, TY )
            if DAMAGE_OVERTIME then
                call UnitDamageTarget( TEMP_UNIT, u, TEMP_REAL, true, true, /*
                */ ATK_TYPE, DMG_TYPE, WPN_TYPE )
            endif

You never null your local.

The more leaks you have all over your map, the faster Timer Red is going to crash.



Fix the leaks in all of the code in your entire map and you will fix your problem.


Also, you shouldn't be using TimerUtils for this. Check out T32. This should fix the total number of timers in your map and increase overall performance : ).
 
Level 22
Joined
Feb 3, 2009
Messages
3,292
This means that you ran out of the initial quantity and it couldn't allocate any new ones-
private constant integer QUANTITY = 256

That only creates 256 timers at the start. When you go over that number, it tries to create new timers. Creating new timers can be a problem if you have made lots of handles.

Either you have almost 7935 handles active in your map at a time (handles you create yourself), or you have leaks.

For example, here is one leak just in your spell
JASS:
            local unit u = GetEnumUnit()
            call SetUnitX( u, TX )
            call SetUnitY( u, TY )
            if DAMAGE_OVERTIME then
                call UnitDamageTarget( TEMP_UNIT, u, TEMP_REAL, true, true, /*
                */ ATK_TYPE, DMG_TYPE, WPN_TYPE )
            endif
You never null your local.

The more leaks you have all over your map, the faster Timer Red is going to crash.



Fix the leaks in all of the code in your entire map and you will fix your problem.


Also, you shouldn't be using TimerUtils for this. Check out T32. This should fix the total number of timers in your map and increase overall performance : ).

The funny part is that this isn't even my spell, it's from here on Hive.
My spells don't have leaks nor use any external libs. I hope this is the only one that has leaks of which I downloaded.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Bribe is looking at it right now.


It was approved like 1 year ago back when the mods weren't as careful about reviewing resources. There are currently a lot of approved leaky resources in spells section as well as deprecated resources.

That just means it's all the harder to find a resource that doesn't leak and uses latest stuff ; P.
 
Status
Not open for further replies.
Top