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

Agent type counter

Status
Not open for further replies.
Level 13
Joined
Nov 7, 2014
Messages
571
Atc - Agent type counter

Atc counts the agents (handle types) stored in the handle table. I think these counts can give an insight into the robustness of a resource (spell, system, etc.) by answering the question of whether something leaks or not.

You can configure the script to only show specific agents like units, locations and groups.
By default the agent counts are shown in a leaderboard but you can disable that if you want to do something else with the data.

JASS:
library Atc /*
*/ uses /*
*/     Memory, /* // both libraries can be obtained from:
*/     Version /* // https://www.hiveworkshop.com/threads/accessing-memory-from-the-script-its-time-of-the-revolution.279262/
*/

globals
    // set this to false if you want to show the count information some other way (with a multiboard)
    // or just print it to the screen
    private constant boolean Show_Counts_On_Leaderboard = true

    private constant real Leaderboard_Update_Interval = 0.1 // in seconds

    private constant boolean Show_Force_Count = true
    private constant boolean Show_Player_Count = false
    private constant boolean Show_Group_Count = true
    private constant boolean Show_Unit_Count = true
    private constant boolean Show_Item_Count = false
    private constant boolean Show_Destructable_Count = false
    private constant boolean Show_Location_Count = true
    private constant boolean Show_Rect_Count = false
    private constant boolean Show_Region_Count = false
    private constant boolean Show_Trigger_Count = true
    private constant boolean Show_Boolexpr_Count = false
    private constant boolean Show_Triggercondition_Count = false
    private constant boolean Show_Triggeraction_Count = false
    private constant boolean Show_Unitpool_Count = false
    private constant boolean Show_Itempool_Count = false
    private constant boolean Show_Timer_Count = true
    private constant boolean Show_Timerdialog_Count = false
    private constant boolean Show_Sound_Count = false
    private constant boolean Show_Camerasetup_Count = false
    private constant boolean Show_Effect_Count = true
    private constant boolean Show_Fogmodifier_Count = false
    private constant boolean Show_Dialog_Count = false
    private constant boolean Show_Button_Count = false
    private constant boolean Show_Multiboard_Count = false
    private constant boolean Show_Multiboarditem_Count = false
    private constant boolean Show_Leaderboard_Count = false
    private constant boolean Show_Trackable_Count = false
    private constant boolean Show_Gamecache_Count = false
    private constant boolean Show_Hashtable_Count = false
    private constant boolean Show_Event_Count = false

    private constant boolean Show_Leaked_Handle_Ids_Count = true
    private constant boolean Show_Leaked_Handles_Count = true
    private constant boolean Show_All_Count = false
endglobals


static if DEBUG_MODE then

private function to_hex takes integer i returns string
    local string hex_digits = "0123456789ABCDEF"
    local string result = ""
    local integer n
    local integer d
    local integer t
    local boolean is_neg

    set is_neg = i < 0
    if is_neg then
        set i = i + 0x80000000
    endif

    set n = 0
    loop
        set n = n + 1

        set t = i / 16
        set d = i - t * 16
        set i = t

        set result = SubString(hex_digits, d, d + 1) + result

        exitwhen n == 7
    endloop

    if is_neg then
        set i = i + 0x8
    endif

    set result = SubString(hex_digits, i, i + 1) + result

    return result
endfunction

private function to_base256 takes integer i returns string
    local string digits = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
    local string result = ""
    local integer n
    local integer d
    local integer t

    set n = 0
    loop
        set n = n + 1

        set t = i / 256
        set d = i - t * 256
        set i = t

        set d = d - 0x21
        if d <= 94 then
            set result = SubString(digits, d, d + 1) + result
        else
            set result = "?" + result
        endif

        exitwhen n == 4
    endloop

    return result
endfunction

endif // DEBUG_MODE

