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 The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1
  • The Helper The Helper:
    New recipe is another summer dessert Berry and Peach Cheesecake - https://www.thehelper.net/threads/recipe-berry-and-peach-cheesecake.194169/
  • The Helper The Helper:
    I think we need to add something to the bottom of the front page that shows the Headline News forum that has a link to go to the News Forum Index so people can see there is more news. Do you guys see what I am saying, lets say you read all the articles on the front page and you get to the end and it just ends, no kind of link for MOAR!

      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