• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

UnitIndexer

Level 9
Joined
Mar 25, 2005
Messages
252
UnitIndexer v0.91 and UnitStructer v0.91

This thread contains two small systems: UnitIndexer and UnitStructer (both of which use vJass).
UnitStructer creates a struct for each unit that enters the map and destroys the struct after the unit has been removed from the game. This struct is stored to the unit's UserData also known as Custom Value.
UnitIndexer does the same except that it 'creates' indexes instead of structs. The creation algorithm it uses is basically the same and produces the same integers as the one of structs, but is inlined and a bit faster. UnitIndexer is basically as light as any system that does the same can be.

The index or struct of a unit can be acquired with a GetUnitUserData call or in GUI with 'Unit - Custom Value of Unit'. The use of SetUnitUserData or 'Unit - Set Custom Value of Unit' screws up the system and as such is prohibited. Also you can't use RemoveUnit() or 'Unit - Remove' on living units. Instead you either have to use RemoveIndexerUnit() or 'Unit - Hide' and 'Unit - Explode'.
The structs created by the UnitStructer can be used just like any other structs are except that you must not destroy the ones that the system itself has assigned to units.
Both these structs as well as the indexes created by the UnitIndexer can be used to make things multi-unit instanceable the same way player numbers can be used to make things multi-player instanceable.
[jass=JASS Example]set someArrayVariable[GetUnitUserData(u)] = data
set data = someArrayVariable[GetUnitUserData(u)][/code][TRIGGER=GUI Example]Set someArrayVariable[(Custom value of (Last created unit))] = data
Set data = someArrayVariable[(Custom value of (Last created unit))]
[/TRIGGER]
Take note that these indexes as well as structs are recycled when units die meaning that values stored to arrays using them as indexes aren't always initialized or nulled unless you do that yourself.[trigger=GUI Example]NullUnitData
Events
Unit - A unit enters (Playable map area)
Conditions
Actions
Set someArrayVariable[(Custom value of (Triggering unit))] = 0
[/trigger]

Anything that uses the UnitIndexer works with the UnitStructer, but not always the other way around. Anything that uses Cohadar's PUI can be made to work with either of these by using this:
JASS:
library PUI uses UnitIndexer
    // All you need to do when switching from PUI to UnitIndexer is to copy paste
    // the UnitIndexer library to your map, disable the previous PUI library that
    // you had in your map and replace RemoveUnit calls with RemoveIndexedUnit calls
    function GetUnitIndex takes unit u returns integer
        return GetUnitUserData(u)
    endfunction
endlibrary
Last important note: neither one of these systems are active by the time structs' onInit methods are called. This means that units won't have indexes or structs by the time of their execution.
JASS:
//==============================================================================
//---DiscipleOfLife's-----------------------------------------------------------
//
//            UnitIndexer // v0.91
//
//------------------------------------------------------------------------------
library UnitIndexer initializer init

    globals
        //----------------------------------------------------------------------
        private constant real TOTAL_DECAY_TIME = 90.00
        // This should match the sum of the 'Decay Time (sec) - Bones'
        // and 'Decay Time (sec) - Flesh' gameplay constants.
        // The system will work with any value but is most optimized with
        // the correct value.
        //----------------------------------------------------------------------
        
        //----------------------------------------------------------------------
        private constant integer UNIT_LIMIT = 4096
        // If the amount of units in the map at once exceedes this limit
        // the UnitIndexer will malfunction.
        // Higher value increases the operations done in the systems init
        // function and might hit the op limit.
        //----------------------------------------------------------------------
        
        private integer FreeIndex = 1
        private integer array NextFreeIndex
        private boolean array IsAlive
    endglobals
    
    //==========================================================================
    // If the native RemoveUnit is used on a living unit the removed unit's
    // index won't be recycled which is why this function must be used instead
    // in such cases.
    //
    function RemoveIndexedUnit takes unit u returns nothing
     local integer uind = GetUnitUserData(u)
        if IsAlive[uind] then
            set IsAlive[uind] = false
            set NextFreeIndex[uind] = FreeIndex
            set FreeIndex = uind
        endif
        call RemoveUnit(u)
    endfunction
    
    //==========================================================================
    // This function is run whenever any unit enters the map.
    //
    private function onEnter takes nothing returns boolean
        set IsAlive[FreeIndex] = true
        call SetUnitUserData(GetFilterUnit(), FreeIndex)
        set FreeIndex = NextFreeIndex[FreeIndex]
     return false
    endfunction
    
    //==========================================================================
    // This function is run whenever any unit dies.
    //
    private function onDeath takes nothing returns nothing
     local unit u = GetTriggerUnit()
     local integer uind = GetUnitUserData(u)
        if IsAlive[uind] then
            set IsAlive[uind] = false
            call TriggerSleepAction(3)
            loop
                //--------------------------------------------------------------
                // Most units that decay exit this loop at the second iteration.
                // Those that don't decay exit right at the first iteration.
                // Heroes stay in the loop untill revived or removed.
                //--------------------------------------------------------------
                exitwhen GetUnitUserData(u) == 0
                if GetWidgetLife(u) >= 0.405 then
                    set IsAlive[uind] = true
                    set u = null
                    return
                endif
                call TriggerSleepAction(TOTAL_DECAY_TIME)
            endloop
            set NextFreeIndex[uind] = FreeIndex
            set FreeIndex = uind
        endif
     set u = null
    endfunction
    
    private function init takes nothing returns nothing
     local trigger trig = CreateTrigger()
     local region rectRegion = CreateRegion()
     local group tempGroup = CreateGroup()
        
     local integer i = UNIT_LIMIT - 1    
        loop
            exitwhen i <= 0
            set NextFreeIndex[i] = i + 1
            set i = i - 1
        endloop
        
        call GroupEnumUnitsInRect(tempGroup, bj_mapInitialPlayableArea, Filter(function onEnter))
        call DestroyGroup(tempGroup)
        set tempGroup = null
        
        call RegionAddRect(rectRegion, bj_mapInitialPlayableArea)
        call TriggerRegisterEnterRegion(CreateTrigger(), rectRegion, Filter(function onEnter))
        
        call TriggerAddAction(trig, function onDeath)
        call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DEATH)
    endfunction
    
