[vJASS] HSAS (Handle Struct Attachment System)
HSAS (Handle Struct Attachment System)
Current Version: 3.60
Requires
- Decent JASS Knowledge (especially about structs)
- JassNewGen v5.a
Credits
- Vexorian/Cohadar with his help on the code
- Bob666 for benchmark system
HSAS (Handle Struct Attachment System) is a system similar to the original CSData system that directly attaches handles to arrays. However there have been modifications, HSAS uses the vJASS sized arrays to store handles, before going to Game Cache, it even allows you to create different databases for struct storage if you want to attach multiple structs to the same handle
The system just uses conventional arrays to store data. The maximum possible number of handles that can be used by the system is specified by a limit per database, however the actual maximum value can be lower, and it decreases as the map time increases (the bigger the gap between the handle indexes the lower the amount of structs you can store). It is however almost IMPOSSIBLE to go through all 3 arrays as long as
1. You are not being stupid
2. MOST CRUCIALLY remember to set your handles to null when you are finished with them. This recycles the index which makes a huge differences in the amount of data the system an store. If you set your handles to null most of the time then you will most likely never reach the limit
3. Someone enjoys playing your map for over a day straight
What is so good about the system, its the fastest way to attach structs to timers,proven by the benchmark system created by Bob666
Changelog
Code
Usage
Test Results
HSAS (Handle Struct Attachment System)
Current Version: 3.60
Requires
- Decent JASS Knowledge (especially about structs)
- JassNewGen v5.a
Credits
- Vexorian/Cohadar with his help on the code
- Bob666 for benchmark system
HSAS (Handle Struct Attachment System) is a system similar to the original CSData system that directly attaches handles to arrays. However there have been modifications, HSAS uses the vJASS sized arrays to store handles, before going to Game Cache, it even allows you to create different databases for struct storage if you want to attach multiple structs to the same handle
The system just uses conventional arrays to store data. The maximum possible number of handles that can be used by the system is specified by a limit per database, however the actual maximum value can be lower, and it decreases as the map time increases (the bigger the gap between the handle indexes the lower the amount of structs you can store). It is however almost IMPOSSIBLE to go through all 3 arrays as long as
1. You are not being stupid
2. MOST CRUCIALLY remember to set your handles to null when you are finished with them. This recycles the index which makes a huge differences in the amount of data the system an store. If you set your handles to null most of the time then you will most likely never reach the limit
3. Someone enjoys playing your map for over a day straight
What is so good about the system, its the fastest way to attach structs to timers,proven by the benchmark system created by Bob666
Changelog
v3.60 - Typos with HSAS debug mode and a bug with interger division has been fixed (thanks Themerion) v3.50 - With the addition of sized arrays in JassNewGen 5.a,you can now specify the limit for static libraries instead of having the fixed limit of 24570. You can also specify the limit for dynamic libraries. Please read the Usage trigger on information on how to deal with the changes v3.40 - Fixed an issue with HSAS not compiling due to a change in the PJASS syntax editor (for JassNewGen v4.x) - Added scope compatibility for HSAS to make it easier for HSAS to be used in libraries for external maps, please read the Usage trigger for more information - Added a compatibility trigger for ABC v5.1 - Updated Cohadar's ABC to v5.1 - Using Cohadars spell layout (thanks heaps) the HSAS trigger now has more information regarding the system, as well as a comparison between HSAS and ABC - Debug statements have been added to HSAS to notify users of current positions in indexes, as well as warning when upper limits are reached (means you are not nulling local/struct handles, naughty you) - Update bob666 benchmarking system to v1.10 (should give much more accurate results) v3.30 - The structure for static and dynamic databases has changed, please refer to the readme for more info v3.20 - Fixed a big bug, the firstindex was not always the firstindex so In some cases the data was lost when storing v3.10 - Removed the unnecessary decleration of some global arrays v3.00 - First release that has had no problems - Issues with the system not using gamecache properly when limit was exceeded is now fixed - Slight increase with efficiency - Another issue solved with conflict in gamecache storage with dynamic and static databases v2.30 - Problem with Dynamic struct attachment (fixed now) and the storage data tests now work properly - Dynamic Storage Test Added v2.20 - An extra test that fills up the system with values and retrieves them (to test stability). Thx again Cohadar v2.10 - The system now uses constants instead of arithmetic for further efficiency (thx Cohadar) v2.00 - Fixed dynamic methods of databasing for HSAS - Fixed loading screen and map description - All of the BenchMark globals (created by Bob666) transferred vJASS globals to remove udg's - A demo featuring the 'Crazy Ass Jumping Taurens' - You can now check between different tests - Added another test which uses array of handles (supposed to be a more accurate representation) v1.00 - Initial release |
Code
JASS:
|
Usage
Static VS Dynamic? What is the difference Simply put when using static databases you can only specify the identifier with functions. Identifiers cannot be linked with variables and cannot be changed during the map course i.e. to store using a static database you would do call AttachStruct<IDENTIFIER> , however that identifier has to be entered in the actual map code and you cannot enter in there a variable (for obvious reasons)Whats the advantage for static? Well its a lot faster then dynamic (and the fastest possible way to attach structs to handles), for most spells static will be more then enough as the IDENTIFIER will never change and you will not need to create a new one during the map course. If you need to attach more then one struct to a handle then you can create multiple static databases (since this will not change during the map course as well) Dynamic databases work similar to gamecache, unlike in static databases you can specify the identifier through an integer variable and you can change the amount of dynamic databases during map course. You can even make dynamic databases of databases and keep on going. They are slower then Static since they use dual arrays, and the amount of data they can store is a lot lower however they are more versatile Whats the advantage for dynamic? Dynamic databases are much more suited to storing data relating to handles in the form of structs. You can for example do AttacStructDynamic(handle, SpellId, struct) and you can make a specific database of spell id's. ********************************************** * * * CREATING STATIC HANDLE - STRUCT DATABASES * * * ********************************************** From v3.40 you can create static databases anywhere with any kind of scope. This can be really handy if you're releasing systems that will use HSAS. You can create local, public or global static databases depending on how you want to use them This is the format for the textmacro that creates the Static Database //! textmacro HSAS_Static takes IDENTIFIER, ARRAYSIZE, SCOPE What the textmacro requires is an identifier, array size and a scope The IDENTIFIER is used to refer to a specific struct, the IDENTIFIER can be any string that is not already a vJASS definined keyword (i.e. 1, ef, 234, a, ag are all valid identifiers). You can create as many different identifiers you want, and the purpouse for creating multiple static databases is if you want to attack more then ONE struct to a SINGLE handle (i.e. if you want to attach 2 structs to a single unit, you would need to create 2 static databases and use one database for one struct and another database for the other struct) NOTE: MAKE SURE THE IDENTIFIER ISNT "Dynamic" and THERE ARE NO WHITESPACES (i.e. " " will cause errors, please use underscores instead of spaces) The array size determines the size of the static database. In pre 3.50 versions of HSAS, the size was fixed at 24570, now you can specify any limit you want to. It is recommended you only use values from 30000-100000 depending on the size of the map and how much data the system is going to be storing. As with every other version of HSAS, if you go over this limit it will resort to gamecache (except that now you can specify the limit). This means that the system can now sustain more abuse then before, so you don't have to be so pedantic about handle leaks. NOTE: The maximum size is 409550, you should never ever have to have such a high array size The SCOPE determines the availability of the static database. For global static databases, that can be accessed anywhere use "" in your SCOPE prefix e.g. //! runtextmacro HSAS("1","32760","") . Note that global static databases don't have to declared in a library/scope where as public/private has to be declared in a library/scope. You can also create private static databases that can only be accessed inside the scope/library they are declared, in this case you use private" (i.e. //! runtextmacro HSAS("1","32760","private") . Finally you can create a public static database (e.g. //! runtextmacro HSAS("1","32760","public") , which like global can be accessed anywhere in the map, but in order to access the static database from OUTSIDE the scope/library you need to use the scope/library prefix followed by an underscore and then the function (this will be explained later)Note that if you create a global static database outside of a library don't expect it to be able to be accessed from anything thats compiled before that instance. You should always create global static databases inside a library The example this system uses does //! runtextmacro HSAS_Static("1","32760","") (as can be seen in the example)Then when you want to store/retrieve data you would do this (using the example of 1) call AttachStruct1(handle,struct) and call GetAttachedStruct1(handle) if you did 2 as your example you would do call AttachStruct2(handle,struct) and call GetAttachedStruct2(handle) Now if you created public static database and you are trying to access it from outside the library/scope that it was declared you need to use the library/scope name that the static database is created in as a prefix, followed by an underscore and then finally either the AttachStruct or GetStruct function e.g. assume that you created a public static database in a library called bob using //! runtextmacro HSAS("1","32760","public") . If you want to access the static database from inside the library bob then you would just use call AttachStruct1(handle,struct) , but if you want to access static library from OUTSIDE the library bob, you would need to do call bob_AttachStruct1(handle,struct) Thats it, there is no limit to the number of struct databases you can create, however dont go crazy, You should never need more then 2 or 3 *********************************************** * * * CREATING DYNAMIC HANDLE - STRUCT DATABASES * * * *********************************************** This is the format for the textmacro that creates the Dynaimc Database //! textmacro HSAS_Dynamic takes LIMIT1, INSTANCE, LIMIT2, SCOPE You need to specify limits for both the amount of handles you will attach and the number of databases for each handle as well as the scope of the function. The maximum limit is 1000, maximum instance is 409550 and the maximum for limit 2 is 1000. The first limit is the number of handles and the second is the number of databases. When you have decided the limits, put. If your limit is too high however due to the nature of dynamic arrays there will be problems with multi-instancability (refer to vJASS manual) As mentioned you also need to specify the scope of the dynamic database, this works in the exact same way as static databases so if you need help then just refer to the static databases section NOTE: You only need do this ONCE, as you can see in the System trigger it is already there so when you copy it into your map you only need to change the limits (if you wish to do so) Now to attach a handle to a struct you would do call AttachStructDynamic(handle, integer database, struct) and to retrieve the struct you would do call GetAttachedStructDynamic(handle, integer database) The integer database can be any integer, it is used similar to gamecache *********************************************** * * * CLEANING UP HANDLE INDEXES * * * *********************************************** I thought I would include this since the system is reliant on the handle index stack. As mentioned so many times before, it is absolutely that you, as much as is feasible, that you clean up handle index leaks. This is done by simply setting LOCAL handles to null when you are done using them, for example if we have a look at the Crazy Ass Jump spell included in the map, you will notice in this function
JASS:
set t= null , this needs to be done for every local handle that is created, you must null them once you are finished using them (convention is that this is done at the end of the function). You also need to do this with structs that contain any members that are handles using the onDestroy method. Yet again if we have a look at the same Crazy Ass Jump trigger in the jump struct
JASS:
method onDestroy which sets this.u to null (this.u refers to the current instance of the unit u). This is also quite important, because people also don't realise that each created struct leaks as many index's as you have members that are handles and if you dont clean them up it all adds up. You can also clean up multiple handles in the onDestroy if your struct has more then one handle for membersNote that you don't have to get fanatic and make 100% sure that EVERY handle index is cleaned up (in most cases this is not possible in more complicated maps). Now that you can specify the limit for both static and dynamic databases, you most likely will never go over the limit. The biggest area of concern is just mainly timers/loops, as they have the capability to leak handles incredibly fast. If you have a few leaks here and there that only happen once, then nothing is going to happen however if you have a spell that leaks 500 handles every cast because it uses a timer (very common situation) then after 50 casts of that spell, there goes a massive 25000 index's, if it goes above the limit the system will start using slow gamecache In version 3.40 you can use Debug Mode to monitor the index handles. If you see that your handle index's are slowly increasing without any spells being cast or any user input it means that you are leaking somewhere. Run this map in debug mode and run the Crazzy Ass Jump test to have a look at what should be happening. Although the handles increase/decrease continuously, they never go above a maximum number, this is what should be hapenning in your map |
Test Results
Attaching and Retrieving Structs through a single handle (timer) HSAS = 75.497 Executions per millisecond ABC = 63.515 Executions per millisecond HAIL = 68.283 Executions per millisecond GC = 62.512 Executions per millisecond Attaching and Retrieving Structs through a multiple handles stored in an array (timer) HSAS = 90.939 Executions per millisecond ABC = 63.717 Executions per millisecond HAIL = 68.510 Executions per millisecond GC = 64.526 Executions per millisecond |
Attachments
Last edited: