• 🏆 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] Handle Counter

Level 22
Joined
Dec 31, 2006
Messages
2,216
This is a simple system that I made which count handles and I'm pretty sure it gives an accurate number.


JASS:
library HandleCounter

globals
    private constant integer safety = 50
endglobals

function CountHandles takes nothing returns integer
    local integer i = 0
    local integer i2 = 0
    local integer space = 0
    local integer space2 = 0
    local integer count = 0
    local location array loc
    local integer hid = 0
    local integer hid2
    loop
        set loc[i] = Location(0., 0.)
        set hid2 = GetHandleId(loc[i]) - 0x100000
        if hid2 == hid + 1 or hid == 0 then
            set space = space + 1
            set space2 = space2 + 1
        else
            set space2 = 0
        endif
        set hid = hid2
        set i = i + 1
        set count = hid
        exitwhen space2 == safety
    endloop
    loop
        exitwhen i2 > i
        call RemoveLocation(loc[i2])
        set loc[i2] = null
        set i2 = i2 + 1
    endloop
    return count - space - safety - 1
endfunction

endlibrary

It works by creating locations and then finds their handle id and removes 0x100000 from it. The safety is for safety lolz. The higher safety you have the higher chance it is for actually being correct, but it will also increase the number of locations created. The safety is added because sometimes handles are destroyed and when it creates a location it will take that handles place and if it returns the handle id - 0x100000 from it, it will only return the slot for the removed handle and not the total amount of handles.
Ex:
1 - 2 - 3 - 4 - 5 //These are handles
1 - 2 - . - 4 - 5 //Handle 3 is destroyed
1 - 2 - 3 - 4 - 5 //The location fills it place and returns 3
I'm not 100% sure it's exactly that way it happens, but when I tried it without safety and I randomly removed handles the handle counter gave me totally different numbers each time.
 
Last edited:
Level 11
Joined
Nov 4, 2007
Messages
337
private constant integer MAX_GAME_DURATION = 5
constant boolean AID = false

function PrintAID takes nothing returns nothing
local real HandlesNeeded = GameTime/(BIGGEST-SMALLEST)*(MAX_GAME_DURATION*60)+SMALLEST+5000
call DisplayTextToForce(GetPlayersAll(),I2S(R2I(HandlesNeeded))+" is probably gonna be the biggest handle Index in your map. Now "+I2S(BIGGEST) +" was the biggest Index.")
endfunction

function HAID takes nothing returns nothing
local unit t = CreateUnit(Player(13),'hpea',0.,0.,0.)
call startAID(t)
call RemoveUnit(t)
endfunction

if ( AID ) then
set GameTimeWanted = true
set t = CreateTimer()
set SMALLEST = h2i(t)-tosub
call TimerStart(CreateTimer(),10.,true,function HAID)
call DestroyTimer(t)
endif

if ( GameTimeWanted ) then
call TimerStart(CreateTimer(),1.,true,function Gametime)
endif

globals
integer BIGGEST = 0
integer SMALLEST = 0
//integer HandlesNeeded = 0
integer seconds = 0
integer hours = 0
integer minutes = 0
integer ElapsedMinutes = 0
integer GameTime = 0
endglobals

function startAID takes handle h returns nothing
local integer i = h2i(h)-tosub
if ( i > BIGGEST ) then
set BIGGEST = i
endif
endfunction

function Gametime takes nothing returns nothing
set GameTime = GameTime + 1
endfunction


===> The Safety is not needed!
Add the feature, that the system calcualtes, the Handles taht will be created in X minutes
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
No, it's useful for map testing. It counts handles in the map, and from here, you can tweak various systems and so on to run more efficiently by setting standard handle counts. This would really only be useful for maps with a limited amount of units on the map, or a with a very low probability that there will be an increased amount of units over the average.

It's quite useful as a testing and dev function for high quality maps ; ).

MapperMalte totally gets it because I believe he's been doing stuff like this for a bit ; ).
 
Level 8
Joined
Jun 28, 2008
Messages
356
This is the trigger I'm testing for leaks:
JASS:
function Trig_Sprint_Actions takes nothing returns nothing

    local integer i = 0
    
    loop   
        if(GetUnitAbilityLevel(player_hero[i], 'B001') > 0) then
            call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl", GetUnitX(player_hero[i]), GetUnitY(player_hero[i])))
        endif
        set i = i + 1
        exitwhen i == 12
    endloop
    