struct Atc
    integer force_count = 0
    integer player_count = 0
    integer group_count = 0
    integer unit_count = 0
    integer item_count = 0
    integer destructable_count = 0
    integer location_count = 0
    integer rect_count = 0
    integer region_count = 0
    integer trigger_count = 0
    integer boolexpr_count = 0 // includes conditionfunc, filterfunc, 'bAnd', '+bOr' and 'bNot'
    integer triggercondition_count = 0
    integer triggeraction_count = 0
    integer unitpool_count = 0
    integer itempool_count = 0
    integer timer_count = 0
    integer timerdialog_count = 0
    integer sound_count = 0
    integer camerasetup_count = 0
    integer effect_count = 0
    integer fogmodifier_count = 0
    integer dialog_count = 0
    integer button_count = 0
    integer multiboard_count = 0
    integer multiboarditem_count = 0
    integer leaderboard_count = 0
    integer trackable_count = 0
    integer gamecache_count = 0
    integer hashtable_count = 0

    // includes:
    // 'vevt' -- TriggerRegisterVariableEvent
    // 'tmvt' -- TriggerRegisterTimerEvent
    // 'tmet' -- TriggerRegisterTimerExpireEvent
    // 'gfvt' -- TriggerRegisterGameStateEvent
    // 'gsvt' -- TriggerRegisterGameStateEvent
    // 'devt' -- TriggerRegisterDialogEvent
    // 'bevt' -- TriggerRegisterDialogButtonEvent
    // 'gevt' -- TriggerRegisterGameEvent
    // '+rev' -- TriggerRegisterEnterRegion, TriggerRegisterLeaveRegion
    // '+tev' -- TriggerRegisterTrackableHitEvent, TriggerRegisterTrackableTrackEvent
    // 'pevt' -- TriggerRegisterPlayerEvent, TriggerRegisterPlayerUnitEvent
    // 'alvt' -- TriggerRegisterPlayerAllianceChange
    // 'psvt' -- TriggerRegisterPlayerStateEvent
    // 'pcvt' -- TriggerRegisterPlayerChatEvent
    // 'wdvt' -- TriggerRegisterDeathEvent
    // 'usvt' -- TriggerRegisterUnitStateEvent
    // 'uevt' -- TriggerRegisterUnitEvent
    // 'rgvt' -- TriggerRegisterUnitInRange
    //
    integer event_count = 0

    // these handle types are not stored in the handle table, i.e
    // they don't use handle-ids >= 0x00100000:
    // ubersplat, image, texttag, lightning, weathereffect, terraindeformation

    // the primitive types are: integer, real, boolean, string, code, handle
    // the rest of the types declared in common.j are just enums (integers)

    integer leaked_handle_ids_count = 0
    integer leaked_handles_count = 0
    integer all_count = 0

    integer force_leak_count = 0
    // integer player_leak_count = 0
    integer group_leak_count = 0
    integer unit_leak_count = 0
    // integer item_leak_count = 0
    // integer destructable_leak_count = 0
    integer location_leak_count = 0
    // integer rect_leak_count = 0
    // integer region_leak_count = 0
    // integer trigger_leak_count = 0
    // integer boolexpr_leak_count = 0
    // integer triggercondition_leak_count = 0
    // integer triggeraction_leak_count = 0
    // integer unitpool_leak_count = 0
    // integer itempool_leak_count = 0
    integer timer_leak_count = 0
    // integer timerdialog_leak_count = 0
    // integer sound_leak_count = 0
    // integer camerasetup_leak_count = 0
    integer effect_leak_count = 0
    // integer fogmodifier_leak_count = 0
    // integer dialog_leak_count = 0
    // integer button_leak_count = 0
    // integer multiboard_leak_count = 0
    // integer multiboarditem_leak_count = 0
    // integer leaderboard_leak_count = 0
    // integer trackable_leak_count = 0
    // integer gamecache_leak_count = 0
    // integer hashtable_leak_count = 0
    integer event_leak_count = 0

static if Show_Counts_On_Leaderboard then

    private static Atc c
    private static player local_player
    private static timer ticker
    private static leaderboard LBC
    private static leaderboard LBL
    private static integer shown_lb = 1 // 1 = LBC, 2 = LBL

    private static method LB_update takes nothing returns nothing
        local Atc c = Atc.c
        local integer i
        local integer ii

        if shown_lb == 1 then
            call PlayerSetLeaderboard(local_player, LBC)

            set i = 1
            set ii = LeaderboardGetItemCount(LBC)
            loop
                exitwhen i > ii
                call LeaderboardRemoveItem(LBC, 0)
                set i = i + 1
            endloop

static if Show_Force_Count then
            call LeaderboardAddItem(LBC, "force", c.force_count, local_player)
endif

