HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Name that handle

03-25-2008, 10:03 PM#1
Strilanc
Here's something I figured I'd try to do today. The challenge is: given a handle, figure out what derivative type it is. It's not possible to do all of them, for example "button" and "dialog" are probably lost causes. Unit group was a bit tough... I was surprised to find that the types of these handles seems to be checked before using natives on them (except for the lightning ones...).

The interesting part is: destroyed handles, except for unit, all seem to come up as "unknown". So it's possible to write an "Is handle destroyed" function that will be accurate for many of the handle type derivatives. Code and output follow.

I am aware I have not done all handle types.

Collapse JASS:
library HandleNamer initializer initHandleNamer
    //! textmacro H2HandleType takes handletype
        private function H2$handletype$ takes handle h returns $handletype$
            return h
            return null
        endfunction
    //! endtextmacro
    //! runtextmacro H2HandleType("unit")
    //! runtextmacro H2HandleType("player")
    //! runtextmacro H2HandleType("location")
    //! runtextmacro H2HandleType("group")
    //! runtextmacro H2HandleType("force")
    //! runtextmacro H2HandleType("timer")
    //! runtextmacro H2HandleType("trigger")
    //! runtextmacro H2HandleType("timerdialog")
    //! runtextmacro H2HandleType("effect")
    //! runtextmacro H2HandleType("lightning")
    //! runtextmacro H2HandleType("item")
    //! runtextmacro H2HandleType("destructable")

    private function isLightning takes handle h returns boolean
        //causes crashes (memory access violation)
        return false
        
        //local lightning ltng = H2lightning(h)
        //local real r = GetLightningColorR(ltng)
        //local real g = GetLightningColorG(ltng)
        //local real b = GetLightningColorB(ltng)
        //local real a = GetLightningColorA(ltng)
        //if r == 0 then
            //call SetLightningColor(ltng, 1, g, b, a)
        //else
            //call SetLightningColor(ltng, 0, g, b, a)
        //endif
        //if r != GetLightningColorR(ltng) then
            //call SetLightningColor(ltng, r, g, b, a)
            //set ltng = null
            //return true
        //else
            //set ltng = null
            //return false
        //endif
    endfunction
    private function isDestructable takes handle h returns boolean
        return GetDestructableTypeId(H2destructable(h)) != 0
    endfunction
    private function isItem takes handle h returns boolean
        return GetItemTypeId(H2item(h)) != 0
    endfunction
    private function isEffect takes handle h returns boolean
        return false //???
    endfunction
    private function isDialog takes handle h returns boolean
        return false //???
    endfunction
    private function isButton takes handle h returns boolean
        return false //???
    endfunction
    private function isTimerDialog takes handle h returns boolean
        local timerdialog d = H2timerdialog(h)
        local boolean b = IsTimerDialogDisplayed(d)
        call TimerDialogDisplay(d, not b)
        if IsTimerDialogDisplayed(d) != b then
            call TimerDialogDisplay(d, b)
            set d = null
            return true
        else
            set d = null
            return false
        endif
    endfunction
    private function isTrigger takes handle h returns boolean
        local trigger t = H2trigger(h)
        local triggeraction a = TriggerAddAction(t, function DoNothing)
        if a != null then
            call TriggerRemoveAction(t, a) //leak?
            set a = null
            set t = null
            return true
        else
            set t = null
            return false
        endif
    endfunction
    private function isPlayer takes handle h returns boolean
        return GetPlayerName(H2player(h)) != null
    endfunction
    private function isTimer takes handle h returns boolean
        local timer t = H2timer(h)
        local boolean b
        //this works even for timers that have not been started
        set b = TimerGetRemaining(t) != 0 or TimerGetElapsed(t) != 0 or TimerGetTimeout(t) != 0
        set t = null
        return b
    endfunction
    private function isForce takes handle h returns boolean
        local force f = H2force(h)
        local boolean b
        if IsPlayerInForce(Player(0), f) then
            return true
        endif
        call ForceAddPlayer(f, Player(0))
        if IsPlayerInForce(Player(0), f) then
            call ForceRemovePlayer(f, Player(0))
            set f = null
            return true
        endif        
        set f = null
        return false
    endfunction
    private function isGroup takes handle h returns boolean
        local group g = H2group(h)
        local group g2
        local unit u
        local boolean b
        local rect r
        if FirstOfGroup(g) != null then
            set g = null
            return true
        endif
        
        //... seems like a lot to do something so simple ...
        set g2 = CreateGroup()
        if g2 == g then
            //clearly something is screwed up (fake handle passed)
            set b = false
        else
            //we don't want to constantly create units, so try to get an existing one
            set r = GetWorldBounds()
            call GroupEnumUnitsInRectCounted(g2, r, null, 1)
            call RemoveRect(r)
            set r = null
            set u = FirstOfGroup(g2)
            
            if u != null then
                call GroupAddUnit(g, u)
                set b = FirstOfGroup(g) == u
                call GroupRemoveUnit(g, u)
            else
                //no existing units, use a temp flying unit (likely not modified)
                //the gibllets will count as a previous unit for a second or two
                set u = CreateUnit(Player(0), 'nnht', 0, 0, 0) //nether dragon hatchling
                call GroupAddUnit(g, u)
                set b = FirstOfGroup(g) == u
                call GroupRemoveUnit(g, u)
                call ShowUnit(u, false)
                call SetUnitExploded(u, true)
                call KillUnit(u)
            endif
            set u = null
        endif
        call DestroyGroup(g2)
        set g2 = null
        set g = null
        
        return b
    endfunction
    private function isUnit takes handle h returns boolean
        return GetUnitTypeId(H2unit(h)) != 0
    endfunction
    private function isLocation takes handle h returns boolean
        local location p = H2location(h)
        local real x = GetLocationX(p)
        local real y = GetLocationY(p)
        call MoveLocation(p, x+1, y)
        if GetLocationX(p) != x then
            call MoveLocation(p, x, y)
            set p = null
            return true
        else
            set p = null
            return false
        endif
    endfunction

    function HandleType takes handle h returns string
        if h == null then
            return "null"
        elseif isTimer(h) then
            return "timer"
        elseif isLocation(h) then
            return "location"
        elseif isUnit(h) then
            return "unit"
        elseif isGroup(h) then
            return "group"
        elseif isPlayer(h) then
            return "player"
        elseif isForce(h) then
            return "force"
        elseif isTrigger(h) then
            return "trigger"
        elseif isDialog(h) then
            return "dialog"
        elseif isTimerDialog(h) then
            return "timerdialog"
        elseif isButton(h) then
            return "button"
        elseif isEffect(h) then
            return "effect"
        elseif isLightning(h) then
            return "lightning"
        elseif isItem(h) then
            return "item"
        elseif isDestructable(h) then
            return "destructable"
        endif
        return "unknown"
        //"unknown" returned on removed handles
        //except not always for units for some reason
    endfunction
    
    private function test takes nothing returns nothing
        local timer t = CreateTimer()
        local location p = Location(0, 0)
        local unit u = CreateUnit(Player(0), 'hfoo', 0, 0, 0)
        local item i = CreateItem('texp', 0, 0)
        local effect e = AddSpecialEffect("Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl", 0, 0)
        local group g = CreateGroup()
        local force f = CreateForce()
        call BJDebugMsg("timer(new) = " + HandleType(t))
        call TimerStart(t, 10, false, null)
        call BJDebugMsg("timer(run) = " + HandleType(t))
        call BJDebugMsg("location = " + HandleType(p))
        call BJDebugMsg("unit = " + HandleType(u))
        call BJDebugMsg("item = " + HandleType(i))
        call BJDebugMsg("effect = " + HandleType(e))
        call BJDebugMsg("group(empty) = " + HandleType(g))
        call GroupAddUnit(g, u)
        call BJDebugMsg("group(1) = " + HandleType(g))
        call BJDebugMsg("force(empty) = " + HandleType(f))
        call ForceAddPlayer(f, Player(0))
        call BJDebugMsg("force(1) = " + HandleType(f))
        
        call BJDebugMsg("*** DESTROY ***")
        call DestroyTimer(t)
        call RemoveLocation(p)
        call RemoveUnit(u)
        call RemoveItem(i)
        call DestroyEffect(e)
        call DestroyGroup(g)
        call DestroyForce(f)

        call BJDebugMsg("timer = " + HandleType(t))
        call BJDebugMsg("location = " + HandleType(p))
        call BJDebugMsg("unit = " + HandleType(u))
        call BJDebugMsg("item = " + HandleType(i))
        call BJDebugMsg("effect = " + HandleType(e))
        call BJDebugMsg("group = " + HandleType(g))
        call BJDebugMsg("force = " + HandleType(f))
    endfunction
    
    private function initHandleNamer takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerAddAction(t, function test)
        call TriggerRegisterTimerEvent(t, 0, false)
    endfunction
endlibrary

OUTPUT
Code:
timer(new) = timer
timer(run) = timer
location = location
unit = unit
item = item
effect = unknown
group(empty) = group
group(1) = group
force(empty) = force
force(1) = force
*** DESTROY ***
timer = unknown
location = unknown
unit = unit
item = unknown
effect = unknown
group = unknown
force = unknown