[System] UnitDex - Unit Indexer

Level 23
Joined
Apr 16, 2012
Messages
4,041
Ehhh...

If you allocate using a queue instead of a stack, locks aren't really necessary. Locks were really there for lazy people that didn't want to clean things up while a spell was running or something and a unit was removed. It's convenience really. Queue does the same thing more or less ; ).

As for the group, only one script would need to do this. From here, other scripts could use it. It does raise dependencies though, so it's give and take. For me, I have no problem with 10000000 dependencies, but I know that other people do : ). This would be your own personal choice I guess. I think a list is more useful than a group.

offtopic, but you know, if we had some normal package manager or the language would support downloading scripts from websites directly or something, then I would have no problems with multiple dependencies, even tho they can be quite sizable for what they are doing, but the issue here is that you cant download them directly via JassHelper nor do we have package manager, so you have to download everything manually, and check every time if you already have script like that, and thats a lot of undeeded work for some funny scripting language.
 
Level 38
Joined
Jun 23, 2007
Messages
4,028
I see TriggerHappy is back with this?

I would actually consider upgrading to this, since AIDS' onDeindex implementation sucks, but I'm missing something in the AIDS compat: PUI textmacros, which were a quick, nice and easy way to create data attached to units.

Could you maybe add that to your AIDS compat version?

I'll look into implementing that :thumbs_up:
 
I'll look into implementing that :thumbs_up:
That would be great.

Here's how PUI is implemented in AIDS:


EDIT: I just realized that you can simply copy & paste this in and change the requirement from AIDS to UnitDex an it works already, so nothing to implement here.

JASS:
library PUI uses AIDS
    //===========================================================================
    //  Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
    //  Do NOT put handles that need to be destroyed here (timer, trigger, ...)
    //  Instead put them in a struct and use PUI textmacro
    //===========================================================================
    //! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
    $VISIBILITY$ struct $NAME$
        private static unit   array pui_unit
        private static $TYPE$ array pui_data
        
        //-----------------------------------------------------------------------
        //  Returns default value when first time used
        //-----------------------------------------------------------------------
        static method operator[] takes unit whichUnit returns $TYPE$
            local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
            if .pui_unit[pui] != whichUnit then
                set .pui_unit[pui] = whichUnit
                set .pui_data[pui] = $DEFAULT$
            endif
            return .pui_data[pui]
        endmethod
        
        //-----------------------------------------------------------------------
        static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
            local integer pui = GetUnitIndex(whichUnit)
            set .pui_unit[pui] = whichUnit
            set .pui_data[pui] = whichData
        endmethod
    endstruct
    //! endtextmacro

    //===========================================================================
    //  Never destroy PUI structs directly.
    //  Use .release() instead, will call .destroy()
    //===========================================================================
    //! textmacro PUI
        private static unit    array pui_unit
        private static integer array pui_data
        private static integer array pui_id
        
        //-----------------------------------------------------------------------
        //  Returns zero if no struct is attached to unit
        //-----------------------------------------------------------------------
        static method operator[] takes unit whichUnit returns integer
            local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
            // Switched the next two lines for optimisation.
            if .pui_unit[pui] != whichUnit then
                if .pui_data[pui] != 0 then
                    // recycled index detected
                    call .destroy(.pui_data[pui])
                    set .pui_unit[pui] = null
                    set .pui_data[pui] = 0            
                endif
            endif
            return .pui_data[pui]
        endmethod
        
        //-----------------------------------------------------------------------
        //  This will overwrite already attached struct if any
        //-----------------------------------------------------------------------
        static method operator[]= takes unit whichUnit, integer whichData returns nothing
            local integer pui = GetUnitIndex(whichUnit)
            if .pui_data[pui] != 0 then
                call .destroy(.pui_data[pui])
            endif
            set .pui_unit[pui] = whichUnit
            set .pui_data[pui] = whichData
            set .pui_id[whichData] = pui
        endmethod

        //-----------------------------------------------------------------------
        //  If you do not call release struct will be destroyed when unit handle gets recycled
        //-----------------------------------------------------------------------
        method release takes nothing returns nothing
            local integer pui= .pui_id[integer(this)]
            call .destroy()
            set .pui_unit[pui] = null
            set .pui_data[pui] = 0
        endmethod
    //! endtextmacro
endlibrary
 
Last edited:
Level 38
Joined
Jun 23, 2007
Messages
4,028
Updated a couple days ago, just notifying.

