• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[JASS] Is this spell code 1.24 safe?

Status
Not open for further replies.

Dr Super Good

Spell Reviewer
Level 65
Joined
Jan 18, 2005
Messages
27,290
It would be more effective I guess if you actually ask at the provider of the resources site, as afterall they should have the most support for it.

Anyway, to answer your question, the jass scripts attached to the thread are 1.24 safe as they use no typecasting at all. If it uses any other systems I can not assure you but the code alone is safe.
 
ABC and PUI is required.


Code:
//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
//==============================================================================

Code:
//TESH.scrollpos=0
//TESH.alwaysfold=0
//==============================================================================
//  PUI -- Perfect Unit Indexing by Cohadar -- v2.0
//==============================================================================
//
//  PURPOUSE:
//       * Extending UnitUserData()
//       * This is basically perfect hashing algorithm for units
//
//  HOW TO USE:
//       * You have only one function at your disposal GetUnitIndex(unit)
//         It will return a unique index for each unit in range 1..8191
//
//       * What you will do with that index is all up to you
//         Of course using global arrays is the most obvious choice
//         Advanced jassers will think of a couple of more clever ones ofc.
//
//  PROS: 
//       * Unlimited UnitUserData()
//         you can assign as many data to a unit as you want
//
//       * This means you can use any number of systems that previously could not work together
//         because they all required exclusive access of UnitUserData()
//
//       * There are no SetUnitIndex() or ClearUnitIndex() function here
//         Each unit gets assigned one index that cannot be changed
//         That index will be automatically recycled when unit is removed from the game.
//         
//  CONS:
//       * This system uses UnitUserData() itself
//         That means that if you have this sys you must recode 
//         any other system that uses UnitUserData() to use this sys instead
//
//       * If you use UnitIndex for arrays of non-native types (timers, effects and similar)
//         you must check if timer on that index already exists before you create a new one.
//         This can happen if GetUnitIndex() assigns a recycled index (index of some dead and removed unit)
//         to the newly created unit for witch you intended to use timer for
//
//       * All in all this is not a sys for newbies, it gives great power,
//         but it requires knowledge and carefull handling
//
//  DETAILS:
//       * I am implicitly using index allocation algorithm by weaaddar
//         that is used in jasshelper to allocate structs.
//
//       * Index recycling is only working when there are new units entering the game,
//         this is logical because if there are no new units there is also
//         no danger that we will run out of indexes.
//
//  HOW TO IMPORT:
//       * Just create a trigger named PUI
//       * convert it to text and replace the whole trigger text with this one
//
//==============================================================================


library PUI

//==============================================================================
globals    
    private constant integer UNIT_INDEX_UNUSED = 0
    private constant integer UNIT_INDEX_TAKEN = -1
    
    private integer topIndex = 0
    private integer recycleIndex=0
endglobals

//==============================================================================
private struct UnitIndexer
    unit whichUnit
    integer state
    
    static method create takes unit u returns UnitIndexer
        local UnitIndexer ret = UnitIndexer.allocate()
        set ret.whichUnit = u
        
        if (ret.state == UNIT_INDEX_UNUSED) then
            set ret.state = UNIT_INDEX_TAKEN
        else
            call BJDebugMsg("|c00FF0000" + "FATAL ERROR: Double allocation of index #" + I2S(ret))
        endif
        
        if (topIndex < integer(ret)) then
            set topIndex = integer(ret)
        endif
        
        return ret
    endmethod
    
    method onDestroy takes nothing returns nothing
        set .whichUnit = null
        set .state = UNIT_INDEX_UNUSED
    endmethod
endstruct

