//TESH.scrollpos=370
//TESH.alwaysfold=0
//==============================================================================
// ABC -- STRUCT ATTACHMENT SYSTEM BY COHADAR -- v5.1
//==============================================================================
//==============================================================================
// Quick function index:
//==============================================================================
//
// ----------------------------------------------------------------------
// Set Functions - these functions attach struct to a handle
// ----------------------------------------------------------------------
// SetTimerStructA(timer, struct)
// SetTimerStructB(timer, struct)
// SetTimerStructC(timer, struct)
//
// SetTriggerStructA(trigger, struct)
// SetTriggerStructB(trigger, struct)
// SetTriggerStructC(trigger, struct)
//
// SetDialogStructA(dialog, struct)
// SetDialogStructB(dialog, struct)
// SetDialogStructC(dialog, struct)
//
// ----------------------------------------------------------------------
// Get Functions - these functions retrieve attached structs
// ----------------------------------------------------------------------
// GetTimerStructA(timer) -> struct
// GetTimerStructB(timer) -> struct
// GetTimerStructC(timer) -> struct
//
// GetTriggerStructA(trigger) -> struct
// GetTriggerStructB(trigger) -> struct
// GetTriggerStructC(trigger) -> struct
//
// GetDialogStructA(dialog) -> struct
// GetDialogStructB(dialog) -> struct
// GetDialogStructC(dialog) -> struct
//
// ----------------------------------------------------------------------
// Clear Functions - these functions clear and return attached value
// ----------------------------------------------------------------------
// ClearTimerStructA(timer) -> struct
// ClearTimerStructB(timer) -> struct
// ClearTimerStructC(timer) -> struct
//
// ClearTriggerStructA(trigger) -> struct
// ClearTriggerStructB(trigger) -> struct
// ClearTriggerStructC(trigger) -> struct
//
// ClearDialogStructA(dialog) -> struct
// ClearDialogStructB(dialog) -> struct
// ClearDialogStructC(dialog) -> struct
//
//==============================================================================
//==============================================================================
// DOCUMENTATION:
//==============================================================================
//
// PURPOUSE OF ABC:
// * Attaching multiple structs to a <handle>
// * Not using stupid game cache.
//
// * Currently supported <handle> types are timer, trigger and dialog
// ABC can NOT be used to attach things to units,
// Systems that can attach stuff to units require a mission key
// ABC does not have mission keys, witch is one of the reasons it is so fast.
//
// * These are the only 3 handle types I found that need attaching (except units)
// If you have the need for some other handle types please let me know
//
// HOW TO USE:
// * Lets assume you want to attach some spell data to a timer.
// You would do the following:
//
// 1. Create struct that will contain all your data.
// 2. call SetTimerStructA(myTimer, myStruct)
//
// and then you decide you need one more struct on that timer...
//
// call SetTimerStructB(myTimer, myStruct2)
//
// Then in a periodic timer you just get the stored value
// set myStruct = GetTimerStructA(myTimer)
//
// In your final itearation of periodic timer
// you need to clear stored values
// ClearTimerStructA(myTimer)
// ClearTimerStructB(myTimer)
// If you don't system will start to overflow
//
// DIFFERENCE FROM v5.0:
// * You cannot use SetStructA two times on the same <handle>
// without clearing the previous value.
//
// * ABC v5.0 used to overwrite the old values when you did that.
// This caused errors when collisions get over 3
// Since collisions almost never happen in real maps noone noticed this before
// Only some hardcore tests will detect this
//
// * New version of ABC treats overwrite as an error.
// BAD: SetStructA(X, value1); SetStructA(X, value2)
// GOOD: SetStructA(X, value1); ClearStructA(X); SetStructA(X, value2)
//
// * HASH_COLLISION_LIMIT is reduced from 8 to 7 to simplyfy algorithms
// (using null borders at modulo 8)
// This effectively means that you can store around 6000 attachments per hash
// before you get an overflow. (used to be 7000)
//
// * This simplyfication increased ABC speed a little.
//
// PROS:
// * ABC is faster than any gamecache based system.
//
// * you can attach any struct to any supported <handle> type
//
// * you CAN attach up to 3 structs on the same <handle>
// but only if you use different functions, for example
// SetTriggerStructA(), SetTriggerStructB(), SetTriggerStructC()
//
// * get and set functions are extremelly fast (using hash algorithm)
//
// * System reports collision, overwrite and overflow.
// Basically if you do anything stupid system will tell you.
//
// * This system will work even if your map leaks
// and will NOT slow down because of it.
//
// * This is the system that spits torment test in the eye.
//
// * For compatibility with ABC v4.6 look at ABCC library
//
//
// CONS:
// * you must manually clean the stored value - REMEMBER THIS RULE!!!
// Don't forget to use ClearStruct() functions
//
// * you should NOT use Get without a Set - REMEMBER THIS RULE!!!
// It will simply return a zero and nothing bad will happen,
// but it will lag really really bad
// (system will report this as an error)
//
// * System starts to collide if you have more than 1024 structs in one hash.
// (you can see this very obviosly with -overload test)
// If that happens it can mean one of 2 things:
// 1. you are using more than 1024 timers, triggers or dialogs - !?
// 2. you forgot to use ClearStruct and you are leaking handles somewhere
// if this is the case simply do a search on your code and find
// what trigger uses ABC but has no ClearStruct calls.
//
// DETAILS:
// * when struct is detached from <handle> it is not destroyed
// you still have to do that manually if necessary
//
// * ABC will not interfere with other attachemt systems
// You can freely use any other system alongside ABC
// But if you use only ABC for attachments in your map
// you don't have to null local variables.
// The reason for this is that gamecache gets abnormaly slow
// when you don't null locals, ABC does not have this flaw.
//
//
// SPECIAL THANKS TO:
// * NagelBagel - for finding errors in versions 4.3 and 4.4
// * Here-b-Trollz - for testing ABC and for making cool spells with it.
// * Toadcop - for being pain in the ass and for pushing me to improve ABC.
// * emjlr3 - for pointing out the need for non-generic trigger attachments
// * PandaMine - I found a bug in ABC by examining his HSAS vs ABC test
// * All those people out there who use and support ABC
// Thank you guys.
//
// HOW TO IMPORT:
// * Just create a trigger named ABC
// * convert it to text and replace the whole trigger text with this one
//
//==============================================================================
//------------------------------------------------------------------------------
// We will use textmacros to create multiple instances of system
//------------------------------------------------------------------------------
//! textmacro ABC takes X, NAME, TYPE
//------------------------------------------------------------------------------
// Global arrays represent our hash tables.
// System is currently using 3 hash tables per handle type. (A, B, C)
//------------------------------------------------------------------------------
globals
private $TYPE$ array $TYPE$Key$X$
private integer array $TYPE$Value$X$
private integer $TYPE$maxCollision$X$ = 0
endglobals
//------------------------------------------------------------------------------
// returns the maximum collision so far
//------------------------------------------------------------------------------
function Get$NAME$Collision$X$ takes nothing returns integer
return $TYPE$maxCollision$X$
endfunction
//------------------------------------------------------------------------------
// initializes hash arrays to prevent lag when ABC is used for the first time
//------------------------------------------------------------------------------
private function Init$NAME$Hash$X$ takes nothing returns nothing
set $TYPE$Key$X$[HASH_INDEX_LIMIT] = null
set $TYPE$Value$X$[HASH_INDEX_LIMIT] = 0
endfunction
//------------------------------------------------------------------------------
// attaches struct to a handle by using hash table
//------------------------------------------------------------------------------
function Set$NAME$Struct$X$ takes $TYPE$ t, integer s returns nothing
debug local integer collision
// hash using 32-bit integer overflow
local integer i = (H2I(t) * HASH_UP) / HASH_DOWN + HASH_BIAS
if $TYPE$Key$X$[i] == null then
set $TYPE$Value$X$[i] = s
set $TYPE$Key$X$[i] = t
return
endif
debug if $TYPE$Key$X$[i] == t then
debug call BJDebugMsg("|cFFFF0000ERROR: Hash[$X$] attachment overwrite on $TYPE$ #" +I2S(H2I(t)))
debug return
debug endif
// if function gets below this line we have a collision
debug set collision = 1
loop
debug if collision >= HASH_COLLISION_LIMIT then
debug call BJDebugMsg("|cFFFF0000ERROR: Hash[$X$] overflow")
debug return
debug endif
debug set collision = collision + 1
set i = i + 1
exitwhen $TYPE$Key$X$[i] == null
debug if $TYPE$Key$X$[i] == t then
debug call BJDebugMsg("|cFFFF0000ERROR: Hash[$X$] attachment overwrite on $TYPE$ #" +I2S(H2I(t)))
debug return
debug endif
endloop
debug if collision > $TYPE$maxCollision$X$ then
debug call BJDebugMsg("|cFFFF4444Warning: Hash[$X$] maximum collision is now: " + I2S(collision))
debug set $TYPE$maxCollision$X$ = collision
debug endif
set $TYPE$Value$X$[i] = s
set $TYPE$Key$X$[i] = t
return
endfunction
//------------------------------------------------------------------------------
// gets stored struct from a handle
//------------------------------------------------------------------------------
function Get$NAME$Struct$X$ takes $TYPE$ t returns integer
debug local integer collision
// hash using 32-bit integer overflow
local integer i = (H2I(t) * HASH_UP) / HASH_DOWN + HASH_BIAS
if $TYPE$Key$X$[i] == t then
return $TYPE$Value$X$[i]
endif
// if function gets below this line we have a collision
debug set collision = 1
loop
debug if collision >= HASH_COLLISION_LIMIT then
debug call BJDebugMsg("|cFFFF0000ERROR: Hash[$X$] : get request on unknown handle")
debug return 0
debug endif
debug set collision = collision + 1
set i = i + 1
exitwhen $TYPE$Key$X$[i] == t
endloop
return $TYPE$Value$X$[i]
endfunction
//------------------------------------------------------------------------------
// clears stored struct from a handle, also returns cleared value
//------------------------------------------------------------------------------
function Clear$NAME$Struct$X$ takes $TYPE$ t returns integer
debug local integer collision
local integer ik
local integer ret
// hash using 32-bit integer overflow
local integer i = (H2I(t) * HASH_UP) / HASH_DOWN + HASH_BIAS
// first find the index on witch key is stored
debug set collision = 0
loop
debug if collision >= HASH_COLLISION_LIMIT then
debug call BJDebugMsg("|cFFFF0000ERROR: Hash[$X$] : clear request on unknown handle")
debug return 0
debug endif
debug set collision = collision + 1
exitwhen $TYPE$Key$X$[i] == t
set i = i + 1
endloop
set ik = i
set ret = $TYPE$Value$X$[ik]
// then find last used key index in bucket
loop
set i = i + 1
// we use the fact bucket borders (mod 8 indexes) are always null
exitwhen $TYPE$Key$X$[i] == null
endloop
// shift last bucket entry to the place of removed one
set $TYPE$Key$X$[ik] = $TYPE$Key$X$[i-1]
set $TYPE$Value$X$[ik] = $TYPE$Value$X$[i-1]
// clear the previous last bucket entry
set $TYPE$Key$X$[i-1] = null
return ret
endfunction
//! endtextmacro
//==============================================================================
// Macro execution -- this is where real functions get created
//==============================================================================
library ABC initializer Init
globals
public constant integer HASH_SIZE = 8192
public constant integer HASH_INDEX_LIMIT = 8190
public constant integer HASH_DOWN = 524288 // 2^19
public constant integer HASH_UP = 2134900736 // 2^22 * 509
public constant integer HASH_BIAS = 4096 // HASH_SIZE / 2
public constant integer HASH_COLLISION_LIMIT = 7 // ABC v5.0 had limit 8
// 509 is the prime closest to 512
endglobals
//------------------------------------------------------------------------------
// conversion function used by the system internally
// you will not need to use it directly
//------------------------------------------------------------------------------
public function H2I takes handle h returns integer
return h
return 0
endfunction
//! runtextmacro ABC("A","Timer","timer")
//! runtextmacro ABC("B","Timer","timer")
//! runtextmacro ABC("C","Timer","timer")
//! runtextmacro ABC("A","Trigger","trigger")
//! runtextmacro ABC("B","Trigger","trigger")
//! runtextmacro ABC("C","Trigger","trigger")
//! runtextmacro ABC("A","Dialog","dialog")
//! runtextmacro ABC("B","Dialog","dialog")
//! runtextmacro ABC("C","Dialog","dialog")
private function Init takes nothing returns nothing
call InitTimerHashA()
call InitTimerHashB()
call InitTimerHashC()
call InitTriggerHashA()
call InitTriggerHashB()
call InitTriggerHashC()
call InitDialogHashA()
call InitDialogHashB()
call InitDialogHashC()
endfunction
endlibrary
//==============================================================================
// END OF ABC STRUCT ATTACHMENT SYSTEM
//==============================================================================