HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Handle Counter

03-07-2009, 02:38 PM#1
Tyrande_ma3x
I know there are a few handle counters out there but they're simply just copied out from some test map or created for temporary usage. And since there isn't one at this site's Scripts section I decided to make one. It shows maximum handles reached and game time. The game time is optional and can be disabled if you would like. Refresh rate of the handle counter can be specified, as well as the leaderboard's colours. You have access to the leaderboard if you'd like to change/destroy it (HandleCounter_LEADERBOARD).

This is useful in debug cases where you'd like to see if your map is leaking a lot of handles since it probes only the top of the free stack, otherwise it isn't really needed.

Requires a vJASS compiler (JASS NewGen Pack). To implement just create a trigger named HandleCounter, convert it to custom script, delete everything and paste this text:
(has 2 "versions" - for 1.23 and bellow and for 1.24 (and 1.24b) (due to the new GetHandleId() native and no return bug))
Collapse JASS:
//===========================================================================
//    A simple handle counter that shows:
//          * Maximum handles reached
//          * Elapsed seconds
//
//=== FOR VERSION 1.23 AND BELLOW ONLY ===
//
//    The leaderboard can be accessed with
//          HandleCounter_LEADERBOARD
//    The clock (timer) can be accessed with
//          HandleCounter_CLOCK
//
//    This could be useful only if you'd like to see
//    if your map is leaking a lot of handles.
//    Otherwise it isn't really needed.
//
//    Requires a vJASS compiler to work.
//
//    To use: 
//          Create a trigger named HandleCounter,
//          convert to custom script and paste this.
//===========================================================================
library HandleCounter initializer Init 

//===========================================================================
// Configurables
//===========================================================================
globals
// Leaderboard title/name.
    private constant string TITLE = "Handle Counter"
// Text that represents the time.
    private constant string TIME_TEXT = "Elapsed Time:"
// Should the leaderboard show time as well? True or false.
    private constant boolean SHOW_TIME = true
// How often should the handle count update (of course this is not for the clock).
    private constant real UPDATE_PERIOD = 0.25
// Number of locations created for better counting result.
    private constant integer STACK_END_TRESHOLD = 15
    
// Player(0) = Player 1 (Red); Player(1) = Player 2 (Blue); etc.
    
// Player color of the maximum handles text.
    private constant player MAX = Player(0)
// Player color of the time text.
    private constant player TIME = Player(1)
endglobals

//===========================================================================
// Do not edit bellow this point unless you know what you're doing.
//===========================================================================

globals
    public leaderboard LEADERBOARD 
    public timer CLOCK 
    private integer CURRENT_HANDLE = 0
    private integer MAX_HANDLE = 0
    private integer SECONDS = 0
endglobals

private function L2I takes location loc returns integer
    return loc
    return 0
endfunction

private function UpdateTime takes nothing returns nothing
    set SECONDS = SECONDS + 1
    call LeaderboardSetItemLabel(LEADERBOARD, 1, TIME_TEXT)
    call LeaderboardSetItemValue(LEADERBOARD, 1, SECONDS)
endfunction
        
private function Callback takes nothing returns nothing
    local location array loc
    local integer a = 0
    local integer index 
    local integer prevIndex = 0
    local integer next = 0
    loop
        set loc[a] = Location(0., 0.)
        set index = L2I(loc[a]) - 0x100000
        set a = a + 1
        if index == prevIndex + 1 then
            set next = next + 1
        else
            set next = 0
        endif
        set prevIndex = index
        exitwhen next >= STACK_END_TRESHOLD
    endloop
    call LeaderboardSetItemValue(LEADERBOARD, 0, index - a)
    loop
        set a = a - 1
        call RemoveLocation(loc[a])
        set loc[a] = null
        exitwhen a <= 0
    endloop
endfunction

private function Actions takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    set LEADERBOARD = CreateLeaderboard()
    call LeaderboardSetLabel(LEADERBOARD, TITLE)
    call PlayerSetLeaderboard(GetLocalPlayer(), LEADERBOARD)
    call LeaderboardDisplay(LEADERBOARD, true)
    call TimerStart(tim, UPDATE_PERIOD, true, function Callback)
    call LeaderboardAddItem(LEADERBOARD, "Max Handles", 0, MAX)
    call LeaderboardSetSizeByItemCount(LEADERBOARD, 1)
    if SHOW_TIME then
        set CLOCK = CreateTimer()
        call TimerStart(CLOCK, 1., true, function UpdateTime)
        call LeaderboardAddItem(LEADERBOARD, "", 1, TIME)
        // Prevent leaderboard showing "1" during the first second.
        call LeaderboardSetItemLabel(LEADERBOARD, 1, TIME_TEXT)
        call LeaderboardSetItemValue(LEADERBOARD, 1, SECONDS)
        call LeaderboardSetSizeByItemCount(LEADERBOARD, 2)
    endif
    set tim = null
