1. We have added the ability to collapse/expand forum categories and widgets on forum home.
    Dismiss Notice
  2. All Civ avatars are brought back and available for selection in the Avatar Gallery! There are 945 avatars total.
    Dismiss Notice
  3. To make the site more secure, we have installed SSL certificates and enabled HTTPS for both the main site and forums.
    Dismiss Notice
  4. Civ6 is released! Order now! (Amazon US | Amazon UK | Amazon CA | Amazon DE | Amazon FR)
    Dismiss Notice
  5. Dismiss Notice
  6. Forum account upgrades are available for ad-free browsing.
    Dismiss Notice

Saving/Loading simple tables with a game

Discussion in 'Civ6 - Utilities, Code Snippets & Art Assets' started by Gedemon, Jan 21, 2017.

  1. Gedemon

    Gedemon Modder Moderator

    Joined:
    Oct 4, 2004
    Messages:
    7,640
    Location:
    France
    Not fully tested, so use it carefully, I've no idea of the size limit and performance on large table.

    I tested it by saving/loading a table with all plots coordinates of a small map and some data for each plot.

    In a lua file loaded as an <UserInterface> component (as GameConfiguration.SetValue doesn't exist in scripts contexts):
    Code:
    -----------------------------------------------------------------------------------------
    --    FILE:     SaveLoad.lua
    --  Gedemon (2017)
    --  Loading/Saving simple tables with the game file using Pickle Table for serialization
    --  http://lua-users.org/wiki/PickleTable
    -----------------------------------------------------------------------------------------
    
    ----------------------------------------------
    -- Pickle.lua
    -- A table serialization utility for lua
    -- Steve Dekorte, http://www.dekorte.com, Apr 2000
    -- Freeware
    ----------------------------------------------
    
    function pickle(t)
      return Pickle:clone():pickle_(t)
    end
    
    Pickle = {
      clone = function (t) local nt={}; for i, v in pairs(t) do nt[i]=v end return nt end
    }
    
    function Pickle:pickle_(root)
      if type(root) ~= "table" then
        error("can only pickle tables, not ".. type(root).."s")
      end
      self._tableToRef = {}
      self._refToTable = {}
      local savecount = 0
      self:ref_(root)
      local s = ""
    
      while #(self._refToTable) > savecount do
        savecount = savecount + 1
        local t = self._refToTable[savecount]
        s = s.."{\n"
        for i, v in pairs(t) do
            s = string.format("%s[%s]=%s,\n", s, self:value_(i), self:value_(v))
        end
        s = s.."},\n"
      end
    
      return string.format("{%s}", s)
    end
    
    function Pickle:value_(v)
      local vtype = type(v)
      if     vtype == "string" then return string.format("%q", v)
      elseif vtype == "number" then return v
      elseif vtype == "boolean" then return tostring(v)
      elseif vtype == "table" then return "{"..self:ref_(v).."}"
      else --error("pickle a "..type(v).." is not supported")
      end
    end
    
    function Pickle:ref_(t)
      local ref = self._tableToRef[t]
      if not ref then
        if t == self then error("can't pickle the pickle class") end
        table.insert(self._refToTable, t)
        ref = #(self._refToTable)
        self._tableToRef[t] = ref
      end
      return ref
    end
    
    ----------------------------------------------
    -- unpickle
    ----------------------------------------------
    
    function unpickle(s)
      if type(s) ~= "string" then
        error("can't unpickle a "..type(s)..", only strings")
      end
      local gentables = loadstring("return "..s)
      local tables = gentables()
    
      for tnum = 1, #(tables) do
        local t = tables[tnum]
        local tcopy = {}; for i, v in pairs(t) do tcopy[i] = v end
        for i, v in pairs(tcopy) do
          local ni, nv
          if type(i) == "table" then ni = tables[i[1]] else ni = i end
          if type(v) == "table" then nv = tables[v[1]] else nv = v end
          t[i] = nil
          t[ni] = nv
        end
      end
      return tables[1]
    end
    
    
    
    -----------------------------------------------------------------------------------------
    -- Load / Save
    -- Using Civ6 GameConfiguration
    -----------------------------------------------------------------------------------------
    
    -- save
    function SaveTableToSlot(t, sSlotName)
        local s = pickle(t)
        GameConfiguration.SetValue(sSlotName, s)
    end
    
    -- load
    function LoadTableFromSlot(sSlotName)
        local s = GameConfiguration.GetValue(sSlotName)
        if s then
            local t = unpickle(s)
            return t
        else
            print("ERROR: can't load table from ".. tostring(sSlotName))
        end
    end
    
    function Initialize()
        ExposedMembers.SaveTableToSlot = SaveTableToSlot
        ExposedMembers.LoadTableFromSlot = LoadTableFromSlot
    end
    Initialize()
    usage (from any context) :
    > ExposedMembers.SaveTableToSlot(t, "myTable")
    > t = ExposedMembers.LoadTableFromSlot("myTable")


    When to save if you can't do it at specific times ?

    Code:
    -- this Lua event is called when listing files on the save/load menu
    function SaveMyTable()
        ExposedMembers.SaveTableToSlot(myTable, "myTable")
    end
    LuaEvents.FileListQueryComplete.Add( SaveMyTable )
    
    -- could get the quicksave key here
    function OnInputHandler( pInputStruct:table )
        local uiMsg = pInputStruct:GetMessageType();
        if uiMsg == KeyEvents.KeyDown then
            if pInputStruct:GetKey() == Keys.VK_F5 then -- but the binding can be changed...
                ExposedMembers.SaveTableToSlot(myTable, "myTable")
            end
        end
        -- pInputStruct:IsShiftDown() and pInputStruct:IsAltDown()
    end
    ContextPtr:SetInputHandler( OnInputHandler, true )
    
    -- Or on this event for quick save
    function OnInputAction( actionID )
        if actionID == Input.GetActionId("QuickSave") then
            ExposedMembers.SaveTableToSlot(myTable, "myTable")
        end
    end
    Events.InputActionTriggered.Add( OnInputAction )
     
    Last edited: Aug 21, 2018
    raen and SoehnelS like this.
  2. FearSunn

    FearSunn Chieftain

    Joined:
    Dec 10, 2017
    Messages:
    189
    Gender:
    Male
    Thank you @Gedemon for this code snippet! It is lightweight and works perfectly! Exactly what I needed for one of my mods.
     
  3. Tiramisu

    Tiramisu Chieftain

    Joined:
    Aug 16, 2013
    Messages:
    129
    Location:
    Leonberg (BW, Germany)
    Yes, this is a very valuable tool for modders. The code works well, but you have forgot to put "Add" in the last line. So it should be
    Code:
    Events.InputActionTriggered.Add( OnInputAction )
    to catch quicksave-events.
    I guess forgetting the "Add"-statement is a common mistake for Lua-modders. :D
     
  4. Gedemon

    Gedemon Modder Moderator

    Joined:
    Oct 4, 2004
    Messages:
    7,640
    Location:
    France
    Yes, common, thanks, edited.
     

Share This Page

Ebates: Get Paid to Shop