System Struct attachment system

Status
Not open for further replies.

Cohadar

master of fugue
Reaction score
209
Version 2.0

Code:
//==============================================================================
//  STRUCT ATTACHMENT SYSTEM BY COHADAR   
//==============================================================================
//
//  PURPOUSE:
//       * Attaching structs to a handle (unit, group, item, timer ...)
//
//  PROS: 
//       * you can attach any struct to any handle
//       * get and set functions are extremelly fast (using hash algorithm)
//       * get(handle, true) function clears setted value, 
//         so you don't have to do it yourself
//       * system reports overflow and unknown handle requests
//         also has debug messages for collision efficiency testing
//       * this system will work even if your map leaks
//
//  CONS:
//       * you can have only 8192 attached structs at any time
//       * you canNOT attach 2 structs on same handle (second erases the first one)
//
//  DETAILS:
//       * we are using the fact that structs never have zero index
//         to mark unused entries in hash table
//       * probability of collision is approximately 1/8192 = 0.012% = almost never
//       * collision is not an error, it will only slow functions down
//       * when struct is deatached from handle it is not destroyed
//         you still have to do that manually if necessary
//
//  HOW TO IMPORT:
//       * Just create a trigger named SAS
//       * convert it to text and replace the whole trigger text with this one
//
//==============================================================================


library StructAttachmentSystem


//------------------------------------------------------------------------------
// global arrays represent our hash table
//------------------------------------------------------------------------------
globals
    constant integer ARRAY_SIZE = 8192  // full hash size
    
    handle  array Handles
    integer array Structs
    integer Counter = 0
    integer oldCollision = 0
endglobals


//------------------------------------------------------------------------------
// converts handle to integer
//------------------------------------------------------------------------------
function C_H2I takes handle h returns integer i
    return h
    return 0
endfunction

//------------------------------------------------------------------------------
// Prints out collision size (debug mode only)
//------------------------------------------------------------------------------
function ReportCollision takes integer newCollision returns nothing
    if (oldCollision != newCollision) then
        call BJDebugMsg("Collision size: "+I2S(newCollision)+" Hash size: "+I2S(Counter))
        set oldCollision = newCollision
    endif
endfunction

//------------------------------------------------------------------------------
// attaches integer(struct index) to a handle by using hash table
//------------------------------------------------------------------------------
function SetStruct takes handle h, integer s returns nothing
    local integer i 
    local integer startingHash
    
    set i = ModuloInteger(C_H2I(h), ARRAY_SIZE)
    
    set startingHash = i
    
    if (Structs[i] == 0) then
        set Structs[i] = s
        set Handles[i] = h
        set Counter = Counter + 1
        return
    else 
        // hash collision
        // it will almost never happen, and even if it does
        // it will never get bigger than 10 because wc3 is using
        // incremental memory allocation
        
        // find next available hash value
        loop 
            set i = i + 1
            exitwhen i >= ARRAY_SIZE
                if (Structs[i] == 0) then
                    set Structs[i] = s
                    set Handles[i] = h
                    set Counter = Counter + 1
                    debug call ReportCollision(i-startingHash)
                    return
                endif
        endloop
 
        // start from the beginning       
        set i = 0
        loop 
            exitwhen i >= startingHash
                if (Structs[i] == 0) then
                    set Structs[i] = s
                    set Handles[i] = h
                    set Counter = Counter + 1
                    debug call ReportCollision(ARRAY_SIZE-startingHash+i)
                    return
                endif
            set i = i + 1                
        endloop

        call BJDebugMsg("|cFFFF0000ERROR: Hash overflow")
    endif
endfunction

//------------------------------------------------------------------------------
// gets integer(struct index) from a handle 
// optionally clears a hash entry (removes handle attachment)
//------------------------------------------------------------------------------
function GetStruct takes handle h, boolean clear returns integer
    local integer i 
    local integer startingHash
    local integer ret // return value
    
    set i = ModuloInteger(C_H2I(h), ARRAY_SIZE)

    set startingHash = i
    
    if (Structs[i] != 0 and Handles[i] == h) then
        set ret = Structs[i]
        if (clear == true) then
            set Structs[i] = 0
            set Handles[i] = null
            set Counter = Counter - 1
        endif
        return  ret
    else 
        // hash collision
        // it will almost never happen, and even if it does
        // it will never get bigger than 10 because wc3 is using
        // incremental memory allocation
        
        // find next available hash value
        loop 
            set i = i + 1
            exitwhen i >= ARRAY_SIZE
                if (Structs[i] != 0 and Handles[i] == h) then
                    set ret = Structs[i]
                    if (clear == true) then
                        set Structs[i] = 0
                        set Handles[i] = null
                        set Counter = Counter - 1
                    endif
                    debug call ReportCollision(i-startingHash)
                    return ret                    
                endif
        endloop
 
        // start from the beginning       
        set i = 0
        loop 
            exitwhen i >= startingHash
                if (Structs[i] != 0 and Handles[i] == h) then
                    set ret = Structs[i]
                    if (clear == true) then
                        set Structs[i] = 0
                        set Handles[i] = null
                        set Counter = Counter - 1
                    endif
                    debug call ReportCollision(ARRAY_SIZE-startingHash+i)
                    return ret
                endif
            set i = i + 1                
        endloop

        call BJDebugMsg("|cFFFF0000ERROR: Unknown handle: " + I2S(C_H2I(h)))
        return 0
    endif
    
endfunction

endlibrary
//==============================================================================
//  END OF STRUCT ATTACHMENT SYSTEM
//==============================================================================


function Trig_SAS_Actions takes nothing returns nothing
endfunction

//===========================================================================
function InitTrig_SAS takes nothing returns nothing
    set gg_trg_SAS = CreateTrigger(  )
    call TriggerAddAction( gg_trg_SAS, function Trig_SAS_Actions )
endfunction


Old Version:

Code:
//==============================================================================
//  STRUCT ATTACHMENT SYSTEM BY COHADAR   
//==============================================================================
//
//  PURPOUSE:
//       * Attaching structs to a handle (unit, group, item, timer ...)
//
//  PROS: 
//       * you can attach any struct to any handle
//       * get and set functions are extremelly fast (using hash algorithm)
//       * get(handle, true) function clears setted value, 
//         so you don't have to do it yourself
//       * system reports overflow and unknown handle requests
//         also has debug messages for collision efficiency testing
//       * this system will work even if your map leaks
//
//  CONS:
//       * you can have only 8192 attached structs at any time
//       * you canNOT attach 2 structs on same handle (second erases the first one)
//
//  DETAILS:
//       * we are using the fact that structs never have zero index
//         to mark unused entries in hash table
//       * probability of collision is approximately 1/8192 = 0.012% = almost never
//       * collision is not an error, it will only slow functions down
//       * when struct is deatached from handle it is not destroyed
//         you still have to do that manually if necessary
//
//==============================================================================


library StructAttachmentSystem


//------------------------------------------------------------------------------
// global arrays represent our hash table
//------------------------------------------------------------------------------
globals
    constant integer ARRAY_SIZE = 8192
    
    handle  array Handles
    integer array Structs
    integer Counter = 0
endglobals


//------------------------------------------------------------------------------
// converts handle to integer
//------------------------------------------------------------------------------
function C_H2I takes handle h returns integer i
    return h
    return 0
endfunction

//------------------------------------------------------------------------------
// attaches integer(struct index) to a handle by using hash table
//------------------------------------------------------------------------------
function SetStruct takes handle h, integer s returns nothing
    local integer i 
    local integer startingHash
    
    set i = ModuloInteger(C_H2I(h), ARRAY_SIZE) // getting the hash value
    set startingHash = i
    
    if (Structs[i] == 0) then
        set Structs[i] = s
        set Handles[i] = h
        set Counter = Counter + 1
        return
    else 
        // hash collision
        // it will almost never happen, and even if it does
        // it will never get bigger than 10 because wc3 is using
        // incremental memory allocation
        
        // find next available hash value
        loop 
            set i = i + 1
            exitwhen i >= ARRAY_SIZE
                if (Structs[i] == 0) then
                    set Structs[i] = s
                    set Handles[i] = h
                    set Counter = Counter + 1
                    debug call BJDebugMsg("Collision size: "+I2S(startingHash-i))
                    debug call BJDebugMsg("Hash table size: "+I2S(Counter))
                    return
                endif
        endloop
 
        // start from the beginning       
        set i = 0
        loop 
            exitwhen i >= startingHash
                if (Structs[i] == 0) then
                    set Structs[i] = s
                    set Handles[i] = h
                    set Counter = Counter + 1
                    debug call BJDebugMsg("Collision size: "+I2S(ARRAY_SIZE-startingHash+i))
                    debug call BJDebugMsg("Hash table size: "+I2S(Counter))
                    return
                endif
            set i = i + 1                
        endloop

        call BJDebugMsg("ERROR: Hash overflow")
    endif
