15 years
edited 8 years
This is the closest you could reproduce Handle Vars' functionality and interface in (currently westfall) patch 1.24, requires jasshelper 0.9.H.2 and patch 1.24 or later.
Are intended so implementing these handle vars in your map should be able to replace Handle Vars functionality, however, in some odd cases in which you do very odd stuff it won't work (will raise compiler errors), those cases should be rare.
Faux Handle Vars do not have the I2H-related issues that plagued original handle vars, should also be mildly faster (I still recommend AgentVars for a better speed improvement, or even better, structs). However, I do not recommend these functions for anything other than porting old code to patch 1.24. It is because this interface is very leak-friendly (these leaks are also very common in code that uses handle vars, and are almost by-design) Although the leaks weren't a big issue for most maps using Handle vars (not even DOTA) they are still undesirable...
Faux CSCache works the same, except in some cases they might not work 100% well because of how in CSCache you were allowed to use inline gamecache calls and other retardedness... do notice that Faux CSCache will leak memory like ... well, like something that leaks a lot of memory...
CSCache has worked to make spell factory and also some other spell maps work again (once you also move CS to 16.0)
Due to blizzard's failed failedness, You may find some cases like:
These lines will cause a syntax error, why? Because blizzard for some INCREDIBLY STUPID reason didn't make [lcode=jass]triggeraction[/lcode] extend [lcode=jass]agent[/lcode] in common.j
The fix for that would be something like:
Differences between these handle vars and the original ones
They are called handle vars and have function names like SetHandleHandle just for the reason of compatibility, in reality, you can only use these functions to attach stuff to objects of the [lcode=jass]agent[/lcode] type. SetHandleHandle is only able to attach agents. I.E you cannot use SetHandleHandle to attach a lightning effect, would have to use SetHandleLightning instead) Since GetHandleHandle is basically impossible (even one that returns agent) it has been removed.
Faux Handle Vars
Faux CSCache
You will need two additional libraries from Vex's original CasterSystem. They are CSSafeCache and CSData. Their code is below and all 3 libraries are in the attached map to show how they should be implemented.
CSData
CSSafeCache
Are intended so implementing these handle vars in your map should be able to replace Handle Vars functionality, however, in some odd cases in which you do very odd stuff it won't work (will raise compiler errors), those cases should be rare.
Faux Handle Vars do not have the I2H-related issues that plagued original handle vars, should also be mildly faster (I still recommend AgentVars for a better speed improvement, or even better, structs). However, I do not recommend these functions for anything other than porting old code to patch 1.24. It is because this interface is very leak-friendly (these leaks are also very common in code that uses handle vars, and are almost by-design) Although the leaks weren't a big issue for most maps using Handle vars (not even DOTA) they are still undesirable...
Faux CSCache works the same, except in some cases they might not work 100% well because of how in CSCache you were allowed to use inline gamecache calls and other retardedness... do notice that Faux CSCache will leak memory like ... well, like something that leaks a lot of memory...
CSCache has worked to make spell factory and also some other spell maps work again (once you also move CS to 16.0)
Due to blizzard's failed failedness, You may find some cases like:
call AttachObject( meh, "...", #TriggerAddAction(t, function ff)# )
These lines will cause a syntax error, why? Because blizzard for some INCREDIBLY STUPID reason didn't make [lcode=jass]triggeraction[/lcode] extend [lcode=jass]agent[/lcode] in common.j
The fix for that would be something like:
call AttachObject( meh, "...", null )
call TriggerAddAction(t, function ff)
Differences between these handle vars and the original ones
They are called handle vars and have function names like SetHandleHandle just for the reason of compatibility, in reality, you can only use these functions to attach stuff to objects of the [lcode=jass]agent[/lcode] type. SetHandleHandle is only able to attach agents. I.E you cannot use SetHandleHandle to attach a lightning effect, would have to use SetHandleLightning instead) Since GetHandleHandle is basically impossible (even one that returns agent) it has been removed.
Faux Handle Vars
/*******************************************************************************************
Faux Handle Vars
----------------
Do not use these functions for new stuff, it is just not the right thing to do...
The intention of Faux Handle Vars is to be a patch fix for a map's migration to
patch 1.24 This library might not cover all uses of handle vars, some of them are just
impossible with the new rules set in patches 1.23b and 1.24, but they should work for
most of the cases....
All of them but the SetHandle*** ones are inline friendly. I follow the interface
in official Kattana's handle vars from wc3jass.com, if your handle vars functions
look different, then you were not using handle vars but another thing...
*******************************************************************************************/
//==========================================================================================
library HandleVars initializer init
globals
private hashtable ht
endglobals
// too bad the Handle vars' old functionality forces me to make these things
// inline-unfriendly
function SetHandleHandle takes agent subject, string label, agent value returns nothing
if(value==null) then
call RemoveSavedHandle( ht, GetHandleId(subject), StringHash(label))
else
call SaveAgentHandle( ht, GetHandleId(subject), StringHash(label), value)
endif
endfunction
function SetHandleInt takes agent subject, string label, integer value returns nothing
if value==0 then
call RemoveSavedInteger(ht, GetHandleId(subject), StringHash(label))
else
call SaveInteger(ht, GetHandleId(subject), StringHash(label), value)
endif
endfunction
function SetHandleBoolean takes agent subject, string label, boolean value returns nothing
if (value == false) then
call RemoveSavedBoolean(ht, GetHandleId(subject), StringHash(label))
else
call SaveBoolean(ht, GetHandleId(subject), StringHash(label), value)
endif
endfunction
function SetHandleReal takes agent subject, string label, real value returns nothing
if (value == 0.0) then
call RemoveSavedReal(ht, GetHandleId(subject), StringHash(label))
else
call SaveReal(ht, GetHandleId(subject), StringHash(label), value)
endif
endfunction
function SetHandleString takes agent subject, string label, string value returns nothing
if ((value=="") or (value==null)) then
call RemoveSavedString(ht, GetHandleId(subject), StringHash(label))
else
call SaveStr(ht, GetHandleId(subject), StringHash(label), value) //yay for blizz' consistent naming scheme...
endif
endfunction
function GetHandleHandle takes agent subject, string label returns agent
debug call BJDebugMsg("[debug] What the heck? Why would you call HandleHandle I guess this was caused by a search and replace mistake")
return null
endfunction
// these are inline friendly, ok, maybe they aren't because jasshelper does not recognize
// GetHandleId as non-state changing. But they will be once I fix jasshelper...
function GetHandleInt takes agent subject, string label returns integer
return LoadInteger(ht, GetHandleId(subject), StringHash(label))
endfunction
function GetHandleBoolean takes agent subject, string label returns boolean
return LoadBoolean(ht, GetHandleId(subject), StringHash(label))
endfunction
function GetHandleString takes agent subject, string label returns string
return LoadStr(ht, GetHandleId(subject), StringHash(label))
endfunction
function GetHandleReal takes agent subject, string label returns real
return LoadReal(ht, GetHandleId(subject), StringHash(label))
endfunction
// got bored so I now use a textmacro...
//! textmacro FAUX_HANDLE_VARS_GetHandleHandle takes NAME, TYPE
function SetHandle$NAME$ takes agent subject, string label, $TYPE$ value returns nothing
if(value==null) then
call RemoveSavedHandle( ht, GetHandleId(subject), StringHash(label))
else
call Save$NAME$Handle( ht, GetHandleId(subject), StringHash(label), value)
endif
endfunction
function GetHandle$NAME$ takes agent subject, string label returns $TYPE$
return Load$NAME$Handle( ht, GetHandleId(subject), StringHash(label))
endfunction
//! endtextmacro
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Player","player")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Widget","widget")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Destructable","destructable")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Item","item")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Unit","unit")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Ability","ability")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Timer","timer")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Trigger","trigger")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("TriggerCondition","triggercondition")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("TriggerAction","triggeraction")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("TriggerEvent","event")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Force","force")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Group","group")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Location","location")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Rect","rect")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("BooleanExpr","boolexpr")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Sound","sound")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Effect","effect")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("UnitPool","unitpool")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("ItemPool","itempool")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Quest","quest")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("QuestItem","questitem")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("DefeatCondition","defeatcondition")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("TimerDialog","timerdialog")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Leaderboard","leaderboard")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Multiboard","multiboard")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("MultiboardItem","multiboarditem")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Trackable","trackable")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Dialog","dialog")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Button","button")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("TextTag","texttag")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Lightning","lightning")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Image","image")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Ubersplat","ubersplat")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Region","region")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("FogState","fogstate")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("FogModifier","fogmodifier")
//! runtextmacro FAUX_HANDLE_VARS_GetHandleHandle("Hashtable","hashtable")
function FlushHandleVars takes agent subject returns nothing
call FlushChildHashtable(ht, GetHandleId(subject))
endfunction
function FlushHandleLocals takes agent subject returns nothing
call FlushHandleVars(subject)
endfunction
private function init takes nothing returns nothing
set ht=InitHashtable()
endfunction
endlibrary
Faux CSCache
/*******************************************************************************************
Faux CSCache AttachVars
----------------
Do not use these functions for new stuff, it is just not the right thing to do...
The intention of Faux CSCache is to be a patch fix for a map's migration to
patch 1.24 This library might not cover all uses of handle vars, some of them are just
impossible with the new rules set in patches 1.23b and 1.24, but they should work for
most of the cases....
*******************************************************************************************/
//==========================================================================================
library CSCache initializer init requires CSSafeCache
globals
private hashtable ht
endglobals
// too bad the Handle vars' old functionality forces me to make these things
// inline-unfriendly
function SetTableObject takes string subject, string label, agent value returns nothing
if(value==null) then
call RemoveSavedHandle( ht, StringHash(subject), StringHash(label))
else
call SaveAgentHandle( ht, StringHash(subject), StringHash(label), value)
endif
endfunction
function GetTableHandle takes string subject, string label returns agent
debug call BJDebugMsg("[debug] What the heck? Why would you call HandleHandle I guess this was caused by a search and replace mistake")
return null
endfunction
// these are inline friendly, ok, maybe they aren't because jasshelper does not recognize
// GetHandleId as non-state changing. But they will be once I fix jasshelper...
// got bored so I now use a textmacro...
//! textmacro FAUX_HANDLE_VARS_GetTableHandle takes NAME, TYPE
function SetTable$NAME$ takes string subject, string label, $TYPE$ value returns nothing
if(value==null) then
call RemoveSavedHandle( ht, StringHash(subject), StringHash(label))
else
call Save$NAME$Handle( ht, StringHash(subject), StringHash(label), value)
endif
endfunction
function GetTable$NAME$ takes string subject, string label returns $TYPE$
return Load$NAME$Handle( ht, StringHash(subject), StringHash(label))
endfunction
//! endtextmacro
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Player","player")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Widget","widget")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Destructable","destructable")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Item","item")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Unit","unit")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Ability","ability")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Timer","timer")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Trigger","trigger")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TriggerCondition","triggercondition")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TriggerAction","triggeraction")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TriggerEvent","event")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Force","force")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Group","group")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Location","location")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Rect","rect")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("BooleanExpr","boolexpr")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Sound","sound")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Effect","effect")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("UnitPool","unitpool")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("ItemPool","itempool")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Quest","quest")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("QuestItem","questitem")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("DefeatCondition","defeatcondition")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TimerDialog","timerdialog")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Leaderboard","leaderboard")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Multiboard","multiboard")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("MultiboardItem","multiboarditem")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Trackable","trackable")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Dialog","dialog")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Button","button")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TextTag","texttag")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Lightning","lightning")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Image","image")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Ubersplat","ubersplat")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Region","region")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("FogState","fogstate")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("FogModifier","fogmodifier")
//! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Hashtable","hashtable")
function CS_KillTrigger takes trigger t returns nothing
// let em leak
endfunction
// too bad the Handle vars' old functionality forces me to make these things
// inline-unfriendly
function AttachObject takes agent subject, string label, agent value returns nothing
call SetTableObject( GetAttachmentTable(subject) , label, value)
endfunction
function GetAttachedHandle takes agent subject, string label returns agent
debug call BJDebugMsg("[debug] What the heck? Why would you call HandleHandle I guess this was caused by a search and replace mistake")
return null
endfunction
// these are inline friendly, ok, maybe they aren't because jasshelper does not recognize
// GetHandleId as non-state changing. But they will be once I fix jasshelper...
// got bored so I now use a textmacro...
//! textmacro FAUX_HANDLE_VARS_GetAttachedHandle takes NAME, TYPE
function Attach$NAME$ takes agent subject, string label, $TYPE$ value returns nothing
call SetTable$NAME$( GetAttachmentTable(subject), label, value)
endfunction
function GetAttached$NAME$ takes agent subject, string label returns $TYPE$
return GetTable$NAME$( GetAttachmentTable(subject), label)
endfunction
//! endtextmacro
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Player","player")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Widget","widget")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Destructable","destructable")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Item","item")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Unit","unit")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Ability","ability")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Timer","timer")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Trigger","trigger")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TriggerCondition","triggercondition")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TriggerAction","triggeraction")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TriggerEvent","event")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Force","force")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Group","group")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Location","location")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Rect","rect")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("BooleanExpr","boolexpr")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Sound","sound")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Effect","effect")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("UnitPool","unitpool")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("ItemPool","itempool")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Quest","quest")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("QuestItem","questitem")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("DefeatCondition","defeatcondition")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TimerDialog","timerdialog")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Leaderboard","leaderboard")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Multiboard","multiboard")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("MultiboardItem","multiboarditem")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Trackable","trackable")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Dialog","dialog")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Button","button")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TextTag","texttag")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Lightning","lightning")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Image","image")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Ubersplat","ubersplat")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Region","region")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("FogState","fogstate")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("FogModifier","fogmodifier")
//! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Hashtable","hashtable")
private function init takes nothing returns nothing
set ht=InitHashtable()
endfunction
endlibrary
Pyrogasm recommendations
For anyone attempting to use the Faux CSCache system:
You will need two additional libraries from Vex's original CasterSystem. They are CSSafeCache and CSData. Their code is below and all 3 libraries are in the attached map to show how they should be implemented.
CSData
library CSData initializer init
//****************************************************************************************************
// CSData 16.0
// ¯¯¯¯¯¯¯¯¯¯¯
// CSDatas are like UserData in units and items, they now use a hashtable instead of big arrays,
// should be faster and safer
//
// Notice that for public spells or systems to be distributed you should only use these
// for private objects (those who the mapper would never have access to) If you are making something
// for your map you can use them wherever you want.
//
// Best to be used in conjunction to CSArrays so you just specify an array id for a handle.
//
// DO NOT USE THIS ON THESE HANDLE TYPES: -lightning, -ubersplat, -image, -texttag,
// -any 'argument' handle (like playerstate, damagetype, etc)
//
//****************************************************************************************************
//====================================================================================================
globals
private constant integer MAX_HANDLE_ID_COUNT = 408000
// values lower than 8191: very fast, but very unsafe.
// values bigger than 8191: not that fast, the bigger the number is the slower the function gets
// Most maps don't really need a value bigger than 50000 here, but if you are unsure, leave it
// as the rather inflated value of 408000
endglobals
//=================================================================================================
// a.k.a H2I, changed name to CS_H2I to prevent conflicts with other systems, it then stayed that
// instead of changing to a private or public function since many outside spells use it.
//
function CS_H2I takes handle h returns integer //For compat
return GetHandleId(h) //
endfunction //
//==================================================================================================
globals
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetCSData takes handle h, integer v returns nothing
call SaveInteger(ht , 0, GetHandleId(h) , v)
endfunction
function GetCSData takes handle h returns integer
return LoadInteger( ht, 0, GetHandleId(h) )
endfunction
private function init takes nothing returns nothing
set ht = InitHashtable()
endfunction
endlibraryCSSafeCache
library CSSafeCache initializer init requires CSData
//*************************************************************************************************
//* *
//* CSSafeCache 15.3 [url]http://wc3campaigns.net/vexorian[/url] *
//* ¯¯¯¯¯¯¯ *
//* CSCache is evil now, use just CSSafeCache when possible as it now contains all functions *
//* that do not use I2H or similar functions that are considered to cause unstability in the map *
//* *
//* CSCache has been deprecated, and is now unusuable since it can't be ported to patch 1.23b *
//* Migration from CSCache to CSSafeCache+structs should be easy *
//* *
//* However, for old compatibility, CSSafeCache needs to stick to using gamecache, which is not *
//* as good as hashtable (it is slower and may be vulnerable to Sync attacks) I recommend *
//* switching from CSSafeCache to hashtable or Table *
//* *
//*************************************************************************************************
//=================================================================================================
// CSSafeCache globals:
//
globals
gamecache cs_cache = null
integer array cs_array1
integer array cs_array3
integer array cs_array2
private integer array cs_freeindexes
private integer array cs_pairx
private integer array cs_pairy
private integer array cs_freepairs
endglobals
private function init takes nothing returns nothing
// CSSafeCache initializer :
call FlushGameCache(InitGameCache("cscache"))
set cs_cache=InitGameCache("cscache")
call ExecuteFunc("InitArrayIndexes")
call ExecuteFunc("Pool_SetupCharMap")
endfunction
//=================================================================================================================
// CS Pairs
// ¯¯¯¯¯¯¯¯
// This is a sub system to assist csarrays, you can use them but CSArrays of size 2 have the same functionality
// although cspairs are be faster their space is limited and will start using gamecache if abused
//
function NewPair takes integer x, integer y returns integer
local integer i
if (cs_freepairs[0]==0) then
set cs_freepairs[8190]=cs_freepairs[8190]+1
set i= cs_freepairs[8190]
else
set i= cs_freepairs[cs_freepairs[0]]
set cs_freepairs[0]=cs_freepairs[0]-1
endif
if (i>=8189) then //because we can only recycle up to 8189 (free pairs array uses indexes 0 and 8190 for other purposes)
call StoreInteger(cs_cache,"pairx",I2S(i),x)
call StoreInteger(cs_cache,"pairy",I2S(i),y)
else
set cs_pairx[i]=x
set cs_pairy[i]=y
endif
return i
endfunction
function DestroyPair takes integer id returns nothing
if (id>=8189) then
call FlushStoredInteger(cs_cache,"pairx",I2S(id))
call FlushStoredInteger(cs_cache,"pairy",I2S(id))
else
set cs_freepairs[0]=cs_freepairs[0]+1
set cs_freepairs[cs_freepairs[0]] = id
endif
endfunction
function SetPairXY takes integer id, integer x, integer y returns nothing
if (id>=8189) then
call StoreInteger(cs_cache,"pairx",I2S(id),x)
call StoreInteger(cs_cache,"pairy",I2S(id),y)
else
set cs_pairx[id]=x
set cs_pairy[id]=y
endif
endfunction
function SetPairX takes integer id, integer x returns nothing
if (id>=8189) then
call StoreInteger(cs_cache,"pairx",I2S(id),x)
else
set cs_pairx[id]=x
endif
endfunction
function SetPairY takes integer id, integer y returns nothing
if (id>=8189) then
call StoreInteger(cs_cache,"pairy",I2S(id),y)
else
set cs_pairy[id]=y
endif
endfunction
function GetPairX takes integer id returns integer
if (id>=8189) then
return GetStoredInteger(cs_cache,"pairy",I2S(id))
endif
return cs_pairx[id]
endfunction
function GetPairY takes integer id returns integer
if (id>=8189) then
return GetStoredInteger(cs_cache,"pairx",I2S(id))
endif
return cs_pairy[id]
endfunction
//=================================================================================================================
// CS Dynamic Arrays
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Thanks: Pipedream, Peppar
//
// We can now create arrays in game! , also pass them as arguments or return values!
// a 1 length array is a pointer!
//
function Array_TryFree takes nothing returns nothing
local integer i
local integer N=cs_array1[0]
local integer k
local boolean cleaned=false
local integer loc
local integer q
local integer r
set i=cs_array1[146]
if (i>144) then
call TimerStart(GetExpiredTimer(),60.,false,function Array_TryFree)
return
endif
set loc=cs_freeindexes[i]
set q=0
loop
exitwhen (loc==0)
// we could just have used:
//set k=GetPairX(loc)
//set r=GetPairY(loc) But it is slower than direct usage:
if (loc>=8192) then
set k=GetStoredInteger(cs_cache,"pairx",I2S(loc))
set r=GetStoredInteger(cs_cache,"pairy",I2S(loc))
else
set k=cs_pairx[loc]
set r=cs_pairy[loc]
endif
if (k+i-1==N) then
//we found one we can remove from the list
set cleaned=true
//decrement N
set N=k-2
//Remove from the list:
if (q==null) then
//That was the first, update the array as well
set cs_freeindexes[i]=r
else
//Update the next of the previous one
//We could use : call SetPairY(q,,r) but it is slower
if (q>=8189) then
call StoreInteger(cs_cache,"pairy",I2S(q),r)
else
set cs_pairy[q]=r
endif
endif
if (r==null) then
//This was the last one, update it in the array as well
set cs_freeindexes[i+4096]=q
endif
call DestroyPair(loc)
set loc=q
endif
set q=loc
set loc=r
endloop
if (cleaned) then
set cs_array1[0]=N
set cs_array1[146]=1
else
set cs_array1[146]=cs_array1[i+1]
endif
call TimerStart(GetExpiredTimer(),0.2,false,function Array_TryFree)
endfunction
function InitArrayIndexes2 takes nothing returns nothing
local integer i=0
loop
exitwhen (i==8191)
set cs_pairx[i]=777
set cs_pairy[i]=777
set i=i+1
endloop
endfunction
function InitArrayIndexes takes nothing returns nothing
local integer i=0
local integer a=1
local integer b=1
local integer c
//By placing 777 there instead of 0 it is easier to recognize non correctly initialized bugs
loop
exitwhen (i== 8191)
set cs_array1[i]=777
set cs_array2[i]=777
set cs_array3[i]=777
//set cs_pairx[i]=777
//set cs_pairy[i]=777
set i=i+1
endloop
call ExecuteFunc("InitArrayIndexes2")
set cs_freeindexes[0]=0 //The stack for the table indexes.
set cs_freepairs[0]=0
set cs_freepairs[8190]=0
set i=1
loop
set c=a+b
set a=b
set b=c
exitwhen (b>144) //max size is 144
set cs_freeindexes[b]=0 //the first of the list
set cs_freeindexes[b+4096]=0 //the last of the list
loop
exitwhen (i>b)
set cs_array1[i]=b
set i=i+1
endloop
endloop
set cs_array1[i]=b //i is 145
set cs_array1[146]=1
set cs_array1[147]=101 //initial table index is 101
set cs_array1[0]=147
//index 0: Last used index
// 1 to 145 : Fibonacci sequence
// 146 : last check
// 147 : Table indexes check
//This has a good chance to compress the thing when necesary
call TimerStart(CreateTimer(),60.,false,function Array_TryFree)
endfunction
//=============================================================================================
// Create an array of size, max size is 144, if doinit is true it will put a bunch of zeros
// in the indexes
//
function NewArray takes integer size, boolean doinit returns integer
local integer i
local integer rsize=cs_array1[size]
local integer loc
set loc=cs_freeindexes[rsize]
if (loc!=0) then
set cs_freeindexes[rsize]= GetPairY(loc)
if (cs_freeindexes[rsize]==0) then
set cs_freeindexes[4096+rsize]=0
endif
set i=GetPairX(loc)
call DestroyPair(loc)
if (i==0) then
//this code was probably a good idea when we used locations for the free indexes list, now we use pairs which should not ever
//do this unless someone modiffied the pair array incorrectly
call BJDebugMsg("Caster System: Unexpected error (5): corrupt stack, attempt to recover "+I2S(rsize))
// recovering involves forgetting about the stack which got corrupted and start again from zero, it will leak
// and probably get slow due to usage of gamecache but it is better than the problems that a corrupt stack might cause
set cs_freeindexes[rsize]=0
set cs_freeindexes[4096+rsize]=0
return NewArray(size,doinit)
endif
else
//sz i i+1 i+2
//[ ][ ][ ][ ]
set i=cs_array1[0]+2
set cs_array1[0]=i+rsize-1
endif
//It used to store size in the index equal to the array's id
// but that required the get/set functions to increment 1 in every index
// calculation. Instead, making size the previous index to the array works
if (i<=8191) then
set cs_array1[i-1]=size
elseif (i<=16382) then
set cs_array2[i-8192]=size
else
call StoreInteger(cs_cache,I2S(-i),"size",size)
endif
if (not doinit) then
return i
endif
// 3
//[i][i+1][i+2]
set size=i+size-1
if (size>=16382) then
set size=16381
endif
loop
exitwhen (size<i) or (size<8191)
set cs_array2[size-8191]=0
set size=size-1
endloop
loop
exitwhen (size<i)
set cs_array1[size]=0
set size=size-1
endloop
//call DisplayTextToPlayer(GetLocalPlayer(),0,0,I2S(i))
return i
endfunction
//===============================================================================================================
// Remember to destroy arrays when you no longer need them, else new arrays will get slower after a bunch of
// arrays are active
//
function DestroyArray takes integer id returns nothing
local integer L
local integer loc
// local string k=I2S(-id)
local integer lstpace
if (id<=8191) then
set L=cs_array1[cs_array1[id-1]]
elseif (id<=16382) then
set L=cs_array1[cs_array2[id-8191]]
else
set L=cs_array1[GetStoredInteger(cs_cache,I2S(-id),"size")]
//No way you are gonna call DestroyArray without calling
//NewArray first, so we can use the gamecache variable directly instead
endif
set lstpace=id+L-1
call FlushStoredMission(cs_cache,I2S(-id))
if (lstpace>16382) then
if (lstpace==cs_array1[0]) then
//We just destroyed the array that happens to be at the end of the heap.
//Just get it back
set cs_array1[0]=id-2
set cs_array1[146]=1
else
//Add to the last
set loc=cs_freeindexes[L+4096]
if (loc==0) then
set loc=NewPair(id,0)
set cs_freeindexes[L]=loc
set cs_freeindexes[L+4096]=loc
else
set cs_freeindexes[L+4096]= NewPair(id,0)
//we could just use: call SetPairY(loc, cs_freeindexes[L+4096] ) //but that's slower
if (loc>=8189) then
call StoreInteger(cs_cache,"pairy",I2S(loc), cs_freeindexes[L+4096] )
else
set cs_pairy[loc]=cs_freeindexes[L+4096]
endif
endif
endif
elseif (lstpace==cs_array1[0]) then
//We just destroyed the array that happens to be at the end of the heap.
//Just get it back
set cs_array1[0]=id-2
set cs_array1[146]=1
else
set loc=cs_freeindexes[L]
set cs_freeindexes[L]=NewPair(id,loc)
if (loc==0) then
set cs_freeindexes[L+4096]=cs_freeindexes[L]
endif
endif
endfunction
//================================================================================================================
// Int Set/Get array usage prototypes.
//
// These are the actual functions, the rest are just the result of copy paste, if these functions are updated
// the other ones should be updated as well (They are just return bugged variations)
//
function SetArrayInt takes integer id, integer index, integer val returns nothing
set index=id+index
if (index<8191) then
set cs_array1[index]=val
elseif (index<16382) then
set cs_array2[index-8191]=val
else
call StoreInteger(cs_cache,I2S(-id),I2S(index),val)
endif
endfunction
function GetArrayInt takes integer id, integer index returns integer
set index=id+index
if (index<8191) then
return cs_array1[index]
elseif (index<16382) then
return cs_array2[index-8191]
endif
return GetStoredInteger(cs_cache,I2S(-id),I2S(index))
endfunction
//================================================================================================================
// String Set/Get array
//
// Due String related return bug issues, these are forced to use gamecache
//
function SetArrayString takes integer id, integer index, string val returns nothing
call StoreString(cs_cache,I2S(-id),I2S(index),val)
endfunction
function GetArrayString takes integer id, integer index returns string
return GetStoredString(cs_cache,I2S(-id),I2S(index))
endfunction
//(Boolean is not needed)
//==========================================================================================================================
// Returns the size of an array (the specified by player one, not the actual size of it) should be useful.
//
function GetArraySize takes integer id returns integer
if (id<=8191) then
return cs_array1[id-1]
elseif (id<=16382) then
return cs_array2[id-8192]
endif
return GetStoredInteger(cs_cache,I2S(-id),"size")
endfunction
//===========================================================================================================================
// Returns an array that is an exact copy of the given array
//
function CloneArray takes integer id returns integer
local integer sz
local integer i
local integer sz2
local integer x
local integer y
if (id<=8191) then
set sz=cs_array1[id-1]
elseif (id<=16382) then
set sz=cs_array2[id-8192]
else
set sz=GetStoredInteger(cs_cache,I2S(-id),"size")
//No way you are gonna call DestroyArray without calling
//NewArray first, so we can use the gamecache variable directly instead
endif
set i=NewArray(sz,false)
set sz2=i+sz-1
set sz=id+sz-1
set x=i
set y=id
loop
exitwhen ((y>sz) or (y>=8191) or (x>=8191))
set cs_array1[x]=cs_array1[y]
set y=y+1
set x=x+1
endloop
loop
exitwhen ((y>sz) or (y>=8191) or (x>=16382))
set cs_array2[x-8191]=cs_array1[y]
set y=y+1
set x=x+1
endloop
loop
exitwhen ((y>sz) or (y>=8191))
call StoreInteger(cs_cache,I2S(-i),I2S(x-i),cs_array1[y])
set x=y+1
set y=y+1
endloop
//...
loop
exitwhen ((y>sz) or (y>=16382) or (x>=8191))
set cs_array1[x]=cs_array2[y-8191]
set y=y+1
set x=x+1
endloop
loop
exitwhen ((y>sz) or (y>=16382) or (x>=16382))
set cs_array2[x-8191]=cs_array2[y-8191]
set y=y+1
set x=x+1
endloop
loop
exitwhen ((y>sz) or (y>=16382))
call StoreInteger(cs_cache,I2S(-i),I2S(x-i),cs_array2[y-8191])
set y=y+1
set x=x+1
endloop
//...
loop
exitwhen ((y>sz) or (x>=8191))
set cs_array1[x]=GetStoredInteger(cs_cache,I2S(-id),I2S(y-id))
set y=y+1
set x=x+1
endloop
loop
exitwhen ((y>sz) or (x>=16382))
set cs_array2[x-8191]=GetStoredInteger(cs_cache,I2S(-id),I2S(y-id))
set y=y+1
set x=x+1
endloop
loop
exitwhen (y>sz)
call StoreInteger(cs_cache,I2S(-i),I2S(x-i),GetStoredInteger(cs_cache,I2S(-id),I2S(y-id)))
set y=y+1
set x=x+1
endloop
return i
endfunction
//==================================================================================================
// Attachable vars : Attacheable variables are what most other people call Handle Variables, they
// allow to relate data with any handle, using a label, and its value, the stuff auto flushes if
// the value is 0, false, "", or null .
//
// Differences between Attacheable variables and "Local Handle Variables" :
// - The names of the functions
// - The name of the function group does not cause confusion, it is difficult to say:
// "you should set local handle variables to null at the end of a function" since
// it sounds as if you were talking about the "Local Handle Variables"
// - And can work together with Tables.
//
// Gamecache stuff are NOT Case Sensitive, don't ever use "" for label (Crashes game!)
//
//
// Although locations and dynamic arrays are faster than gamecache, gamecache still keeps the flexibility
// Best thing to do in my opinion is to combine these options. By combining you can acquire gamecache
// flexibility and arrays/locs speed to solve a problem
//
//============================================================================================================
// For integers
//
function AttachInt takes handle h, string label, integer x returns nothing
if x==0 then
call FlushStoredInteger(cs_cache,I2S(GetHandleId(h)),label)
else
call StoreInteger(cs_cache,I2S(GetHandleId(h)),label,x)
endif
endfunction
function GetAttachedInt takes handle h, string label returns integer
return GetStoredInteger(cs_cache, I2S(GetHandleId(h)), label)
endfunction
//=============================================================================================================
function AttachReal takes handle h, string label, real x returns nothing
if x==0 then
call FlushStoredReal(cs_cache,I2S(GetHandleId(h)),label)
else
call StoreReal(cs_cache,I2S(GetHandleId(h)),label,x)
endif
endfunction
function GetAttachedReal takes handle h, string label returns real
return GetStoredReal(cs_cache,I2S(GetHandleId(h)),label)
endfunction
//=============================================================================================================
function AttachBoolean takes handle h, string label, boolean x returns nothing
if not x then
call FlushStoredBoolean(cs_cache,I2S(GetHandleId(h)),label)
else
call StoreBoolean(cs_cache,I2S(GetHandleId(h)),label,x)
endif
endfunction
function GetAttachedBoolean takes handle h, string label returns boolean
return GetStoredBoolean(cs_cache,I2S(GetHandleId(h)),label)
endfunction
//=============================================================================================================
function AttachString takes handle h, string label, string x returns nothing
if ((x=="") or (x==null)) then
call FlushStoredString(cs_cache,I2S(GetHandleId(h)),label)
else
call StoreString(cs_cache,I2S(GetHandleId(h)),label,x)
endif
endfunction
function GetAttachedString takes handle h, string label returns string
return GetStoredString(cs_cache,I2S(GetHandleId(h)),label)
endfunction
//============================================================================================================
function CleanAttachedVars takes handle h returns nothing
call FlushStoredMission(cs_cache,I2S(GetHandleId(h)))
endfunction
//============================================================================================================
// Left for compat
function CleanAttachedVars_NoSets takes handle h returns nothing
call FlushStoredMission(cs_cache,I2S(GetHandleId(h)))
endfunction
//=============================================================================================
// Tables
//
// Tables are lame, the real name would be hash tables, they are just abbreviated usage
// of gamecache natives with the addition that you can also Copy the values of a table to
// another one, but don't expect it to be automatic, it must use a FieldData object to know
// which fields and of wich types to copy, Copying a table to another, with a lot of Fields,
// should surelly be lag friendly.
//
// The other thing about tables is that I can say that the Attached variables of a handle work
// inside a table and GetAttachmentTable which is just return bug and I2S , works to allow you
// to manipulate a handle's attached variables through a table.
//
// NewTable and DestroyTable were created to allow to create tables in the fly, but you can
// simply use strings for tables, but place the table names should be between "("")" for example
// "(mytable)" to avoid conflicts with other caster system stuff.
//
function NewTableIndex takes nothing returns integer
local integer loc=cs_freeindexes[0]
local integer i
if (loc!=0) then
set i=GetPairX(loc)
set cs_freeindexes[0]=GetPairY(loc)
call DestroyPair(loc)
return i
endif
set i=cs_array1[147]+1
set cs_array1[147]=i
return i
endfunction
function NewTable takes nothing returns string
local integer loc=cs_freeindexes[0]
local integer i
if (loc!=0) then
set i=GetPairX(loc)
set cs_freeindexes[0]=GetPairY(loc)
call DestroyPair(loc)
return I2S(i)
endif
set i=cs_array1[147]+1
set cs_array1[147]=i
return I2S(i)
endfunction
function GetAttachmentTable takes handle h returns string
return I2S(GetHandleId(h))
endfunction
//============================================================================================================
function DestroyTable takes string table returns nothing
local integer i=S2I(table)
local integer n
call FlushStoredMission(cs_cache,table)
if ((i>100) and (i<1000000)) then //All right, more than 1000000 tables is just wrong.
if (i==cs_array1[147]) then
set cs_array1[147]=cs_array1[147]-1
else
set cs_freeindexes[0]= NewPair(i,cs_freeindexes[0])
endif
endif
endfunction
//============================================================================================================
function ClearTable takes string table returns nothing
call FlushStoredMission(cs_cache,table)
endfunction
//============================================================================================================
function SetTableInt takes string table, string field, integer val returns nothing
if (val==0) then
call FlushStoredInteger(cs_cache,table,field)
else
call StoreInteger(cs_cache,table,field,val)
endif
endfunction
function GetTableInt takes string table, string field returns integer
return GetStoredInteger(cs_cache,table,field)
endfunction
//============================================================================================================
function SetTableReal takes string table, string field, real val returns nothing
if (val==0) then
call FlushStoredReal(cs_cache,table,field)
else
call StoreReal(cs_cache,table,field,val)
endif
endfunction
function GetTableReal takes string table, string field returns real
return GetStoredReal(cs_cache,table,field)
endfunction
//============================================================================================================
function SetTableBoolean takes string table, string field, boolean val returns nothing
if (not(val)) then
call FlushStoredBoolean(cs_cache,table,field)
else
call StoreBoolean(cs_cache,table,field,val)
endif
endfunction
function GetTableBoolean takes string table, string field returns boolean
return GetStoredBoolean(cs_cache,table,field)
endfunction
//============================================================================================================
function SetTableString takes string table, string field, string val returns nothing
if (val=="") or (val==null) then
call FlushStoredString(cs_cache,table,field)
else
call StoreString(cs_cache,table,field,val)
endif
endfunction
function GetTableString takes string table, string field returns string
return GetStoredString(cs_cache,table,field)
endfunction
//============================================================================================================
// Returns true if the fiel contains a value different from 0, false, null, or "" (depending on the type)
// it is worthless to use this with boolean, since it would be the same as reading the boolean value
//
function HaveSetField takes string table, string field, integer fieldType returns boolean
if (fieldType == bj_GAMECACHE_BOOLEAN) then
return HaveStoredBoolean(cs_cache,table,field)
elseif (fieldType == bj_GAMECACHE_INTEGER) then
return HaveStoredInteger(cs_cache,table,field)
elseif (fieldType == bj_GAMECACHE_REAL) then
return HaveStoredReal(cs_cache,table,field)
elseif (fieldType == bj_GAMECACHE_STRING) then
return HaveStoredString(cs_cache,table,field)
endif
return false
endfunction
//============================================================================================================
// Allows to copy a table to another one, but it needs a FieldData object to know which fields of which type
// it is supposed to copy.
//
function CopyTable takes integer FieldData, string sourceTable, string destTable returns nothing
local integer i=1
local string k=I2S(FieldData)
local string k2
local string k3
local integer n=GetStoredInteger(cs_cache,k,"N")
local integer t
loop
exitwhen (i>n)
set k2=I2S(i)
set t=GetStoredInteger(cs_cache,k,k2)
set k3=GetStoredString(cs_cache,k,k2)
if (t==bj_GAMECACHE_BOOLEAN) then
if (HaveStoredBoolean(cs_cache,sourceTable,k3)) then
call StoreBoolean(cs_cache,destTable,k3,GetStoredBoolean(cs_cache,sourceTable,k3))
else
call FlushStoredBoolean(cs_cache,destTable,k3)
endif
elseif (t==bj_GAMECACHE_INTEGER) then
if (HaveStoredInteger(cs_cache,sourceTable,k3)) then
call StoreInteger(cs_cache,destTable,k3,GetStoredInteger(cs_cache,sourceTable,k3))
else
call FlushStoredInteger(cs_cache,destTable,k3)
endif
elseif (t==bj_GAMECACHE_REAL) then
if (HaveStoredReal(cs_cache,sourceTable,k3)) then
call StoreReal(cs_cache,destTable,k3,GetStoredReal(cs_cache,sourceTable,k3))
else
call FlushStoredReal(cs_cache,destTable,k3)
endif
elseif (t==bj_GAMECACHE_STRING) then
if (HaveStoredString(cs_cache,sourceTable,k3)) then
call StoreString(cs_cache,destTable,k3,GetStoredString(cs_cache,sourceTable,k3))
else
call FlushStoredString(cs_cache,destTable,k3)
endif
endif
set i=i+1
endloop
endfunction
//=============================================================================================
// FieldData inherits from Table, was just designed to be used by CopyTable.
//
function FieldData_Create takes nothing returns integer
return NewTableIndex()
endfunction
//============================================================================================================
// valueType uses the same integer variables from blizzard.j :
// bj_GAMECACHE_BOOLEAN, bj_GAMECACHE_INTEGER, bj_GAMECACHE_REAL and bj_GAMECACHE_STRING
//
function FieldData_AddField takes integer fielddata, string field, integer valueType returns nothing
local string k=I2S(fielddata)
local integer n=GetStoredInteger(cs_cache,k,"N")+1
local string k2=I2S(n)
call StoreString(cs_cache,k,k2,field)
call StoreInteger(cs_cache,k,k2,valueType)
call StoreInteger(cs_cache,k,"N",n)
endfunction
//=============================================================================================
// Destroys Field Data
function FieldData_Destroy takes integer fielddata returns nothing
call DestroyTable(I2S(fielddata))
endfunction
//=============================================================================================
// Pools
//
// A better name for pools would be sets, but by the time I made them I couldn't think of that
// name, besides the word set is already a JASS syntax word so it would have been problematic.
//
// Another naming failure is that values of a pool are called "items" but that conflicts with
// the word item that points to wc3 items, Pools can only store integer values, but if you want
// you can go and use the return bug on them.
//
function CreatePool takes nothing returns integer
local integer i=NewArray(34,false)
call SetArrayInt(i,0,0)
return i
endfunction
function ClearPool takes integer poolid returns nothing
call SetArrayInt(poolid,0,0) //[0:integer:n]
call FlushStoredMission(cs_cache,I2S(-poolid))
endfunction
function DestroyPool takes integer poolid returns nothing
call DestroyArray(poolid)
endfunction
function PoolAddItem takes integer poolid, integer value returns nothing
local integer n
local string k=I2S(-poolid)
local string vk="#"+I2S(value)
if not HaveStoredInteger(cs_cache,k,vk) then
set n=GetArrayInt(poolid,0)+1 //[0:integer:N]
call StoreInteger(cs_cache,k,vk,n)
if (n>33) then
call StoreInteger(cs_cache,k,I2S(n),value)
else
call SetArrayInt(poolid,n,value)
endif
call SetArrayInt(poolid,0,n) //[0:integer:N]
endif
endfunction
function PoolRemoveItem takes integer poolid, integer value returns nothing
local string k=I2S(-poolid)
local string vk="#"+I2S(value)
local integer p=GetStoredInteger(cs_cache,k,vk)
local integer n
if (p!=0) then
set n=GetArrayInt(poolid,0) //[0:integer:N]
call FlushStoredInteger( cs_cache, k, vk)
if (n>p) then
if (n>33) then
set vk=I2S(n)
set value=GetStoredInteger(cs_cache,k,vk)
call FlushStoredInteger(cs_cache,k,vk)
else
set value=GetArrayInt(poolid,n)
endif
call StoreInteger(cs_cache,k,"#"+I2S(value),p)
if (p>33) then
call StoreInteger(cs_cache,k,I2S(p),value)
else
call SetArrayInt(poolid,p,value)
endif
elseif (p>33) then
call FlushStoredInteger(cs_cache,k,I2S(p))
endif
call SetArrayInt( poolid,0,n - 1) //[0:integer:N]
endif
endfunction
//===================================================================================
function PoolGetItem takes integer poolid, integer itemn returns integer
if (itemn>33) then
return GetStoredInteger( cs_cache, I2S(-poolid), I2S(itemn))
endif
return GetArrayInt(poolid,itemn)
endfunction
//===================================================================================
function CountItemsInPool takes integer poolid returns integer
return GetArrayInt(poolid,0) //[0:integer:N]
endfunction
//===================================================================================
// Removed : GetEnumPoolItem , ForPool and ForPool2 they are much worse than just
// using CountItemsInPool and PoolGetItem to iterate the pool
//
//===================================================================================
function GetFirstOfPool takes integer poolid returns integer
return GetArrayInt(poolid,1) //[1 is just the first of the pool]
endfunction
//===================================================================================
function PoolPickRandomItem takes integer poolid returns integer
local integer p=GetRandomInt( 1, GetArrayInt(poolid,0) )
if (p>33) then
return GetStoredInteger( cs_cache, I2S(-poolid), I2S(p))
endif
return GetArrayInt(poolid,p)
endfunction
//===================================================================================
function GetItemPositionInPool takes integer poolid, integer it returns integer
return GetStoredInteger( cs_cache, I2S(-poolid), "#"+I2S(it))
endfunction
//===================================================================================
function IsItemInPool takes integer poolid, integer it returns boolean
return(HaveStoredInteger( cs_cache, I2S(-poolid), "#"+I2S(it)) )
endfunction
//===================================================================================
// This had to be optimized for speed, if it was just a loop using the above functions
// that would have been too slow to be worth keeping. That's a bad thing about JASS
// it is such an slow language that code reusability always has the cost of speed
//
function PoolAddPool takes integer sourcepoolid, integer destpoolid returns nothing
local integer a=1
local integer n=GetArrayInt( sourcepoolid,0) //[0:integer:N]
local integer dn=GetArrayInt( destpoolid,0) //[0:integer:N]
local string sk=I2S(-sourcepoolid)
local string k=I2S(-destpoolid)
local integer v
local string vk
loop
exitwhen (a>n)
if (a>33) then
set v=GetStoredInteger(cs_cache,sk,I2S(a))
else
set v=GetArrayInt(sourcepoolid,a)
endif
set vk="#"+I2S(v)
if not HaveStoredInteger(cs_cache,k,vk) then
set dn=dn+1
call StoreInteger(cs_cache,k,vk,dn)
if (dn>33) then
call StoreInteger(cs_cache,k,I2S(dn),v)
else
call SetArrayInt(destpoolid,dn,v)
endif
endif
set a=a+1
endloop
call SetArrayInt(destpoolid,0,dn) //[0:integer:N]
endfunction
//=============================================================================================
// Oh darn, After making PoolAddPool I don't feel like writting this one
// All right I am at least make the get code
//
function PoolRemovePool takes integer sourcepoolid, integer destpoolid returns nothing
local integer a=1
local integer n=GetArrayInt( sourcepoolid,0) //[0:integer:N]
local integer v
local string sk=I2S(-sourcepoolid)
loop
exitwhen a>n
if (a>33) then
set v=GetStoredInteger(cs_cache,sk,I2S(a) )
else
set v=GetArrayInt(sourcepoolid,a)
endif
call PoolRemoveItem( destpoolid, v)
set a=a+1
endloop
endfunction
//===================================================================================
// Adds a tokenized string to a pool,
// Example: PoolAddS(udg_p, "1;2;3;4") will add to the udg_p pool : 1,2,3 and 4
//
function PoolAddS takes integer poolid, string s returns nothing
local integer i=0
local integer st
local string c
set s=s+";"
set st=0
loop
set c=SubString(s, i, i+1)
exitwhen (c == "") or (c == null)
if (c == ";") then
call PoolAddItem( poolid, S2I(SubString(s, st, i)) )
set st=i+1
endif
set i=i+1
endloop
endfunction
//===================================================================================
// Converts a tokenized string into a pool,
// Example: S2Pool( "1;2;3;4") will return a pool that has 1,2,3 and 4 inside
//
function S2Pool takes string s returns integer
local integer spool= CreatePool()
call PoolAddS(spool,s)
return spool
endfunction
//===================================================================================
// Does the opposite of S2Pool, debugging is a good use for this function.
//
function Pool2S takes integer P returns string
local integer N=CountItemsInPool(P)
local integer i
local string s
if (N>=1) then
set s=I2S(PoolGetItem(P,1) )
set i=2
else
return ""
endif
loop
exitwhen (i>N)
set s=s+";"+I2S(PoolGetItem(P,i))
set i=i+1
endloop
return s
endfunction
//=============================================================================================================
// Fixes a lame bug by blizzard related to the custom script section (The condition of the if statement might
// actually be true.
//
function Pool_Percent takes nothing returns string
if ("%"=="") then
return "%%"
endif
return "%"
endfunction
function Pool_SetupCharMap takes nothing returns nothing
local string cm=".................................!.#$"+Pool_Percent()+"&'()*+,-./0123456789:;<=>.@ABCDEFGHIJKLMNOPQRSTUVWXYZ[.]^_`abcdefghijklmnopqrstuvwxyz{|}~..."
local integer i=0
local string c
if HaveStoredInteger(cs_cache,"charmap_upper","A") then
return
endif
loop
set c=SubString(cm,i,i+1)
exitwhen (c==null) or (c=="")
if (c!=".") then
if c==StringCase(c,true) then
call StoreInteger(cs_cache,"charmap_upper",c,i)
else
call StoreInteger(cs_cache,"charmap_lower",c,i)
endif
endif
set i=i+1
endloop
endfunction
function Pool_Rawcode2Int takes string s returns integer
local string c
local integer i=0
local integer r=0
loop
exitwhen i>3
set c=SubString(s,i,i+1)
set r=r*256
if c==StringCase(c,true) then
set r=r+GetStoredInteger(cs_cache,"charmap_upper",c)
else
set r=r+GetStoredInteger(cs_cache,"charmap_lower",c)
endif
set i=i+1
endloop
return r
endfunction
function PoolAddRawcodes_thread takes nothing returns nothing
//Threaded because I don't want it to halt execution for no reason
//
local string s=bj_lastPlayedMusic
local integer poolid=bj_groupEnumTypeId
local string c
local integer i=0
local integer st=0
set s=s+";"
loop
set c=SubString(s, i, i+1)
exitwhen (c == "") or (c == null)
if c == ";" then
call PoolAddItem(poolid, Pool_Rawcode2Int(SubString(s,st,i) ))
set st=i+1
endif
set i=i+1
endloop
endfunction
//=====================================================================================================================
// Adds a string of tokenized rawcodes to a pool
// Example: PoolAddRawcodes(udg_p,"A000;A001") will add 'A000' and 'A001' to the pool
//
// (Saves some lines, but is not as good efficiency wise)
//
function PoolAddRawcodes takes integer poolid, string s returns nothing
local string b=bj_lastPlayedMusic
set bj_groupEnumTypeId=poolid
set bj_lastPlayedMusic=s
call ExecuteFunc("PoolAddRawcodes_thread")
set bj_lastPlayedMusic=b
endfunction
//===================================================================================================================
// Converts a tokenized string of rawcodes into a pool,
// Example: Rawcodes2Pool( "A000;A001;AHbz;S000") will return a pool that has 'A000,'A001','AHbx' and 'S000' inside
//
// (Saves some lines, but is not as good efficiency wise)
//
function Rawcodes2Pool takes string s returns integer
local integer spool= CreatePool()
call PoolAddRawcodes(spool,s)
return spool
endfunction
//===================================================================================================================
// A subproduct of the Pool's Rawcode support is that we can easily have this function so I am including it even if
// it has nothing to do with data storage.
//
// takes "Aloc" and converts it into 'Aloc'
// it is different to the Pool_Rawcode2Int function in that it is safe to use it when it is the first CSCache
// function ever used. But it is a little slower (wc3mapoptimizer should make it as fast though)
//
function CS_Rawcode2Int takes string s returns integer
local string c
local integer i=0
local integer r=0
loop
exitwhen i>3
set c=SubString(s,i,i+1)
set r=r*256
if c==StringCase(c,true) then
set r=r+GetStoredInteger(cs_cache,"charmap_upper",c)
else
set r=r+GetStoredInteger(cs_cache,"charmap_lower",c)
endif
set i=i+1
endloop
return r
endfunction
endlibrary