1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[Trigger] Hashtable

Discussion in 'Triggers & Scripts' started by GywGod133, Apr 29, 2014.

  1. GywGod133

    GywGod133

    Joined:
    Jul 16, 2012
    Messages:
    646
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    :ogre_datass: Hello Guyz.. can you make me a very simple Hashtable with Good Documentation :ogre_hurrhurr: cuz i want to learn Hashtable to create a Spell :ogre_datass:

    Yeah!
     
  2. Rybak

    Rybak

    Joined:
    Oct 1, 2012
    Messages:
    166
    Resources:
    0
    Resources:
    0
    Well, I'm guessing you'd prefer it in GUI, so here it goes, the simples hash ever.

    • Hashtables
      • Events
        • Map initialization
      • Conditions
      • Actions
        • Hashtable - Create a hashtable
        • Set Hash = (Last created hashtable)
        • Hashtable - Save 4 as 1 of 1 in Hash
        • Hashtable - Save 15.00 as 1 of 1 in Hash
        • Unit - Create (Load 1 of 1 from Hash) Footman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
        • Unit - Set life of (Last created unit) to (Load 1 of 1 from Hash)


    Hashtables, as I always understood them (and it serves me quite well to this day) are two dimensional coordinate systems with X and Y values always being integers. To put more simply - these are tables of values with coordinates (x, y).

    You can save numeric values to every "coordinate", but you can also save other values (like a player), but I myself always prefer doing it only with numbers, as once I tried saving ability handle and the game crashed (yet that might be because of the fact, that abilities are recognized by the game as integers and it is a little messed up, but whatever, that's not the case).

    Anywho... as seen in this trigger, first thing you have to do is create a hashtable. Then you save it as a variable with your chosen name. You can of course make it shorter like this:

    • Custom script: set udg_Hash = InitHashtableBJ()


    But this doesn't really matter as this won't make any problems anyhow.

    After you made a hashtable, you can freely save some values to it. You can save integers, reals, strings and handles (the last being, say, units or players, as I mentioned above, but not abilities or items, as these are, in fact, integers).

    As you see, I saved integer "4" to X=1 and Y=1 in the Hash hashtable. I also saved real "15.00" to X=1 and Y=1 in the same hashtable. These do not collide with one another as they are of a different type.

    So, these are the basics. If you want to make a spell with hashtables, then you must remember, that you can't save an ability to the hastable, you can save it's code. If you want to get to know what is the ability's code, go to Ability Editor and push CTRL+D. Then, next to every ability's name, you will see it's code (like A000 for your first custom-made ability or Aply for polimorph). Same goes with items, buffs, unit types, etc.

    And one more thing: A000 is an integer in ASCII, you put it in the hastable as 'A000' like that:

    • Custom script: SaveIntegerBJ('A000', 1, 1, udg_Hash)


    I really didn't know what to tell you, so I guess that's what you wanted.

    Cheers
     
  3. Nichilus

    Nichilus

    Joined:
    Sep 26, 2009
    Messages:
    1,968
    Resources:
    0
    Resources:
    0
    Hashtables are a way to save something under 2 different "indexes", where the order of both indexes is important. E.g. saving something under "[2][1]" is not same as saving it under "[1][2]" - you'll see why below.

    Hashtables can (or rather: "should") save practically everything you would need to save in WCIII, but afaik it's not the case - just like Rybak pointed out, some actions are buggy... like Ability that will crash your WE.

    Note:
    These actions are buggy all the time, so if they crash your WE once, they will do that every time -> actions that do not do that are not buggy.
    So if you're not sure what actions are buggy, you can open empty map and try that action yourself.



    #1 - Creating Hashtable
    First, you have to create hashtable. This actions should be done only once per hashtable - meaning if you use Hashtable for spell, then creating new Hashtable every time someone casts the spell is a big NO NO.
    Also there's limit for maximum number of hashtables. I thinks it's somewhere around ~250, however if you generalize and correctly use hashtables, then you won't need many (better said: you may use 1 hashtables for multiple systems).

    Here's how you create hashtable:
    • Actions
      • Hashtable - Create a hashtable
      • Set Hashtable_var = (Last created hashtable)

    Or you can use the action rybak wrote:
    • Custom script: set udg_Hashtable_variable_name = InitHashtableBJ()

    (although there may be better way to create it through custom script, as many BJ functions are replaced by better ones in JASS).

    Indexes for hashtable can either be integers or strings.
    Note
    For hashtable, the indexes are actually called 'Keys', so if you are to specify what 'Key' you want to use, then it means that it wants the *index*... however 'Key' is more appropriate, as the word "index" implies that it will be *integer* number, while "Key" can stand for "Key word", which is true for both Integers and strings.



    #2 - Saving/Loading data in/from Hashtable
    This is how you save things in hashtable:
    • Hashtable - Save *something* as *Child* of *Parent* in *Hashtable name*

    Of course, you have to specify what you want to save - if you want to save number without decimal point, choose the "Save integer" option; if you want to save True/False statement, use "Save Boolean" option, etc.
    E.g. I will save Boolean value 'TRUE' into hashtable.
    • Hashtable - Save True as 1 of 2 in Hashtable_var



    Loading value from hashtable is done in 2 ways:
    a) By loading that value into variable/array
    • Set Boolean_var = (Load 1 of 2 from Hashtable_var)
    • Set Boolean_arr[1] = (Load 1 of 2 from Hashtable_var)

    Now the variable "Boolean_var" and array "Boolean_arr[1]" will contain the value you saved into hashtable under parent '2' and child '1'.

    b) By loading the value directly - you simply load the value immediately when you are using it, instead of loading it into variable first and then using the variable. However I find most of the time that it doesn't work - it returns the default value of said variable type.

    If the stuff you want to load is not in the hashtable, then it will return:
    a) NULL - this is the case of all Handles (e.g. units, points, player groups, etc.)
    b) default value - that's the case of non-handles (e.g. integers, booleans, reals, ...)

    Now why are the indexes important. First let's look again how the command for saving anything into hashtable looks:
    • Hashtable - Save *something* as *Child* of *Parent* in *Hashtable name*

    Notice the *Child* and *Parent* - these are the keys that can be either integers or strings.


    #3 - How saving/loading works
    This is how hashtable would look if you were to visualize it:
    Hashtable pic
    [​IMG]
    When you save stuff into hashtable, the game first finds the Hashtable name you used (= hashtable variable you used), then it finds the Parent key and then it finds the Child key. Then it saves that information into correct variable type it represents (e.g. Boolean won't be saved as integer, but as boolean).

    The same goes for loading. The game first finds the Parent, then it finds the Child and lastly, it finds the correct variable type.
    What does this imply: You can save different variable types under same Child of same Parent of same Hashtable.
    If you do this:
    • Actions
      • Hashtable - Save 10.00 as 1 of 1 in Hashtable_var
      • Hashtable - Save 55 as 1 of 1 in Hashtable_var
      • Hashtable - Save True as 1 of 1 in Hashtable_var
      • Hashtable - Save stringXYZ as 1 of 1 in Hashtable_var

    And then you load... e.g. integer from child '1' of parent '1' from Hashtable_var, then it will correctly return number 55 as that value was saved as an integer in 1 of 1 in Hashtable_var.

    Of course, doing this:
    • Actions
      • Hashtable - Save 10.00 as 1 of 1 in Hashtable_var
      • Hashtable - Save 35.49 as 1 of 1 in Hashtable_var

    and then loading "real" from child '1' of parent '1' in Hashtable_var will return number 35.49, because into variable type "real" was first saved number 10.00 and then that number was replaced by number 35.49 as that is real as well.


    Example of how it would work in some situation:
    Example step by step

    1) We have empty Hashtable called Hash for simplicity

    2) We save 'True' as 'Forgive' of 'FirstTick' in Hash
    Step 2
    [​IMG]

    This is nothing special. Since there was no such parent, then there could be no such children and so we saved data into unique "layer" in the hashtable - we don't have to worry about rewriting anything.
    3) We save 'True' as 'Forgive' of 'Resistance' in Hash
    Step 3
    [​IMG]

    As you can see, even though the child's name is same as in step #2, we save it under different parent, hence these two won't collide with each other.
    4) We save integer number 5 as 'Forgive' of 'FirstTick' in Hash
    Step 4
    [​IMG]

    Notice:
    We saved different variable type, which is why our Boolean value was not rewritten. If I were to save another Boolean value here, the current one would be removed/replaced.
    5) Now some time later, after we have saved many things inside our hashtable we want to load something
    Step 5
    [​IMG]

    The picture shows how we found the real value saved as Magic of Resistance in Hash.

    The order at which is the hashtable searched through is shown by numbers. First the parent in said hashtable is searched for (1). If it has been found, it goes through that parent's childs and searches for the one we want (2). If it did find the child, it searches for the correct variable type (3). If correct variable type has been found, it gives us that value.

    In case the seach was stopped in any step, it returns us default or null value.


    Now we no longer have any need for some/all data in hashtable and we want go get rid of them.
    There are two types of clearing hashtables. By now it should be obvious what type of clearing does what, but let's write it just to be sure:
    Clearing Hashtables

    1) Clearing Child
    As the name suggests, we clear all data related to Parent in Hashtable - it removes all Childs of specified Parent.
    Example: We remove all children of Parent "Resistance" from Hash:
    Pic
    • Hashtable - Clear all child hashtables of child (Key Resistance) in Hash


    [​IMG]

    As you can see, all the children related to parent "Resistance" are removed. Because parent "Resistance" contains no children after that action, it can no longer contain any data and so we can consider it an empty parent.
    Since we cleared only parent "Resistance", this action did not affect the "FirstTick" parent and so its data remain unaffected.
    2) Clearing Parent
    This completely clears the hashtable, as all parents are removed. I don't think it needs any more explanation than this.


    A very important note:
    Clearing Hashtable does not mean it removes leaks. It only means that it will return NULL or default value.
    If you save point (= location) into hashtable, that location is not assigned to any variable and you clear the hashtable, then you will get memory leak, because you lose all reference to that location.

    So how do you clear leaks? Well, it's the same way you remove them normally.
    Clearing leaks

    Let's say that in child '1' of parent '1' in hashtable Hashtable_var you saved location. This is how you remove it:
    • Set loc = (Load 1 of 1 in Hashtable_var)
    • Custom script: call RemoveLocation(udg_loc)


    Now, since the possible memory leak has been removed, you can clear the hashtable (or at least the child) without any fear of having memory leaks.



    #4 - Using handle ID
    Handle IDs are great way to use the identification number of any handle as either Parent, Child or even the value itself.
    The ID is always an integer number and every handle has it.

    What are handles?
    Well, they're basically everything in WCIII -> units are handles, player groups are handles, players, points, unit groups, special effects, etc. etc. are all handles.
    What isn't a handle: integers, real numbers, boolean values (there may be some more than this, but there are the most commonly used ones).

    Each handle upon creation has its unique ID. Example situation:
    You have 2 Footman units in map. Even though they are same unit-types, they are each different handle - different object. Hence they have different handle IDs.

    You could say that Handle ID is the same as unit indexing systems found on hive (these assign unique number for each unit in map), but handle ID is not limited to units only.
    However the ID is quite a large ingeted number (around 7 or more digits) so they can't be used as indexes for variables... however they can be used as Child or Parent keys for hashtables.

    Getting handle ID of a unit is in GUI usually done this way:
    • Set int = (Key (Triggering unit))

    this will assing the handle ID of (Triggering unit) into the integer variable "int".

    However there is a better way:
    • Custom script: set udg_Integer_variable_name = GetHandleId(handle)

    This approach is better because you are not limited to function calls like "Triggering unit" or "Last created special effect" etc. -> you can also use variables, which makes the whole process faster and simpler.

    Example: I will assign the handle id into integer variable called IntID by using:
    a) function call:
    • Custom script: set udg_IntId = GetHandleId(GetTriggerUnit())

    GetTriggerUnit() is the jass equivalent for GUI's (Triggering Unit)

    b) variables:
    • Set unit_var = (Triggering unit)
    • Custom script: set udg_IntId = GetHandleId(udg_unit_var)



    Well, let's say I want to save "Target point of (ability being cast)" into Hashtable for some MUI spell. Since the spell is MUI, I cannot save it under some predetermined keys (e.g.: "... as 1 of 1 in Hash"), as it would replace the current point that is being saved there. That's where the Handle ID comes in - using Handle ID as e.g. Child key will save that location into unique layer. That location will be replaced only if the SAME unit casts the spell again.
    • Custom script: set udg_ID = GetHandleId(GetTriggerUnit())
    • Hashtable - Save Handle Of(Target point of ability being cast) as (Key Spell_TargetPoint) of ID in Hashtable_var


    Do note: If you are using Handle ID as a parent of some Hashtable and into that very same Hashtable you save other important things related to that unit (so you save them under handle ID as well), then clearing all children of Parent (Handle ID) from that hashtable will not be the best idea, because you will loose all children - that includes those that still hold meaningful and important data of that unit.


    I think I covered all important parts of hashtables.

    Hope it helps :thumbs_up:



    Attachments:
     

    Attached Files:

    Last edited: Apr 30, 2014
  4. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,181
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    @Nichilus, great man to write all this. :thumbs_up:

    With "InitHashtableBJ()" you were right, just remove the BJ and the result will be the same here, but faster.

    What do you mean with strings as index?

    Your "Hashtable pic" is not working.

    And to this Save Ability bug. It bugs in gui, yes, but you can save abilities with jass/custom script. You can save their raw-code as integers, just like you can do with unit types.
     
  5. Nichilus

    Nichilus

    Joined:
    Sep 26, 2009
    Messages:
    1,968
    Resources:
    0
    Resources:
    0
    indexes are the keys... I wrote it a little bit further down, but I moved it up - right below the statement you quoted as it makes more sense to place it there.

    the pic has been reuploaded. Seems the attachment got lost when I attached another pics.
     
  6. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,855
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    just to elaborate a bit more, all handles share the same "space", so storing unit at the place where location was stored will overwrite it

    You can have either 255 or 256 initialized hashtables per map(I tested this earlier, but I forgot haha).

    Please elaborate on "integers or strings". Strings are used in
    gamecache
    .

    Simple check from common.j:


    native  SaveInteger takes hashtable table, integer parentKey, integer childKey, integer value returns nothing


    oddly enough,
    Code (vJASS):

    scope s initializer i
       
        private function i takes nothing returns nothing
            local hashtable h = InitHashtable()
            local string s = LoadStr(h, 0, 0)
            if s == null then
                call BJDebugMsg("null")
            elseif s == "" then
                call BJDebugMsg("\"\"")
            endif
        endfunction
       
    endscope
     


    shows that even strings will return null not ""


    this does not remove all "leaks", because you still leak handleId of the location, but is good enough in most cases

    Neither string nor code(this is really nonexistant in GUI, I agree) are handles as well

    This is also required way if you use JNGP, because it has no "Key" function in GUI

    This should cover all up. Other than this, the post is almost worth a Tutorial submission
     
  7. Ceday

    Ceday

    Joined:
    Feb 22, 2010
    Messages:
    1,063
    Resources:
    0
    Resources:
    0
    Lol nichilus this is not tutorial section.
     
  8. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,611
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    @Ceday

    true indeed :p there's like 3 hashtable tutorials there anyway.
     
  9. Geshishouhu

    Geshishouhu

    Joined:
    Oct 11, 2012
    Messages:
    710
    Resources:
    1
    Spells:
    1
    Resources:
    1
    @Nichilus, nice tutorial. LOL