static if Show_Player_Count then
            call LeaderboardAddItem(LBC, "player", c.player_count, local_player)
endif

static if Show_Group_Count then
            call LeaderboardAddItem(LBC, "group", c.group_count, local_player)
endif

static if Show_Unit_Count then
            call LeaderboardAddItem(LBC, "unit", c.unit_count, local_player)
endif

static if Show_Item_Count then
            call LeaderboardAddItem(LBC, "item", c.item_count, local_player)
endif

static if Show_Destructable_Count then
            call LeaderboardAddItem(LBC, "destructable", c.destructable_count, local_player)
endif

static if Show_Location_Count then
            call LeaderboardAddItem(LBC, "location", c.location_count, local_player)
endif

static if Show_Rect_Count then
            call LeaderboardAddItem(LBC, "rect", c.rect_count, local_player)
endif

static if Show_Region_Count then
            call LeaderboardAddItem(LBC, "region", c.region_count, local_player)
endif

static if Show_Trigger_Count then
            call LeaderboardAddItem(LBC, "trigger", c.trigger_count, local_player)
endif

static if Show_Boolexpr_Count then
            call LeaderboardAddItem(LBC, "boolexpr", c.boolexpr_count, local_player)
endif

static if Show_Triggercondition_Count then
            call LeaderboardAddItem(LBC, "triggercondition", c.triggercondition_count, local_player)
endif

static if Show_Triggeraction_Count then
            call LeaderboardAddItem(LBC, "triggeraction", c.triggeraction_count, local_player)
endif

static if Show_Unitpool_Count then
            call LeaderboardAddItem(LBC, "unitpool", c.unitpool_count, local_player)
endif

static if Show_Itempool_Count then
            call LeaderboardAddItem(LBC, "itempool", c.itempool_count, local_player)
endif

static if Show_Timer_Count then
            call LeaderboardAddItem(LBC, "timer", c.timer_count, local_player)
endif

static if Show_Dialog_Count then
            call LeaderboardAddItem(LBC, "timerdialog", c.timerdialog_count, local_player)
endif

static if Show_Sound_Count then
            call LeaderboardAddItem(LBC, "sound", c.sound_count, local_player)
endif

static if Show_Camerasetup_Count then
            call LeaderboardAddItem(LBC, "camerasetup", c.camerasetup_count, local_player)
endif

static if Show_Effect_Count then
            call LeaderboardAddItem(LBC, "effect", c.effect_count, local_player)
endif

static if Show_Fogmodifier_Count then
            call LeaderboardAddItem(LBC, "fogmodifier", c.fogmodifier_count, local_player)
endif

static if Show_Dialog_Count then
            call LeaderboardAddItem(LBC, "dialog", c.dialog_count, local_player)
endif

static if Show_Button_Count then
            call LeaderboardAddItem(LBC, "button", c.button_count, local_player)
endif

static if Show_Multiboard_Count then
            call LeaderboardAddItem(LBC, "multiboard", c.multiboard_count, local_player)
endif

static if Show_Multiboarditem_Count then
            call LeaderboardAddItem(LBC, "multiboarditem", c.multiboarditem_count, local_player)
endif

static if Show_Leaderboard_Count then
            call LeaderboardAddItem(LBC, "leaderboard", c.leaderboard_count, local_player)
endif

static if Show_Trackable_Count then
            call LeaderboardAddItem(LBC, "trackable", c.trackable_count, local_player)
endif

static if Show_Gamecache_Count then
            call LeaderboardAddItem(LBC, "gamecache", c.gamecache_count, local_player)
endif

static if Show_Hashtable_Count then
            call LeaderboardAddItem(LBC, "hashtable", c.hashtable_count, local_player)
endif

static if Show_Event_Count then
            call LeaderboardAddItem(LBC, "event", c.event_count, local_player)
endif

static if Show_Leaked_Handle_Ids_Count then
            call LeaderboardAddItem(LBC, "leaked handle ids", c.leaked_handle_ids_count, local_player)
endif

static if Show_Leaked_Handles_Count then
            call LeaderboardAddItem(LBC, "leaked handles", c.leaked_handles_count, local_player)
endif

static if Show_All_Count then
            call LeaderboardAddItem(LBC, "all", c.all_count, local_player)
