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

[vJASS] Timer Utils alternative

Status
Not open for further replies.
Level 13
Joined
Jul 26, 2008
Messages
1,009
Darn, I forgot that two years ago I thought I had to destroy triggers I wasn't using anymore because they were a handle and leaked.

Only read recently after going through some new systems that they cause unrelated and unusual errors. Starting to realize what might be causing it. There's gotta be more than a couple dozen Destroy Trigger calls in my old code.. Thanks! Maybe this will fix the issue.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
I have never run into any errors when destroying triggers. The only time errors occur is when you destroy a trigger with a TSA in it before that TSA occurs.

Believe me, I have more than a few resources that destroy triggers in masses >.>. Triggers will leak if you don't destroy them when u no longer need them. If they have events on them, those events will still fire ; \.

Destroy your triggers, clean your leaks. Just don't use TSA stupidly in place of timers when you really don't need it =).
 
Level 13
Joined
Jul 26, 2008
Messages
1,009
Alright, though I'm not sure what good it'll do. A lot of the problems are rather unusual.

For instance the death timer I will post here seems to cut out part of the way through, and jump to 3+ minutes instead of a few seconds. Then, it'll completely freeze. This happens when the Timer is not on a periodic callback.

Use Hashtables is also set to true, with the same problems occurring. The problem occurs randomly as well, and affects most things using TimerUtils, especially when they're on periodics longer than a few seconds.

