1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. Travel to distant realms and encounter scenes unknown to the common folk. The Greatest of Adventures is upon us with the 8th Cinematic Contest. Join in on a fun ride.
    Dismiss Notice
  4. The 18th Icon Contest is ON! Choose any ingame unit and give him/her Hero abilities. Good luck to all.
    Dismiss Notice
  5. The Secrets of Warcraft 3 have revealed interesting works. The RESULTS for Abelhawk's Mini-Mapping Contest #15 have come out!
    Dismiss Notice
  6. Contestants are to create a scene set in the Stone Age. Come and see what you can come up with. We wish you the best of luck!
    Dismiss Notice
  7. Colour outside the lines! Techtree Contest #13 is a go. The contest is optionally paired.
    Dismiss Notice
  8. Night Rider gained several songs for his journey. The poll for the 12th Music Contest has started. Check it out!
    Dismiss Notice
  9. Greetings cerebrates, our Swarm needs new spawners that will have numerous children. Join the HIVE's 31st Modeling Contest - Spawners and Spawned! The contest is optionally paired.
    Dismiss Notice
  10. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[System] Light Unit Indexer

Discussion in 'Graveyard' started by Ruke, Dec 5, 2012.

  1. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
    Notice: Stable version.

    Code (vJASS):
    library LightUnitIndexer /* v0.2.1
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    *
    *   Light Unit Indexer by Ruke
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *       Assigns unique ids to units.
    *
    *   Why choose it?
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *       If you need a light system, who does not requires other systems, work with
    *       hashtable instead of UnitUserData (custom value in GUI) and does not use
    *       interfaces, structs and other stuffs like that, choose it.
    *      
    *       Also, if you need a GUI friendly unit indexer, choose it too :3.
    *
    *   How to import?
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *       First, create a new trigger called LightUnitIndexer and then paste this code.
    *       You also will need to create a new ability based on Defend (Human -> Units),
    *       no need to modify nothing on it.
    *
    *   Functions
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *       function TriggerRegisterIndexEvent takes trigger t returns event
    *       function TriggerRegisterDeIndexEvent takes trigger t returns event
    *
    *       function GetIndexedUnit takes nothing returns unit
    *           -   Returns the unit who has been indexed
    *       function GetDeIndexedUnit takes nothing returns unit
    *           -   Returns the unit who has been deindexed
    *
    *       function GetUnitById takes integer i returns unit
    *       function GetUnitId takes unit u returns integer
    *
    *       function IsIndexLocked takes integer i returns boolean
    *       function LockIndex takes integer i, boolean b returns nothing
    *           -   Lock (or unlock) the index. If you lock it,
    *           -   the index will not be recycled when the
    *           -   unit is deindexed
    *
    *       function IsUnitIndexed takes unit u returns boolean
    *
    *   Thanks to
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *       Jesus4Lyf
    *       Nestharus
    *       muZk
    *
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

        globals
            // 1.00 = Index event
            // 2.00 = DeIndex event
            // ------------------------------------------
            // Uncomment this line if you will not create
            // the variable LUIS_EVENT with the variable
            // editor (Ctrl + B)
            // ------------------------------------------
            //real udg_LUIS_EVENT = 0.00
           
            // Change it to the rawcode of the ability based on
            // Defend (Human -> Units)
            private constant integer LEAVER_ABILITY = 'LUIS'
           
            private integer array lock
            private integer array recycle
            private integer recycleCount = 0
           
            private group indexCache = CreateGroup()
            private group deIndexCache = CreateGroup()
            private boolean inCache = true
           
            private unit array units
            private integer count = 0
           
            private unit eventUnit = null
           
            private hashtable ht = InitHashtable()
        endglobals
       
        function TriggerRegisterIndexEvent takes trigger t returns event
            return TriggerRegisterVariableEvent(t, "udg_LUIS_EVENT", EQUAL, 1.00)
        endfunction
       
        function TriggerRegisterDeIndexEvent takes trigger t returns event
            return TriggerRegisterVariableEvent(t, "udg_LUIS_EVENT", EQUAL, 2.00)
        endfunction
       
        function GetIndexedUnit takes nothing returns unit
            return eventUnit
        endfunction
       
        function GetDeIndexedUnit takes nothing returns unit
            return eventUnit
        endfunction
       
        function GetUnitById takes integer i returns unit
            return units[i]
        endfunction
       
        function GetUnitId takes unit u returns integer
            return LoadInteger(ht, GetHandleId(u), 0)
        endfunction
       
        function IsIndexLocked takes integer i returns boolean
            return lock[i] > 0
        endfunction
       
        function LockIndex takes integer i, boolean b returns nothing
            if (b) then
                set lock[i] = lock[i] + 1
            else
                set lock[i] = lock[i] - 1
            endif
        endfunction
       
        function IsUnitIndexed takes unit u returns boolean
            return HaveSavedInteger(ht, GetHandleId(u), 0)
        endfunction
       
        private function FireEvent takes unit u, real r returns nothing
            local unit prev = eventUnit
            set eventUnit = u
           
            set udg_LUIS_EVENT = r
            set udg_LUIS_EVENT = 0.00
           
            set eventUnit = prev
            set prev = null
        endfunction
       
        private function IndexUnit takes unit u returns nothing
            local integer i
           
            if (IsUnitIndexed(u) == false) then
                if (recycleCount == 0) then
                    set count = count + 1
                    set i = count
                else
                    set i = recycle[recycleCount]
                    set recycleCount = recycleCount - 1
                endif
               
                call SaveInteger(ht, GetHandleId(u), 0, i)
               
                call UnitAddAbility(u, LEAVER_ABILITY)
                call UnitMakeAbilityPermanent(u, true, LEAVER_ABILITY)
               
                if (inCache) then
                    call GroupAddUnit(indexCache, u)
                else
                    call FireEvent(u, 1.00)
                endif
            endif
        endfunction
       
        private function RemoveIndex takes unit u returns nothing
            local integer handleId = GetHandleId(u)
            local integer id = GetUnitId(u)
           
            if (IsIndexLocked(id) == false) then
                set recycleCount = recycleCount + 1
                set recycle[recycleCount] = id
            endif
           
            set units[id] = null
           
            call SaveInteger(ht, handleId, 0, 0)
            call RemoveSavedInteger(ht, handleId, 0)
        endfunction
       
        private function DeIndexUnit takes unit u returns nothing
            if (GetUnitAbilityLevel(u, LEAVER_ABILITY) == 0 and IsUnitIndexed(u)) then
                if (inCache) then
                    call GroupAddUnit(deIndexCache, u)
                else
                    call FireEvent(u, 2.00)
                    call RemoveIndex(u)
                endif
            endif
        endfunction
       
        private function OnEnter takes nothing returns boolean
            call IndexUnit(GetFilterUnit())
            return false
        endfunction
       
        private function OnLeave takes nothing returns boolean
            call DeIndexUnit(GetFilterUnit())
            return false
        endfunction
       
        private function Run takes nothing returns nothing
            local unit u
           
            set inCache = false
           
            loop
                set u = FirstOfGroup(indexCache)
                exitwhen u == null
                call GroupRemoveUnit(indexCache, u)
               
                call FireEvent(u, 1.00)
            endloop
           
            loop
                set u = FirstOfGroup(deIndexCache)
                exitwhen u == null
                call GroupRemoveUnit(deIndexCache, u)
               
                call FireEvent(u, 2.00)
                call RemoveIndex(u)
            endloop
           
            call DestroyGroup(indexCache)
            call DestroyGroup(deIndexCache)
           
            call DestroyTimer(GetExpiredTimer())
           
            set indexCache = null
            set deIndexCache = null
        endfunction
       
        private module Init
            private static method onInit takes nothing returns nothing
                local integer maxPlayers = bj_MAX_PLAYERS
                local trigger t = CreateTrigger()
                local boolexpr onLeave = Condition(function OnLeave)
                local region r = CreateRegion()
                local player p
                local unit u
               
                loop
                    exitwhen maxPlayers < 0
                   
                    set p = Player(maxPlayers)
                   
                    call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, onLeave)
                    call SetPlayerAbilityAvailable(p, LEAVER_ABILITY, false)
                   
                    call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, p, null)
                   
                    loop
                        set u = FirstOfGroup(bj_lastCreatedGroup)
                        exitwhen u == null
                        call GroupRemoveUnit(bj_lastCreatedGroup, u)
                       
                        call IndexUnit(u)
                    endloop
                   
                    set maxPlayers = maxPlayers - 1
                endloop
           
                call RegionAddRect(r, GetWorldBounds())
                call TriggerRegisterEnterRegion(CreateTrigger(), r, Condition(function OnEnter))
               
                set t = null
                set onLeave = null
                set r = null
                set p = null
               
                call TimerStart(CreateTimer(), 0, false, function Run)
            endmethod
        endmodule
       
        private struct LUI extends array
            implement Init
        endstruct
    endlibrary


    Changelog:
    • v0.2.1
      • Added LUI struct
      • Added Init module
    • v0.2.0
      • Added RemoveIndex function
      • Changed documentation
      • Initialization feature
      • Added FireEvent function
    • v0.1.0
      • Added index lock/unlock feature (thanks Nestharus)
      • Fixed buggy UNIT_EVENT (thanks Nestharus)
    • v0.0.1
      • Changed name (RIP LUIS :()

    Enjoy it, comments and suggestions are welcome.
     

    Attached Files:

    Last edited: Dec 8, 2012
  2. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,860
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    i knew it!
    that's why the function names are somewhat like AIDS :D
    This is amazing,im going to convert this to GUI
     
  3. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Please elaborate

    Also, your locks should use a counter, not a boolean.

    edit
    I should also point out that this doesn't work with modules or any sort of initializer

    edit
    Also, acronyms for system names aren't really acceptable at THW. Only well known acronyms are really acceptable. If you look through all of the JASS resources on THW, you will not find one with an acronym unless it is a well known one, like MD5 or AES.

    Only on wc3c and TH were acronyms really acceptable, and even then, very few on wc3c were acronyms.

    Timer Tools lib is named Tt as the optimizer didn't work when it was made, so it had all of the shortened stuff applied to it to increase its speed. However, in an update, you can be sure that Tt is going to be renamed to TimerTools.

    CTL was given an acronym again for speed. It is too late to rename it as too many resources now use it : (.

    edit
    oh yea, and your EVENT_UNIT w/e is buggy, haha...

    Code (vJASS):

    //on index instance 1
    if (create < 2) then
    set create = create + 1
    call CreateUnit(...)
    //event unit == null
    endif

    //on index instance 2
    if (create < 2) then
    set create = create + 1
    call CreateUnit(...)
    //event unit == created unit
    endif
     


    nice first attempt, but lots of common mistakes ^)^
     
  4. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
    Putting info...

    Why? ._.

    Well yeah, but naming a function/global variable LightUnitIndexerSystem___Name its kinda ugly :c, but can be changed if needed.

    Sacrilege!
    I will look it out, thanks for reporting :3.

    Fixed.

    Next wave please ;D
     
    Last edited: Dec 5, 2012
  5. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    You haven't elaborated on what skills reset unit user data

    don't ask why on the locks, there is a very valid reason, figure it out ;p. There is a reason both AIDS and Unit Indexer use integers, not booleans. Until you do integers, this will still have bugged behavior.
     
  6. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
    I want to find the ability, give me a moment.

    :ogre_icwydt:
    Haha, well lets check then >.<
     
  7. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    timer1
    lock index

    timer2
    lock index

    timer1 expires
    unlock index

    timer2 expires
    doh, index doesn't exist!
    if a unit was created and another timer locked it, this'll end up locking that unit incorrectly
    this will also end up cleaning stuff up that doesn't exist for that unit and may even cause permanent leaks

    thus the need for a counter

    edit
    unit event variable wasn't fixed.. you are still nulling it and not using some sort of stack. Do not use an array to fix it, that's not the best answer ;o.
     
  8. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
    I realize that I was having a wrong concept of what the lock feature does.

    I didn't want to add an stack here, you can easily save the unit on a variable... but meh I will have to add it.
    You're right.
     
    Last edited: Dec 5, 2012
  9. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Don't use an array, use locals like I linked in the tutorial

    edit
    your initialization is still broken
     
  10. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
    How is that? ._.
     
  11. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    at initialization (all before game starts, considered to be 1 instant, considered to be at the same time)

    module1
    register

    module2
    create unit (runs module1)

    module3
    register (uh oh, won't fire for unit created by module2)

    you'll have to maintain a list of index/deindex stuff for before the game starts and run all of those events for all modules registering before the game starts

    UnitIndexer is the only lib that correctly does this, so it is the only unit indexer out there that does initialization correctly

    for this system to even be considered, you need to provide all correct behavior like unit indexer does, otherwise your system will be considered buggy and vastly inferior to unit indexer
     
  12. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
    Again, you're right, I'm working on it.

    edit
    Running a timer on init:

    Code (vJASS):
    private function Run takes nothing returns nothing
        local integer i = bj_MAX_PLAYERS
        local trigger t = CreateTrigger()
        local boolexpr oL = Condition(function OnLeave)
        local region r = CreateRegion()
        local player p
        local unit u
       
        loop
            exitwhen i < 0
           
            set p = Player(i)
           
            call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, oL)
            call SetPlayerAbilityAvailable(p, LEAVER_ABILITY, false)
           
            call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, p, null)
           
            loop
                set u = FirstOfGroup(bj_lastCreatedGroup)
                exitwhen u == null
                call GroupRemoveUnit(bj_lastCreatedGroup, u)
               
                call IndexUnit(u)
            endloop
           
            set i = i - 1
        endloop
       
        call RegionAddRect(r, GetWorldBounds())
        call TriggerRegisterEnterRegion(CreateTrigger(), r, Condition(function OnEnter))
       
        set t = null
        set oL = null
        set r = null
        set p = null
       
        call DestroyTimer(GetExpiredTimer())
    endfunction

    private function Init takes nothing returns nothing
        // to avoid problems (firing events) on init u.u
        call TimerStart(CreateTimer(), 0, false, function Run)
    endfunction


    Fix the problem, but, this is valid?.

    Back on the "UnitUserData is reset to 0 by some abilities", I was testing all the morph/hexx abilities and nothing seems happen :\, I will wait for feedback for one of my map makers friend.

    Glasses on, here comes a quotation of Troll-Brain u.u:
     
    Last edited: Dec 5, 2012
  13. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
    Changed to version 0, this young beast is not stable yet <.<
     
  14. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Given that unit user data doesn't apparently reset, you might as well use unit user data over a hashtable.

    It is, after all, a solid improvement ; P. Lowers memory cost by freeing a hashtable, lowers code size, and improves performance.
     
  15. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
    Apparently, but some users want that space free... what to do <.<.

    Thanks to you :ogre_hurrhurr:. So, version 1? :3

    edit
    So, at the end, the timer trick is valid?
     
  16. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    timer trick does not fix problem

    GetUnitUserData(CreateUnit(...)) == 0 at init if you use timer

    edit
    now what u have is the exact problem u had before...

    also, modules won't work with this on vex's jasshelper
     
    Last edited: Dec 6, 2012
  17. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    If you want to make a "light" one, you would not use any event at all.

    And the only way where it would worth it, it's when you have the full control of unit creation/destruction (so it requires a custom decay system, unit training, and so one).

    This is how i see the thing :

    - a custom CreateUnit function which creates the unit and then eventually index it (if the unit is created because of valid function arguments)
    - a custom RemoveUnit function which removes the unit and eventually deindex it (if the unit is being removed).

    This way, if for some reason you don't want to index an unit, then you just have to use the native CreateUnit function instead of your custom one.

    Also, for a not indexed unit, use RemoveUnit is fine, but you should always use the custom RemoveUnit function for every unit.

    In fact i started to make a such unit indexer, but the requirements are too much imho, even if yeah full control of units means more possibilities.

    EDIT :

    Ok we have not the same meaning for "light", my idea is the best one in term of efficiency but it's the worst one in term of requirements, because it requires a bunch of custom systems if you want to do even simple things such as unit decay/training, which are already available in a native way ...
    Now, in some custom maps it's still a valid option.

    Also obvioulsy that would not be gui friendly at all.

    The only real con i have about UnitIndexer is about this silly WorldBounds requirement, an UnitIndexer is something really used, even for some quick test.
    And for this kind of test, the library requirements are just annoying.
    That's why i have "repacked" it to have 0 requirements for my personal usage.
     
    Last edited: Dec 6, 2012
  18. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
    That is one option, in fact it's a good one :3. But I will keep the events to make it more GUI friendly.

    xD, my "light" idea is low requirements (0 until now :3) and low numbers of code lines (of course having in mind efficiency). Besides, I try in all the cases that I can, to follow the KISS concept.

    I will update this in a few days ;), I have some work to do :/.

    Greetings.
     
  19. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    I think most, if not all of the JASSer have the same idea of light as Troll does. I do as well. Same with mag.

    Troll pretty much summed up what everyone was thinking but wouldn't say ;p.

    I'm also sure that everyone thinks that the WorldBounds req is pretty annoying. I think that as well ;p. It's a really silly req, lol... but I required it back when I was the "uber let's go modular" crazy person >.<.
     
  20. moyackx

    moyackx

    Joined:
    Feb 15, 2006
    Messages:
    789
    Resources:
    7
    Maps:
    4
    Spells:
    2
    Tutorials:
    1
    Resources:
    7
    I was wondering... what's the usage of an unit indexing code? I ask because I've never had the need of something like that.