System SetHandleUserData

Reaction score
333
This library contains the functions (and an interface):

JASS:
function SetHandleUserData takes handle h, integer key, integer value returns nothing
function GetHandleUserData takes handle h, integer key returns integer
function DeleteHandleUserData takes handle h, integer key returns nothing
function DeleteAllHandleUserData takes handle h returns nothing

function interface TraversalFunction takes integer i returns nothing
function TraverseHandleUserData takes handle h, TraversalFunction f returns nothing


SetHandleUserData allows you to attach an integer value to any handle type under a certain key. Keys can be positive values, negative values or large values. GetHandleUserData retrieves an attached value from a handle under a certain key.

There is a limit of around 8190 attached integers in total, but there are no sub-limits on how many keys can be attached to a single handle. However, adding too many keys could increase the time it takes to find recently added values.

Use DeleteHandleUserData if you are finished using a key, or DeleteAllHandleUserData if you are finished using a handle or otherwise want to clear all associated values.

You can call TraverseHandleUserData to perform an action on every value attached to a handle. The TraversalFunction must take an integer (which will be the value, sort of like GetEnumUnit() in a ForGroup()).

Here is the code (copy paste into empty trigger or map header):

JASS:
library HandleUserData
    globals
        private boolean array g_bools1
    endglobals
    
    function interface TraversalFunction takes integer i returns nothing

    private struct node
        node left = 0
        node right = 0
        integer key
        integer value

        static method create takes integer key, integer value returns node
            local node n = node.allocate()

            set n.key = key
            set n.value = value
            set g_bools1[n] = true
            
            return n
        endmethod

        method onDestroy takes nothing returns nothing
            set g_bools1[this] = false
        endmethod
    endstruct
    
    globals
        private node array g_nodes1
        private node array g_nodes2
        private node array g_nodes3
        private node array g_nodes4
    endglobals

    private function H2I takes handle h returns integer
        return h
        return 0
    endfunction

    private function InsertNode takes node root, node insert returns nothing
        local node n = root

        loop
            if (n.key > insert.key) then
                if not g_bools1[n.left] then
                    set n.left = insert
                    exitwhen true
                endif
                set n = n.left
            elseif (n.key < insert.key) then
                if not g_bools1[n.right] then
                    set n.right = insert
                    exitwhen true
                endif

                set n = n.right
            else
                set n.value = insert.value
                call insert.destroy()
                exitwhen true
            endif
        endloop
    endfunction
    
    private function DeleteNode takes node root returns nothing
        local node n

        if not g_bools1[root.left] then
            if not g_bools1[root.right] then
                call root.destroy()

            else
                set n = root.right
                set root.key = n.key
                set root.value = n.value
                set root.left = n.left
                set root.right = n.right
                call n.destroy()
            endif
        else
            if not g_bools1[root.right] then
                set n = root.left
                set root.key = n.key
                set root.value = n.value
                set root.left = n.left
                set root.right = n.right
                call n.destroy()
            else
                set n = root.right

                loop
                    exitwhen not g_bools1[n.left]
                    set n = n.left
                endloop

                set root.key = n.key
                set root.value = n.value
                call DeleteNode.execute(n)
            endif
        endif
    endfunction

    private function PruneNodes takes node root returns nothing
        if g_bools1[root] then
            call PruneNodes.execute(root.left)
            call PruneNodes.execute(root.right)
            call root.destroy()
        endif
    endfunction

    function SetHandleUserData takes handle h, integer key, integer value returns nothing
        local integer index = H2I(h) - 0x100000
        local integer i
        local node n
        if (index < 8190) then
            set i = index
            set n = g_nodes1<i>

            if (not g_bools1[n]) then
                set g_nodes1<i> = node.create(key, value)
            else
                call InsertNode(n, node.create(key, value))
            endif
        elseif (index &lt; 16380) then
            set i = index-8190
            set n = g_nodes2<i>

            if (not g_bools1[n]) then
                set g_nodes2<i> = node.create(key, value)
            else
                call InsertNode(n, node.create(key, value))
            endif
        elseif (index &lt; 24570) then
            set i = index-16380
            set n = g_nodes3<i>

            if (not g_bools1[n]) then
                set g_nodes3<i> = node.create(key, value)
            else
                call InsertNode(n, node.create(key, value))
            endif
        elseif (index &lt; 32760) then
            set i = index-24570
            set n = g_nodes4<i>

            if (not g_bools1[n]) then
                set g_nodes4<i> = node.create(key, value)
            else
                call InsertNode(n, node.create(key, value))
            endif
        else
            //
        endif
    endfunction

    function GetHandleUserData takes handle h, integer key returns integer
        local integer index = H2I(h) - 0x100000
        local node n

        if (index &lt; 8190) then
            set n = g_nodes1[index]
        elseif (index &lt; 16380) then
            set n = g_nodes2[index-8190]
        elseif (index &lt; 24570) then
            set n = g_nodes3[index-16380]
        elseif (index &lt; 32760) then
            set n = g_nodes4[index-24570]
        else
            //
        endif

        loop
            exitwhen not g_bools1[n]
            if (n.key &gt; key) then
                set n = n.left
            elseif (n.key &lt; key) then
                set n = n.right
            else
                return n.value
            endif
        endloop

        return 0
    endfunction
    
    private function TraverseNodes takes node root, TraversalFunction f returns nothing
        if g_bools1[root] then
            call TraverseNodes.execute(root.left, f)
            call TraverseNodes.execute(root.right, f)
            call f.execute(root.value)
        endif
    endfunction
    
    function TraverseHandleUserData takes handle h, TraversalFunction f returns nothing
        local integer index = H2I(h) - 0x100000
        local node n

        if (index &lt; 8190) then
            set n = g_nodes1[index]
        elseif (index &lt; 16380) then
            set n = g_nodes2[index-8190]
        elseif (index &lt; 24570) then
            set n = g_nodes3[index-16380]
        elseif (index &lt; 32760) then
            set n = g_nodes4[index-24570]
        else
            //
        endif
        
        call TraverseNodes(n, f)
    endfunction

    function DeleteHandleUserData takes handle h, integer key returns nothing
        local integer index = H2I(h) - 0x100000
        local node n

        if (index &lt; 8190) then
            set n = g_nodes1[index]
        elseif (index &lt; 16380) then
            set n = g_nodes2[index-8190]
        elseif (index &lt; 24570) then
            set n = g_nodes3[index-16380]
        elseif (index &lt; 32760) then
            set n = g_nodes4[index-24570]
        else
            //
        endif
        
        loop
            exitwhen not g_bools1[n]
            if (n.key &gt; key) then
                set n = n.left
            elseif (n.key &lt; key) then
                set n = n.right
            else
                call DeleteNode(n)
            endif
        endloop
    endfunction

    function DeleteAllHandleUserData takes handle h returns nothing
        local integer index = H2I(h) - 0x100000
        local node n

        if (index &lt; 8190) then
            set n = g_nodes1[index]
        elseif (index &lt; 16380) then
            set n = g_nodes2[index-8190]
        elseif (index &lt; 24570) then
            set n = g_nodes3[index-16380]
        elseif (index &lt; 32760) then
            set n = g_nodes4[index-24570]
        else
            //
        endif

        if (g_bools1[n]) then
            call PruneNodes.execute(n)
        endif
    endfunction