endfunction

private function Init takes nothing returns nothing
    call TimerStart(CreateTimer() , 0., false, function Actions)
endfunction

endlibrary    

Collapse JASS:
//===========================================================================
//    A simple handle counter that shows:
//          * Maximum handles reached
//          * Elapsed seconds
//
//========== FOR VERSION 1.24+ ONLY ==========
//
//    The leaderboard can be accessed with
//          HandleCounter_LEADERBOARD
//    The clock (timer) can be accessed with
//          HandleCounter_CLOCK
//
//    This could be useful only if you'd like to see
//    if your map is leaking a lot of handles.
//    Otherwise it isn't really needed.
//
//    Requires a vJASS compiler to work.
//
//    To use: 
//          Create a trigger named HandleCounter,
//          convert to custom script and paste this.
//===========================================================================
library HandleCounter initializer Init 

//===========================================================================
// Configurables
//===========================================================================
globals
// Leaderboard title/name.
    private constant string TITLE = "Handle Counter"
// Text that represents the time.
    private constant string TIME_TEXT = "Elapsed Time:"
// Should the leaderboard show time as well? True or false.
    private constant boolean SHOW_TIME = true
// How often should the handle count update (of course this is not for the clock).
    private constant real UPDATE_PERIOD = 0.25
// Number of locations created for better counting result.
    private constant integer STACK_END_TRESHOLD = 15
    
// Player(0) = Player 1 (Red); Player(1) = Player 2 (Blue); etc.
    
// Player color of the maximum handles text.
    private constant player MAX = Player(0)
// Player color of the time text.
    private constant player TIME = Player(1)
endglobals

//===========================================================================
// Do not edit bellow this point unless you know what you're doing.
//===========================================================================

globals
    public leaderboard LEADERBOARD = null 
    public timer CLOCK = null
    private integer CURRENT_HANDLE = 0
    private integer MAX_HANDLE = 0
    private integer SECONDS = 0
endglobals

private function UpdateTime takes nothing returns nothing
    set SECONDS = SECONDS + 1
    call LeaderboardSetItemLabel(LEADERBOARD, 1, TIME_TEXT)
    call LeaderboardSetItemValue(LEADERBOARD, 1, SECONDS)
endfunction
        
private function Callback takes nothing returns nothing
    local location array loc
    local integer a = 0
    local integer index 
    local integer prevIndex = 0
    local integer next = 0
    loop
        set loc[a] = Location(0., 0.)
        set index = GetHandleId(loc[a]) - 0x100000
        set a = a + 1
        if index == prevIndex + 1 then
            set next = next + 1
        else
            set next = 0
        endif
        set prevIndex = index
        exitwhen next >= STACK_END_TRESHOLD
    endloop
    call LeaderboardSetItemValue(LEADERBOARD, 0, index - a)
    loop
        set a = a - 1
        call RemoveLocation(loc[a])
        set loc[a] = null
        exitwhen a <= 0
    endloop
endfunction

private function Actions takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    set LEADERBOARD = CreateLeaderboard()
    call LeaderboardSetLabel(LEADERBOARD, TITLE)
    call PlayerSetLeaderboard(GetLocalPlayer(), LEADERBOARD)
    call LeaderboardDisplay(LEADERBOARD, true)
    call TimerStart(tim, UPDATE_PERIOD, true, function Callback)
    call LeaderboardAddItem(LEADERBOARD, "Max Handles", 0, MAX)
    call LeaderboardSetSizeByItemCount(LEADERBOARD, 1)
    if SHOW_TIME then
        set CLOCK = CreateTimer()
        call TimerStart(CLOCK, 1., true, function UpdateTime)
        call LeaderboardAddItem(LEADERBOARD, "", 1, TIME)
        // Prevent leaderboard showing "1" during the first second.
        call LeaderboardSetItemLabel(LEADERBOARD, 1, TIME_TEXT)
        call LeaderboardSetItemValue(LEADERBOARD, 1, SECONDS)
        call LeaderboardSetSizeByItemCount(LEADERBOARD, 2)
    endif
    set tim = null
endfunction

private function Init takes nothing returns nothing
    call TimerStart(CreateTimer() , 0., false, function Actions)
endfunction

endlibrary    
Attached Images
File type: gifhc.gif (6.6 KB)
03-07-2009, 03:14 PM#2
Vexorian
Collapse JASS:
call TimerStart(GetExpiredTimer(), UPDATE_PERIOD, true, function Callback)
And forget about the part that pauses/destroys the timer.