endif

            call LeaderboardSetSizeByItemCount(LBC, LeaderboardGetItemCount(LBC))
            call LeaderboardSetLabelColor(LBC, /*R:*/ 0xFF, /*G:*/ 0xFF, /*B:*/ 0xFF, /*A:*/ 0xFF)
            call LeaderboardSetValueColor(LBC, /*R:*/ 0xFF, /*G:*/ 0xFF, /*B:*/ 0xFF, /*A:*/ 0xFF)
            call LeaderboardSortItemsByValue(LBC, /*ascending:*/ false)
            call LeaderboardDisplay(LBC, true)

        else // shown_lb == 2
            call PlayerSetLeaderboard(local_player, LBL)

            set i = 1
            set ii = LeaderboardGetItemCount(LBL)
            loop
                exitwhen i > ii
                call LeaderboardRemoveItem(LBL, 0)
                set i = i + 1
            endloop

            if c.force_leak_count != 0 then
                call LeaderboardAddItem(LBL, "force", c.force_leak_count, local_player)
            endif

            if c.group_leak_count != 0 then
                call LeaderboardAddItem(LBL, "group", c.group_leak_count, local_player)
            endif

            if c.unit_leak_count != 0 then
                call LeaderboardAddItem(LBL, "unit", c.unit_leak_count, local_player)
            endif

            if c.location_leak_count != 0 then
                call LeaderboardAddItem(LBL, "location", c.location_leak_count, local_player)
            endif

            if c.timer_leak_count != 0 then
                call LeaderboardAddItem(LBL, "timer", c.timer_leak_count, local_player)
            endif

            if c.effect_leak_count != 0 then
                call LeaderboardAddItem(LBL, "effect", c.effect_leak_count, local_player)
            endif

            if c.event_leak_count != 0 then
                call LeaderboardAddItem(LBL, "event", c.event_leak_count, local_player)
            endif

            call LeaderboardSetSizeByItemCount(LBL, LeaderboardGetItemCount(LBL))
            call LeaderboardSetLabelColor(LBL, /*R:*/ 0xFF, /*G:*/ 0xFF, /*B:*/ 0xFF, /*A:*/ 0xFF)
            call LeaderboardSetValueColor(LBL, /*R:*/ 0xFF, /*G:*/ 0xFF, /*B:*/ 0xFF, /*A:*/ 0xFF)
            call LeaderboardSortItemsByValue(LBL, /*ascending:*/ false)
            call LeaderboardDisplay(LBL, true)
        endif // shown_lb
    endmethod