endlibrary    // End of the UnitIndexer
//==============================================================================
JASS:
//==============================================================================
//---DiscipleOfLife's-----------------------------------------------------------
//
//            UnitStructer // v0.91
//
//------------------------------------------------------------------------------
library UnitStructer initializer init

    //**************************************************************************
    //*                             Editable Part                               *
    //**************************************************************************

    globals
        private constant real TOTAL_DECAY_TIME = 90.00
        // This should match the sum of the 'Decay Time (sec) - Bones'
        // and 'Decay Time (sec) - Flesh' gameplay constants.
        // The system will work with any value but is most optimized with
        // the correct one.
    endglobals

    struct UnitStruct

        boolean someSystemFlag = true // don't touch this line

    endstruct
    
    private function onEnter takes nothing returns boolean
     local UnitStruct uind = UnitStruct.create()
     local unit u = GetFilterUnit()
        call SetUnitUserData(u, integer(uind))
     
        // I suggest that you don't edit the existing lines in this
        // function unless you know exactly what you're doing
        
     set u = null
     return false
    endfunction
    
    //**************************************************************************
    //*                           End of Editable Part                           *
    //**************************************************************************
    
    //==========================================================================
    // If the native RemoveUnit is used on a living unit, the unit's struct
    // won't be recycled. If there is a possibility that the unit is alive
    // use this instead of RemoveUnit().
    //
    function RemoveIndexedUnit takes unit u returns nothing
     local UnitStruct uind = UnitStruct(GetUnitUserData(u))
        if uind.someSystemFlag then
            set uind.someSystemFlag = false
            call uind.destroy()
        endif
        call RemoveUnit(u)
    endfunction
    
    //==========================================================================
    // This function is run whenever any unit dies.
    //
    private function onDeath takes nothing returns nothing
     local unit u = GetTriggerUnit()
     local UnitStruct uind = UnitStruct(GetUnitUserData(u))
        if uind.someSystemFlag then
            set uind.someSystemFlag = false
            call TriggerSleepAction(3)
            loop
                //--------------------------------------------------------------
                // Most units that decay exit this loop at the second iteration.
                // Those that don't decay exit right at the first iteration.
                // Heroes stay in the loop untill revived or removed.
                //--------------------------------------------------------------
                exitwhen GetUnitUserData(u) == 0
                if GetWidgetLife(u) >= 0.405 then
                    set uind.someSystemFlag = true
                    set u = null
                    return
                endif
                call TriggerSleepAction(TOTAL_DECAY_TIME)
            endloop
            call uind.destroy()
        endif
     set u = null
    endfunction
    
    private function init takes nothing returns nothing
     local trigger trig = CreateTrigger()
     local region rectRegion = CreateRegion()
     local group tempGroup = CreateGroup()
        
        call GroupEnumUnitsInRect(tempGroup, bj_mapInitialPlayableArea, Filter(function onEnter))
        call DestroyGroup(tempGroup)
        set tempGroup = null
        
        call RegionAddRect(rectRegion, bj_mapInitialPlayableArea)
        call TriggerRegisterEnterRegion(CreateTrigger(), rectRegion, Filter(function onEnter))
        
        call TriggerAddAction(trig, function onDeath)
        call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DEATH)
    endfunction
    
endlibrary    // End of the UnitStructer
library UnitIndexer uses UnitStructer
    // Anything that uses UnitIndexer works with UnitStructer
endlibrary
//==============================================================================
v0.91
  • removed dynamic array feature
  • posted UnitStructer
v0.9
  • drastically improved the performance of the system
  • rewrote parts of the readme
 
Last edited:
Level 9
Joined
Mar 25, 2005
Messages
252
[jass=A structs create method]
function s__normal__allocate takes nothing returns integer
local integer this=si__normal_F
if (this!=0) then
set si__normal_F=si__normal_V[this]
else
set si__normal_I=si__normal_I+1
set this=si__normal_I
endif
set si__normal_V[this]=-1
return this
endfunction[/code][jass=A structs destroy method]
function s__normal_destroy takes integer this returns nothing
set si__normal_V[this]=si__normal_F
set si__normal_F=this
endfunction[/code]

[jass=UnitIndexer's create algorithm (using same parameter names)]local integer this=si__normal_F
set si__normal_F=si__normal_V[this][/code][jass=UnitIndexer's destroy algorithm (using same parameter names)] set si__normal_V[this]=si__normal_F
set si__normal_F=this[/code]

As you can see using structs would simply be a degradation. Not something I would call "much more powerful". If you like vJass syntax you can typecast the indexes to structs but in reality that won't improve the performance of your code.

About your idea of using structs without a system to store data to a unit's user data without interfering with other spells or systems that need to do the same: give an example please. I just don't believe it.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
About your idea of using structs without a system to store data to a unit's user data without interfering with other spells or systems that need to do the same: give an example please. I just don't believe it.
Add more variables to the struct? This uses UserData anyways, so it would have to be custom coded correctly anyways. (As in, so that everything in the map used this instead of UserData directly)

As you can see using structs would simply be a degradation. Not something I would call "much more powerful". If you like vJass syntax you can typecast the indexes to structs but in reality that won't improve the performance of your code.
Negligibly slower, but much cleaner and more powerful features are added to structs (Structs extending others, methods, and so on) - I am not suggesting that structs are any faster (I have not checked/tested for myself, but if you are correct about the allocation functions yours is at least faster for that), but that they are more powerful in terms of what they can do.
 
Level 9
Joined
Mar 25, 2005
Messages
252
Add more variables to the struct?

When do you create those structs? When do you destroy them? UnitIndexer does those two things for you and nothing more. UnitIndexer creates indexes when units enter the map and destroy them when the units no longer can hold user data. The main purpouse of the UnitIndexer is to save you from having to figure out if a unit already has a user 'struct' or when it should be destroyed and by doing so also reducing the amount of actions that need to be done when retrieving the data.
The only other solution I can think of is doing something similar to what Srilanc does in HAIL but with user data instead of H2I and without that useless 'property' wrapper thing. In other words having some kind of reference counter in the struct. When using such a system (or inlining the same by hand) you would have to manually release the indexes, which is ok in many situations but would rule out the use of a damage detection engine or any other similar system that needs units to have indexes from their very beginning to the end, which is exactly what the UnitIndexer provides. Also such a solution would require an if statement most of the time to check if the unit has a struct at all. UnitIndexer is simpler and faster.

This uses UserData anyways, so it would have to be custom coded correctly anyways.

Yes "custom coded correctly" meaning that you dont set the user data to a struct. Instead you just use the index that already is stored in it (assuming ofc that you want to store more than 1 value to a unit).

(As in, so that everything in the map used this instead of UserData directly)

Everything meaning the one system that could use UserData directly?

Negligibly slower, but much cleaner and more powerful features are added to structs (Structs extending others, methods, and so on) - I am not suggesting that structs are any faster (I have not checked/tested for myself, but if you are correct about the allocation functions yours is at least faster for that), but that they are more powerful in terms of what they can do.

Cleaner? Did I already mentiont that you can typecast indexes to structs? I think did. Infact you already have to typecast unit user data to structs even if they were structs to begin with so I really don't see what the difference in syntax is. Can you spot a difference between these two?
[jass=User data holds a struct] local Struct this = Struct(GetUnitUserData(u))[/code]
[jass=User data holds an index] local Struct this = Struct(GetUnitUserData(u))[/code]
I honestly can't. Did I miss what you ment with 'much cleaner'? Did you mean something else than syntax? If you did then please elaborate because my imagination doesn't stretch that far.

As for the powerful features of structs: you can use most of them after a simple typecast. The only thing you can't do is extending. Though your propably the only one who has ever even imagined of needing to do so (when talking about a unit's 'struct'). You can go take a look at the PUI thread at wc3c which has been there for over 6 months and see for yourself if anyone has bothered Cohadar with this issue. Though the system could easily be modified to use real structs in a matter of minutes if one knows what he's doing (though I won't be doing that untill I have an actual reason to do so).
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
Yes "custom coded correctly" meaning that you dont set the user data to a struct. Instead you just use the index that already is stored in it (assuming ofc that you want to store more than 1 value to a unit).
What I meant is spells and systems etc that use the user data anyways would not do any better with this.

When do you create those structs? When do you destroy them?
Making a trigger for that takes about five seconds.