v1.2.1
- UnitDexRemove now returns true on success
- Remove method added to struct
- UnitDexRemove now just calls .Remove
- Fixed a bug where UnitDex.Count wouldn't decrease when a unit left
- OnUnitIndex/OnUnitDeindex functions added
- LastIndex now readonly
- Hashtable option removed (due to static if's not working properly with modules outside of the struct)
- Minor optimizations

I added support for Bribe's GUI indexer on the original post.
 
Level 31
Joined
Jul 10, 2007
Messages
6,307
I think that this is a good medium between Bribe's GUI Unit Indexer and mine. It is sort of like how Cohadar filled the gap between LFH and I on Damage Event ^_-.

This is the resource that virtually all vJASS coders want. Mine is for the niche/hardcore crowd, sort of like how unreal engine used to be, and Bribe's is for the beginner crowd =).

Thumbs up from me.
 
Level 8
Joined
Jul 10, 2008
Messages
354
How can I retrieve the unit integer assigned.

GetUnitId, returns same id for same type units


Heads up, this system uses SetUnitUserData(), so if you use it for something else adjust it (make hashtable for custom setunituserdata)

JASS:
//globals
    hashtable CustomUserData = InitHashtable()
add this

JASS:
function CSetUnitUserData takes unit u, integer i returns nothing
    call SaveInteger(CustomUserData, GetHandleId(u), 0, i)
endfunction
  
function CGetUnitUserData takes unit u returns integer
    local integer i = 0
    set i = LoadInteger(CustomUserData, GetHandleId(u), 0)
    return i
endfunction
replace GetUnitUserData and SetUnitUserData in script with those
 
Last edited:
Level 8
Joined
Jul 10, 2008
Messages
354
Am not saying that is there anything wrong with the script, am just saying that if you already using unituserdata somewhere else you should replace it.
 
Level 13
Joined
Nov 7, 2014
Messages
570
OnUnitIndex and OnUnitDeindex should define boolexpr instead of code as argument.
In debug mode, it's ok, but else it might inline, and might throw an error.

JASS:
function OnUnitIndex takes code func returns triggercondition
    return TriggerAddCondition(IndexTrig[EVENT_UNIT_INDEX], Filter(func))
endfunction

function OnUnitDeindex takes code func returns triggercondition
    return TriggerAddCondition(IndexTrig[EVENT_UNIT_DEINDEX], Filter(func))
endfunction

Suppose OnUnitIndex gets inlined:
JASS:
    // local triggercondition tc = OnUnitIndex(function my_func)
    local triggercondition tc = TriggerAddCondition(IndexTrig[EVENT_UNIT_INDEX], Filter(function some_function))
how/why would it "throw an error"?
 
Level 13
Joined
Nov 7, 2014
Messages
570
Try to disbale debug mode, and then pass an ( you can empty) function which takes nothing and returns nothing to OnUnitIndex.
It inlined for me and expects to returns a boolean value from my function. So requiring a boolexr is probably safer, and should always work.

JASS:
library foo initializer init requires UnitDex

private function bar takes nothing returns nothing
endfunction

private function init takes nothing returns nothing
    call OnUnitIndex(function bar)
endfunction

endlibrary


// =>

//library foo:

function foo__bar takes nothing returns nothing
endfunction

function foo__init takes nothing returns nothing
call TriggerAddCondition(UnitDex__IndexTrig[EVENT_UNIT_INDEX], Filter((function foo__bar))) // INLINED!!
endfunction


//library foo ends
 
Level 13
Joined
Nov 7, 2014
Messages
570
This was one of the early things i changed in pjass...
I see, old pjass complains with "Functions passed to Filter or Condition must return a boolean".

Apparently in the past this could cause desyncs so I am not sure if its a good feature.

But I am pretty sure the uninitialized variable check is really awesome, it has saved me many many times! =)

PS: I wonder why Blizzard didn't initialize scalar variables with default values just like they did with arrays
 
Level 13
Joined
Jan 16, 2009
Messages
687
Why would you enum the player units after the initialization ?
This means that if I create a dummy unit for any system at init and disable the Unit indexer before creating it and then renabling it after it won't actually change a thing because it will only be indexed after the init. So basically you can't create unit on init that won't be indexed unless you put them on the filter I guess.

As often with jass resources on the Hive, I feel that they are created only for the sake of creating them and never used properly.
 
Level 38
Joined
Jun 23, 2007
Messages
4,028
Why would you enum the player units after the initialization ?
This means that if I create a dummy unit for any system at init and disable the Unit indexer before creating it and then renabling it after it won't actually change a thing because it will only be indexed after the init. So basically you can't create unit on init that won't be indexed unless you put them on the filter I guess.