endif // Show_Counts_On_Leaderboard


    // iterate the handle table's entries and count the different types of agents
    //
    // we try to avoid the op-limit
    private static Atc c_ef
    private static integer ptr_ef
    private static boolean done_ef
    private static force exec

    private static method count_ef takes nothing returns nothing
        // we make at most this many iterations so that we can avoid the op-limit
        local integer N = 1000

         // this many consecutive empty entries in the handle table denote its end;
         // we do it this way because we don't know how many entries are in the handle table =)
        local integer M = 5

        local integer m
        local integer p
        local integer q
        local integer rc // reference count
        local integer a // agent pointer
        local integer ty // agent type
        local Atc c
        local boolean leak

        set p = Atc.ptr_ef
        set c = Atc.c_ef
        loop
            set rc = Memory[p/4]

            set a = Memory[(p+0x04)/4]

            if a == 0 then
                if rc != 0 then
                    set c.all_count = c.all_count + 1
                    set c.leaked_handle_ids_count = c.leaked_handle_ids_count + 1
                else
                    set m = M - 1
                    set q = p + 0x0C

                    loop
                        exitwhen m == 0
                        if Memory[q/4] != 0 then
                            exitwhen true
                        endif
                        set q = q + 0x0C
                        set m = m - 1
                    endloop

                    if m == 0 then
                        set Atc.done_ef = true
                        exitwhen true
                    endif
                endif // rc != 0
            else
                set c.all_count = c.all_count + 1

                // get the agent's type;
                // note the unaligned memory read (Memory1);
                set ty = Memory1[Memory[(Memory[a/4] + 0x1C)/4]/4]

                set leak = rc == 1
                if leak then
                    set c.leaked_handles_count = c.leaked_handles_count + 1
                endif

                if ty == '+loc' then
                    set c.location_count = c.location_count + 1
                    if leak then
                        set c.location_leak_count = c.location_leak_count + 1
                    endif

                elseif ty == '+w3u' then
                    set c.unit_count = c.unit_count + 1
                    if leak then
                        set c.unit_leak_count = c.unit_leak_count + 1
                    endif

                elseif ty == '+EIP' or ty == '+EIm' then
                    set c.effect_count = c.effect_count + 1
                    if leak then
                        set c.effect_leak_count = c.effect_leak_count + 1
                    endif

                elseif ty == '+grp' then
                    set c.group_count = c.group_count + 1
                    if leak then
                        set c.group_leak_count = c.group_leak_count + 1
                    endif

                elseif ty == '+trg' then
                    set c.trigger_count = c.trigger_count + 1

                elseif ty == '+tmr'  then
                    set c.timer_count = c.timer_count + 1
                    if leak then
                        set c.timer_leak_count = c.timer_leak_count + 1
                    endif

                elseif ty == '+mbi' then
                    set c.multiboarditem_count = c.multiboarditem_count + 1

                elseif ty == 'item'  then
                    set c.item_count = c.item_count + 1

                elseif ty == '+rct' then
                    set c.rect_count = c.rect_count + 1

                elseif ty == '+dlb' then
                    set c.button_count = c.button_count + 1


                elseif ty == '+frc' then
                    set c.force_count = c.force_count + 1
                    if leak then
                        set c.force_leak_count = c.force_leak_count + 1
                    endif

                elseif ty == '+w3d' then
                    set c.destructable_count = c.destructable_count + 1

                elseif ty == '+flt' or ty == 'bAnd' or ty == '+bOr' or ty == 'bNot' then
                    set c.boolexpr_count = c.boolexpr_count + 1

                elseif ty == 'tcnd' then
                    set c.triggercondition_count = c.triggeraction_count + 1
                elseif ty == '+tac' then
                    set c.triggeraction_count = c.triggeraction_count + 1

                elseif ty == '+snd' then
                    set c.sound_count = c.sound_count + 1

                elseif ty == '+cst' then
                    set c.camerasetup_count = c.camerasetup_count + 1

                elseif ty == '+agr' then
                    set c.region_count = c.region_count + 1

                elseif ty == '+dlg' then
                    set c.dialog_count = c.dialog_count + 1

                elseif ty == '+mdb' then
                    set c.multiboard_count = c.multiboarditem_count + 1

                elseif ty == 'ghth' then
                    set c.hashtable_count = c.hashtable_count + 1

                elseif ty == 'gcch' then
                    set c.gamecache_count = c.gamecache_count + 1

                elseif ty == '+fgm' then
                    set c.fogmodifier_count = c.fogmodifier_count + 1

                elseif ty == '+ply' then
                    set c.player_count = c.player_count + 1

                elseif ty == '+ldb' then
                    set c.leaderboard_count = c.leaderboard_count + 1

                elseif ty == '+tid' then
                    set c.timerdialog_count = c.timerdialog_count + 1

                elseif ty == '+trk' then
                    set c.trackable_count = c.trackable_count + 1

                elseif ty == 'pool' then
                    set c.unitpool_count = c.unitpool_count + 1

                elseif ty == 'ipol' then
                    set c.itempool_count = c.itempool_count + 1

                elseif ty == 'pevt' /*
                */ or ty == 'uevt' /*
                */ or ty == 'usvt' /*
                */ or ty == 'psvt' /*
                */ or ty == 'tmet' /*
                */ or ty == 'tmvt' /*
                */ or ty == 'wdvt' /*
                */ or ty == '+rev' /*
                */ or ty == 'bevt' /*
                */ or ty == 'devt' /*
                */ or ty == 'gevt' /*
                */ or ty == 'gfvt' /*
                */ or ty == 'gsvt' /*
                */ or ty == 'rgvt' /*
                */ or ty == 'pcvt' /*
                */ or ty == 'alvt' /*
                */ or ty == '+tev'/*
                */ or ty == 'vevt' /*
                */ then
                    set c.event_count = c.event_count + 1

                    if leak then
                        set c.event_leak_count = c.event_leak_count + 1
                    endif
                else