JASS:
struct DyingHeroWindowTimer

    private unit u = null
    private player owner = null
    private timerdialog timdia

    private static method invulnOff takes nothing returns nothing
     local thistype this = GetTimerData(GetExpiredTimer())
        call SetUnitInvulnerable(.u, false)
        call ReleaseTimer(GetExpiredTimer())
        call .destroy()
    endmethod

    private static method onExpire takes nothing returns nothing
        local thistype this = GetTimerData(GetExpiredTimer())
        local integer i = GetRandomInt(1, 5)
        local integer index = 0
        local item indexItem = null
        local unit Bat = null
        local real X = 0.00
        local real Y = 0.00
        local string fxpath = "" //I would recommend using clear variable names ;)

        if IsUnitInForce(.u, BloodPack) then
            if Teams == 2 or Teams == 3 then
                set i = GetRandomInt(1,3)
            else
                set i = GetRandomInt(1,5)
            endif
            set X = HavenX[i]
            set Y = HavenY[i]
        elseif IsUnitInForce(.u, FangSquad) then
            if Teams == 2 or Teams == 3 then
                set i = GetRandomInt(9,11)
            else
                set i = GetRandomInt(9,13)
            endif
            set X = HavenX[i]
            set Y = HavenY[i]
        elseif IsUnitInForce(.u, HellHorde) then
            set i = GetRandomInt(6,8)
            set X = HavenX[i]
            set Y = HavenY[i]
        elseif IsUnitInForce(.u, CrimsonHunters) then
            set i = GetRandomInt(12,14)
            set X = HavenX[i]
            set Y = HavenY[i]
        elseif IsUnitInForce(.u, VileLegion) then
            set i = GetRandomInt(15,17)
            set X = HavenX[i]
            set Y = HavenY[i]
        elseif IsUnitInForce(.u, ChosenVampire) then
            set X = 15879.
            set Y = 9892.
        else
            set X = 2576
            set Y = -200
        endif
        call GroupRemoveUnit(udg_BurnGroup, .u)
        call GroupRemoveUnit(udg_RoamingGroup, .u)
        call DestroyEffect(FireFX[GetUnitId(.u)])

        if lives[GetPlayerId(.owner)] <= 0 then
            if LANGUAGE[GetPlayerId(.owner)] == "francais" then
                call DisplayTextToPlayer(.owner, 0, 0, "Votre dernière vie a été gaspillée et votre âme de vampire condamnée à l'enfer pour l'éternité. Néanmoins, vous pouvez rester et regarder la partie se dérouler.")
            else
                call DisplayTextToPlayer(.owner, 0, 0, "Your last life has been wasted, and your vampire soul damned with little hope of salvation.  However, feel free to stay and watch the game.")
            endif
            loop //Check for Item in Inventory
                set indexItem = UnitItemInSlot(.u, index)
                if indexItem != null and GetItemType(indexItem) == ITEM_TYPE_CAMPAIGN then
                    call SetItemPosition(indexItem, GetUnitX(.u), GetUnitY(.u))
                endif
                set index = index + 1
                exitwhen index > bj_MAX_INVENTORY
            endloop
            call GroupAddUnit(Dead, .u)
            set Bat = CreateUnit(.owner, 'nshf', GetUnitX(.u), GetUnitY(.u), 270)
            call SetUnitInvulnerable(Bat, true)
            set i = 0 //no need for another variable for a loop, thus i isn't required anymore
            loop
            exitwhen i == 13
                if Player(i) != .owner then
                    call SetPlayerAlliance(.owner, Player(i), ALLIANCE_SHARED_VISION, false)
                endif
                set i = i + 1
            endloop
        elseif ReviveHero(.u, X, Y, false) then
            if GetUnitTypeId(.u) == 'GBaF' or GetUnitTypeId(.u) == 'OC91' then
                if GetUnitAbilityLevel(.u, 'fbfl') > 0 then
                    call UnitAddAbility(.u, 'Hunt')
                    call SetUnitAbilityLevel(.u, 'Hunt', GetUnitAbilityLevel(.u, 'fbfl'))
                endif
                if GetUnitAbilityLevel(.u, 'frdr') > 0 then
                    call UnitAddAbility(.u, 'rend')
                    call SetUnitAbilityLevel(.u, 'rend', GetUnitAbilityLevel(.u, 'frdr'))
                endif
            endif
            if (GetLocalPlayer() == .owner) then
                call TimerDialogDisplay(.timdia, false)
                call PanCameraTo(X, Y)
                set fxpath = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
                call ClearSelection()
                call SelectUnit(.u, true)
            else
                set fxpath = "Abilities\\Spells\\Human\\ReviveHuman\\ReviveHuman.mdl"
            endif
            call DestroyEffect(AddSpecialEffectTarget(fxpath, .u, "origin"))
        else
            call BJDebugMsg("This hero failed to revive for unknown reasons.")
        endif
        
        //cleanup struct data
        call ReleaseTimer(GetExpiredTimer())
        call DestroyTimerDialog(.timdia)
        call SetUnitInvulnerable(.u, true)
        call TimerStart(NewTimerEx(this), 10, false, function thistype.invulnOff)
        
        set Bat = null
    endmethod

    private static method create takes unit died returns thistype
        local thistype this = thistype.allocate()
        local real dur = 5 + 0.2 * GetHeroLevel(died)
        local timer tim = NewTimerEx(this)

        set .u = died //Store the unit in the struct
        set .owner = GetOwningPlayer(.u) //Just store the owner within the struct
        set .timdia = CreateTimerDialog(tim)
        call GroupRemoveUnit(udg_RoamingGroup, .u)
        call GroupRemoveUnit(udg_BurnGroup, .u)
        call DestroyEffect(FireFX[GetUnitId(.u)])

        call TimerDialogSetTitleColor(.timdia, GetPlayerTagColor(.owner, "red"), GetPlayerTagColor(.owner, "green"), GetPlayerTagColor(.owner, "blue"), 255)
        call TimerDialogSetTimeColor(.timdia, 255, 255, 255, 255)
        call TimerDialogSetTitle(.timdia, "Respawn")
    
        set lives[GetPlayerId(.owner)] = lives[GetPlayerId(.owner)] - 1

        if IsUnitInForce(.u, BloodPack) then
            call DisplayTextToPlayer(GetLocalPlayer(), 0.00, 0.00, GetPlayerName(.owner) + " of the Blood Pack has perished. It has " + I2S(lives[GetPlayerId(owner)]) + " lives left." )
        elseif IsUnitInForce(.u, FangSquad) then
            call DisplayTextToPlayer(GetLocalPlayer(), 0.00, 0.00, GetPlayerName(.owner) + " of the Fang Squad has perished. It has " + I2S(lives[GetPlayerId(owner)]) + " lives left." )
        elseif IsUnitInForce(.u, HellHorde) then
            call DisplayTextToPlayer(GetLocalPlayer(), 0.00, 0.00, GetPlayerName(.owner) + " of the Hell Horde has perished. It has " + I2S(lives[GetPlayerId(owner)]) + " lives left." )
        elseif IsUnitInForce(.u, CrimsonHunters) then
            call DisplayTextToPlayer(GetLocalPlayer(), 0.00, 0.00, GetPlayerName(.owner) + " of the Crimson Hunters has perished. It has " + I2S(lives[GetPlayerId(owner)]) + " lives left." )
        elseif IsUnitInForce(.u, VileLegion) then
            call DisplayTextToPlayer(GetLocalPlayer(), 0.00, 0.00, GetPlayerName(.owner) + " of the Vile Legion has perished. It has " + I2S(lives[GetPlayerId(owner)]) + " lives left." )
        else
            call DisplayTextToPlayer(GetLocalPlayer(), 0.00, 0.00, GetPlayerName(.owner) + " of the rogue Vampires has perished. It has " + I2S(lives[GetPlayerId(owner)]) + " lives left." )
        endif

        if GetLocalPlayer() == .owner then
            call TimerDialogDisplay(.timdia, true)
        endif

        call TimerStart(tim, dur, false, function thistype.onExpire) //The timer callback function
     return this
    endmethod

    private static method Actions takes nothing returns nothing
     local unit d = GetDyingUnit()
     local player p = GetOwningPlayer(d)
        if(IsUnitType(d, UNIT_TYPE_HERO) and IsUnitType(d, UNIT_TYPE_UNDEAD))and(GetPlayerController(p) == MAP_CONTROL_USER and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING) and d != Tynan then
            debug call BJDebugMsg(GetUnitName(d) + " is a valid, dying unit.")
            if Mode == "Haven Control" then
                if LANGUAGE[GetPlayerId(p)] == "francais" then
                    call DisplayTextToPlayer(p, 0, 0, "Vous êtes mort. Vous devez attendre jusqu'à la tombée de la nuit avant de ressusciter.")
                    call DisplayTextToPlayer(p, 0, 0, "|cffff0000Tous les refuges sous votre contrôle vous ont été retirés.|r")
                else
                    call DisplayTextToPlayer(p, 0, 0, "You have died. You must wait until nightfall before you revive.")
                    call DisplayTextToPlayer(p, 0, 0, "|cffff0000All your Havens have been released from your control.|r")
                endif
                call GroupAddUnit(REVIVE_GROUP, d)
                set TEMP = d
                call GroupEnumUnitsInRect(ENUM_GROUP, bj_mapInitialPlayableArea, function ReleaseHavensFromPlayer)
            else
                debug call BJDebugMsg("No haven control.")
                call thistype.create(d)
            endif
        endif
     set d = null
     set p = null
    endmethod