Good point. I haven't touched this code in a while. I'll look into it and see if I can move the enumeration to init without affecting anything.

By the looks of it though, I would guess that pre-placed units are created before any vJass initializers, thus requiring me to index them on game start. I would have to test this to be sure.

As often with jass resources on the Hive, I feel that they are created only for the sake of creating them and never used properly.

Nice assumption, but I use this in all my maps and it's ridiculous that you got that conclusion simply because I enumerate units after init. I don't even see the point of making that remark other than to be negative or try to be insulting.

This library was somewhat necessary in my opinion after Nestharus made a mess of UnitIndexer.
 
Level 13
Joined
Jan 16, 2009
Messages
687
Sorry the goal was not to be insulting or offensive towards you.

It's just that at this point I was very frustrated that I had to debug yet another approved resource.
It's not the first time and I find it quite distrubing.

Don't take me wrong, I am grateful that you are doing this and I am the first to make mistakes in my code. However this is a public resource that has been here for years so it must be worthy of approval.

Also UnitDexRemove should be after the struct, it's forcing an evaluate where it is right now.
 
Last edited:
Level 38
Joined
Jun 23, 2007
Messages
4,028
It's just that at this point I was very frustrated that I had to debug yet another approved resource.
It's not the first time and I find it quite distrubing.

Don't take me wrong, I am grateful that you are doing this and I am the first to make mistakes in my code. However this is a public resource that has been here for years so it must be worthy of approval.

No worries I understand. It would be nice if there were more rigorous reviewing like on wc3c, but I don't think people have the time or care enough currently. Hopefully the work Blizzard is doing towards the game will change that by bringing more players/modders.

Also UnitDexRemove should be after the struct, it's forcing an evaluate where it is right now.

Thanks for pointing that out I'll get that fixed too.
 
Level 24
Joined
Mar 19, 2008
Messages
3,136
JASS:
    function UnitDexRemove takes unit u, boolean runEvents returns boolean
        return UnitDex.Remove(u, runEvents)
    endfunction
Asks for UnitDex::Remove which is implemented below its declaration, thus unnecessary caller method is created by jasshelper during compile time.
Position struct UnitDex above UnitDexRemove function.
 
I'm wondering if there could be a compatibility patch for UnitEvent? I tried using the GUI Unit Indexer compatibility patch but it somehow broke the EVENT_UNIT_CONSTRUCTION_START for ConstructEvent. Only after disabling UnitEvent did ConstructEvent start working again.

Either that or... anyone know how to make a Load/Unload event detection? UnitEvent has some really neat features like detecting Reincarnation, Morphs and Load/Unload so I would be interesting in hearing if there's a JASS/vJASS lib out there that does that. I'd rather only have 1 unit indexer on my map and since I'm going with UnitDex, I'm dropping all the GUI stuff.
 
Level 24
Joined
Mar 19, 2008
Messages
3,136
I've just checked ConstructEvent with a) GUI UnitIndexer and b) GUI UnitEvent. Just make sure you use the vJass plugin and readjust requirements in ConstructEvent library declaration (change requires UnitDex to requires UnitIndexerGUI or even change this to: requires optional UnitDex optional UnitIndexerGUI.
For the first, remember that Bribe's GUI indexer alone won't fire deindex the moment unit is actually removed, thus INTERRUPT event (and any other deindex based function, no matter the system) will fail.

However, if you use UnitEvent instead, deindex now becomes precise thus all ConstructEvents would work just fine. You will need to update module initializer:
JASS:
        call OnUnitIndex(function OnIndex)
        call OnUnitDeindex(function OnDeindex)
        //call RegisterUnitIndexEvent(Condition(function OnIndex), EVENT_UNIT_INDEX)
        //call RegisterUnitIndexEvent(Condition(function OnDeindex), EVENT_UNIT_DEINDEX)
This is a must becuase indexer libraries are not compatible when it comes to event registration.

There is/was UnitEvent for vJass, although it is not working correctly with UnitDex. I might have UnitDex-friendly version hidden in my dev map. If I find it, I'll upload it.
 
Last edited:
Level 38
Joined
Sep 26, 2009
Messages
8,465
Hmm, I'll keep this solution in mind, but has no one beside Bribe made a library for special events like morph, load/unload? Either that or maybe I could learn to make one myself but I'm not sure where to start. Any pointers?
The morph is a one of a kind, but load and unload is from Jesus 4Lyfs Transport and I think even AutoEvents.
 
Top