| 04-22-2009, 05:40 PM | #1 |
These modules can be used to turn any struct into augmented hash tables. There are 5 basic module types available, for the various uses you might have. Some are for integer keys, some are for handles, some support automated allocation, and some support automated cleanup. The exact details are in the code header. The example map shows off some simple potential applications. Automated cleanup is only supported for some handle types (unit, item, destructable, trigger, and timer). JASS:///////////////////////////////////////////////////// /// PAM /// Strilanc's Property Attachment Modules /// Version: 1.00 /// Last Updated: April 22, 2009 ///////////////////////////////////////////////////// /// Description: /// - Provides modules which implement attachment functionality for structs. /// /// Usage: /// - Use 'implement PAM_TYPE' at the top of a struct to add attachment functionality to it. /// - The return type is the type of the implementing struct. The attached data goes inside the struct. /// - Use OnDestroy for manual cleanup required when automatic cleanup occurs for automated and tidy properties. /// - Use the static create(K) method for manual initialization required when automatic creation occurs for automated properties. /// - Use struct[key] to get the struct instance associated with a key. /// /// Available Types: /// - PAM_RawProperty is the simplest type. It takes integers and returns instances of the implementing struct. /// - "set struct[K] = S" sets the struct instance associated with K to S /// - "struct[K]" returns the struct instance associated with K. /// - struct.RemoveKey[K] clears the struct instance associated with K. /// - PAM_RawAutoProperty is like PAM_RawProperty, except it automates allocation of instances. /// - "struct[K]" returns the struct instance associated with K, creating a new one if none exists. /// - The struct must have a static create method with the signature S(int K). /// - PAM_Property is the same as RawProperty, except it uses handles instead of integers as keys. /// - "set struct[K] = S" sets the struct instance associated with K to S /// - "struct[K]" returns the struct instance associated with K. /// - struct.RemoveKey[K] clears the struct instance associated with K. /// - PAM_TidyProperty_[TYPE] uses handles as keys and manages cleanup of decayed keys. Not all handle types are supported. /// - "struct.ManagedCreate(K)" returns the struct instance associated with K, creating a new one if none exists. /// - "struct[K]" returns the struct instance associated with K. /// - The struct must have a static create method with the signature S([Type] K). /// - PAM_AutoProperty_[TYPE] is like TidyProperty, except it also automates allocation of instances. /// - "struct[K]" returns the struct instance associated with K, creating a new one if none exists.. /// - The struct must have a static create method with the signature S([Type] K). ///////////////////////////////////////////////////// library PAM globals private constant boolean SHOW_ERROR_MESSAGES = true private constant boolean SHOW_DEBUG_MESSAGES = true private constant integer TABLE_CAPACITY = 6000 //max keys in a hash table private constant integer TABLE_SIZE = 8191 //hash output range private constant integer PROBE_JUPAM = 29 //the number of slots probing skips backwards over private constant integer STATE_USED = 1 private constant integer STATE_EPAMTY = 0 private constant integer STATE_GHOST = -1 endglobals private function H2I takes handle h returns integer return h return 0 endfunction ///An int->int hash table with values restricted to [0, 8191) private module Indexer private static integer array states private static integer array keys private static integer num_keys = 0 ///Returns (with optional creation on failure) the index associated with the given key, or -1 if no such index. public static method PAM_IndexGet takes integer key, boolean create returns integer local integer e local integer h = key - (key/TABLE_SIZE)*TABLE_SIZE //try opportunistic match if thistype.keys[h] == key and thistype.states[h] == STATE_USED then return h endif //Find slot set e = -1 loop //keep in range if h < 0 then set h = h + TABLE_SIZE endif //return match if found if thistype.keys[h] == key and thistype.states[h] == STATE_USED then return h endif //remember the first open slot if e == -1 and thistype.states[h] != STATE_USED then set e = h endif //exit when the probe chain ends exitwhen thistype.states[h] == STATE_EPAMTY //next probe slot set h = h - PROBE_JUPAM endloop //Slot not found; give up if not creating if not create then return -1 endif //Fail if at capacity if thistype.num_keys >= TABLE_CAPACITY then if SHOW_ERROR_MESSAGES then call BJDebugMsg("TI: Couldn't index key[" + I2S(key) + "] due to maximum capacity.") endif return -1 endif //Create a new index for the key set thistype.keys[e] = key set thistype.states[e] = STATE_USED set thistype.num_keys = thistype.num_keys + 1 debug if SHOW_DEBUG_MESSAGES then debug call BJDebugMsg("TI: Added key[" + I2S(key) + "] => " + I2S(e) + " [total:" + I2S(thistype.num_keys) + "]") debug endif return e endmethod ///Returns and destroys the index associated with the given key, or -1 if no such index. public static method PAM_IndexDestroy takes integer key returns integer local integer i = thistype.PAM_IndexGet(key, false) local integer r = i if i < 0 then return -1 endif //Clean the index set thistype.states[i] = STATE_GHOST set thistype.num_keys = thistype.num_keys - 1 debug if SHOW_DEBUG_MESSAGES then debug call BJDebugMsg("TI: Removed key[" + I2S(key) + "] => " + I2S(i) + " [total:" + I2S(thistype.num_keys) + "]") debug endif //Check if the following slot is in a probe tail set i = i - PROBE_JUPAM if i < 0 then set i = i + TABLE_SIZE endif if thistype.states[i] != STATE_EPAMTY then return r endif //We're at the end of a probe tail; cut the tail loop set i = i + PROBE_JUPAM if i >= TABLE_SIZE then set i = i - TABLE_SIZE endif exitwhen thistype.states[i] != STATE_GHOST set thistype.states[i] = STATE_EPAMTY endloop return r endmethod endmodule ///A handle->int hash table with values restricted to [0, 8191) private module HandleIndexer implement Indexer static method PAM_HIndexGet takes handle key, boolean create returns integer return thistype.PAM_IndexGet(H2I(key), create) endmethod static method PAM_HIndexDestroy takes handle key returns integer return thistype.PAM_IndexDestroy(H2I(key)) endmethod endmodule ///Keeps a list of instances of a struct. private module InstanceList static thistype array PAM_list static integer PAM_num = 0 public integer PAM_list_index ///Ensures the instance is in the list. method PAM_ListAdd takes nothing returns nothing if this != 0 and thistype.PAM_list[.PAM_list_index] != this then set thistype.PAM_list[thistype.PAM_num] = this set .PAM_list_index = thistype.PAM_num set thistype.PAM_num = thistype.PAM_num + 1 endif endmethod ///Ensures the instance is removed from the list. method PAM_ListRemove takes nothing returns nothing if this != 0 and thistype.PAM_list[.PAM_list_index] == this then set thistype.PAM_num = thistype.PAM_num - 1 set thistype.PAM_list[.PAM_list_index] = thistype.PAM_list[thistype.PAM_num] set thistype.PAM_list[.PAM_list_index].PAM_list_index = .PAM_list_index set thistype.PAM_list[thistype.PAM_num] = -1 set .PAM_list_index = -1 endif endmethod endmodule ///An unmanaged integer property. public module RawProperty implement Indexer private static thistype array map ///Clears a key's value. public static method RemoveKey takes integer key returns nothing call thistype.PAM_IndexDestroy(key) endmethod ///Gets a key's value. public static method operator[]= takes integer key, thistype val returns nothing set thistype.map[thistype.PAM_IndexGet(key, true)] = val endmethod ///Sets a key's value. public static method operator[] takes integer key returns thistype return thistype.map[thistype.PAM_IndexGet(key, false)] endmethod endmodule ///An integer property which is automatically allocated. public module RawAutoProperty implement Indexer private static thistype array map ///Creates an associated instance for the given key, or returns the previously associated instance. ///Delegates instanciation to thistype.create(key). If thistype.create(key) returns 0, no association is created. public static method operator[] takes integer key returns thistype local thistype this = thistype.map[thistype.PAM_IndexGet(key, false)] if this == 0 then set this = thistype.create(key) if this != 0 then set thistype.map[thistype.PAM_IndexGet(key, true)] = this endif endif return this endmethod endmodule ///An unmanaged handle property. public module Property implement HandleIndexer private static thistype array map ///Clears a key's value. public static method RemoveKey takes handle key returns nothing call thistype.PAM_HIndexDestroy(key) endmethod ///Gets a key's value. public static method operator[]= takes handle key, thistype val returns nothing set thistype.map[thistype.PAM_HIndexGet(key, true)] = val endmethod ///Sets a key's value. public static method operator[] takes handle key returns thistype return thistype.map[thistype.PAM_HIndexGet(key, false)] endmethod endmodule //! textmacro PAM_AddType takes type, check private module TidyHashTable_$type$ implement HandleIndexer implement InstanceList private static thistype array map private static integer tid = 0 private $type$ key ///Checks one of the allocated instances and destroys it if its key has decayed. private static method tidy takes nothing returns nothing local thistype this local $type$ key if thistype.PAM_num == 0 then return endif //next set thistype.tid = thistype.tid + 1 if thistype.tid >= thistype.PAM_num then set thistype.tid = 0 endif //check set this = thistype.PAM_list[thistype.tid] set key = .key if ($check$) then //destroy set thistype.map[thistype.PAM_HIndexDestroy(.key)] = 0 set .key = null call .PAM_ListRemove() call .destroy() endif set key = null endmethod ///Retrieves an instance associated with the key, or 0 if no association exists. public static method PAM_ManagedGet takes $type$ key returns thistype return thistype.map[thistype.PAM_HIndexGet(key, false)] endmethod ///Creates an associated instance for the given key, or returns the previously associated instance. ///Delegates instanciation to thistype.create(key). If thistype.create(key) returns 0, no association is created. public static method PAM_ManagedCreate takes $type$ key returns thistype local thistype this = thistype.map[thistype.PAM_HIndexGet(key, false)] if this == 0 then //cleanup others call thistype.tidy() call thistype.tidy() //try create set this = thistype.create(key) if this != 0 then //create set thistype.map[thistype.PAM_HIndexGet(key, true)] = this set .key = key call .PAM_ListAdd() endif endif return this endmethod endmodule ///A handle property which automates cleanup on decay. public module TidyProperty_$type$ implement TidyHashTable_$type$ ///Retrieves an instance associated with the key, or 0 if no association exists. public static method operator[] takes $type$ key returns thistype return thistype.PAM_ManagedGet(key) endmethod ///Creates an associated instance for the given key, or returns the previously associated instance. ///Delegates instanciation to thistype.create(key). If thistype.create(key) returns 0, no association is created. public static method ManagedCreate takes $type$ key returns thistype return thistype.PAM_ManagedCreate(key) endmethod endmodule ///A handle property which automates allocation on read and cleanup on decay. public module AutoProperty_$type$ implement TidyHashTable_$type$ ///Creates an associated instance for the given key, or returns the previously associated instance. ///Delegates instanciation to thistype.create(key). If thistype.create(key) returns 0, no association is created. public static method operator[] takes $type$ key returns thistype return thistype.PAM_ManagedCreate(key) endmethod endmodule //! endtextmacro private function isTriggerDecayed takes trigger t returns boolean local triggeraction ta = TriggerAddAction(t, function DoNothing) if ta != null then call TriggerRemoveAction(t, ta) set ta = null return false endif return true endfunction //! runtextmacro PAM_AddType("unit", "GetUnitTypeId(key) == 0") //! runtextmacro PAM_AddType("item", "GetItemTypeId(key) == 0") //! runtextmacro PAM_AddType("destructable", "GetDestructableTypeId(key) == 0") //! runtextmacro PAM_AddType("timer", "not (TimerGetElapsed(key) != 0)") //! runtextmacro PAM_AddType("trigger", "isTriggerDecayed(key)") endlibrary |
| 04-25-2009, 12:35 PM | #2 |
I was expecting a bit more commentary. For example: JASS:private constant integer STATE_EPAMTY = 0 ![]() |
| 04-25-2009, 02:10 PM | #3 | |
Quote:
It seems official reviwers are all missing in action. I may want to take a peek on this later. |
| 04-25-2009, 10:37 PM | #4 |
My view is that we need more concrete examples of what this is useful for. |
| 04-25-2009, 10:54 PM | #5 |
I assume you mean in addition to the example map? Let me give a real-world example: the physics and vehicle systems in my lordaeron grand prix map, which I work on now and then. There are three places I use the system: - Each unit being handled by the physics system has Particle struct. The first line of the Particle struct is "implement Property", which allows me to use Particle[u] to retrieve a unit's particle. (I'm not using an auto property because currently particles don't map 1-1 with units). JASS:
struct Particle
implement PAM_Property
....
endstruct
...
set p = Particle[GetTriggerUnit()]
....
- Cars should have more traction on roads, and they should slide easily on dirt. So I have a TileData class, containing things like friction and bounciness, which implements RawProperty. I can then use TileData[GetEnvironmentType(x, y)] to retrieve the struct containing the tile's properties. I do a sortof dirty hack here by assigning default values to the struct at index 0 instead of using a RawAutoProperty to ensure I always get a valid struct. JASS:
struct TileData
implement PAM_RawProperty
real friction
real elasticity
real boost
public static method create takes nothing returns TileData
local TileData this = TileData.allocate()
call .Reset()
return this
endmethod
public method Reset takes nothing returns nothing
set .friction = 1
set .elasticity = 0.25
set .boost = 0
endmethod
endstruct
...
function init_terrain takes nothing returns nothing
local TileData dat
call TileData(0).Reset() //[return default values for naive hash misses]
//ice is slippery
set dat = TileData.create()
set dat.friction = 0.01
set TileData['Nice'] = dat
set TileData['Iice'] = dat
set TileData['Idki'] = dat
//roads are grippy
set dat = TileData.create()
set dat.friction = 1.2
set TileData['Zbks'] = dat
set TileData['Yblm'] = dat
//dirt is loose
set dat = TileData.create()
set dat.friction = 0.8
set TileData['Zdrt'] = dat
//lava is boost
set dat = TileData.create()
set dat.friction = 3.0
set dat.boost = 2.0
set TileData['Dlvc'] = dat
endfunction
- The cars themselves have stats, so I do the same thing as I did with tile data, except with unit types. JASS:
struct UnitTypeData
implement PAM_RawProperty
real mass
real elasticity
real collisionDamageBuffer
real collisionDamageFactor
real thrust
real drag
real friction
real collisionLiftZ
boolean thrusting
boolean rolling
public static method create takes nothing returns UnitTypeData
local UnitTypeData this = UnitTypeData.allocate()
call .Reset()
return this
endmethod
public method Reset takes nothing returns nothing
set .mass = 1
set .elasticity = 1
set .collisionDamageBuffer = 10
set .collisionDamageFactor = 0.2
set .thrust = 0
set .drag = 0.00005
set .friction = 1
set .collisionLiftZ = 0
set .thrusting = false
set .rolling = false
endmethod
endstruct
...
function init_units takes nothing returns nothing
local UnitTypeData dat
call UnitTypeData(0).Reset() //[default values for naive hash misses]
//car
set dat = UnitTypeData.create()
set dat.mass = 4
set dat.collisionDamageBuffer = 50
set dat.collisionLiftZ = 100
set UnitTypeData['h001'] = dat
...
endfunction
|
| 04-29-2009, 12:21 AM | #6 |
not that bad. It seems all right, not that great for handle attachment but raw data is cute enough. /approved |
| 08-01-2009, 07:46 PM | #7 |
The Rough Dirt's name is typoed as "Rought Dirt". |
