• 🏆 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] Help with tweaking TimerUtils

Status
Not open for further replies.
So, I'm using the Blue flavor of TimerUtils. Everything works as it should but, apparently I'm using too many timers, because after a while, things in my maps that use timers randomly stop working. I'm guessing this is because I reached the timer limiit? I thought this wasn't possible with the blue version of TimerUtils?

So, looking through the configurables of TimerUtils, I found this configurable:

JASS:
//Changing this  to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190

Would setting this to a higher number solve my problem, considering that I am using the blue version already? If so, are there any disadvantages of using this?

... Or maybe this is not the issue, maybe too many timers have collided or something?

In any case, what should I do to TimerUtils to stop it from bugging after a while? Note that this does not happen all the time apparently, only when many spells that requires many timers in my map are being used simultaneously, that it bugs and timers dont expire properly.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
I cant imagine having 8000 running timers at the same time, that would lagg the heck out of the map

anyway when you are done with the timer try calling ReleaseTimer(the timer) because if you dont release them back to stack you will earlier or later run out of timers and you will not be able to get anymore
Also try running the map in debug mode, it should print some debug messages to screen that you run out of timers or something like that
Timers should ever expires properly, maybe you dont get the timer from the system and also I cant imagine having 8000 spells cast at once
 
I'm pretty sure it's not my code, since it's multiple spells that use TimerUtils that break. Plus, even imported spells that use TimerUtils break. Anyways here's two of my spells that sometimes break, even though they shouldnt:

[jass=Surf]scope SurfS initializer OnInit
//Surf by Strikest


globals
private constant integer ABIL_ID = 'A002' //Raw code of ability
private constant integer UNIT_ID = 'n000' //Raw code of the puddle dummy unit
private constant integer MOVE_ID = 'A003' //Raw code of the movement speed bonus ability
private constant integer SLOW_ID = 'S000' //Raw code of the slow aura ability
private constant integer DMG_ID = 'A001' //Raw code of the damage over time ability
private constant string EFFECT = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveMissile.mdl" //Path of the desired special effect you want attached to the caster
private constant string ATTACH = "origin" //String of the desired attachment point for the caster
private constant string KB_SFX = "war3mapImported\\SlideWater.mdx" //Path of the desired special effect you want attached to knocked back units
private constant real AOE = 124. //Area of effect around caster that units will be knocked back
private constant real MINIMUM_DISTANCE = 30. //Minimum distance required for another puddle to spawn.
private constant boolean ALLOW_NO_COLLISION = false //Choose whether or not you want to turn collision off for the caster.

private constant real TREE_AOE= 0.0
private constant boolean ALLOW_MOVE = false
private constant boolean CHECK_PATHING = false
//Copied from kenny's KBS trigger
//** - TREE_AOE - This determines whether or not trees will be knocked down. **
//** For trees to be knocked down, a positive number (real) must **
//** be used, such as 150.00, which would give a radius of 150.00 **
//** in which trees will be knocked down. **
//** For trees to not be knocked down, a negative number (real) **
//** must be used, such as -150.00, which would create a radius **
//** that if a tree comes within it, the unit will stop moving. **
//** For none of those effects, the number 0 (0.00) can be used. **
//** This will just cause the units to "bounce" off trees.
//** -ALLOW_MOVE - This boolean will decided whether or not you want the unit **
//** to have the ability to move while being knocked back. **
//** "true" will allow them to move, while "false" will not. **
//** -CHECK_PATHING - A boolean that, if true, will check for unpathable terrain **
//** such as a wall or cliff, or where doodads may be. If false **
//** it will ignore these changes and the unit will be pushed **
//** along the wall, cliff or doodad.

private constant group GROUP = CreateGroup()//Don't touch.
private unit CASTER //Don't touch this either.
endglobals

private constant function DURATION takes integer level returns integer
return 12 + level*2
endfunction

private constant function BONUS_SPEED takes integer level returns real //Amount of speed gained through MSX trigger. Requires MSX. Be careful with this, if the value is too high, it might bug.
return 500.
endfunction
//////////////////////////////////////////////////////NO TOUCHY PAST THIS POINT UNLESS YOU KNOW WHAT YOU ARE DOING/////////////////////////////////////////////////////////////////////////////

private struct Surf
unit cast
real x
real y
integer dur
effect attach
endstruct

native UnitAlive takes unit id returns boolean

private function FilterActions takes nothing returns boolean
local real x1 = GetUnitX(CASTER)
local real y1 = GetUnitY(CASTER)
local real x2 = GetUnitX(GetFilterUnit())
local real y2 = GetUnitY(GetFilterUnit())
local real a = bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
local unit u = GetFilterUnit()

if UnitAlive(u) and IsUnitEnemy(u,GetOwningPlayer(CASTER)) then
call KBS_BeginEx(u,AOE,.6,a,KB_SFX,TREE_AOE,ALLOW_MOVE,CHECK_PATHING)
endif