endlibrary
</i></i></i></i></i></i></i></i>
 

emjlr3

Change can be a good thing
Reaction score
395
perhaps showing an example of its usage would help...
 
Reaction score
333
perhaps showing an example of its usage would help...

Well, for simple attachment you can use:

JASS:
local trigger t = CreateTrigger()
call SetHandleUserData(t, 1, 10)
call SetHandleUserData(t, 6, 88)
call GetHandleUserData(t, 6) // Returns 88
call GetHandleUserData(t, 1) // Returns 10
call GetHandleUserData(t, 393) //Returns zero (null)


You can also define a relationship between handles using H2I. This following code block may be found in a "unit takes damage" trigger:

JASS:
local unit u1 = GetTriggerUnit()
local unit u2 = GetDamageEventSource()

if (GetHandleUserData(u1, H2I(u2)) != 0) then
    //unit has been damaged by the source before
else
    SetHandleUserData(u1, H2I(u2), 1)
    //unit has never been damaged by the source before
endif


Traversing all attachments for a certain handle is possible:

JASS:
function Test1 takes integer i returns nothing
    call BJDebugMsg(I2S(i))
endfunction

function Test2 takes nothing returns nothing
    local location l = Location(0,0)
    local TraversalFunction f = TraversalFunction.Test1
    
    call SetHandleUserData(l, 2, 1020)
    call SetHandleUserData(l, 1, 69)
    call SetHandleUserData(l, 3, 1320)
    call SetHandleUserData(l, 3228053, 667)
    call SetHandleUserData(l, -667, 997)
    
    call TraverseHandleUserData(l, f)
    //This will output, in some order: 1020, 69, 1320, 667, 997