Everything meaning the one system that could use UserData directly?
Again, what I meant is it doesn't save you the trouble of having to make sure nothing else tried to do so (which is what it seemed to me like you were complaining about with structs earlier)

Cleaner? Did I already mentiont that you can typecast indexes to structs? I think did. Infact you already have to typecast unit user data to structs even if they were structs to begin with so I really don't see what the difference in syntax is. Can you spot a difference between these two?
[jass=User data holds a struct] local Struct this = Struct(GetUnitUserData(u))[/code]
[jass=User data holds an index] local Struct this = Struct(GetUnitUserData(u))[/code]
I honestly can't. Did I miss what you ment with 'much cleaner'? Did you mean something else than syntax? If you did then please elaborate because my imagination doesn't stretch that far.
I see a difference; one way you don't have a system in your map -.-

As for the powerful features of structs: you can use most of them after a simple typecast. The only thing you can't do is extending. Though your propably the only one who has ever even imagined of needing to do so (when talking about a unit's 'struct').
Example: You are making a medieval RTS map, you have Archers, Catapults, Cavalry, and Infantry. Cavalry get a boost in damage every time they hit an Archer, meaning Archers need an onDamage trigger and Cavalry need some other special data. Archers and Siege have a special attack type (basically, they attack where the unit is, but we don't want to have an Attack Ground option!), so that will require some stuff to, etc, etc. In a situation such as this, it is obviously more efficient to use a master struct and then child forms for each unit type, to avoid having useless things (like onDamage events on siege, cavalry, and infantry) that will slow the game down and not give any benefits.

Anyways, since we apparently can't agree on this, I'd like to get some other opinions on this (Earth-Fury/HINDY/wyrmlord/CG or someone if they want to/etc)
 
Level 14
Joined
Nov 20, 2005
Messages
1,156
Type casting an arbitary number into a struct is a very bad idea. In order to make it properly work with structs, you'd need to have a linked list system to align structs and unit IDs properly. At this point you've got a lot of complications to make something that is in all likelyhood slower than attatching by GC.

If you really feel the need to use a system like this, you might want to use Handle Array Index Library by Strilanc, which fast, secure and doesn't use up the UnitUserData option.
 
Level 9
Joined
Mar 25, 2005
Messages
252
What I meant is spells and systems etc that use the user data anyways would not do any better with this.

You are forgetting that with this you can have more than one of such systems or spells.

Making a trigger for that takes about five seconds.

It took me a bit more than 5 seconds to code this thing. If it really takes so little effort for you to write a code that does the same then please write and post it. Im still mostly curious of when you create and destroy the structs, since you somehow failed to answer my question about that.

Again, what I meant is it doesn't save you the trouble of having to make sure nothing else tried to do so (which is what it seemed to me like you were complaining about with structs earlier)

The system is designed to allow different spells and systems store to units effortlessly without knowing of or interfering with eachother. Isn't that already clear to you?

Example: You are making a medieval RTS map, you have Archers, Catapults, Cavalry, and Infantry. Cavalry get a boost in damage every time they hit an Archer, meaning Archers need an onDamage trigger and Cavalry need some other special data. Archers and Siege have a special attack type (basically, they attack where the unit is, but we don't want to have an Attack Ground option!), so that will require some stuff to, etc, etc. In a situation such as this, it is obviously more efficient to use a master struct and then child forms for each unit type, to avoid having useless things (like onDamage events on siege, cavalry, and infantry) that will slow the game down and not give any benefits.

What's wrong with function objects? Is there a particular reason why you want your onDamage functions to be methods instead? Also how come are you willing to sacrifice so much performance for syntax candy?
 
Level 9
Joined
Mar 25, 2005
Messages
252
Type casting an arbitary number into a struct is a very bad idea. In order to make it properly work with structs, you'd need to have a linked list system to align structs and unit IDs properly. At this point you've got a lot of complications to make something that is in all likelyhood slower than attatching by GC.

Arbitary isn't the right word here, unless if that's what you would describe structs. The indexes are allocated almost exactly the same as structs with dynamic arrays of size 2. The only difference in use is that the UnitIndexer's indexes don't neccessarely start from 1.
In an earlier post I already showed the struct allocation algorithm and the one of UnitIndexer side by side. Would you care to explain what exactly makes the UnitIndexer's version arbitary?
When I have said that you can typecast the system's indexes to structs I obviously didn't mean any struct, but a struct that you use only for the systems indexes.

I don't understand these three things: why would you want to align structs and unit IDs, why would you use linked lists for doing so and what are these complications your talking about. (examples == thx) (other than using methods as function objects)

It's funny how you estimate that GetUnitUserData + array (lookup or write) is slower than GC.

If you really feel the need to use a system like this, you might want to use Handle Array Index Library by Strilanc, which fast, secure and doesn't use up the UnitUserData option.

Seems like you missed something rather obvious. This is essentially just an improved version of PUI. Why didn't you suggest it? Why suggest something slower?
Also UnitIndexer is secure.




EDIT: Ok I removed from the readme the part that said that you can typecast to structs. This is to avoid any more unneccessary hassle about it. You still can typecast the integers if you dont use the structs methods as function objects and if you don't create or destroy that struct type yourself.

Also if no one will even begin to understand how this extremely simple and efficient system works I might bother to make an example of using the system and challenge anyone willing to accomplish the same more efficiently with another system (or no system).
 
Level 14
Joined
Nov 20, 2005
Messages
1,156
Arbitary isn't the right word here, unless if that's what you would describe structs. The indexes are allocated almost exactly the same as structs with dynamic arrays of size 2. The only difference in use is that the UnitIndexer's indexes don't neccessarely start from 1.
In an earlier post I already showed the struct allocation algorithm and the one of UnitIndexer side by side. Would you care to explain what exactly makes the UnitIndexer's version arbitary?
When I have said that you can typecast the system's indexes to structs I obviously didn't mean any struct, but a struct that you use only for the systems indexes.

They won't necessarily be the same number for the same units. Fact. That makes your numbering system arbitary. Type casting between them is a bad idea.

I don't understand these three things: why would you want to align structs and unit IDs, why would you use linked lists for doing so and what are these complications your talking about. (examples == thx) (other than using methods as function objects)

It's funny how you estimate that GetUnitUserData + array (lookup or write) is slower than GC.

What you seem to be suggesting people do would be faster, but completely and utterly unsafe. That's vital. The safe adaptation (ie: linked lists) would then be slower.

Seems like you missed something rather obvious. This is essentially just an improved version of PUI. Why didn't you suggest it? Why suggest something slower?
Also UnitIndexer is secure.

I see no real use for this.

EDIT: Ok I removed from the readme the part that said that you can typecast to structs. This is to avoid any more unneccessary hassle about it. You still can typecast the integers if you dont use the structs methods as function objects and if you don't create or destroy that struct type yourself.

Also if no one will even begin to understand how this extremely simple and efficient system works I might bother to make an example of using the system and challenge anyone willing to accomplish the same more efficiently with another system (or no system).

If you don't type cast, then you have to use a linked list, which makes it slower. If you do type cast, all hell breaks loose.
 
Level 9
Joined
Mar 25, 2005
Messages
252
They won't necessarily be the same number for the same units.

Give an example or any other kind of proof of this claim.

What you seem to be suggesting people do would be faster, but completely and utterly unsafe. That's vital. The safe adaptation (ie: linked lists) would then be slower.

It is safe. PUI is already used like that.

This system has and never will have anything to do with linked lists.

Again if you really want to prove something you need to give me an example because I frankly have no idea what is going inside your head.

I see no real use for this.

You didn't answer any of my questions that you quoted before that sentence.
I'd be happy if you didn't dogde them like that.

If you don't type cast, then you have to use a linked list, which makes it slower. If you do type cast, all hell breaks loose.

All hell breaks loose only if you typecast it into a struct of type that extends an interface and use any of the methods defined by that interface. Not something I have personally ever needed to do. I'm not saying that it's not possible that someone would, but even if they did they could use structs and just store them using a unit's index. Not that big of a deal and surely still a fast solution compared to anything else.

If you don't typecast you don't have to use a linked list. You can use arrays as shown in the example in the system's readme.

Why don't you go harras Cohadar and say that PUI is slower than GC, unsafe, needs to use a linked list and all other crap like that?
UnitIndexer is so close to PUI that if you find a bug in UnitIndexer that doesn't involve units bypassing the EVENT_PLAYER_UNIT_DEATH event, then that bug can be found in PUI also. Because the thing is that the only real difference between these two are that:

1. UnitIndexer indexes all units that enter the map.
2. UnitIndexer discludes all living units from its removal checks that determine when a unit's index should be recycled.

This logicly leads to the fact that an issue found in UnitIndexer but not in PUI must be caused by either of those two. The ones you have claimed to exist don't seem to have anything to do with those. How come PUI is still unaffected by them?


If you can come up with a single example where the UnitIndexer malfunctions and an alternative attachment system wouldn't, I will be happy to drop this system and never speak of it again.
 
Level 9
Joined
Mar 25, 2005
Messages
252
Which happens to be slower than just allocating a struct and putting it on the user data.

If you're that interested in speed you can typecast the index to a struct and then manually set it's type id to the one of the struct you wish in the system's onEnter function. It's this simple: set si__InterfaceName_type[FreeIndex]=StructName.typeid. This solution is again faster than using a real struct.


Btw where is the 5 second trigger you advertised about?
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
Why would I post it here? It should be relatively obvious how to do it...

As for speed, seeing as this isn't really any faster than structs anyways (one or two variable sets and an if amounts to zilch, especially when I doubt you will be mass-allocating...) and it has obvious disadvantages, mentioned before, I still don't see what makes this worth anything.

For your information, I never liked PUI either :p
 
Level 9
Joined
Mar 25, 2005
Messages
252
Why not post it here? This thread has 12+ screenfulls of arguing and you don't care enough to take that 5 seconds to show your alternative, ending this conversation and rendering, not only this, but also PUI completely useless?

Why do you compare this system to structs? This system != struct. This system allocates home-made structs for units. This system != struct. The point of the system, or why it is great, isn't that it uses a tiny bit faster allocation algorithm. The point of the system is to allocate a home-made struct for each unit that enters the map so you don't have to figure out yourself when to create or destroy them.

You haven't proven any of the claimed disadvantages.
You have not proven that my allocation algorithm is arbitary.
You have not proven that the UnitUserData of a unit might miraculously change during the game. :eekani:
You have not proven that this is unsafe, let alone utterly unsafe.
You have not proven that there is an alternative involving structs that can be coded in an extremely short period of time.
If these disadvantages are so obvious it should be easy to confirm them.

At the moment I have written a damage detection system, a caster system, a missile system, a pathing engine, a movement speed mod and an attack speed mod that all use the UnitIndexer and I have yet to come in contact with any complications with it. You should understand that it takes a bit more than your unfounded claims to convince me that most of everything I have coded and tested for the past 4+ months doesn't work, especially when all of it seems to work precicely as intended.



EDIT:

New version 0.9 now posted. The system is now even more optimized than before.
 
Last edited:
Level 9
Joined
Mar 25, 2005
Messages
252
Wrong on both counts.

EDIT: since simple words are getting me nowhere I coded the simplest example script I could come up with:
JASS:
library IsUnitCasting initializer init requires UnitIndexer

    globals
        private boolean array Unit_isCasting
    endglobals
    
    function IsUnitCasting takes unit u returns boolean
        return Unit_isCasting[GetUnitUserData(GetTriggerUnit())]
    endfunction

    private function onEffect takes nothing returns boolean
        set Unit_isCasting[GetUnitUserData(GetTriggerUnit())] = true
     return false
    endfunction
    
    private function onEndCast takes nothing returns boolean
        set Unit_isCasting[GetUnitUserData(GetTriggerUnit())] = false
     return false
    endfunction
    
    private function init takes nothing returns nothing
     local trigger trig = CreateTrigger()
        call TriggerAddCondition(trig, Filter(function onEffect))
        call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        
        set trig = CreateTrigger()
        call TriggerAddCondition(trig, Filter(function onEndCast))
        call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
    endfunction
    
endlibrary

Attached is also a small map with this script. Whenever you select a unit in it a message tells you whether or not the unit is casting. Please explain to me why it works. Was I just lucky when I tested it?

(in the map the UnitIndexer is in debug mode so it gives all other kinds of messages too)
 

Attachments

  • UnitIndexerWorking1.w3x
    22.8 KB · Views: 92
Last edited:
Level 14
Joined
Nov 20, 2005
Messages
1,156
Structs, however, are so much easier, so much more organised, and so much more expandable. A slight speed boost for calls for some systems (requiring the using up of UnitUserData) isn't really significant, particularly when put against the cost of assigning and deassigning for each unit.

I'm also curious as to why you're submitting parallel arrays with your name on it when you've actually invented nothing? We used similar things before (but more efficienctly, without wastefully assigning to units), but they got swept away by structs.
 
Level 9
Joined
Mar 25, 2005
Messages
252
Structs, however, are so much easier, so much more organised, and so much more expandable. A slight speed boost for calls for some systems (requiring the using up of UnitUserData) isn't really significant, particularly when put against the cost of assigning and deassigning for each unit.

My system != structs. My system can be changed to use structs in less than a minute, but it would change hardly anything. Though you can do that if you believe in magic or for some reason want the struct to extend something.

The cost of assigning and deassigning for each unit pays off each time you retrieve the index or struct of a unit. A few actions run for each unit in the game compared to a few actions run each time you retrieve the index of a unit. The first one is the lesser evil unless you do very little attaching to units, in which case you could be using anything from GC to a linear handle array search without any lag.

Also, the moment you put a DD or a physics engine in your map or anything else that requires units to have an index (or any other way to store them) from their birth to their removal, UnitIndexer becomes the best choice without a doubt, unless of course if you use a practicallly identical integrated solution.

I'm also curious as to why you're submitting parallel arrays with your name on it when you've actually invented nothing? We used similar things before (but more efficienctly, without wastefully assigning to units), but they got swept away by structs.

I invented nothing when I coded my GetClosestUnit script. Nor have I said I invented anything here. I doubt PipeDream invented dynamic arrays and I highly doubt anyone accused him for claiming of doing so.

I haven't heard the word parallel array before this day. After a quick read about it at Wikipedia Im confused as to why you called my system parallel arrays. Parallel arrays means that values that have something in common are simply stored in arrays with the same index. That sounds like structs to me. However my system != structs. You have a hard time understanding that?
Here is a link to the jasshelper manual. Read about structs there. Hopefully that will clear your head from much of the bullshit dwelling in it atm. You will notice that structs do not have onEnter methods, nor do they keep track of decaying units. If they did then it would be a good idea to compare my system to a struct, but still NO WAY IN HELL to parallel arrays. It is ridicilous that this still isn't clear to you.

Im also curious of knowing why you didn't comment on the map I posted which was supposed to fuck up left and right according to your previous lies. What might have been the reason? Is it that it works flawlessly?

I must say that your posts are getting funnier all the time, but because Im actually trying to get something done here I would appreciate it if you stopped fooling around. Ty.
 
Level 14
Joined
Nov 20, 2005
Messages
1,156
My system != structs. My system can be changed to use structs in less than a minute, but it would change hardly anything. Though you can do that if you believe in magic or for some reason want the struct to extend something.

Not safely. Not properly.

The cost of assigning and deassigning for each unit pays off each time you retrieve the index or struct of a unit. A few actions run for each unit in the game compared to a few actions run each time you retrieve the index of a unit. The first one is the lesser evil unless you do very little attaching to units, in which case you could be using anything from GC to a linear handle array search without any lag.

Not really, since the difference between the speed of GetUnitUserData and game cache is very small.

Also, the moment you put a DD or a physics engine in your map or anything else that requires units to have an index (or any other way to store them) from their birth to their removal, UnitIndexer becomes the best choice without a doubt, unless of course if you use a practicallly identical integrated solution.

I'm curious as to why a physics engine would need your system? They'd use structs, and handle their own assignments (assign the struct to the unit, not the unit to the struct), or use something else.

I invented nothing when I coded my GetClosestUnit script. Nor have I said I invented anything here. I doubt PipeDream invented dynamic arrays and I highly doubt anyone accused him for claiming of doing so.

What you've got is an outdated system. You're two years late to the party. And using vJASS for it.

I haven't heard the word parallel array before this day. After a quick read about it at Wikipedia Im confused as to why you called my system parallel arrays. Parallel arrays means that values that have something in common are simply stored in arrays with the same index. That sounds like structs to me. However my system != structs. You have a hard time understanding that?

Okay, I'm giving up at this point. Go learn what parallel arrays are, and then you'll see that your example was an example of parallel arrays. Structs are SOO much more useful.

Here is a link to the jasshelper manual. Read about structs there. Hopefully that will clear your head from much of the bullshit dwelling in it atm. You will notice that structs do not have onEnter methods, nor do they keep track of decaying units. If they did then it would be a good idea to compare my system to a struct, but still NO WAY IN HELL to parallel arrays. It is ridicilous that this still isn't clear to you.

Your system's use appears to be entirely parallel arrays...okay, if this isn't for parallel arrays, then it really should be thrown out the airlock.

Im also curious of knowing why you didn't comment on the map I posted which was supposed to fuck up left and right according to your previous lies. What might have been the reason? Is it that it works flawlessly?

No, I never said that would fuck up, because, guess what, it's not trying to be structs - it's trying to be parallel arrays.

I must say that your posts are getting funnier all the time, but because Im actually trying to get something done here I would appreciate it if you stopped fooling around. Ty.

You're going toe-to-toe with easily one of the top JASSers on THW, without even knowing what a parallel array is, and telling him to go and learn about structs. Even though earlier you were suggesting taking arbitary integers and type casting them into structs.

I'm not going to respond again, and, quite frankly, if you ever want any help with JASS stuff in future, you may as well forget it.
 
Level 9
Joined
Mar 25, 2005
Messages
252
Not safely. Not properly.

Struct.create() not safe? Not proper? Tell me, what here is not safe: (or did you just lie yet again?)
JASS:
library UnitIndexer initializer init

    struct UnitUserData
        boolean isAlive
    endstruct

    globals
        debug constant boolean UnitIndexer_SHOW_DEBUG_MESSAGES = false
        debug constant boolean UnitIndexer_SHOW_DEBUG_MESSAGES_ON_DEATH = true
        private constant real DECAY_CHECK_PERIOD = 30.00
    endglobals

    function RemoveIndexedUnit takes unit u returns nothing
        local UnitUserData dat = UnitUserData(GetUnitUserData(u))
        if dat.isAlive then
            set dat.isAlive = false
            call dat.destroy()
        endif
        call RemoveUnit(u)
    endfunction
    
    private function onEnter takes nothing returns boolean
        local UnitUserData dat = UnitUserData.create()
        set dat.isAlive = true
        call SetUnitUserData(GetFilterUnit(), dat)
        return false
    endfunction
    
    private function onDeath takes nothing returns nothing
        local unit u = GetTriggerUnit()
        local UnitUserData dat = UnitUserData(GetUnitUserData(u))
        if dat.isAlive then
            set dat.isAlive = false
            call TriggerSleepAction(3)
            loop
                exitwhen GetUnitUserData(u) == 0
                if GetWidgetLife(u) >= 0.405 then
                    set dat.isAlive = true
                    set u = null
                    return
                endif
                call TriggerSleepAction(DECAY_CHECK_PERIOD)
            endloop
            call dat.destroy()
        endif
        set u = null
    endfunction
    
    private function init takes nothing returns nothing
        local trigger trig = CreateTrigger()
        local region rectRegion = CreateRegion()
        local group tempGroup = CreateGroup()
        
        call GroupEnumUnitsInRect(tempGroup, bj_mapInitialPlayableArea, Filter(function onEnter))
        call DestroyGroup(tempGroup)
        set tempGroup = null
        
        call RegionAddRect(rectRegion, bj_mapInitialPlayableArea)
        call TriggerRegisterEnterRegion(CreateTrigger(), rectRegion, Filter(function onEnter))
        
        call TriggerAddAction(trig, function onDeath)
        call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DEATH)
    endfunction
    
endlibrary


Not really, since the difference between the speed of GetUnitUserData and game cache is very small.

GetStoredInteger(gc, I2S(H2I(u)), "value") - 294 exec/ms
GetUnitUserData(u) - 1177 exec/ms

Latter is 4 times faster. What did I tell you: you're a funny guy :D.
(Benchmark done with Benchmark 1.2. Map is attached to the post.


I'm curious as to why a physics engine would need your system? They'd use structs, and handle their own assignments (assign the struct to the unit, not the unit to the struct), or use something else.

Because it just as well could use this. Sure it can do it's own assignments. I actually said that myself too didnt I? Read my post again.

What you've got is an outdated system. You're two years late to the party. And using vJASS for it.

Outdated system that is 4 times faster than gamecache, and as such 2 times faster than HAIL, your new favourite newbfriendly ABC clone.
If this has been done 2 years ago then give me a link. If not then stop the bullshit.

Okay, I'm giving up at this point. Go learn what parallel arrays are, and then you'll see that your example was an example of parallel arrays. Structs are SOO much more useful.

Ah my example? I thought we were talking about my system here.

Well I think the only two examples I have given has been the simple: set arrayVar[GetUnitUserData(u)] = value and the IsUnitCasting library. Both of these were, as you said examples. I made them very simple so you could understand them.
So those are what you call parallel arrays. What's so wrong about it?

How come are structs more useful? Tell me one thing that you can do with structs that can't be done with parallel arrays.

Your system's use appears to be entirely parallel arrays...okay, if this isn't for parallel arrays, then it really should be thrown out the airlock.

Yes you can use a unit's index as the index of parallel arrays. My system however, is not a parallel array.

No, I never said that would fuck up, because, guess what, it's not trying to be structs - it's trying to be parallel arrays.

You said that it would be utterly unsafe. If it functions 100% correctly at all times, I don't see how that is utterly unsafe.

Yet again: my system is not parallel arrays.
There are no onEnter or onDeath methods in parallel arrays. You can use the index of a unit with parallel arrays, but no, my system is not and is not trying to be parallel arrays. I repeat: my system is not parallel arrays. Get over it.

You're going toe-to-toe with easily one of the top JASSers on THW, without even knowing what a parallel array is, and telling him to go and learn about structs. Even though earlier you were suggesting taking arbitary integers and type casting them into structs.

:xxd:

So one of the top jassers on THW:
  • estimates that GetUnitUserData is only slightly faster than GC
  • has problems understanding the similarities between structs and parallel arrays
  • claims that an allocation algorithm that works practically identical to the one used by structs results in arbitary integers
  • claims that the user data of a unit might change during the game even if it stays untouched
  • thinks it's a good idea to write a 3 pages long system using gamecache to do something that could be achieved by a single tick in gameplay constants (Gameplay - Use Relative Upgrade Costs -> false), but doesn't even work as well (doesn't work for lumber)

I'm not going to respond again, and, quite frankly, if you ever want any help with JASS stuff in future, you may as well forget it.

What are you going to do? Ban me from the forums? Delete all my threads? All this because I didn't believe your unfounded claims? Was it too much to ask for the slightest bit of proof to back up your claims? Well I can't say I expected any more mature behaviour from you.
 

Attachments

  • gamecache vs getunituserdata.w3x
    15.7 KB · Views: 59
Level 14
Joined
Nov 20, 2005
Messages
1,156
So, wtf are you saying? You say that your system doesn't use structs, then you use structs...consistency, please. You can't suddenly use a considerably different system to make a point about the other system...

GetStoredInteger(gc, I2S(H2I(u)), "value") - 294 exec/ms
GetUnitUserData(u) - 1177 exec/ms

Latter is 4 times faster. What did I tell you: you're a funny guy :D.
(Benchmark done with Benchmark 1.2. Map is attached to the post.

You'd call it once per group of actions, so the importance is very small. Also, having to call several functions every time a unit enters the game, decays, etc. would be costly and negate any slight gains.

Outdated system that is 4 times faster than gamecache, and as such 2 times faster than HAIL, your new favourite newbfriendly ABC clone.
If this has been done 2 years ago then give me a link. If not then stop the bullshit.

HAIL has full security (whereas your original system would be open to data transfering with a recycled index), works on other handles, doesn't take up UnitUserData.

Ah my example? I thought we were talking about my system here.

Well I think the only two examples I have given has been the simple: set arrayVar[GetUnitUserData(u)] = value and the IsUnitCasting library. Both of these were, as you said examples. I made them very simple so you could understand them.
So those are what you call parallel arrays. What's so wrong about it?

How come are structs more useful? Tell me one thing that you can do with structs that can't be done with parallel arrays.

onCreate and onDestroy methods are highly important. Structs are an easy method of parallel arrays. Your system takes over the creation and destruction, and, as such, will leave a gapping big whole with data going forward after a recycle.

Yes you can use a unit's index as the index of parallel arrays. My system however, is not a parallel array.

Your system has no other use that I can see.

You said that it would be utterly unsafe. If it functions 100% correctly at all times, I don't see how that is utterly unsafe.

Yet again: my system is not parallel arrays.
There are no onEnter or onDeath methods in parallel arrays. You can use the index of a unit with parallel arrays, but no, my system is not and is not trying to be parallel arrays. I repeat: my system is not parallel arrays. Get over it.

Your system could only really be used for parallel arrays, and without leaving any easy method of cleaning up on creation or deletion.

estimates that GetUnitUserData is only slightly faster than GC
has problems understanding the similarities between structs and parallel arrays

I addressed this earlier.

claims that an allocation algorithm that works practically identical to the one used by structs results in arbitary integers

Arbitary in the sense that it won't assign the same number to the same unit.

claims that the user data of a unit might change during the game even if it stays untouched

That's not the only kind of security. Either you are twisting my words (ie: trolling), or you are ignorant.

thinks it's a good idea to write a 3 pages long system using gamecache to do something that could be achieved by a single tick in gameplay constants (Gameplay - Use Relative Upgrade Costs -> false), but doesn't even work as well (doesn't work for lumber)

Pray explain how this would suddenly create a GetUnitCost native?

What are you going to do? Ban me from the forums? Delete all my threads? All this because I didn't believe your unfounded claims? Was it too much to ask for the slightest bit of proof to back up your claims? Well I can't say I expected any more mature behaviour from you.

No, but given that you've pissed off several JASSers and made yourself look like an arrogent troll to the rest, your odds of getting help are really going down the drain. And in large areas of JASS, I am basically the only active person who can help you on THW - and I won't.
 
Level 9
Joined
Mar 25, 2005
Messages
252
Thx for finally answering some questions. If you had posted something like this in the first place this discussion or whatever would have ended a lot sooner.

So, wtf are you saying? You say that your system doesn't use structs, then you use structs...consistency, please. You can't suddenly use a considerably different system to make a point about the other system...

I coded that to show you that using structs would make almost no difference at all. I guess the point wasn't delivered exactly as planned.

You'd call it once per group of actions, so the importance is very small. Also, having to call several functions every time a unit enters the game, decays, etc. would be costly and negate any slight gains.

I agree with what you're saying here. When used like that the speed difference is very small especially (most of the time) when it counts. Still I prefer my way.

HAIL has full security (whereas your original system would be open to data transfering with a recycled index), works on other handles, doesn't take up UnitUserData.

onCreate and onDestroy methods are highly important. Structs are an easy method of parallel arrays. Your system takes over the creation and destruction, and, as such, will leave a gapping big whole with data going forward after a recycle.

For me having no create or destroy methods is a vital pro in some cases. But yes, having no onCreate nor onDestroy methods is a con.

Your system could only really be used for parallel arrays, and without leaving any easy method of cleaning up on creation or deletion.

Making an onEnter function is an easy way to have an onCreate method. Overkill for many situations but not for DDs and the like. There is another rather easy way that I use for smaller codes in which I check if the indexed unit has changed whenever I use the index of a unit.
Though I agree that an onCreate or onDestroy method would be easier than that.

Parallel arrays are all that I need this system for and all that I need for storing to units. So "could only really be used for parallel arrays" translates to me: "fills all my unit storing needs".

Arbitary in the sense that it won't assign the same number to the same unit.

Luckily this doesn't matter at all since a unit is given an index only once.

That's not the only kind of security. Either you are twisting my words (ie: trolling), or you are ignorant.

b) is correct

Pray explain how this would suddenly create a GetUnitCost native?

I remember playing some TD some years ago where the sell function only gave me % of the last upgrade. With this in mind I just assumed that there is a GetUnitCost native, but that it returns just the value in the object editor. This led me to think that checking that constant to false and using non relative building costs would solve the problem. Sorry.

No, but given that you've pissed off several JASSers and made yourself look like an arrogent troll to the rest, your odds of getting help are really going down the drain. And in large areas of JASS, I am basically the only active person who can help you on THW - and I won't.

After PurplePoot pissed me off by implying that my system is worse than something he can code in 5 seconds (without even caring enough to show me how).
After you said that my system isn't safe and I should use linked lists to solve that.
After that I felt more than pissed.
Yeah, I didn't feel like throwing my system away and agree with you that my system is unsafe when I have whitnessed absolutely nothing that indicates so. Perhpas that is what a mannered THW member would have done but I wanted answers. I wanted to know how you came to the conclusion that my system is unsafe and possibly what kind of proof or reason you might have have for it. Because of the fact that I had no reason to believe that what you were saying was true and all my tests showed otherwise my attitude was naturally somewhat hostile. Untill now I didn't get a single answer, explanation or anything of that sort that would put my mind to rest.

For a long time now I haven't come to the hive to ask for help. If you want to get back at me you are better off trashing any future submissions of mine, if I happen to decide to submit anything after this warm response.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
After PurplePoot pissed me off by implying that my system is worse than something he can code in 5 seconds (without even caring enough to show me how).
Apparently you misread what I said, what I had said was that the "add an index to every unit on the map" part could be done in 5 seconds.

For example, a basic approach, using structs:

JASS:
//Struct indexes are stored as +1 and thus must be accessed at -1, 0 means an unfilled user data
function EnterActs takes nothing returns nothing
    if GetUnitUserData(GetTriggerUnit()) == 0 then
        call SetUnitUserData(GetTriggerUnit(),UnitData.create()+1)
    endif
endfunction

function Enter takes nothing returns nothing//this function is run on init from somewhere
    local trigger t = CreateTrigger()
    local region r = CreateRegion()
    call RegionAddRect(r,bj_mapInitialPlayableArea)
    call TriggerRegisterEnterRegion(t,r,null)
    call TriggerAddAction(t,function EnterActs)
endfunction

function DeathActs takes nothing returns nothing
    if GetUnitUserData(GetTriggerUnit()) > 0 then
        call UnitData(GetUnitUserData(GetTriggerUnit())).destroy()
        //if you want to have heroes keep indices, or anything, you can just switch this around, this is just the simplest case
    endif
endfunction

function Death takes nothing returns nothing//this function is run on init from somewhere
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_DEATH)
    call TriggerAddAction(t,function EnterActs)
endfunction
 
Level 9
Joined
Mar 25, 2005
Messages
252
Apparently you misread what I said, what I had said was that the "add an index to every unit on the map" part could be done in 5 seconds.

I read what you wrote.

This is what you first said:
PurplePoot said:
If you are using vJass anyways, why not just attach a struct to every unit? - no need of a system, and much more powerful
Then I asked for an example of that, to which you responded that I should add more variables to the struct. (good example btw) Then I asked:
DiscipleOfLife said:
When do you create those structs? When do you destroy them? UnitIndexer does those two things for you and nothing more. --
With "those structs" I of course refer to the "much more powerful" ones that don't need a system to be used. Then you finally claimed:
PurplePoot said:
Making a trigger for that takes about five seconds.
Here you can't be referring to anything else except the creation and destruction of the same structs we were talking about, the ones that are much more powerful.

So this trigger you are talking about should by your own words "attach a struct to every unit", which includes dead units. In other words it does exactly the same as the UnitIndexer with structs instead of indexes. It should also be "much more powerful". You can't honestly suggest that "much more powerful" does not mean something better.

Also you shouldn't have assumed that I can guess that what you finally posted is what you ment by something else than a system. It isn't placed in a library, it uses vJass features to accomplish the same things I use regular jass for and it destroys structs when units die instead of when they are removed. It is nowhere near obvious that with "no system" you mean something with practically only those differences. If you had been correct you would have simply said that there is no need to retain dead units' indexes and that you'd rather use structs instead of indexes and arrays.

Btw structs already use 0 for null so you don't have to have that +-1 in your script.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
With "those structs" I of course refer to the "much more powerful" ones that don't need a system to be used. Then you finally claimed:
Exactly? I claimed you could make a trigger in about 5 seconds that automatically adds and removes structs from units upon their entrance and exit to the map. Last time I checked, that is what I did.

Then I asked for an example of that, to which you responded that I should add more variables to the struct. (good example btw)
If that is sarcasm (which I assume it is), my suggestion achieves the same ends as your adding more arrays which are referenced by the unit index.

To respond to some old quotes (I don't know if CG already replied to them) --

Why do you compare this system to structs? This system != struct. This system allocates home-made structs for units. This system != struct. The point of the system, or why it is great, isn't that it uses a tiny bit faster allocation algorithm. The point of the system is to allocate a home-made struct for each unit that enters the map so you don't have to figure out yourself when to create or destroy them.
Isn't that quote self-explanatory? It asks a question and then answers it, look at how you term what you make from your system. And earlier, seeing as you were bragging about the slightly faster create method in yours, I naturally assumed that that was what you thought the point of your system was. And, as I said, it does take about 5 seconds to write a simple enter/exit allocation trigger (with structs, seeing as that is what I am advocating anyways!), as provided above.

You have not proven that the UnitUserData of a unit might miraculously change during the game.
As to that topic, I misread what you had said; I thought you were claiming changes to UnitUserData (done in systems etc) would interfere with structs but not your system.
 
Level 9
Joined
Mar 25, 2005
Messages
252
Exactly? I claimed you could make a trigger in about 5 seconds that automatically adds and removes structs from units upon their entrance and exit to the map. Last time I checked, that is what I did.

You destroy structs when units die, not when they are removed.

If that is sarcasm (which I assume it is), my suggestion achieves the same ends as your adding more arrays which are referenced by the unit index.

I asked for an example because I had no secure way of knowing what you were talking about. Since you said "no need of a system" I thought you were talking about something different in the sense that it doesn't need a separate 'handler' library to be used, for example what Captain_Griffen prefers over this. Your response had nothing to do with an example of structs being given to all units. That is what the sarcasm was pointed at.

Isn't that quote self-explanatory? It asks a question and then answers it, look at how you term what you make from your system. And earlier, seeing as you were bragging about the slightly faster create method in yours, I naturally assumed that that was what you thought the point of your system was. And, as I said, it does take about 5 seconds to write a simple enter/exit allocation trigger (with structs, seeing as that is what I am advocating anyways!), as provided above.

You kept comparing my system to structs as if they allocated and attached themselves to units. If you wanted to compare my system to the script you finally bothered to post you should have first posted it or atleast described it and then referred to it. This might come as a surprise to you but I can't argue against something that exists purely inside your head without making assumptions or speculations. At that point it should have been 100% clear to you that I did not know that you were talking about what you really were since I had already asked for an example of it and speculated that you were talking about some solution involving reference counters.

My first intention was not to brag, it was to show you that the difference between using structs and using these indexes is small, as in they work internally pretty much the same.

One of the reasons why I submitted this is that I have been planning to upload a damage detection engine (an orbless one btw) that needs something exactly like this. I could have just tagged this system along with it but I felt that the most reasonable thing to do would be to have this approved before doing that. I thought that getting this approved would be easy since imo nothing out there compares to this when attaching to every single unit on the map and retrieving data from them every time a unit is attached. Perhaps I will submit my engine and this tagged with it and add a note saying that the user can write his or her own version in a matter of seconds if my solution doesn't please him or her...

One argument that I for some reason haven't brought up yet is that if you use structs and their syntax you naturally have to declare all members inside the struct type being used, meaning that it will be a pain in the ass to modify and import systems and spells because you have to edit the structs declaration whenever you want to add or alter its members as opposed to modifying a global array inside the spell/system's scope. You might even have to start using prefixes for the methods and variable names of the struct to avoid conflicts. The same goes for create and onDestroy methods.

Another thing that I realized recently is that you can use this in GUI without any changes to any TriggerString files or anything. This is because Unit - Custom Value of Unit references to GetUnitUserData. You can't find the RemoveIndexedUnit function in the GUI, but for living units you can use "Unit - Hide" + "Unit - Explode" instead and for dead units the normal "Unit - Remove" action, since it actually works for dead units with indexes.

As to that topic, I misread what you had said; I thought you were claiming changes to UnitUserData (done in systems etc) would interfere with structs but not your system.

Actually what you quoted with that exact sentence was pointed at Captain_Griffen's claim of the indexes' not being "the same number for the same units". A unit is given an index only once, meaning that its index stays the same until it is removed, and thus is the same for the same unit, unless its user data miraculously changes. Though he might have also ment that the indexes aren't derived from the units' handle ids, but either way it has absolutely nothing to do with typecasting them to structs. My bad for not being clear with this one.

On a final note I find it slightly unfair and strange how most of the arguments against the UnitIndexer rely on certain scripts that aren't available to the community and even though described as obvious, are used by few (none that I really know of). If you think they are better than the UnitIndexer they should be out there for people to use, unless you are fine with PUI dominating the field on this one.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
You destroy structs when units die, not when they are removed.
As I said in the comments in that area, that could be changed relatively easily (Just change it to a simple loop). Also, to a certain extent you have to code smart (If you are talking about using RemoveUnit that is).

Since you will probably request an example --

JASS:
loop
    exitwhen GetUnitTypeId(u) == 0 or GetWidgetLife(u) > 0
    call TriggerSleepAction(1)
endloop
if GetUnitTypeId(u) == 0 then
    //destroy your struct
endif

If you think they are better than the UnitIndexer they should be out there for people to use, unless you are fine with PUI dominating the field on this one.
I assure you that I don't think any higher of PUI than I do of this (You can actually ask HINDY about this, since the first time I saw it, it was 12am, and I asked him whether I was sleep-deprived or the system was just pointless, and he agreed to the latter. At the time it even attached a struct to the UserData, which completely defeated the purpose)

One of the reasons why I submitted this is that I have been planning to upload a damage detection engine (an orbless one btw) that needs something exactly like this. I could have just tagged this system along with it but I felt that the most reasonable thing to do would be to have this approved before doing that. I thought that getting this approved would be easy since imo nothing out there compares to this when attaching to every single unit on the map and retrieving data from them every time a unit is attached. Perhaps I will submit my engine and this tagged with it and add a note saying that the user can write his or her own version in a matter of seconds if my solution doesn't please him or her...
If you want to submit this system with the DamageDetect system, I think that it would be fine (Unless, of course, there are problems with the DamageDetect system that need to be solved first (bugs, leaks, etc), though I doubt there will be).
 
Level 9
Joined
Mar 25, 2005
Messages
252
JASS:
loop
    exitwhen GetUnitTypeId(u) == 0 or GetWidgetLife(u) > 0
    call TriggerSleepAction(1)
endloop
if GetUnitTypeId(u) == 0 then
    //destroy your struct
endif

In that version when a unit dies, is resurrected and then dies before the first loop releases it, there will be two loops handling the same unit.
If you fix that it will start looking much like the UnitIndexers version:
JASS:
        if IsAlive[uind] then // this is to prevent that death -> resurrect -> death bug
            set IsAlive[uind] = false
            call TriggerSleepAction(3)
            loop
                exitwhen GetUnitUserData(u) == 0 // same as GetUnitTypeId(u) == 0 (afaik)
                if GetWidgetLife(u) >= 0.405 then
                    set IsAlive[uind] = true
                    set u = null
                    return
                endif
                call TriggerSleepAction(90)
            endloop
            set NextFreeIndex[uind] = FreeIndex
            set FreeIndex = uind
        endif

Only remarkable difference now is that I dont use structs -.-;;
Tell me if I'm right: your point is that those, for whom this really is a good choice, can just as well code their version instead of copying this from a website (even if it will end up essentially the same)?

Personally I find a GetClosestUnit function easier to code (less exceptions to deal with). Though that is propably more widely used which compensates for its simplicity I guess...

I assure you that I don't think any higher of PUI than I do of this --

I have actually known before this that you do not like PUI, but I figured that the reason for that was the if checks PUI does 4 times a second for all indexed units.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
In that version when a unit dies, is resurrected and then dies before the first loop releases it, there will be two loops handling the same unit.
With the odds of that happening (especially if you reduce the wait time), I find simpler code which might occasionally run slightly slower is still better than consistently slower code, which can be faster in rare situations (Or maybe not even).

Tell me if I'm right: your point is that those, for whom this really is a good choice, can just as well code their version instead of copying this from a website (even if it will end up essentially the same)?
True, I guess with the extra stuff it can be useful (like accurate unit checks), didn't think of it that way :p

Well then, a suggestion to make everyone happy, make an alternate version (also in this thread) that uses an interface so that people can make this work for structs?
 
Level 9
Joined
Mar 25, 2005
Messages
252
With the odds of that happening (especially if you reduce the wait time), I find simpler code which might occasionally run slightly slower is still better than consistently slower code, which can be faster in rare situations (Or maybe not even).

The struct of a unit in such case would be double freed (I think) so it isn't just a matter of speed. If it was I would be happy to do it your way.

Well then, a suggestion to make everyone happy, make an alternate version (also in this thread) that uses an interface so that people can make this work for structs?

I'll see to that tomorrow. There is of course the struct version that I posted somewhere along this thread but I haven't tested or documented it yet.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
Ah yes, that is a good point, if the struct(/index) was reallocated there would be problems.

Then again, if you just stored a unit 'self' in the struct (which would be useful for other reasons anyways), you could just check if 'self' was referencing a null unit. (And thus the struct had not been reallocated), which would still work for speed (but only in a struct version, of course)
 
Level 9
Joined
Mar 25, 2005
Messages
252
It double frees even when the struct isn't reallocated, and in such case your solution wouldn't work without nulling the 'self' variable at destruction. With that nulling procedure both of the solutions would be practically just as fast, though it might be useful to have that self variable for other reasons also so I'll think about it.

I posted a struct version of the system called UnitStructer now as you requested. Scripts that use the UnitIndexer work fully with the UnitStructer but not the other way around which is why it has a name of its own. I also removed the dynamic array feature from the UnitIndexer because it turned out that even if a struct has dynamic array variables its integer 'forms' will still be 1, 2, 3 etc. as opposed to 1, 5, 9 etc. for example and as such the struct version wouldn't have been as compatible with the index version if I had not removed the option from the latter.
 
Top