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. The raddest synthwave tracks were chosen - Check out our Music Contest #12 - Results and congratulate the winners!
    Dismiss Notice
  4. The poll for Hive's 12th Concept Art Contest is up! Go cast your vote for your favourite genie!
    Dismiss Notice
  5. 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
  6. The 18th Icon Contest is ON! Choose any ingame unit and give him/her Hero abilities. Good luck to all.
    Dismiss Notice
  7. 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
  8. Colour outside the lines! Techtree Contest #13 is a go. The contest is optionally paired.
    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] StructuredDD (Structured Damage Detection)

Discussion in 'JASS Resources' started by Cokemonkey11, May 30, 2012.

  1. Cokemonkey11

    Cokemonkey11

    Wurst Reviewer

    Joined:
    May 9, 2006
    Messages:
    3,240
    Resources:
    18
    Tools:
    1
    Maps:
    5
    Spells:
    3
    Tutorials:
    2
    JASS:
    7
    Resources:
    18
    [System] StructuredDD

    StructuredDD

    Preface:

    StructuredDD is a damage detection system which enables users to register a pseudo-generic "unit damaged" event. Many systems exist to accomplish the same result, but the intended design paradigms represented by StructuredDD make it unique:

    • Code Simplicity: StructuredDD is well commented and is self-documented. The design concept is easy to understand for programmers and JASS users with intermediate experience.
    • Fully independent of UnitUserData. Many inexperienced spell programmers use UnitUserData to create their abilities, and I want to ensure that it is always available for that.
    • Cohesion: The system does exactly what it intends to do, and nothing more. There are no required libraries, and any additional functionality is handled in separate components.

    Design Explanation:

    1. User defines one or more
      code
      to be executed on damage detection
    2. User indexes units they want to be used for damage detection (alternatively StructuredDD can auto-index all units)
    3. StructuredDD handles instances of detection triggers, each tied to a "bucket" of units
    4. StructuredDD automatically checks each trigger periodically if the entire bucket contains null units before destroying the trigger

    Limitations:

    • StructuredDD requires vJass
    • There is currently no support for deallocating handlers for dynamic use
    • There is currently no support for periodic cleanup timeouts that self-optimize.
    • There is currently no support for "long-term" unit buckets built to handle units that aren't removed from the game (for example heroes)
    • There is no included "safe" method for damaging a unit - it is trivial for the client to do this in their own handler(s).
    • There is no included method to check for physical/spell/code damage - the computational cost for such systems is a higher order of magnitude than the system itself, thus these systems are built as extensions.
    • Handlers are added in order to an array and are executed in order. If you have handlers that depend on each other, it is your job to avoid race conditions.

    The script:

    Code (vJASS):


    // StructuredDD is a damage detection system for handling the generic "unit is
    // damaged" event response. The API follows:
    //
    //
    //      StructuredDD.addHandler(function f)
    //
    //          * Registers function f as a callback function, which will occur
    //            whenever a unit, which has been added to the system, is damaged.
    //          * In the context of a handler function f, use standard jass natives
    //            like:
    //                - GetTriggerUnit()       - returns the damaged unit
    //                - GetEventDamageSource() - returns the attacking unit
    //                - GetEventDamage()       - returns how much damage was dealt
    //
    //
    //      StructuredDD.add(unit u)
    //
    //          * Manually adds a unit the damage detection system, so that all
    //            damage handlers are called when it receives damage.
    //          * Note that if `ADD_ALL_UNITS` is enabled, there is no need to
    //            manually add a unit.
    //
    //
    library StructuredDD
        globals

            ///////////////////////////////////////////////////////////////////////
            //                            CONFIGURATION
            ///////////////////////////////////////////////////////////////////////

            // When enabled, all units in the map will be added to the damage
            // detection system.
            private constant boolean ADD_ALL_UNITS = true

            // The bucket size determines how many units should be added to each
            // damage detection bucket. Many variables impact the performance
            // for this process. If in doubt, use a number between 10 and 30.
            private constant integer BUCKET_SIZE = 20

            // Controls the period, in seconds, for the vacuum interval. Higher
            // values will have higher success rate, but may become more costly. If
            // in doubt, use a number between 30. and 180.
            private constant real PER_CLEANUP_TIMEOUT = 60.

            ///////////////////////////////////////////////////////////////////////
            //                          END CONFIGURATION
            ///////////////////////////////////////////////////////////////////////

        endglobals


        // A bucket represents a fragment of the units that are managed by the
        // damage detection engine. Essentially, each bucket has a trigger and a
        // set of units. When all the units in the bucket die, the trigger is
        // destroed. This allows more granular recycling of triggers, rather than
        // rebuilding one huge trigger synchronously, as traditional methods do.
        private struct bucket
            integer bucketIndex = 0
            trigger trig = CreateTrigger()
            unit array members[BUCKET_SIZE]
        endstruct


        // The StructuredDD struct is just a wrapper, which provides the API with
        // "dot" syntax.
        struct StructuredDD extends array

            // The conditions array manages *all* conditions in the context of the
            // damage detector. When a new bucket is instantiated, all conditions
            // are added to it. When a condition is added, all existing bucket's
            // triggers receive the condition.
            private static boolexpr array conditions

            private static bucket   array bucketDB

            private static integer conditionsIndex = -1
            private static integer dbIndex         = -1
            private static integer maxDBIndex      = -1

            // This auxilliary method is used for getting the next available
            // bucket. Since buckets get units added to them one at a time, but
            // have capacity for many, this method arbitrates when to use the last
            // bucket and when to build a new one.
            private static method getBucket takes nothing returns integer
                local integer index    =  0
                local integer returner = -1
                local bucket tempDat

                if thistype.dbIndex != -1 and thistype.bucketDB[thistype.dbIndex].bucketIndex < BUCKET_SIZE then

                    // A non-full bucket context is already known, so use it.
                    return thistype.dbIndex
                else

                    // This is either the first bucket requested, or the last
                    // bucket used is now full - instantiate a new one.
                    set thistype.maxDBIndex = thistype.maxDBIndex + 1
                    set thistype.dbIndex = thistype.maxDBIndex
                    set tempDat = bucket.create()
                    set thistype.bucketDB[.maxDBIndex] = tempDat

                    // Add all known handlers to the new bucket.
                    loop
                        exitwhen index > thistype.conditionsIndex

                        call TriggerAddCondition(tempDat.trig, thistype.conditions[index])

                        set index = index + 1
                    endloop

                    return thistype.dbIndex
                endif

                // This line never executes, but some versions of JassHelper will
                // flag an error without it.
                return -1
            endmethod

            // Adds a new "handler" function to the list of functions that are
            // executed when a unit is damaged. Adding a handler will immediately
            // enable it in all buckets. Removing handlers is not supported, so
            // this should not be used dynamically.
            public static method addHandler takes code func returns nothing
                local bucket tempDat
                local integer index = 0

                set thistype.conditionsIndex = thistype.conditionsIndex + 1
                set thistype.conditions[thistype.conditionsIndex] = Condition(func)

                // Immediately add this new handler to all buckets.
                loop
                    exitwhen index > thistype.maxDBIndex

                    set tempDat = thistype.bucketDB[index]
                    call TriggerAddCondition(tempDat.trig, thistype.conditions[thistype.conditionsIndex])

                    set index = index + 1
                endloop
            endmethod

            // Adds a unit to the damage detection system using some bucket. When
            // the unit dies or is removed, the bucket will eventually empty and
            // get recycled. If you enable the `ADD_ALL_UNITS` configuration
            // variable, then there is no need to use this method.
            public static method add takes unit member returns nothing
                local bucket tempDat
                local integer whichBucket = thistype.getBucket()

                set tempDat = thistype.bucketDB[whichBucket]
                set tempDat.bucketIndex = tempDat.bucketIndex+1
                set tempDat.members[tempDat.bucketIndex] = member

                // When a unit is added to a bucket, the event for it is
                // immediately enabled.
                call TriggerRegisterUnitEvent(tempDat.trig,member, EVENT_UNIT_DAMAGED)
            endmethod

            // This auxilliary method is part of the implementation for
            // `ADD_ALL_UNITS`. It adds a unit to the damage detection context,
            // when triggered below.
            static if ADD_ALL_UNITS then
                private static method autoAddC takes nothing returns boolean
                    call thistype.add(GetTriggerUnit())

                    return false
                endmethod
            endif

            // This auxilliary method is used to check if a bucket is empty, and
            // is used to arbitrate when a bucket (and its associated trigger) can
            // be deallocated. This occurs as part of the periodic cleanup system
            // and can be disabled.
            private static method bucketIsEmpty takes integer which returns boolean
                local bucket tempDat = thistype.bucketDB[which]
                local integer index = 0

                loop
                    exitwhen index == BUCKET_SIZE

                    // If a unit is removed, it's `TypeId` will return 0, at least
                    // for some period before its pointer leaves cache or is
                    // reused.
                    if GetUnitTypeId(tempDat.members[index]) != 0 then
                        return false
                    endif

                    set index = index + 1
                endloop

                return true
            endmethod

            // This method is called periodically and checks the buckets contents,
            // and recycles them if they are empty. A better implementation would
            // cycle through one bucket a time per iteration, rather than
            // synchronously iterating through all buckets.
            private static method perCleanup takes nothing returns nothing
                local integer index = 0

                loop
                    exitwhen index > thistype.maxDBIndex

                    if index != thistype.dbIndex and thistype.bucketIsEmpty(index) then

                        // The bucket at this index is empty, so begin
                        // deallocating.
                        call DestroyTrigger(thistype.bucketDB[index].trig)
                        call thistype.bucketDB[index].destroy()

                        set thistype.bucketDB[index] = thistype.bucketDB[thistype.maxDBIndex]
                        set thistype.maxDBIndex = thistype.maxDBIndex - 1

                        if thistype.maxDBIndex == thistype.dbIndex then
                            set thistype.dbIndex = index
                        endif

                        set index = index - 1
                    endif

                    set index = index + 1
                endloop
            endmethod

            // This struct initialization method is necessary for setting up the
            // damage detection system.
            private static method onInit takes nothing returns nothing
                local group   grp
                local region  reg
                local trigger autoAddUnits
                local timer   perCleanup
                local unit    FoG

                // If the `ADD_ALL_UNITS` configuration is enabled, turns on a
                // trigger which allocates all new units to a bucket. Also checks
                // units that are on the map at initialization.
                static if ADD_ALL_UNITS then

                    // Add pre-placed units.
                    set grp = CreateGroup()
                    call GroupEnumUnitsInRect(grp, bj_mapInitialPlayableArea, null)

                    loop
                        set FoG = FirstOfGroup(grp)
                        exitwhen FoG == null

                        call thistype.add(FoG)

                        call GroupRemoveUnit(grp,FoG)
                    endloop

                    // Add units that enter the map using a trigger.
                    set autoAddUnits = CreateTrigger()
                    set reg = CreateRegion()

                    call RegionAddRect(reg, bj_mapInitialPlayableArea)
                    call TriggerRegisterEnterRegion(autoAddUnits, reg, null)
                    call TriggerAddCondition(autoAddUnits, Condition(function thistype.autoAddC))

                    set autoAddUnits = null
                    set reg = null
                endif

                // Enable the periodic cleanup module, which vacuums each bucket
                // periodically.
                set perCleanup = CreateTimer()
                call TimerStart(perCleanup, PER_CLEANUP_TIMEOUT, true, function thistype.perCleanup)

                set perCleanup = null
            endmethod
        endstruct
    endlibrary

     


    Example Test Script:

    Code (vJASS):


    // This script requires that StructuredDD.ADD_ALL_UNITS is enabled.
    scope Test initializer init
        private function h takes nothing returns nothing
            local string attackerName = GetUnitName(GetEventDamageSource())
            local string damage       = R2S(GetEventDamage())
            local string struckName   = GetUnitName(GetTriggerUnit())

            call DisplayTextToPlayer(GetLocalPlayer(), 0., 0., attackerName + " dealt " + damage + " to " + struckName)
        endfunction

        private function init takes nothing returns nothing
            call FogMaskEnable(false)
            call FogEnable(false)

            call StructuredDD.addHandler(function h)
        endfunction
    endscope

     


    Change Log:

    list

    2015.09.07 - fixed up white space and improved documentation
    2013.05.25 - removed UnitAlive declaration and reduced length of processed code using static if
    2013.05.24 - made one small change (StructuredDD extends array) which will reduce the size of the compiled code. This change does not affect the API.
    2013.05.13 - updated the API and documentation
    2013.01.18 - big update - updated API, removed some components, fixed a massive bug. Please note that I have also stopped maintaining the version pastebin mirrors as they were a waste of time to maintain.
    2012.06.03 - Updated the null check to use GetUnitTypeId() which fixes a deindexing bug where DEAD_UNITS_ARE_NULL was false. The script now declares native UnitAlive, thus IsUnitType() has been replaced with UnitAlive(). Will revert this change if it is not recommended.
    2012.06.02 - The periodic cleanup now uses a timer instead of a trigger for efficiency reasons.
    2012.06.01 - Updating the script to follow proper CamelCase. The API has also been changed to reflect only 1 use of addHandler, which takes code as an argument as suggested by fellow hive users. Also fixed two major bugs in the decrement portion of the stack.
    2012.05.30 #2 - Updated submission with additional method and changed constant nomenclature.
    2012.05.30 - Initial submission to Hive: Jass Resources: Submissions


    Special Thanks:

    • PurplePoot who thought of bucket-based damage detection systems and created it in the first place, but never released his version.
    • Nestharus and Troll-Brain for explaining how to use GetUnitTypeId() for null checks.
    • -Kobas- for creating a map submission template, which turned out useful for system submissions as well.
    • Vexorian for developing JassHelper. Without vJass, I almost certainly would not still be scripting for wc3.
    • The various developers of JNGP including PitzerMike and MindworX.
     
    Last edited: Sep 7, 2015
  2. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,021
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Please remove your signature. Also, APILIKETHISISHARDLYREADABLE, IT_IS_MUCH_MORE_READABLE_LIKE_THIS. Also, boolexpr's as an argument is pretty ugly syntax, I recommend using code arguments and convert them to boolexpr's yourself, saving the user the workload. Also, bj_mapInitialPlayableArea does not cover the whole map where units may be and you also miss out on Locust units this way (not that you need them for a damage detection system, but it's possible). Basing this off an indexer makes a lot more sense.
     
  3. Cokemonkey11

    Cokemonkey11

    Wurst Reviewer

    Joined:
    May 9, 2006
    Messages:
    3,240
    Resources:
    18
    Tools:
    1
    Maps:
    5
    Spells:
    3
    Tutorials:
    2
    JASS:
    7
    Resources:
    18
    Thanks for the reply.

    That's your opinion. Blizzard seems to disagree since they take boolexpr's as arguments, but regardless I've added a new method to the API which takes code as an argument instead.

    The ADD_ALL_UNITS functionality is just for users who want to make using my system easy. If they are doing something complex like locust units outside the initial playable map are on initialization, they can write their own "add all units" script. Regardless, I've added this as a note in the API to save users the trouble of reading my script.

    I don't use indexing systems (and neither should you). This script is open source however, if you'd like to change it, you're more than welcome.


    ---

    Again, thanks for the comments. (+reps of course)
     
  4. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,021
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    >> Blizzard seems to disagree since they take boolexpr's as arguments

    If you are not using the true or false utility of boolexpr's, accept code as the argument.

    >> I don't use indexing systems (and neither should you).

    Indexing systems are great. You have provided no reason why you nor I shouldn't use indexing systems. GUI Unit Indexer has been a really great help for lots of users recently, which was unprecedented seeing as how Jesus4Lyf's "GUI AIDS" never really took off.
     
  5. Cokemonkey11

    Cokemonkey11

    Wurst Reviewer

    Joined:
    May 9, 2006
    Messages:
    3,240
    Resources:
    18
    Tools:
    1
    Maps:
    5
    Spells:
    3
    Tutorials:
    2
    JASS:
    7
    Resources:
    18
    Yep I've added that to the API as I said. But I have no plans to use TriggerAddAction. If you want to use TSA in your DD handlers, don't use this script.

    Because unit indexing systems use unit custom value and/or hashtables. My system isn't intended for "new" or "casual" developers, but instead for people who want to use efficient vJass. Chances are the users of this system know how to make it themself, but simply don't want to spend the time (I know I've been putting this off for awhile)
     
  6. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,021
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    >> But I have no plans to use TriggerAddAction.

    You don't need to. Trigger conditions can return nothing and therefore give the same speed benefit. However, when using vJass which is slaved to pJass, pJass blocks those kind of "returns nothing" conditions from compiling. The way to bypass it is to pass the code argument to a function and that function calls the "Filter/Condition" native to compile it without pJass' obsolete safety. Thus it is less verbose to the user and preserves conditions as replacements to actions.

    >> My system is ... for people who want to use efficient vJass.

    UnitUserData / hashtables are too slow to be useful? Don't go down this slippery slope of efficiency. Once you crop you can't stop. Seriously, no one cares. Except Nestharus but he really is addicted to it. Your system is not the most efficient anyway, you might as well focus on more important areas.
     
  7. Cokemonkey11

    Cokemonkey11

    Wurst Reviewer

    Joined:
    May 9, 2006
    Messages:
    3,240
    Resources:
    18
    Tools:
    1
    Maps:
    5
    Spells:
    3
    Tutorials:
    2
    JASS:
    7
    Resources:
    18
    That's quite nice to know actually, can you show me how in an example?

    UnitUserData is too limited because it only supports 1 value per unit.

    Hashtables are too slow for unit control and spells. They are useful in other ways, like when O(n) is slower than the hashtable counter-part, but for my own preferences I'm going to stick with my linear stacks.

    I understand you don't care about efficiency (Or you wouldn't still be making systems for GUI) but that doesn't mean that "no one" cares. Seriously don't generalize stuff like that.
     
  8. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,021
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    >> Hashtables are too slow for unit control and spells.

    I use hashtables pretty frequently in Retro for really comprehensive data, my poor-grade Intel Atom N450 can handle it even on the scale of 100+ units without dropping in FPS.

    >> UnitUserData is too limited because it only supports 1 value per unit.

    Too limited? It provides an array index for every unit, it is a supplementary utility. You don't need more than one UnitUserData.

    >> When O(n) is slower than the hashtable counter-part.

    Which is essentially every time "n" exceeds 2 or 3. JASS reading is extremely "slow" thanks to it being an interpreted language. Calling hashtables slow is like saying the FLASH is slower than SUPERMAN. Who cares? Which brings us to...

    >> That doesn't mean that "no one" cares.

    No one cares about a 0.0000001% margin, which is the case here. If people think they care, they are overanalyzing and need to look at real-world scenarios. It's ridiculous percentiles like this that allows Intel to sell i3-2130's at a price difference 5x the difference between the 2100 and the 2120, despite providing only half the edge in performance (a 100MHz overclock instead of a 200MHz overclock costing $25 more instead of $5 more resulting in a huge, totally worth the money 3% performance gain).
     
  9. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,006
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Here's an example:

    Code (vJASS):
    function Hi takes nothing returns nothing


    endfunction

    // ...
    local code c = function Hi
    call TriggerAddCondition(t, Filter(c))
    // ...


    The compiler can't stop you from doing this since you're passing a variable ;)
    Of course, when the code is a parameter, you would need to make your function 2 lines long (add a return statement after the actual code) so that it wouldn't inline.

    If it inlines, pJass says "Woah bro, that function doesn't return a boolean. You're in big trouble Mister."
     
  10. LeP

    LeP

    Joined:
    Feb 13, 2008
    Messages:
    445
    Resources:
    0
    Resources:
    0
    Someone counted how often i mentioned Zoxcs Jass Parser?
    Or how to use it.

    But this is new: using my incredible google-fu i ofc found a valid download of the cli-parser here.

    And ofc you could always compile it from source.

    Or change pJass' source.
     
  11. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,021
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    LeP, it's fine for your own resources, but for public resources people don't like to download a new .exe just to run your script. I know about that resource and yes one can get it to work but no one cannot expect everyone to have it.
     
  12. LeP

    LeP

    Joined:
    Feb 13, 2008
    Messages:
    445
    Resources:
    0
    Resources:
    0
    But it's okay for a public resource to circumvent the syntax-checker?
    Cool idea.
     
  13. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,006
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Yes, it is.

    There's no reason as to why workarounds are bad, hence, they are not bad, in other words, it's fine to use them.
     
  14. LeP

    LeP

    Joined:
    Feb 13, 2008
    Messages:
    445
    Resources:
    0
    Resources:
    0
    They were technically bad (desync).
    They are still semantically bad (TriggerAddCondition, and we all know a conditions return something truthy or falsy.).

    ______
    Also, when your needs change you adopt your tools.

    "Hey heard of this new tool called JassHelper?", "Yeah, user don't want to download any programm.", "Okay, let's continue using Jass the way it is."
     
  15. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,006
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Users wouldn't care.

    They would rather just omit the return false line (It's such an evil line.)
     
  16. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,021
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    LeP, you are way overanalyzing. JASS and its subculture have implemented so many hacky things at this point what you are implicating is nothing out of the norm for us.
     
  17. Cokemonkey11

    Cokemonkey11

    Wurst Reviewer

    Joined:
    May 9, 2006
    Messages:
    3,240
    Resources:
    18
    Tools:
    1
    Maps:
    5
    Spells:
    3
    Tutorials:
    2
    JASS:
    7
    Resources:
    18
    That's excellent! I'm glad hashtables work for you and I won't try to convince you otherwise.

    And what happens if a user of your system isn't so knowledgeable and wants to use unituserdata to make his or her spells? What happens if there's an equally important system which is designed to be used in GUI and also needs unituserdata?

    What I'm saying is that using it in a system is not fundamentally safe unless you declare "this system uses unituserdata!"

    2 or 3? I'm not going to lie to you and tell you I've tested it myself, but I'm quite sure you're wrong. I'm pretty sure "n" has to be quite a bit higher than 200 (which is a pretty large number of units/spells occuring simultaneously for wc3) before it becomes slower than hashtables.

    That's just excellent, don't use my system then (I won't feel bad)

    Excellent! Thanks for the example, I will look into updating the script to do just that. Thanks to you as well as Bribe.

    That looks like quite a bit of work for every JNGP user to do just to use my script.

    However, JNGP is not some proprietary work. If you go and make v5e using something better than pJass (And while you're at it, use the newest JassHelper and update TESH to include GetSpellTargetX() and GetSpellTargetY() ) and I'll gladly use it, as well as recommend to others to do the same :)
     
  18. LeP

    LeP

    Joined:
    Feb 13, 2008
    Messages:
    445
    Resources:
    0
    Resources:
    0
    It's not about the user but about nice code. And they aren't even exclusive.
    It doesn't harm the user in any way.

    That's why you use TriggerAddAction or, even better, function-interfaces.
    No unneeded return-line. No tricking the syntax-checker. Same speed as TriggerEvaluate. You can even use friggin' arguments.
     
  19. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,021
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    @LeP, function interfaces doubling map size is quite criminal. I had some really big functions which were used in conjunction with Berb's Projectile system, my code size exploded. Although the use of arguments is very useful syntax, it's an absolute mess when you're looking at compiled code.

    >> 2 or 3? I'm not going to lie to you and tell you I've tested it myself, but I'm quite sure you're wrong.

    Being quite sure is what gets a lot of bad ideas publicity. A hashtable read is only a few times slower than an array read, and that HAS been benchmarked. So yes, a loop iterating through and checking 2-3 different array indices will achieve parity with a hashtable at best.

    >> What I'm saying is that using it in a system is not fundamentally safe unless you declare "this system uses unituserdata!"

    That's why AutoIndex has a "Use Hashtable" mode. It's a good feature. Unfortunately, I had to omit that kind of modularity in GUI Unit Indexer. I may just as well make a version of vJass Unit Indexer that has a "Use Hashtable" mode, but I'm not passionate if someone beats me to it because I have so little passion for modding WC3 these days.

    >> That's just excellent, don't use my system then (I won't feel bad)

    That's not what I meant, and I would use your system even though it's not the most efficient and the reason why is because I don't CARE about a 0.00001% margin which is what would be gained if you optimized yours. I seriously don't care about that margin and no one should. You made a great resource.
     
  20. Cokemonkey11

    Cokemonkey11

    Wurst Reviewer

    Joined:
    May 9, 2006
    Messages:
    3,240
    Resources:
    18
    Tools:
    1
    Maps:
    5
    Spells:
    3
    Tutorials:
    2
    JASS:
    7
    Resources:
    18
    If you prove it to me I'll feel like a dipshit, but for now I'm content with being "that guy who thinks he's more efficient".

    Oh, didn't expect that. Thanks then.

    Regarding the use of Condition() with a code, I've decided I'm going to leave it how it is for now. If JNGP is updated with a replacement for pJass I'll update this script to reflect that.

    But I'm still willing to take suggestions. Surprised no one has asked for a "removeHandler" method yet.