endfunction
 

Cohadar

master of fugue
Reaction score
209
Jet another 0x100000 based system.

I have seen like 30 variants of this written by 30 different people.
Just give it up, it is a bad concept, you cannot build a house on bad foundations.

The only half-decent system of this type was the one made by Toadcop,
and only because it was using hashing functions from ABC.
 
Reaction score
333
Jet another 0x100000 based system.

I have seen like 30 variants of this written by 30 different people.
Just give it up, it is a bad concept, you cannot build a house on bad foundations.

The only half-decent system of this type was the one made by Toadcop,
and only because it was using hashing functions from ABC.

How is it a bad concept, exactly?

The Crash function doesn't make it any better...

Yeah, that won't make it into any final version. I will replace it with something when I can be bothered.
 

Cohadar

master of fugue
Reaction score
209
How is it a bad concept, exactly?

Well since you asked:

1. no error/warning reporting (and no way to add one because of bad concept)
2. not type-safe
3. gets slower the higher handle indexes get
4. stops working after handle indexes go over 24570
5. uses linear if-ing to find proper index (instead of a binary one)
6. slow algorithm? (loops and nodes, I mean really...)

If I ever had to use a 0x100000 system, I would use Vexorian's CSData.
Thank god I don't have to.

This stuff is simply flawed, I have seen to many people waste their time with it.
Just give it up, it is for your own good.
 
Reaction score
333
Well since you asked:

1. no error/warning reporting (and no way to add one because of bad concept)
2. not type-safe
3. gets slower the higher handle indexes get
4. stops working after handle indexes go over 24570
5. uses linear if-ing to find proper index (instead of a binary one)
6. slow algorithm? (loops and nodes, I mean really...)

If I ever had to use a 0x100000 system, I would use Vexorian's CSData.
Thank god I don't have to.

This stuff is simply flawed, I have seen to many people waste their time with it.
Just give it up, it is for your own good.

1. What sort of errors would I be warning people about? Perhaps myriad leaks in their map, but not much else.

2. I don't consider this an issue.

3. The speed decrease is minimal, and it only occurs if your map is leaking or using many handles.

4. If a map is that leaky, it has other problems.

5. A few ifs are fine.

6. The nodes and loops have nothing to do with the 0x100000 attachment method. Binary search trees give you complete freedom to use many keys on a single handle, negative keys, large keys etc. You can still opt to attach only one integer to a handle at a time if you wish and the speed won't suffer too much.
 

emjlr3

Change can be a good thing
Reaction score
395
so you can only store integers?

in this case you would haveto abuse H2I and I2H often in order to actually make this any useful
 

Rheias

New Helper (I got over 2000 posts)
Reaction score
232
> in this case you would haveto abuse H2I and I2H often in order to actually make this any useful

Structs for the win?

Anyway, seems like a nice system, didn't bother to read the code yet, but I assum it works well enough, coming from you.
 

emjlr3

Change can be a good thing
Reaction score
395
ok then this is for storing structs, like the other dozen systems around

i still don't see the need for 200 lines of code when CSData can do it in 15
 
Reaction score
333
ok then this is for storing structs, like the other dozen systems around

i still don't see the need for 200 lines of code when CSData can do it in 15

