• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Lua] Hashtable2Table

This is a Lua Code that overwrittes the native hashtable functions. The result is, replacing hashtables with Lua tables innerly. I wrote this for variable Debugger so I could handle hashtable content the same way as all other variables. But it could be useful outside of it.
Lua:
do --"Converts" Hashtables into Lua Tables
  HashtableAsTable = {}
 
  function InitHashtable()
    return {}
  end
 
  HashtableAsTable.CreateNeededTables = function(table, parentKey, childKey)
    if not table[parentKey] then table[parentKey] = {} end
    if not table[parentKey][childKey] then table[parentKey][childKey] = {} end
  end

  HashtableAsTable.DoNeededTablesExist = function(table, parentKey, childKey)
    if not table then return false end
    if not table[parentKey] then return false end
    if childKey and not table[parentKey][childKey] then return false end
    return true
  end
 
  function SaveInteger(table, parentKey, childKey, value)
    HashtableAsTable.CreateNeededTables(table, parentKey, childKey)
    table[parentKey][childKey].Integer = value
    return true
  end

  function SaveReal(table, parentKey, childKey, value)
    HashtableAsTable.CreateNeededTables(table, parentKey, childKey)
    table[parentKey][childKey].Real = value
    return true
  end

  function SaveBoolean(table, parentKey, childKey, value)
    HashtableAsTable.CreateNeededTables(table, parentKey, childKey)
    table[parentKey][childKey].Boolean = value
    return true
  end

  function SaveStr(table, parentKey, childKey, value)
    HashtableAsTable.CreateNeededTables(table, parentKey, childKey)
    table[parentKey][childKey].String = value
    return true
  end

  function HashtableAsTable.SaveHandle(table, parentKey, childKey, handle)
    HashtableAsTable.CreateNeededTables(table, parentKey, childKey)
    table[parentKey][childKey].Handle = handle
    return true
  end
  --change the executive code of Save Handle functions
 