static if DEBUG_MODE then
                    if ty == 'pcmd' then
                        // skip
                    else
                        call BJDebugMsg("unknown handle type '" + to_base256(ty) + "' at: 0x" + to_hex(a))
                        call BJDebugMsg("handle type in hex: " + to_hex(ty))
                        set Atc.done_ef = true
static if Show_Leaderboard_Count then
                        call PauseTimer(ticker)
endif
                        call I2S(1 / 0)
                    endif
endif // DEBUG_MODE

                endif // ty
            endif // a == 0

            // the size of a handle table entry seems to be 12 bytes
            set p = p + 0x0C

            set N = N - 1
            exitwhen N == 0
        endloop

        set Atc.ptr_ef = p
    endmethod

    method count takes nothing returns nothing
        local Atc c = this

        set c.force_count = 0
        set c.player_count = 0
        set c.group_count = 0
        set c.unit_count = 0
        set c.item_count = 0
        set c.destructable_count = 0
        set c.location_count = 0
        set c.rect_count = 0
        set c.region_count = 0
        set c.trigger_count = 0
        set c.boolexpr_count = 0
        set c.triggercondition_count = 0
        set c.triggeraction_count = 0
        set c.unitpool_count = 0
        set c.itempool_count = 0
        set c.timer_count = 0
        set c.timerdialog_count = 0
        set c.sound_count = 0
        set c.camerasetup_count = 0
        set c.effect_count = 0
        set c.fogmodifier_count = 0
        set c.dialog_count = 0
        set c.button_count = 0
        set c.multiboard_count = 0
        set c.multiboarditem_count = 0
        set c.leaderboard_count = 0
        set c.trackable_count = 0
        set c.gamecache_count = 0
        set c.hashtable_count = 0
        set c.event_count = 0
        set c.leaked_handle_ids_count = 0
        set c.leaked_handles_count = 0
        set c.all_count = 0

        set c.force_leak_count = 0
        // set c.player_leak_count = 0
        set c.group_leak_count = 0
        set c.unit_leak_count = 0
        // set c.item_leak_count = 0
        // set c.destructable_leak_count = 0
        set c.location_leak_count = 0
        // set c.rect_leak_count = 0
        // set c.region_leak_count = 0
        // set c.trigger_leak_count = 0
        // set c.boolexpr_leak_count = 0
        // set c.triggercondition_leak_count = 0
        // set c.triggeraction_leak_count = 0
        // set c.unitpool_leak_count = 0
        // set c.itempool_leak_count = 0
        set c.timer_leak_count = 0
        // set c.timerdialog_leak_count = 0
        // set c.sound_leak_count = 0
        // set c.camerasetup_leak_count = 0
        set c.effect_leak_count = 0
        // set c.fogmodifier_leak_count = 0
        // set c.dialog_leak_count = 0
        // set c.button_leak_count = 0
        // set c.multiboard_leak_count = 0
        // set c.multiboarditem_leak_count = 0
        // set c.leaderboard_leak_count = 0
        // set c.trackable_leak_count = 0
        // set c.gamecache_leak_count = 0
        // set c.hashtable_leak_count = 0
        set c.event_leak_count = 0

        set Atc.ptr_ef = Memory[Memory[GameState/4+7]/4+103] + 0x0C
        set Atc.done_ef = false
        set Atc.c_ef = c
        loop
            exitwhen Atc.done_ef
            call ForForce(exec, function Atc.count_ef)
        endloop

static if Show_Counts_On_Leaderboard then
        call Atc.LB_update()
endif
    endmethod

