Difference between revisions of "Data Tables"
Line 102: | Line 102: | ||
=== See Also === | === See Also === | ||
http://www.sc2mod.com/board/index.php?page=Thread&threadID=599 | http://www.sc2mod.com/board/index.php?page=Thread&threadID=599 | ||
− | [[Category:StarCraft II]][[Category: | + | [[Category:StarCraft II]][[Category:GalaxyScript]][[Category:Reference]] |
Revision as of 01:56, 23 November 2010
In StarCraft II, we have a magical container for every single native type. It is called a Data Table, and it is quite like Warcraft 3's hashtable, or a Java Hashmap<String, Object>.
The differences between it and Warcraft 3's hashtable are simple: instead of two integers to save a value, you save it within one string, and in StarCraft II, there are only ever two data tables around at a time. In addition, StarCraft II Data Tables are a lot slower.
The two data tables in use at a time in StarCraft II are the local data table and the global data table.
The difference was not immediately obvious (although you can guess).
Local Data Table
The local data table is a data table that is created for every thread. You get a new local data table whenever a trigger event fires, for example. The data table is cleared when the thread ends. The data table is available inside functions you call. This can be used a way of passing multiple parameters to a function. Just save the argument count into the table and grab them inside the function.
Want to test this? Here are is simple GUI trigger: [galaxy] Untitled Action 001
Options: Action Return Type: (None) Parameters Grammar Text: Untitled Action 001() Hint Text: (None) Custom Script Code Local Variables Actions General - If (Conditions) then do (Actions) else do (Actions) If ("0" value exists in the Local data table) == true Then Debug - Display ("It exists inside the function! Magi..." + (Text(("0" from the Local data table)))) as debug output using Type 1, and Do display it in the game window Else Debug - Display "Alack! For it does not exist inside..." as debug output using Type 1, and Do display it in the game window
Untitled Trigger 001
Events Timer - Every 1.0 seconds of Game Time Local Variables Conditions Actions Untitled Action 001() General - If (Conditions) then do (Actions) else do (Actions) If ("0" value exists in the Local data table) == true Then Debug - Display "It exists! Magical." as debug output using Type 1, and Do display it in the game window Else Debug - Display "Alack! For it does not exist!" as debug output using Type 1, and Do display it in the game window Variable - Modify Count: + 1 Data Table - Save Count as "0" in the Local data table Untitled Action 001() Debug - Display "--------------------------" as debug output using Type 1, and Do display it in the game
[/galaxy]
The output is:
[galaxy] Alack! For it does not exist inside the function! Alack! For it does not exist! It exists inside the function! Magical! Its value is: 2
(Note: this is when the trigger fires again after a second)
Alack! For it does not exist inside the function! Alack! For it does not exist! It exists inside the function! Magical! Its value is: 3 [/galaxy]
Other uses than passing parameters to functions include "dynamically sized" arrays that clean themselves up. You cannot permanently save data, however.
Global Data Table
The global data table, as should be obvious when you know what the local data table does, saves data permanently. It is never cleaned out unless you yourself do it. It is created on map init and destroyed when the map ends.
You must manually clean up data if you are no longer using it.
Using Them Safely
If you attempt to use a data table as an array, you should realize a problem very quickly. If you want to have two "arrays", you need to prefix the strings you use to access data in order to not overwrite the other one. This is both annoying and time consuming.
I have made a wrapper class and put in the standard library to deal with this. (Note: geX, you honestly need to allow overloading of functions with the same parameters but different return types. I want to be able to do int func() and bool func() like C# allows me to do. This is kind of a missing feature. Also, being able to use the word "get" as a method name would be awesome.
The reason this tutorial was made, despite the obviousness (well, except for what local data tables do) of the difference between local and global is so I can tell people that they are causing huge memory leaks if they create one of my wrappers as a global table and do not manually delete the elements when they are done with the table. For efficiency, my wrapper class will not be saving a record of everything you put into the table and will not delete the elements you put in when you delete the wrapper.
This wrapper class should be the standard class used for dynamically sized arrays of generic types until geX adds auto boxing/unboxing for wrapper classes, and even then it will likely still have its uses. It will, for example, always be more efficient than an Andromeda Hashmap<String, x>.
a.util.DataTable
Using the wrapper class is simple. Make sure to add an import statement so you actually can USE it.
[galaxy] import a.util.DataTable; [/galaxy]
Anywhere in a function where you want to use a dynamically sized array (heck, you dont even have to specify the size), simply pretend it is a Java ArrayList. Note: The class does not support use of a foreach loop. It does not keep track of entries. A DataTableArray class will be added to deal with this later.
[galaxy] DataTable dt = new DataTable(DataTable.LOCAL); //DataTable.GLOBAL also works. However, it will cause a memory leak unless you manually delete the elements within. Use local in all cases you are not permanently storing an array into a global varaible. for (int i = 0; i < 100; i++) {
dt.put(i, "this is string " + i); //Note, overloaded methods. put takes parameters like so: (key, value) where key is a string or int, and value is any native type in Galaxy. If you are using a not-quite-int, you should use put<name>(key, int). ie, if using a cinematic, use putCinematic(5, intValueGoesHere);. dt.put((string)i, "this is a string! and we overwrote the last thing."); //Note: ints are just casted to strings internally. The keys 5 and "5" refer to the same value. Ints are overloaded specifically so you can pretend it is an array. string b = dt.getString(i); //You have to use get<varType>() because Andromeda does not support Objects or overloaded return types (I don't blame Andromeda either for that, but it would be nice to have) System.debug(b); //This will cause the game to output "this is a string and we overwrote the last thing." 100 times.
} [/galaxy]
See Also
http://www.sc2mod.com/board/index.php?page=Thread&threadID=599