SavePlayerHandle = HashtableAsTable.SaveHandle
SaveWidgetHandle = HashtableAsTable.SaveHandle
  SaveDestructableHandle = HashtableAsTable.SaveHandle
  SaveItemHandle = HashtableAsTable.SaveHandle
  SaveUnitHandle = HashtableAsTable.SaveHandle
  SaveAbilityHandle = HashtableAsTable.SaveHandle
  SaveTimerHandle = HashtableAsTable.SaveHandle
  SaveTriggerHandle = HashtableAsTable.SaveHandle
  SaveTriggerConditionHandle = HashtableAsTable.SaveHandle
  SaveTriggerActionHandle = HashtableAsTable.SaveHandle
  SaveTriggerEventHandle = HashtableAsTable.SaveHandle
  SaveForceHandle = HashtableAsTable.SaveHandle
  SaveGroupHandle = HashtableAsTable.SaveHandle
  SaveLocationHandle = HashtableAsTable.SaveHandle
  SaveRectHandle = HashtableAsTable.SaveHandle
  SaveBooleanExprHandle = HashtableAsTable.SaveHandle
  SaveSoundHandle = HashtableAsTable.SaveHandle
  SaveEffectHandle = HashtableAsTable.SaveHandle
  SaveUnitPoolHandle = HashtableAsTable.SaveHandle
  SaveItemPoolHandle = HashtableAsTable.SaveHandle
  SaveQuestHandle = HashtableAsTable.SaveHandle
  SaveQuestItemHandle = HashtableAsTable.SaveHandle
  SaveDefeatConditionHandle = HashtableAsTable.SaveHandle
  SaveTimerDialogHandle = HashtableAsTable.SaveHandle
  SaveLeaderboardHandle = HashtableAsTable.SaveHandle
  SaveMultiboardHandle = HashtableAsTable.SaveHandle
  SaveMultiboardItemHandle = HashtableAsTable.SaveHandle
  SaveTrackableHandle = HashtableAsTable.SaveHandle
  SaveDialogHandle = HashtableAsTable.SaveHandle
  SaveButtonHandle = HashtableAsTable.SaveHandle
  SaveTextTagHandle = HashtableAsTable.SaveHandle
  SaveLightningHandle = HashtableAsTable.SaveHandle
  SaveImageHandle = HashtableAsTable.SaveHandle
  SaveUbersplatHandle = HashtableAsTable.SaveHandle
  SaveRegionHandle = HashtableAsTable.SaveHandle
  SaveFogStateHandle = HashtableAsTable.SaveHandle
  SaveFogModifierHandle = HashtableAsTable.SaveHandle
  SaveAgentHandle = HashtableAsTable.SaveHandle
  SaveHashtableHandle = HashtableAsTable.SaveHandle
  SaveFrameHandle = HashtableAsTable.SaveHandle

  function HashtableHaveSavedData(table, parentKey, childKey, name)
    return HashtableAsTable.DoNeededTablesExist(table, parentKey, childKey) and table[parentKey][childKey][name]
  end
  function HaveSavedInteger(table, parentKey, childKey)
    return HashtableHaveSavedData(table, parentKey, childKey, "Integer")
  end
  function HaveSavedReal(table, parentKey, childKey)
    return HashtableHaveSavedData(table, parentKey, childKey, "Real")
  end
  function HaveSavedBoolean(table, parentKey, childKey)
    return HashtableHaveSavedData(table, parentKey, childKey, "Boolean")
  end
  function HaveSavedString(table, parentKey, childKey)
    return HashtableHaveSavedData(table, parentKey, childKey, "String")
  end
  function HaveSavedHandle(table, parentKey, childKey)
    return HashtableHaveSavedData(table, parentKey, childKey, "Handle")
  end


  function LoadInteger(table, parentKey, childKey)
    if HashtableAsTable.DoNeededTablesExist(table, parentKey, childKey) and table[parentKey][childKey].Integer ~= nil then
      return table[parentKey][childKey].Integer
    else
      return 0
    end
  end

  function LoadReal(table, parentKey, childKey)
    if HashtableAsTable.DoNeededTablesExist(table, parentKey, childKey) and table[parentKey][childKey].Real ~= nil then
      return table[parentKey][childKey].Real
    else
      return 0.0
    end
  end

  function LoadBoolean(table, parentKey, childKey)
    if HashtableAsTable.DoNeededTablesExist(table, parentKey, childKey) and table[parentKey][childKey].Boolean ~= nil then
      return table[parentKey][childKey].Boolean
    else
      return false
    end
  end

  function LoadStr(table, parentKey, childKey)
    if HashtableAsTable.DoNeededTablesExist(table, parentKey, childKey) and table[parentKey][childKey].String ~= nil then
      return table[parentKey][childKey].String
    else
      return ""
    end
  end

  function HashtableLoadHandle(table, parentKey, childKey)
    if HashtableAsTable.DoNeededTablesExist(table, parentKey, childKey) and table[parentKey][childKey].Handle ~= nil then
      return table[parentKey][childKey].Handle
    else
      return nil
    end
  end
  --change the executive code of Load Handle functions
  LoadPlayerHandle = HashtableLoadHandle
  LoadWidgetHandle = HashtableLoadHandle
  LoadDestructableHandle = HashtableLoadHandle
  LoadItemHandle = HashtableLoadHandle
  LoadUnitHandle = HashtableLoadHandle
  LoadAbilityHandle = HashtableLoadHandle
  LoadTimerHandle = HashtableLoadHandle
  LoadTriggerHandle = HashtableLoadHandle
  LoadTriggerConditionHandle = HashtableLoadHandle
  LoadTriggerActionHandle = HashtableLoadHandle
  LoadTriggerEventHandle = HashtableLoadHandle
  LoadForceHandle = HashtableLoadHandle
  LoadGroupHandle = HashtableLoadHandle
  LoadLocationHandle = HashtableLoadHandle
  LoadRectHandle = HashtableLoadHandle
  LoadBooleanExprHandle = HashtableLoadHandle
  LoadSoundHandle = HashtableLoadHandle
  LoadEffectHandle = HashtableLoadHandle
  LoadUnitPoolHandle = HashtableLoadHandle
  LoadItemPoolHandle = HashtableLoadHandle
  LoadQuestHandle = HashtableLoadHandle
  LoadQuestItemHandle = HashtableLoadHandle
  LoadDefeatConditionHandle = HashtableLoadHandle
  LoadTimerDialogHandle = HashtableLoadHandle
  LoadLeaderboardHandle = HashtableLoadHandle
  LoadMultiboardHandle = HashtableLoadHandle
  LoadMultiboardItemHandle = HashtableLoadHandle
  LoadTrackableHandle = HashtableLoadHandle
  LoadDialogHandle = HashtableLoadHandle
  LoadButtonHandle = HashtableLoadHandle
  LoadTextTagHandle = HashtableLoadHandle
  LoadLightningHandle = HashtableLoadHandle
  LoadImageHandle = HashtableLoadHandle
  LoadUbersplatHandle = HashtableLoadHandle
  LoadRegionHandle = HashtableLoadHandle
  LoadFogStateHandle = HashtableLoadHandle
  LoadFogModifierHandle = HashtableLoadHandle
  LoadAgentHandle = HashtableLoadHandle
  LoadHashtableHandle = HashtableLoadHandle
  LoadFrameHandle = HashtableLoadHandle


  function RemoveSavedInteger(table, parentKey, childKey)
    if HashtableAsTable.DoNeededTablesExist(table, parentKey, childKey) then
      table[parentKey][childKey].Integer = nil
    end
  end

  function RemoveSavedReal(table, parentKey, childKey)
    if HashtableAsTable.DoNeededTablesExist(table, parentKey, childKey) then
      table[parentKey][childKey].Real = nil
    end
  end

  function RemoveSavedBoolean(table, parentKey, childKey)
    if HashtableAsTable.DoNeededTablesExist(table, parentKey, childKey) then
      table[parentKey][childKey].Boolean = nil
    end
  end

  function RemoveSavedString(table, parentKey, childKey)
    if HashtableAsTable.DoNeededTablesExist(table, parentKey, childKey) then
      table[parentKey][childKey].String = nil
    end
  end

  function RemoveSavedHandle(table, parentKey, childKey)
    if HashtableAsTable.DoNeededTablesExist(table, parentKey, childKey) then
      table[parentKey][childKey].Handle = nil
    end
  end
 
  function FlushParentHashtable(table)
    for key in pairs(table)
    do
      table[key] = nil
    end
  end

  function FlushChildHashtable(table, parentKey)
    if HashtableAsTable.DoNeededTablesExist(table, parentKey) then
      table[parentKey] = nil
    end
  end