static if Show_Counts_On_Leaderboard then

    private static method do_count takes nothing returns nothing
        call Atc.c.count()
    endmethod

    private static method leaderboards_init takes nothing returns nothing
        set LBC = CreateLeaderboard()
        // call LeaderboardSetLabel(LBC, "Agent Type Counter")
        call LeaderboardSetStyle(LBC, /*show_title:*/ false, /*show_item_labels:*/ true, /*show_item_values:*/ true, /*show_item_icons:*/ false)

        set LBL = CreateLeaderboard()
        call LeaderboardSetLabel(LBL, "Leaked Handles")
        call LeaderboardSetStyle(LBL, /*show_title:*/ true, /*show_item_labels:*/ true, /*show_item_values:*/ true, /*show_item_icons:*/ false)
        call PlayerSetLeaderboard(local_player, LBL)

        set ticker = GetExpiredTimer()
        call TimerStart(ticker, Leaderboard_Update_Interval, true, function Atc.do_count)
    endmethod

    private static string array T
    private static integer T_count = 0

    private static method tokenize takes nothing returns nothing
        local string s = GetEventPlayerChatString()
        local integer si
        local integer i
        local string ch

        set T_count = 0
        set i = 0
        loop
            loop
                set ch = SubString(s, i, i + 1)
                exitwhen ch != " "
                set i = i + 1
            endloop

            exitwhen ch == ""
            set si = i

            loop
                set i = i + 1
                set ch = SubString(s, i, i + 1)
                exitwhen ch == " " or ch == ""
            endloop

            set T_count = T_count + 1
            set T[T_count] = SubString(s, si, i)
        endloop
    endmethod

    private static method on_input takes nothing returns nothing
        local integer i
        call tokenize()

        if T[1] == "!a" then
            set i = S2I(T[2])
            if i == 1 then
                set shown_lb = 1
            elseif i == 2 then
                set shown_lb = 2
            else
                call BJDebugMsg("command 'a': expected second argument to be 1 or 2")
            endif
        endif
    endmethod

endif // Show_Counts_On_Leaderboard

    private static method onInit takes nothing returns nothing
        local trigger t

        set Atc.c = Atc.create()

        set exec = CreateForce()
        call ForceAddPlayer(exec, Player(15))

static if Show_Counts_On_Leaderboard then
        set local_player = GetLocalPlayer()

        set t = CreateTrigger()
        call TriggerRegisterPlayerChatEvent(t, local_player, "", false)
        call TriggerAddAction(t, function Atc.on_input)

        call TimerStart(CreateTimer(), 0.0, false, function Atc.leaderboards_init)
endif
    endmethod
endstruct

endlibrary
 

Attachments

  • agent-type-counter-img-01.png
    agent-type-counter-img-01.png
    30.9 KB · Views: 165
  • agent-type-counter-img-02.png
    agent-type-counter-img-02.png
    14.3 KB · Views: 90
Last edited:
Level 9
Joined
Jul 30, 2012
Messages
156
This is some really good stuff! I'm happy to see that somebody is making good use of my work :)

The crash on the handle table reallocation is actually my fault, not yours. Because I thought it would be a good idea to optimize handle conversion by creating a custom array with the HandleTable address, but I didn't know that the game reallocated the table, so I must remove that array from my library and revert to the old ConvertHandle function.

But to solve the crash for your case, you just need to use set Atc.ptr_ef = Memory[Memory[GameState/4+7]/4+103] + 0x0C instead of using HandleTable.address. This way you will always be using the correct address of the table.

Btw this snippet opens many possibilities. It would be cool if it also displayed the types of the leaked handles (either in a separate multiboard for leaked handles only, or as a second number in a parenthesis right after the total number of handles for each type).

Then we could also start some researching on the reference counting. For example, what happens if a timer is started but its handle is not stored anywhere? Maybe it would count as a leaked handle since no place in the code holds a reference to it. But it's not technically leaked because we will be able to access it again once it expires (via GetExpiredTimer)

And as an ultimate dream, maybe its possible to a implement a "Handle Watcher" using this. I mean, it could peridically search the handle table for possibly leaked handles, then do something to actually check if the object is in use or not (say, if it was a timer, check if it's started or not), and in that case, typecast it to a timer handle and call DestroyTimer! If this is really doable, I suppose many maps could benefit from it, especially GUI maps since the user would just need to include our library and not worry about anything else.
 
Level 13
Joined
Nov 7, 2014
Messages
571
you just need to use set Atc.ptr_ef = Memory[Memory[GameState/4+7]/4+103] + 0x0C
Thanks, fixed.

Then we could also start some researching on the reference counting.
It seems that handles created in a globals block have a reference count of 1 but handles initialized in a function have it set to 2:
JASS:
globals
    group g1 = CreateGroup() // g1's reference count = 1
    group g2
endglobals
function g2_init takes nothing returns nothing
    set g2 = CreateGroup() // g2's reference count = 2
endfunction

It would be cool if it also displayed the types of the leaked handles
Added a second leaderboard that currently only shows leaked instances of these types: force, group, unit, location, timer, effect and event.
 
Status
Not open for further replies.
Top