set u = null
return false
endfunction

private function Handler takes nothing returns nothing
local timer t = GetExpiredTimer()
local Surf data = GetTimerData(t)
local real x1 = data.x
local real y1 = data.y
local real x2 = GetUnitX(data.cast)
local real y2 = GetUnitY(data.cast)
local real d = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)
local unit u

if data.dur <= 0 then
call DestroyEffect(data.attach)
if ALLOW_NO_COLLISION then
call SetUnitPathing(data.cast,true)
endif
static if LIBRARY_MSX then
call MSX_RemoveAllSpeed(data.cast)
endif
call UnitRemoveAbility(data.cast,MOVE_ID)
call data.destroy()
call ReleaseTimer(t)
else
set CASTER = data.cast
call GroupEnumUnitsInRange(GROUP,GetUnitX(data.cast),GetUnitY(data.cast),170.,Filter(function FilterActions))
if d >= MINIMUM_DISTANCE*MINIMUM_DISTANCE then
set u = CreateUnit(GetOwningPlayer(data.cast),UNIT_ID,x2,y2,0.)
call SetUnitAbilityLevel(u,SLOW_ID,GetUnitAbilityLevel(data.cast,ABIL_ID))
call SetUnitAbilityLevel(u,DMG_ID,GetUnitAbilityLevel(data.cast,ABIL_ID))
call IssueImmediateOrderById(u,852177)
call UnitApplyTimedLife(u,'BTLF',(I2R(data.dur)*.02))
set data.x = x2
set data.y = y2
endif

set data.dur = data.dur -1
call SetTimerData(t,data)
call TimerStart(t,.02,false,function Handler)
endif

set t = null
set u = null
endfunction


private function Actions takes nothing returns nothing
local timer t = NewTimer()
local Surf data = Surf.create()

set data.cast = GetTriggerUnit()
set data.x = GetUnitX(data.cast)
set data.y = GetUnitY(data.cast)
set data.attach = AddSpecialEffectTarget(EFFECT,data.cast,ATTACH)
set data.dur = 50*DURATION(GetUnitAbilityLevel(data.cast,ABIL_ID))

call UnitAddAbility(data.cast,MOVE_ID)

static if LIBRARY_MSX then
call MSX_SetPureSpeed(data.cast,BONUS_SPEED(GetUnitAbilityLevel(data.cast,ABIL_ID)))
endif

if ALLOW_NO_COLLISION then
call SetUnitPathing(data.cast,false)
endif

call SetTimerData(t,data)
call TimerStart(t,.02,false,function Handler)

set t = null
endfunction

private function OnSpell takes nothing returns boolean
if GetSpellAbilityId() == ABIL_ID then
call Actions()
endif
return false
endfunction

private function OnInit takes nothing returns nothing
local trigger trig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(trig,Condition(function OnSpell))
set trig = null
endfunction

endscope[/code]


[jass=Condense]scope Condense initializer OnInit
//Condense by Strikest
globals
//Configurables
private constant integer ABIL_ID = 'A000' //Raw code of ability
private constant integer DBFF_ID = 'A004' //Raw code of magic amplification ability
private constant integer ORB_ID = 'h001' //Raw code of the water orb dummy
private constant integer CROW_ID = 'Amrf' //Raw code of Crow Form ability in your map. Should be 'Amrf', unless you modified it.
private constant string EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" //Path of the special effect model when orb makes contact
private constant string ATTACH_POINT = "origin" //String of the attachment point you want
private constant boolean BOOLEAN_INCLUDE_GREEN_INT = true //Choose whether or not to include green int bonuses in damage calculations(If factoring int).
private constant real AOE = 116. //Area of effect of the orb

private constant attacktype ATK_TYPE = ATTACK_TYPE_NORMAL //Attacktype you want
private constant damagetype DMG_TYPE= DAMAGE_TYPE_NORMAL //Damagetype you want

private constant group GROUP = CreateGroup()//Dont touch this
private group GROUP2 //Or this
private unit CASTER //Or this
private integer CAST_LVL //Nor this
endglobals

private constant function DAMAGE takes integer levelofpassive,integer levelofcastspell, integer heroint returns real //Configure damage in this function
return levelofcastspell*heroint + 0.
endfunction

private constant function AMOUNT_OF_REVOLUTIONS takes integer levelofpassive,integer levelofcastspell returns integer //Configure how many times the orb will make a complete revolution around the caster
return levelofcastspell
endfunction

private constant function DURATION_NORMAL takes integer levelofpassive,integer levelofcastspell, integer heroint returns real //Configure the duration of the debuff for non-hero units
return 10.
endfunction

private constant function DURATION_HERO takes integer levelofpassive,integer levelofcastspell, integer heroint returns real //Configure the duration of the debuff for hero units
return 5.
endfunction
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~NO TOUCHY PAST THIS POINT UNLESS YOU KNOW WHAT YOU ARE DOING~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