end
ChangeLog: Added Missing functions
SaveEffectHandle
SaveTriggerConditionHandle
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,467
I think the API could use some re-evaluation, as it seems unnecessary to have to write "HashtableAsTable" with so many of these calls.

I feel like there is a lot of overlap with this resource and the newer [Lua] - Multidimensional Table. The proof-of-concept Lua Table featured here seems to do the trick at replaceing hashtables in Lua, without using the ugly hashtable API itself.

I don't see your Variable Debugger as a Submission here, although it looks extremely comprehensive. Could you please submit that one here (or whatever newest version you feel is more up-to-date)?
 
I didn't wanted to create it as API. It is more a hook to replace the Hashtable usage into Lua Table usage without the user having to change api. I think, I made it for GUI maps, as they don't write Lua itself with that also no Lua tables.

Y, since the last time I looked at this, I admit it could be better/smaller.

I don't see your Variable Debugger as a Submission here, although it looks extremely comprehensive. Could you please submit that one here (or whatever newest version you feel is more up-to-date)?
I didn't continue Variable Debugger, it does this broken TimerStart in Lua root code to start it self which is broken and it is a bit ugly that it requires a toc/file import. Would need some updates before I could upload it in the submission sections. But I am not sure, if I want to do that.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,467
As your intention is to improve the GUI experience for hashtable usage, then I recommend hooking the BJ hashtable functions instead of the common natives: WC3 Modding Information Center - Jass documentation Database (towards the middle of that page you can see the list of BJ hashtable functions, and they just have swapped parameters).

This then allows Lua users to access the hashtable natives directly (what for? Typecasting with ConvertFogState, perhaps, but that might be a stretch). The only one that has a totally different API is HaveSavedValue (it is available to GUI when used as a condition). The "RemoveSaved" ones are totally absent from GUI, but are still worth hooking to check if the value passed is a "table" or a "hashtable" type and proccess the respective behavior accordingly.