//===========================================================================
    private static method onInit takes nothing returns nothing
     local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
        call TriggerAddAction(t, function thistype.Actions )
        set HavenX[1] = -11072.//Top Left
        set HavenY[1] = 9200.
        set HavenX[2] = -4400.//By North Queen
        set HavenY[2] = 10400.
        set HavenX[3] = -11114.//By the Western Waters
        set HavenY[3] = -975.
        set HavenX[4] = -5350.//Cliff near Graham
        set HavenY[4] = 2200.
        set HavenX[5] = -7950.//Forest Village
        set HavenY[5] = -1150.
        set HavenX[6] = 3958.//Beach
        set HavenY[6] = 8763.
        set HavenX[7] = 4822.//Eastern Wall North
        set HavenY[7] = 1844.
        set HavenX[8] = 10090.//QOTD
        set HavenY[8] = 10720.
        set HavenX[9] = 8652.//Eastern Park
        set HavenY[9] = -8843.
        set HavenX[10] = 6191.//Near Salamar
        set HavenY[10] = -7309.
        set HavenX[11] = 0.//Southern Waters
        set HavenY[11] = -11400.
        set HavenX[12] = -11111.//South West Cliffs
        set HavenY[12] = -10000.
        set HavenX[13] = -3756.//Southern Trees
        set HavenY[13] = -11566.
        set HavenX[14] = -2736.//Under Palace
        set HavenY[14] = -7340.
        set HavenX[15] = -5830.//Near Galant
        set HavenY[15] = 5450.
        set HavenX[16] = 2514.//East of Palace
        set HavenY[16] = -6501.
        set HavenX[17] = -4471.//Graveyard
        set HavenY[17] = -3195.
    endmethod

endstruct
 
Level 13
Joined
Jul 26, 2008
Messages
1,009
Thanks. All my maps are rather old and thus have been running on old, outdated systems. Overhauling pivotal engines, while normally not standard practice, seems like the way to go in this case where the bugs are game-breaking.

EDIT: By the way, is there a way to tell where an error like this is coming from:
[TimerUtils]Error: Tried to release a non-active Timer!

I'm getting the error in some odd places and can't find the source.

EDIT2: On an unfortunate note I'm still getting this odd bug where the Timer will just completely freeze and not count down to execute it's events.
 
Last edited:
Level 13
Joined
Jul 26, 2008
Messages
1,009
Bump for new issues:

On an unfortunate note I'm still getting this odd bug where the Timer will just completely freeze and not count down to execute it's events.

This seems to happen whether it's counting on a periodic or one-shot.

Is there a chance it's in anyway related to this:

By the way, is there a way to tell where an error like this is coming from:
[TimerUtils]Error: Tried to release a non-active Timer!

I'm getting this error in some odd places and can't find the source.

This issue has been occuring for a while, and is rather hard to explain. One-shot timers freezing in the middle of the countdown is just odd.
 
Level 13
Joined
Jul 26, 2008
Messages
1,009
Wish there was an easier way than added a debug to all 100+ timers then removing it from all 100+ timers. The problem isn't that serious either is it?

Well the problem is that before the timer can even Release/Destroy/Pauses it's frozen.

Like I do TimerStart(NewTimer(), etc.) in a private struct and before it can execute the callback it just freezes and never reaches the callback. I've even seen the countdown value of a Timer jump from 7 to 200 through Timer Dialog. With the new system it just freezes at 7 now.
 
Level 13
Joined
Jul 26, 2008
Messages
1,009
I imagine stack over flow. Should I raise/lower the stack quantity as well?

Testing the results of that may take a little bit of time. I assume I'm just looking to make sure the timer still function properly up to stack overflow.

EDIT: The result is a lot of double frees, which is what I expected. Still testing bugs and failure to countdown.
 
Last edited:
Status
Not open for further replies.
Top