//==============================================================================
private function RecycleIndex takes nothing returns nothing
    set recycleIndex = recycleIndex + 1
    if recycleIndex >= topIndex then
        set recycleIndex = 1 // zero index is not used because we use it for null
    endif

    // if curent recycling index is taken try to recycle it
    if UnitIndexer(recycleIndex).state == UNIT_INDEX_TAKEN then
        // Unit can have index zero only if it was removed
        // This is because we register all units as soon as they enter the map
        if (GetUnitUserData(UnitIndexer(recycleIndex).whichUnit)==0) then
            // remove (unit <--> index) link
            call UnitIndexer(recycleIndex).destroy()
            call BJDebugMsg("|c0000FF00" + "Index successfully recycled #" + I2S(recycleIndex))
        endif
    endif
endfunction


//==============================================================================
//  Automatically add index to units that enter playble map area
//  also try to recycle 2 indexes from index list
//==============================================================================
private function IndexEnteringUnits takes nothing returns boolean
    // for every new unit we try to recycle two indexes
    // this makes sure we never run out of indexes
    call RecycleIndex()
    call RecycleIndex()  
    return false
endfunction

//==============================================================================
//  type -topIndex in gamechat to see the top index
//  This represents the maximum number of units that was in game at the same time
//==============================================================================
private function DisplayTopIndex takes nothing returns boolean
    call BJDebugMsg("topIndex = " + I2S(topIndex))
    return false
endfunction

//==============================================================================
public function InitTrig takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterEnterRectSimple( trig, bj_mapInitialPlayableArea )
    call TriggerAddCondition( trig, Condition(function IndexEnteringUnits) )
    set trig = null
    
    set trig = CreateTrigger()
    call TriggerRegisterPlayerChatEvent( trig, Player(0), "-topIndex", true )
    call TriggerAddCondition( trig, Condition(function DisplayTopIndex) )
    set trig = null
endfunction


//==============================================================================
//  Main and only function exported by this library
//==============================================================================
function GetUnitIndex takes unit u returns integer
    local integer index = GetUnitUserData(u)

    //call BJDebugMsg("index request for: " + GetUnitName(u))
    
    if index == 0 then
        set index = UnitIndexer.create(u)
        call SetUnitUserData(u, index)
    debug else 
        // unit already has index, check if everything is ok
        // this error can happen if you have some other sys that uses SetUnitUserData()
        debug if UnitIndexer(index).whichUnit != u then
        debug   call BJDebugMsg("|c00FF0000" + "FATAL ERROR: Unit Index pointing to wrong unit #" + I2S(index))
        debug endif
    endif
    
    return index
endfunction

endlibrary
 

Dr Super Good

Spell Reviewer
Level 65
Joined
Jan 18, 2005
Messages
27,290
Well depending on how WC3 1.24 is finalized, all you may have to do to get it to work is marly delete all H2I functions ( I only saw one by a rough glance).
If they do not include a native or wrapper H2I, then you will have to update all references to H2I with references to the added native which does the same function.
 
This is how you do:

Simply look for a function named H2I (it could have any name really, but it should be short, take a handle, and return an integer)
Then replace it with this:

JASS:
function H2I takes handle h returns integer
    return GetHandleId(h)
endfunction

Note that the GetHandleId native requires the new patch and cannot be called whithout it.

Make sure the name of the function you change to (the one above) is given the exact same name as the one you replace.
 
Level 16
Joined
Oct 12, 2008
Messages
1,570
This is the easiest solution, but also one that makes unnessecary calls, pretty much like BJ's do. Though, if you use JNGP, your code will probably get inlined, which solves this last and makes it good to go.

Oh, but dont forget to make the function H2I private or public (if not already) to avoid redeclaration of functions (Blizzard is going to make a native called H2I, though i dont know what should be its use..... )
 
This is the easiest solution, but also one that makes unnessecary calls, pretty much like BJ's do. Though, if you use JNGP, your code will probably get inlined, which solves this last and makes it good to go.

Oh, but dont forget to make the function H2I private or public (if not already) to avoid redeclaration of functions (Blizzard is going to make a native called H2I, though i dont know what should be its use..... )

No, blizzard isn't going to make a native called H2I for just that reason.
That is why it's called GetHandleId.
 
Status
Not open for further replies.
Top