endfunction

//------------------------------------------------------------------------------
// gets integer(struct index) from a handle 
// optionally clears a hash entry (removes handle attachment)
//------------------------------------------------------------------------------
function GetStruct takes handle h, boolean clear returns integer
    local integer i 
    local integer startingHash
    local integer ret // return value
    
    set i = ModuloInteger(C_H2I(h), ARRAY_SIZE) // getting the hash value
    set startingHash = i
    
    if (Structs[i] != 0 and Handles[i] == h) then
        set ret = Structs[i]
        if (clear == true) then
            set Structs[i] = 0
            set Handles[i] = null
        endif
        return  ret
    else 
        // hash collision
        // it will almost never happen, and even if it does
        // it will never get bigger than 10 because wc3 is using
        // incremental memory allocation
        
        // find next available hash value
        loop 
            set i = i + 1
            exitwhen i >= ARRAY_SIZE
                if (Structs[i] != 0 and Handles[i] == h) then
                    set ret = Structs[i]
                    if (clear == true) then
                        set Structs[i] = 0
                        set Handles[i] = null
                    endif
                    debug call BJDebugMsg("Collision size: "+I2S(startingHash-i))
                    debug call BJDebugMsg("Hash table size: "+I2S(Counter))
                    return ret                    
                endif
        endloop
 
        // start from the beginning       
        set i = 0
        loop 
            exitwhen i >= startingHash
                if (Structs[i] != 0 and Handles[i] == h) then
                    set ret = Structs[i]
                    if (clear == true) then
                        set Structs[i] = 0
                        set Handles[i] = null
                    endif
                    debug call BJDebugMsg("Collision size: "+I2S(ARRAY_SIZE-startingHash+i))
                    debug call BJDebugMsg("Hash table size: "+I2S(Counter))
                    return ret
                endif
            set i = i + 1                
        endloop

        call BJDebugMsg("ERROR: Unknown handle: " + I2S(C_H2I(h)))
        return 0
    endif
    
endfunction

endlibrary
//==============================================================================
//  END OF STRUCT ATTACHMENT SYSTEM
//==============================================================================


function Trig_SAS_Actions takes nothing returns nothing
endfunction

//===========================================================================
function InitTrig_SAS takes nothing returns nothing
    set gg_trg_SAS = CreateTrigger(  )
    call TriggerAddAction( gg_trg_SAS, function Trig_SAS_Actions )
endfunction

And here is a test map with a slide spell as a proof of concept.
 

Cohadar

master of fugue
Reaction score
209
I improved system a bit, also added a testing map
so you can test the system yourself
and see how it reacts under pressure.

It look really funny lol :p

NOTE: for the purpose of testing hash size is reduced from 8192 to 64
 

Steel

Software Engineer
Reaction score
109
So how is this any different or better than attaching a struct to a handle through handle vars? God forbid we use old methods? I mean all this is, is instead of using a handle to store the data, you put it in a recycling global array.

JASS:
struct Blah
unit pineapple
endstruct

function whatever takes nothing returns nothing
local Blah b = Blah.create()
set b.pineapple = <Some unit>
call SetHandleInt(<something>, "blah", b)
endfunction
 

Cohadar

master of fugue
Reaction score
209
you notice that "blah" part?

It is a string reference in game cache,
thats how handle wars are working right.

Well my system is better because it is simpler to use,
you don't need the unnatural "blah" part

Nothing less and nothing more...
 

Steel

Software Engineer
Reaction score
109
Have you looked at a post compile Struct?

JASS:
function K_Timer takes nothing returns nothing
local timer t= GetExpiredTimer()
local integer slide= GetHandleInt(t , "data")
local real x= GetUnitX(s__SlideUnit_u[slide]) + s__SlideUnit_init[slide] * s__SlideUnit_cos[slide]
local real y= GetUnitY(s__SlideUnit_u[slide]) + s__SlideUnit_init[slide] * s__SlideUnit_sin[slide]

call SetUnitPosition(s__SlideUnit_u[slide] , x , y)
call DamageUnit(s__SlideUnit_p[slide] , s__SlideUnit_dmg[slide] , s__SlideUnit_u[slide])
call DestroyEffect(AddSpecialEffect(s__SlideUnit_sfx[slide] , x , y))
set s__SlideUnit_steps[slide]=s__SlideUnit_steps[slide] - 1
if s__SlideUnit_steps[slide] <= 1 then
call ReleaseTimer(t)
set t = null
call s__SlideUnit_destroy(slide)
endif

set t = null
endfunction

function Trig_K_Actions takes nothing returns nothing
local timer t= NewTimer()
local real angle= GetUnitFacing(GetTriggerUnit())
local integer slide= s__SlideUnit__allocate()
call DamageUnit(GetOwningPlayer(GetTriggerUnit()) , ( GetHeroAgi(GetTriggerUnit() , true) * 3 ) , GetSpellTargetUnit())
set s__SlideUnit_dmg[slide]=( GetHeroAgi(GetTriggerUnit() , true) / 5 )
set s__SlideUnit_p[slide]=GetOwningPlayer(GetTriggerUnit())
set s__SlideUnit_sfx[slide]="Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl"
set s__SlideUnit_init[slide]=15
set s__SlideUnit_u[slide]=GetSpellTargetUnit()
set s__SlideUnit_cos[slide]=Cos(angle * bj_DEGTORAD)
set s__SlideUnit_sin[slide]=Sin(angle * bj_DEGTORAD)
set s__SlideUnit_steps[slide]=30

call SetHandleInt(t , "data" , slide)
call TimerStart(t , .04 , true , function K_Timer)

endfunction

Simple Slide trigger, how would your system modify / help this?
 

Cohadar

master of fugue
Reaction score
209
I am afraid I don't understand what are you asking,
can you explain in a bit more detail?
 

Steel

Software Engineer
Reaction score
109
What I posted above is a Post Compiled Struct. Meaning it had all of that
struct <>
blah blah blah

endstruct

This is how it looks after the map is saved. I do not see how you system would benefit this post compiled struct.
 

SFilip

Gone but not forgotten
Reaction score
634
Well his system is used the same way as handle vars, but can only attach one integer per handle. Structs or no structs.
What has it got to do with the compiled version?
 

Cohadar

master of fugue
Reaction score
209
Actually I can make it attach multiple structs to a handle.

But than I would have to use that "blah" string mentioned earlier
by adding global string array to represent keys.
Those string keys would than be used to uniquely identify
structs attached to same handle.

But if I did that It would slow my system down,
and it would be doing the same thing as handle vars
so there is no point in it.

The system does what is supposed to do,
it attaches one struct to one handle, nothing more.

In most cases you don't need more structs on a handle,
and timers are excellent example of this.
 

Cohadar

master of fugue
Reaction score
209
Nah you are not
people in general dislike to use new stuff when the old stuff is working ok.

It is natural.

Don't fix it if it is not broken...
 

Cohadar

master of fugue
Reaction score
209
BUMP

Version 3.0 is coming to an end.

It should be available to public after I finish all the tests and a demo map.

It will be optimized for speed and easiness of use
Current tests show that it is as fast as CSData.

It can store 24576 (3*8192) data of same type.

Basically it can store AT THE SAME TIME
24576 units + 24576 items + 24576 locations + 24576 .......
without the reduction in speed.

New macro created functions made available
will enable you to store typed handles without the need of handle vars
no more H2I, I2H, Z2W , F2Q and other nonsense

stay tuned ... :)
 

emjlr3

Change can be a good thing
Reaction score
395
I assume we can conclude that this works relatively in the same way as CSData, which I currently use for such endeavors

the only comparsion you seem to be making between the two is that it is "as fast" as the CSData system

browsing through the code leads me to believe it is a good bit longer then CSData, and since it uses the same method, I am not sure how they would be similar in speed

with that said, are there any other benefits to using this system over CSData, rather, why use this over any other system, such as CSData?
 

Cohadar

master of fugue
Reaction score
209
All will be clear once you see version 3.0

I am currently doing tests and writing documentation
and comparisons.

Why is this system better for some things...
how is it different...


Also note:

the functions in 2.0 are longer than those in CSData,
but they get past first else statement only when you get past 8192 handles

in that point CSData uses gamecache,
and I use hash algorithm.

But the strength in 3.0 is in different place....

Just wait and see.
I cant wait to get home from work to complete it :D
 

emjlr3

Change can be a good thing
Reaction score
395
so...this big ol algorithm is faster and safer then using 1-2 game cache calls?
 

Cohadar

master of fugue
Reaction score
209
so...this big ol algorithm is faster and safer then using 1-2 game cache calls?

Not faster (they are equally fast now), just safer, less system dependant
and easier to use.

Also don't forget you are looking at version 2.0 and we are talking about 3.0
 
Status
Not open for further replies.
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top