private struct Cdnse
unit cast
unit orb
integer lvl
integer dur
real a
group g

static method create takes nothing returns Cdnse
local Cdnse data=Cdnse.allocate()
if data.g==null then
set data.g=CreateGroup()
endif
return data
endmethod

method onDestroy takes nothing returns nothing
call GroupClear(.g)
endmethod
endstruct

private struct Dbuff
unit u
endstruct

native UnitAlive takes unit id returns boolean

private function Handler2 takes nothing returns nothing
local timer t = GetExpiredTimer()
local Dbuff data = GetTimerData(t)

call UnitRemoveAbility(data.u,DBFF_ID)

call data.destroy()
call ReleaseTimer(t)

set t = null
endfunction

private function FilterActions takes nothing returns boolean
local unit u = GetFilterUnit()
local integer i = GetUnitAbilityLevel(CASTER,ABIL_ID)
local integer int = GetHeroInt(CASTER,BOOLEAN_INCLUDE_GREEN_INT)
local real damage = DAMAGE(i,CAST_LVL,int)
local Dbuff data
local timer t

if UnitAlive(u) and IsUnitEnemy(u, GetOwningPlayer(CASTER)) and not IsUnitInGroup(u,GROUP2) and not IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) then
call UnitDamageTarget(CASTER,u,damage,false,false,ATK_TYPE,DMG_TYPE,WEAPON_TYPE_WHOKNOWS)
call GroupAddUnit(GROUP2,u)
call DestroyEffect(AddSpecialEffectTarget(EFFECT,u,ATTACH_POINT))
//If you are capable enough, you can add any extra effects you want here
if GetUnitAbilityLevel(u,DBFF_ID) != 1 then
call UnitAddAbility(u,DBFF_ID)
set data = Dbuff.create()
set data.u = u
set t = NewTimer()
call SetTimerData(t,data)
if IsUnitType(u,UNIT_TYPE_HERO) then
call TimerStart(t,DURATION_HERO(i,CAST_LVL,int),false,function Handler2)
else
call TimerStart(t,DURATION_NORMAL(i,CAST_LVL,int),false,function Handler2)
endif
endif
endif
set u = null
set t= null
return false
endfunction

private function Handler takes nothing returns nothing
local timer t = GetExpiredTimer()
local Cdnse data = GetTimerData(t)
local real x
local real y
if data.dur <= 0 then
call RemoveUnit(data.orb)
call data.destroy()
call ReleaseTimer(t)
else
set data.a = data.a + 4.
set x = GetUnitX(data.cast) + 400. * Cos(data.a*bj_DEGTORAD)
set y = GetUnitY(data.cast) + 400. * Sin(data.a*bj_DEGTORAD)
call SetUnitX(data.orb,x)
call SetUnitY(data.orb,y)
if GetUnitFlyHeight(data.cast) >0 then
call SetUnitFlyHeight(data.orb,GetUnitFlyHeight(data.cast),0)
endif
set CASTER = data.cast
set CAST_LVL = data.lvl
set GROUP2 = data.g
call GroupEnumUnitsInRange(GROUP,x,y,AOE,Filter(function FilterActions))
set data.dur = data.dur - 4
call SetTimerData(t,data)
call TimerStart(t,.0325,false,function Handler)
endif

set t = null
endfunction

private function Actions takes nothing returns nothing
local unit cast = GetTriggerUnit()
local real casx = GetUnitX(cast)
local real casy = GetUnitY(cast)
local integer i = GetUnitAbilityLevel(cast,GetSpellAbilityId())
local real a = GetRandomReal(0.,360.) + GetUnitFacing(cast)
local real tarx
local real tary
local timer t = NewTimer()
local Cdnse data = Cdnse.create()

set tarx = casx + 400. * Cos(a*bj_DEGTORAD)
set tary = casy + 400. * Sin(a*bj_DEGTORAD)
set data.cast = cast
set data.lvl = i
set data.dur = 360*AMOUNT_OF_REVOLUTIONS(GetUnitAbilityLevel(cast,ABIL_ID),i)
set data.a = a
set data.orb = CreateUnit(GetOwningPlayer(cast),ORB_ID,tarx,tary,0.0)

call SetUnitPathing(data.orb,false)
call UnitAddAbility(data.orb,CROW_ID)

call SetTimerData(t,data)
call TimerStart(t,.0325,false,function Handler)

set cast = null
set t = null
endfunction

private function OnSpell takes nothing returns boolean
if GetUnitAbilityLevel(GetTriggerUnit(),ABIL_ID) != 0 then
call Actions()
endif
return false
endfunction

private function OnInit takes nothing returns nothing
local trigger trig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(trig,Condition(function OnSpell))
set trig = null
endfunction

endscope[/code]


As you can see I'm pretty sure I'm using the ReleaseTimer function properly.

And how do you run a map in debug mode? I've never used that before o_O
 
Status
Not open for further replies.
Top