endfunction

function InitTrig_Sprint takes nothing returns nothing

    set gg_trg_Sprint = CreateTrigger()
    call TriggerAddAction(gg_trg_Sprint, function Trig_Sprint_Actions)
    call TriggerRegisterTimerEvent(gg_trg_Sprint, 0.25, true)
    
endfunction

This is how I implemented the Handle Counter:
JASS:
library HandleCounter

globals
    private constant integer safety = 50
endglobals

function CountHandles takes nothing returns integer
    local integer i = 0
    local integer i2 = 0
    local integer space = 0
    local integer space2 = 0
    local integer count = 0
    local location array loc
    local integer hid = 0
    local integer hid2
    loop
        set loc[i] = Location(0., 0.)
        set hid2 = GetHandleId(loc[i]) - 0x100000
        if hid2 == hid + 1 or hid == 0 then
            set space = space + 1
            set space2 = space2 + 1
        else
            set space2 = 0
        endif
        set hid = hid2
        set i = i + 1
        set count = hid
        exitwhen space2 == safety
    endloop
    loop
        exitwhen i2 > i
        call RemoveLocation(loc[i2])
        set loc[i2] = null
        set i2 = i2 + 1
    endloop
    return count - space - safety - 1
endfunction

endlibrary

globals

    leaderboard display

endglobals

function Trig_Handle_Counter_Actions takes nothing returns nothing
    
    call LeaderboardSetItemValue(display, 0, CountHandles())
    
endfunction

function ldbd takes nothing returns nothing

    set display = CreateLeaderboard()
    call PlayerSetLeaderboard(Player(0), display)
    call LeaderboardAddItem(display, "Handles: ", CountHandles(), Player(0))
    call LeaderboardDisplay(display, true)
    call LeaderboardSetLabel(display, "Handle Counter")
    call LeaderboardSetSizeByItemCount(display, 2)
    
endfunction

function InitTrig_Handle_Counter takes nothing returns nothing

    local trigger trig = CreateTrigger()
    set gg_trg_Handle_Counter = CreateTrigger()
    
    call TriggerAddAction(trig, function ldbd)
    call TriggerRegisterTimerEvent(trig, 0.10, false)
    call TriggerAddAction(gg_trg_Handle_Counter, function Trig_Handle_Counter_Actions)
    call TriggerRegisterTimerEvent(gg_trg_Handle_Counter, 0.20, true)
    
    set trig = null
    
endfunction

When I cast the spell the handles go on increasing to about 1000-1100 by the end of the spell. Even tho the effects get destroyed the handles do not go back. They remain 1000. What am I doing wrong? I don't see anything leaking in the triggers :S
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I did some tests a few months ago where I was destroying effects immediately and displaying their handle id (wanted to see how high the stack went) the handle count kept climbing until it reached what I assume to be 0x1FFFFF, and then it went back down to 0x100000

There is no stack-fragmentation in the current build of Warcraft 3. At least, not from those tests.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
I did some tests a few months ago where I was destroying effects immediately and displaying their handle id (wanted to see how high the stack went) the handle count kept climbing until it reached what I assume to be 0x1FFFFF, and then it went back down to 0x100000

There is no stack-fragmentation in the current build of Warcraft 3. At least, not from those tests.

but the handle ids take up space = |. wc3 does indeed continue to recycle handles if they aren't being used, even if not all references to them are nulled, but it's still better to recycle them immediately than much later.

Not recycling handle ids is known to lag games.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
That was part of a test I did to test whether nulling variables was necessary. I was testing it using local effects:

JASS:
function test takes nothing returns nothing
    local effect e = AddSpecialEffect("path", 0., 0.)
    call BJDebugMsg(I2S(GetHandleId(e)))
    call DestroyEffect(e)
endfunction

That started to cause extreme lag after some time.

But when I did this:

JASS:
function test takes effect e returns nothing
    call BJDebugMsg(I2S(GetHandleId(e)))
    call DestroyEffect(e)
endfunction

function test2 takes nothing returns nothing
    call test(AddSpecialEffect("path", 0., 0.))
endfunction

that's how I found out that parameters don't need to be nulled.

In fact, now that I think of it, the handle stack seemed to revert back to 0x100000 after a shorter time when I was nulling the variables as opposed to not nulling them. It was only going something like 1000 (base 10) integers high and then switching back... don't quote me on that.
 
Top