Hell, you could even do interesting things like intermingling the Lua (proof of concept) table library here: [Snippet] New Table with the GUI equivalent, thereby granting Lua users access to straightforward, already-established Table (Bribe and/or Vexorian) syntax as well as giving GUI users the ability to play around with that same data. The Lua NewTable library could then expand on Global Variable Remapper to map GUI variable instances to their Lua counterparts, to avoid GUI folks from having to use any custom script.

It would also be possible for GUI to have a variable array "HashTableArray" which is remapped by GlobalRemapArray to automatically initialize hashtables on-demand.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,467
Ok, this should do the trick, using the methods I identified above. I haven't tested it, but the idea is that it completely breaks apart the GUI functions for hashtables, and allows StringHashBJ and GetHandleIdBJ to act as their direct Lua counterparts (indexing by objects rather than integers).

Lua:
do --[[
    GUI hashtable converter version 1.0 by Tasyen and Bribe
  
    Converts GUI hashtables API into Lua Tables, overwrites StringHashBJ and GetHandleIdBJ to permit
    typecasting, bypasses the 256 hashtable limit by avoiding hashtables, provides the variable
    "HashTableArray", which automatically creates hashtables for you as needed (so you don't have to
    initialize them each time).
 
    Requires NewTable Lua: https://www.hiveworkshop.com/threads/snippet-new-table.188084/page-8#post-3532750
    Optional Global Variable Remapper: https://www.hiveworkshop.com/threads/global-variable-remapper-the-future-of-gui.339308/
]]
    --GUI typecasting yo.
    function StringHashBJ(s) return s end
    function GetHandleIdBJ(id) return id end
  
    if GlobalRemapArray then
        local hashes = {}
        GlobalRemapArray("udg_HashTableArray", function(index)
            local val = hashes[index]
            if not val then
                val = HashTable.create()
                hashes[index] = val
            end
            return val
        end)
    end
  
    function InitHashtableBJ() return HashTable.create() end
  
    local function saveInto(value, childKey, parentKey, whichHashTable)
        whichHashTable[parentKey][childKey] = value
    end
  
    local function loadFrom(childKey, parentKey, whichHashTable, default)
        local val = whichHashTable[parentKey][childKey]
        if default and val == nil then
            return default
        end
        return val
    end
  
    SaveIntegerBJ = saveInto
    SaveRealBJ = saveInto
    SaveBooleanBJ = saveInto
    SaveStringBJ = saveInto
  
    local loadNumber = function(childKey, parentKey, whichHashTable) return loadFrom(childKey, parentKey, whichHashTable, 0) end
  
    LoadIntegerBJ = loadNumber
    LoadRealBJ = loadNumber
    LoadBooleanBJ = function(childKey, parentKey, whichHashTable) return loadFrom(childKey, parentKey, whichHashTable, false) end
    LoadStringBJ = function(childKey, parentKey, whichHashTable) return loadFrom(childKey, parentKey, whichHashTable, "") end
  
    local find = string.find
  
    for key in pairs(_G) do
        if         find(key,        "%w+HandleBJ") then
            if     find(key, "^Save") then _G[key] = saveInto
            elseif find(key, "^Load") then _G[key] = loadFrom
            end
        end
    end
  
    function HaveSavedValue(childKey, _, parentKey, whichHashTable)
        return whichHashTable[parentKey][childKey] ~= nil
    end
  
    FlushParentHashtableBJ = HashTable.flush

    function FlushChildHashtableBJ(whichHashTable, parentKey)
        whichHashTable[parentKey].flush()
    end
end
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,467
@Tasyen I have integrated most of the purpose of this resource into Lua-Infused GUI, except that the hashtable implementation I used deliberately disallows index sharing across different key types. With your resource (and with normal hashtables), you are able to store integers and strings in the same parent, child key combo, whereas with the version implemented with Lua-Infused GUI it is not possible to do that.

I did it in part out of contempt that the functionality existed in the first place (as it's bad practice) and another part because I wanted cleaner and leaner code.

Is the functionality worth keeping? I'll let you be the judge.
 
Top