1. Find your way through the deepest dungeon in the 18th Mini Mapping Contest Poll.
    Dismiss Notice
  2. A brave new world lies beyond the seven seas. Join the 34th Modeling Contest today!
    Dismiss Notice
  3. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
Hive 3 Remoosed BETA - NOW LIVE. Go check it out at BETA Hive Workshop! Post your feedback in this new forum BETA Feedback.
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[System] GroupUtils

Discussion in 'Graveyard' started by Magtheridon96, Oct 12, 2011.

  1. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,003
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Now that everyone's using FirstOfGroup loops, I re-evaluated GroupUtils by Rising_Dusk and found that most of the code was useless s***code now.

    Here's a faster, cleaner, and 'better' version of it:

    Code (vJASS):
    /***************************************
    *
    *   GroupTools
    *   v1.1.2.2
    *   By Magtheridon96
    *
    *   - Original version by Rising_Dusk
    *
    *   - Recycles groups, and allows the
    *   enumeration of units while taking
    *   into account collision.
    *
    *   API:
    *   ----
    *
    *       - group ENUM_GROUP
    *
    *       - function NewGroup takes nothing returns group
    *       - function ReleaseGroup takes group g returns nothing
    *           - Get and release group handles
    *
    *       - function GroupRefresh takes group g returns nothing
    *           - Refresh a group so that null units are removed
    *
    *       - function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
    *           - Groups units while taking into account collision
    *
    ***************************************/

    library GroupTools
       
        globals
            // The highest collision size you're using in your map.
            private constant real MAX_COLLISION_SIZE = 197.
            // Data Variables
            private group array groups
            private group gT = null
            private integer gN = 0
            private boolean f = false
            // Global Group (Change it to CreateGroup() if you want)
            group ENUM_GROUP = bj_lastCreatedGroup
        endglobals
       
        static if DEBUG_MODE then
            private struct V extends array
                debug static hashtable ht = InitHashtable()
            endstruct
        endif
       
        private function AE takes nothing returns nothing
            if (f) then
                call GroupClear(gT)
                set f = false
            endif
            call GroupAddUnit(gT,GetEnumUnit())
        endfunction
       
        function GroupRefresh takes group g returns nothing
            debug if null==g then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"[GroupUtils]Error: Attempt to refresh null group!")
                debug return
            debug endif
            set f = true
            set gT = g
            call ForGroup(gT,function AE)
            if (f) then
                call GroupClear(g)
            endif
        endfunction
       
        function NewGroup takes nothing returns group
            if 0==gN then
                return CreateGroup()
            endif
            set gN = gN - 1
            debug call SaveBoolean(V.ht,GetHandleId(groups[gN]),0,false)
            return groups[gN]
        endfunction
       
        function ReleaseGroup takes group g returns nothing
            debug if null==g then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"[GroupUtils]Error: Attempt to release null group!")
                debug return
            debug endif
            debug if LoadBoolean(V.ht,GetHandleId(g),0) then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"[GroupUtils]Error: Double free!")
                debug return
            debug endif
            debug call SaveBoolean(V.ht,GetHandleId(g),0,true)
            call GroupClear(g)
            set groups[gN] = g
            set gN = gN + 1
        endfunction
       
        function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
            local unit u
            debug if null==whichGroup then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"[GroupUtils]Error: Null group passed to GroupUnitsInArea!")
                debug return
            debug endif
            call GroupEnumUnitsInRange(ENUM_GROUP,x,y,radius+MAX_COLLISION_SIZE,null)
            loop
                set u = FirstOfGroup(ENUM_GROUP)
                exitwhen null==u
                if IsUnitInRangeXY(u,x,y,radius) then
                    call GroupAddUnit(whichGroup,u)
                endif
                call GroupRemoveUnit(ENUM_GROUP,u)
            endloop
        endfunction
       
    endlibrary


    By the way, I didn't put an empty GroupEnumUnitsInArea function so that your libraries wouldn't
    compile thus notifying you of the libraries you have to fix to run off of GroupUnitsInArea :p

    Note: The use of groups is very avoidable in Warcraft III (Thank you Bribe).
    The purpose of this is to improve the code of people who use groups anyways.

    Feel free to comment..
     
    Last edited: Oct 20, 2011
  2. noob

    noob

    Joined:
    Sep 28, 2011
    Messages:
    828
    Resources:
    0
    Resources:
    0
    Does not insult Rising_Dusk
     
    Last edited: Oct 12, 2011
  3. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,413
    Resources:
    1
    JASS:
    1
    Resources:
    1
    The double free protection and group > 8190 checkings in debug mode are missing. (public stuff is so fun)

    OFF-TOPIC :

    Define everyone.
    Coz i don't think we live in the same world, most of "jasser" (not that much still alive) probably still use ForForce.

    Btw is there a valid benchmark lying there (for instants groups ofc) ?

    GroupEnum + null filter + FirstOfGroup/GroupRemoveUnit loop
    VS
    GroupEnum + all the code in the filter (return false)
     
  4. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,003
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Well, FoG loops are logically faster than normal group enumerations with filters for 1 reason:

    Assume 100 units.

    The GroupEnumUnitsInRange function with a filter will open up 100 threads (similar to 100 function calls)
    The FoG loop will do nothing but loop through all the units directly.

    ?
     
  5. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,413
    Resources:
    1
    JASS:
    1
    Resources:
    1
    I know, i'm just not sure that even with a null filter an enumed unit doesn't open by itself a new thread (which we have no access in case of a null filter)
     
  6. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,003
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Maybe the Jass interpreter checks for null code/boolexprs/filterfuncs to avoid creating and destroying threads :p
    Let's do a benchmark ^^
     
  7. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,413
    Resources:
    1
    JASS:
    1
    Resources:
    1
    Yeah, it's obvious that the jass interpreter do such optimizations ...
    In fact i would bet 5 against to 1 that the FOG loop would be slower.

    Btw, you are welcome for the benchmark :p
     
  8. Luorax

    Luorax

    Joined:
    Aug 7, 2009
    Messages:
    1,301
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Actually all the "shitcode" that was in GroupUtils had a purpose (like the filter you can pass to GroupEnumUnitsInArea). So actually replacing our old GU with this would break any map.
     
  9. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,003
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    But GroupEnumUnitsInArea can be easily replaced with GroupUnitsInArea and a FoG loop.
    If a map is using the old GU, that's fine, it doesn't have to use this one.

    Oh and most of that shitcode was used to support recursion (Which I didn't find useful since FoG loops are way faster than
    GroupEnumUnitsInRange with a filter, and recursion is impossible with a null filter)

    edit

    Silly me, I found a way to make GroupUnitsInArea faster ^^ (Will update in 24 hours)
     
  10. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,413
    Resources:
    1
    JASS:
    1
    Resources:
    1
    Theories are theorical.
    Until there is a valid benchmark that proves it, i won't assume FOG loop is faster.
    However a FOG loop makes the code more usable, neater than coding directly inside the filter.
     
  11. Dirac

    Dirac

    Joined:
    Jun 20, 2011
    Messages:
    249
    Resources:
    3
    JASS:
    3
    Resources:
    3
  12. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,003
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    I love the course of my threads :3
    *Gets popcorn*

    I'm going to update this tomorrow :)
     
  13. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,413
    Resources:
    1
    JASS:
    1
    Resources:
    1
    Glad to know that i would have lost my bet, since FoG loops are way nicer than split code in a filter.

    Personnaly, with 100 enumed units and a 0.1 timer and a simple action in the loop (setting an integer to the unit custom value) i get not fps lost with a FoG loop (60 fps), but have less than 30 with a GroupEnum and a filter.

    I've cared to use short names and a static group instead of create/destroy a new one each time.
    My benchmark was still not a concrete case but precise enough to see that FoG is the absolute winner (since when coding in jass makes sense ? :p)


    EDIT : Omg i forgot the GroupEnum before the FoG loop ...
    You see, there is a reason for sharing benchmarks test codes :p
    Meh, will make a valid test tomorrow ...

    However FoG should still be the winner, even if Dirac used dynamic groups and no action at all for filtered units (unless he enumed 0 units).
     
  14. Dirac

    Dirac

    Joined:
    Jun 20, 2011
    Messages:
    249
    Resources:
    3
    JASS:
    3
    Resources:
    3
    12 units inside the group
     
  15. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,003
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Updated:

    - Made GroupUnitsInArea faster by avoiding a filter and using an FoG loop.
    - Added debug-only double-free protection.
     
  16. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,430
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Anitarf also made some interesting benchmarks for it, in case you want to take a look.

    Anyway, the code looks good. However, you should use a global group for the GroupUnitsInArea instead of a local group.
     
  17. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,413
    Resources:
    1
    JASS:
    1
    Resources:
    1
    Ok so RIP ugly code in GroupEnum filter, good to know :)
     
  18. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,401
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    GroupUtils also has the ENUM_GROUP variable.

    Dynamic groups are also completely avoidable if you use UnitIndexer and LinkedList, but I don't want to burden all users with such a heavy learning curve and installation process, so this library could still serve as a middle grounds.

    However this breaks backwards compatibility so it's going to suffer the same fate as my rendition of Table if you call it like this.
     
  19. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,003
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    No one needs that ;)
    You could use bj_lastCreatedGroup ^^

    So, should I change the name? :eek:
    Table was fine bro :p
    No one in their right-mind would even THINK about using Vexorian's Table nowadays ^^ (Except those veterans at wc3c.net)

    If I should change the name, I guess I should go with GroupUtilities :p

    edit
    Updated.
    - Now using Global group inside GroupUnitsInArea
    - Changed name to GroupUtilities
    - GroupUnitsInArea now even faster ^_^
     
  20. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,401
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Or GroupTools, because tool is short for utility.

    bj_lastCreatedGroup could mess up if there is some recursion going on, which might catch GUI users off guard and display some odd results. It is very rare such a bug could occur but having a dedicated global group just for temporary enumeration makes sense.

    ENUM_GROUP, for example, would be used instead of your overkill NewGroup/ReleaseGroup calls that you do for that enumeration in GroupUnitsInArea.

    Also, it makes more sense to inline IsUnitInRangeXY than let GroupUnitsInArea do it for me.

    In fact dynamic groups in general are so avoidable in most cases unlike timers. This library was originally made when null filters leaked.