The rest seems fine.
03-07-2009, 03:36 PM#3
Tyrande_ma3x
> And forget about the part that pauses/destroys the timer.
Right. Just give me a minute.

EDIT: Ready.
03-07-2009, 05:48 PM#4
C2H3NaO2
What about making it, to show the time in the usual way?

03:01:32
instead of
3:1:32

you should btw set loc to null and not leak the timer in init ~
03-07-2009, 05:58 PM#5
Tyrande_ma3x
> What about making it, to show the time in the usual way?
Like, uh, how?

> you should btw set loc to null
What's the point, it's going to be reassigned anyway?

> and not leak the timer in init
I thought it gets used during the whole time since I start it again in Actions... and it runs forever.
03-07-2009, 06:44 PM#6
Vexorian
Usual time: 00:30:03

I think that would be a waste of resources, in fact, just show elapsed seconds :)
03-07-2009, 07:33 PM#7
PipeDream
You should mention that since this probes only the top of the free stack it is exact only for heavily leaking maps, though I'm sure it works pretty well in practice anyway. War3err achieves exactness by cheating.
03-07-2009, 09:35 PM#8
Eithyr
>Does not require anything.

It requires a vJASS compiler, though I know your thinking systemwise :P
03-08-2009, 09:09 AM#9
Tyrande_ma3x
> It requires a vJASS compiler, though I know your thinking systemwise :P
Correct, I forgot about that. Though it should be obvious when someone sees the beginning "library".

> You should mention that since this probes only the top of the free stack it is exact only for heavily leaking maps
Done.

> in fact, just show elapsed seconds
Might be better since it wouldn't be used permanently in maps (I guess). Done as well.

EDIT: I added a new picture :P
03-09-2009, 02:51 PM#10
Rising_Dusk
You leak the timer used in Init if SHOW_TIME is set to false. If you fix that, it appears Vex liked it well enough that it can be approved.
03-09-2009, 06:43 PM#11
Tyrande_ma3x
> You leak the timer used in Init if SHOW_TIME is set to false.
Thanks for pointing it out. Will fix.

EDIT: Is it fine now?
03-09-2009, 07:01 PM#12
Anitarf
Right now, the CURRENT_HANDLE is a rather meaningless value as it can recycle pretty much a random handle index; it is only useful for approximating the max handle index but it can take a while to do so. Have you considered instead creating multiple locations until a certain (calibratable) number of them get consecutive handle adresses, which would imply the top of the stack with greater certainty, then you could get the exact number of handles by subtracting the number of locations created from the max location handle id? It's more work for the computer but such a function wouldn't need to be run as often.
03-09-2009, 07:42 PM#13
Tyrande_ma3x
From that part I understood that I should create more locations, right? How am I going to substract the number from all of them? I may need an example here since I'm not very familiar with handles and such...
03-09-2009, 10:18 PM#14
akolyt0r
Hmm ...i created a handle counter for myself some time ago ..
it displayed:
-max handle counter (well the same as yours)
-average handle counter of last X updates (i use 50 most of the time ..obviously the value of that X depends on the update interval)

worked quite well for me..
03-10-2009, 12:10 AM#15
Anitarf
Quote:
Originally Posted by Tyrande_ma3x
From that part I understood that I should create more locations, right? How am I going to substract the number from all of them? I may need an example here since I'm not very familiar with handles and such...
Something like this:
Collapse JASS:
globals
    private constant integer STACK_END_THRESHOLD = 5
endglobals

private function Callback takes nothing returns nothing
    local integer consecutive=0
    local integer i=0
    local location array l
    local integer locIndex
    local integer prevIndex=0

    loop
        set l[i] = Location(0., 0.)
        set locIndex = L2I(l[i]) - 0x100000
        set i = i + 1
        if locIndex = prevIndex + 1 then
            set consecutive = consecutive + 1
        else
            set consecutive = 0
        endif
        set prevIndex=locIndex
        exitwhen consecutive >= STACK_END_THRESHOLD
    endloop

    call LeaderboardSetItemValue(LEADERBOARD, 0, (locIndex - i))

    loop
        set i = i - 1
        call RemoveLocation(l[i])
        set l[i] = null
        exitwhen i == 0
    endloop
endfunction

The assumption is that once you fill all the "holes" (left behind by removed handles) in the handle stack and reach the "top", the new indexes that will get assigned to the locations after that will be consecutive, however before that point getting multiple consecutive handle ids is unlikely. This way, the handle id obtained in the end has a high probability (the higher the threshold, the higher the probability, but also the more overhead) of being the highest handle id, by subtracting from it the number of locations it took to get to it you get the exact total number of handles.