CSData and ABC can only attach a single value to a handle. Even if you use text macros, you can at best attach a fixed number of values, and they can not be stored or retrieved dynamically.

HSAS allows you to store values under a key, but there is still a static key range, and negative keys are not possible.

This system has no such limitations. You don't have to use textmacros, you can store values under any integer, can distribute attachments arbitrarily among handles (you can have, for instance, 8000 on one handle, then 1 each on 100 handles).

You also get the advantage of a traversal function.
 

Cohadar

master of fugue
Reaction score
209
This system has no such limitations. You don't have to use textmacros, you can store values under any integer, can distribute attachments arbitrarily among handles (you can have, for instance, 8000 on one handle, then 1 each on 100 handles).

You people need to learn that in programming limitations are usually a good thing.

Historical examples:

Type-safety was one of the fist limitations invented in programming,
it forbids people from assigning variables of different types to each other,
and while it pissed some assembler programmers in the beginning because they could not do hacks with direct assigning it in general let to a better programming languages

goto: command, nightmare of all early programming languages,
it created so called spaghetti-code until some smart ass decided that goto will be forbidden thus restricting people to using only structured branch and loop instructions (if, case, for, while...)

java vs c++ object model
c++ :
people wanted to be able to do everything with c++ ,
you can throw exceptions of both native and object types,
classes can have multiple inheritance, STL could work the same way with all data types, operator overloading was invented.
java:
only classes inherited from Exception base class can be thrown as exceptions,
classes can have only one base class (no multiple inheritance),
this later on led to a significant development of teory of interfaces,
Java collections (as opposed to STL) can work only with classes, not with native types, operator overloading was forbidden.

------------------

When people say: my sys can attach to handles of any type,
I read that as: no type-safety

When people say: you can store values under any integer,
I read that as: will not report errors if struct index goes out of 0..8190 range

When people say: can distribute attachments arbitrarily among handles
I read that as: lags like hell + noone needs that.

------------------

CSData, Grim001 sys, NagelBagel's hashtables, HSAS, your sys and who knows how many others of this kind all have this arguments that they consider to be their advantages but are in fact their flaws.

The mere fact that there are so many of them and that none of them made it to be generally accepted should be telling you something....

And if it is any comfort to you, your sys is not in the worst 10.
Those kind of champions usually come from the hive.

------------------

You have a decent amount of programming skill there,
you should use it in some better way.
 
Reaction score
333
I agree that limitations are good in certain situations. Type-safety is an important feature of many programming languages, but it is an almost non-existent issue for attachment systems that don't use I2H.

Lightning effects and some other obscure handle types are, apparently, bugged when used with H2I. Should I be forced to babysit the 0.01% of people who feel the need to attach a value to a lightning effect? Should I use textmacros to copy/paste the exact same code a dozen times with minor alterations? Is there any real need to differentiate between the handle types you are attaching things to? No, no and no.

When people say: you can store values under any integer,
I read that as: will not report errors if struct index goes out of 0..8190 range

This is not an intrinsic problem with binary search trees or the 0x100000 method. I could simply check if a newly created struct returns 0 instead of an index, and then report it. Perhaps I will add this, now that you've mentioned it.

When people say: can distribute attachments arbitrarily among handles
I read that as: lags like hell + noone needs that.

I wouldn't be surprised if someone needed to attach a single value to 180 handles, and then 50 values to a single handle. Performance does suffer if keys are added in any sort of numerical order, though.

The mere fact that there are so many of them and that none of them made it to be generally accepted should be telling you something....

The most commonly accepted, historically, has been handlevars and gamecache natives are probably used more than any system. What does that tell you?
 

Cohadar

master of fugue
Reaction score
209
The most commonly accepted, historically, has been handlevars and gamecache natives are probably used more than any system. What does that tell you?

Handlevars was the first attachment system to appear in wc3 mapping,
it played in a league with no competition.
(I wonder why no pro mapper uses handlevars today...)



This is turning into a circular discussion, time to end it.
Good luck with your system.
 

emjlr3

Change can be a good thing
Reaction score
395
because all the "gurus" say its slow and bugged

though I have never had performance issues or bugs using it....
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top