Another basic question: persistance

It's not a dumb question. In fact, it's the million dollar question. You will need to turn the table into a string, and strings can be saved into Player:SetScriptData, or Plot:SetScriptData(), or Unit:SetScriptData. They can be retrieved by Player:GetScriptData, etc...
 
I found your table serialization package, incredibly useful. What I'm trying to do is store empire happiness values from the past several turns. I'm using an array of queue class objects to accomplish this. Is this the proper way to use the serialization methods to store and load this two-dimensional matrix:

PHP:
-- Thank you to Afforess for providing  his code open source. This save/load procedure is based off his work.
function doGameInitialization()
  HappinessTable = {};
  for iPlayerID,pPlayer in pairs(Players) do
    HappinessTable[iPlayerID] = Queue:new();
    if (isValidPlayer(pPlayer)) then
      local SaveData = pPlayer:GetScriptData();
      printDebug("Attempting to Load Save Data");
      if SaveData ~= "" then
        printDebug("Loading Save Data");
        printDebug(SaveData);
        for i=1, QUEUE_CAPACITY do
          HappinessTable[iPlayerID]:push(0);
        end
        SaveData = stringToTable(SaveData, QUEUE_CAPACITY, 1, {","}, 1);
        printContentsOfTable(SaveData);
        HappinessTable[iPlayerID] = SaveData;
      end
    end
  end
end
------------------------------------------------------------------------------
function saveGameData()
  for iPlayerID,pPlayer in pairs(Players) do
    if isValidPlayer(pPlayer) then
      printDebug("Attempting to Save Data");
      local tbl = tableToString(HappinessTable[iPlayerID], QUEUE_CAPACITY, 1, {","}, 1);
      printDebug("Save Data Created");
      printDebug(tbl);
      pPlayer:SetScriptData(tbl);
    end
  end
end
------------------------------------------------------------------------------
function printContentsOfTable(incoming_table) -- For debugging purposes. LOT of table data being handled here.
  printDebug("--------------------------------------------------");
  printDebug("Table printout for table ID:", table);
  for index, data in pairs(incoming_table) do
    printDebug("Table index:", index, "Table entry:", data);
  end
  printDebug("- - - - - - - - - - - - - - - - - - - - - - - - - -");
end

The reason I ask is because saving appears to work correctly, but I think I'm using the methods incorrectly for loading. In the load method, the debug output is indicating SaveData's values are nil after using stringToTable, and subsequently results in an error when accessing it.

Mod: Attempting to Load Save Data
Mod: Loading Save Data
Mod: 20,15,11
Mod: --------------------------------------------------
Mod: Table printout for table ID:
Mod: Table index:
Mod: Table index:
Mod: Table index:
Mod: - - - - - - - - - - - - - - - - - - - - - - - - - -
Mod: Attempting to Load Save Data
Mod: Loading Save Data
Mod: -21,-16,-7
Mod: --------------------------------------------------
Mod: Table printout for table ID:
Mod: Table index:
Mod: Table index:
Mod: Table index:
Mod: - - - - - - - - - - - - - - - - - - - - - - - - - -
Mod: updateHappinessInfo
Runtime Error: [string "Mod.lua"]:163: attempt to call method 'push' (a nil value)
 
I'm not sure if push can be called on a table object in Lua. It looks like you should use table.insert(yourTableHere, NewValueToInsert) instead.

However, I don't see the purpose of adding a bunch of 0's to a table, because it get's wiped out a few steps later when to reassign the whole chunk to the save data. Also, you don't seem to be handling cases where there is no save data.
 
I realized after posting that pushing the 0's was unnecessary, I was trying to understand the purpose behind a similar loop in Revolutions.lua and didn't quite comprehend how your PlayersIndex table was structured yet.

The push method is just part of the queue class facade to hide implementation and automate size management:

PHP:
function Queue:push( value )
    table.insert(self, value);
    if (self:size() >= QUEUE_CAPACITY) then
        self:pop();
    end
end

If there's no save data (game just started), nothing needs to be done, the queues are left empty. I call a updateHappinessInfo() method each turn immediately after the initialization check, and it pushes the current turn's happiness level onto the queue.

PHP:
    ...
    if not HappinessTable then
        doGameInitialization();
    end

    updateHappinessInfo();
    ...

function updateHappinessInfo()
	printDebug("updateHappinessInfo");
	for iPlayerID,pPlayer in pairs(Players) do
		if isValidPlayer(pPlayer) then
			HappinessTable[iPlayerID]:push(pPlayer:GetExcessHappiness());
		end
	end
end

Everything works in the mod after starting a game, until I load a save containing previously stored data. After loading and ending a turn, that debug output posted earlier occurs. What's confusing me is how the string is read properly after loading the savegame, but after converting to a table I don't get any output from printing the table entries. I've been looking through StringUtils.lua to figure out if I might be using the parameter list wrong, but after a while just decided to take a break and see if you have any thoughts on the matter.
 
It looks like the data is being restored properly, but your debug statements aren't syntactically correct. Change printContentsOfTable to this, and see what happens:

PHP:
function printContentsOfTable(incoming_table) -- For debugging purposes. LOT of table data being handled here.
  printDebug("--------------------------------------------------");
  printDebug(string.format("Table printout for table ID: %d", table));
  for index, data in pairs(incoming_table) do
    printDebug(string.format("Table index: %d \t Table entry: %d", index, data));
  end
  printDebug("- - - - - - - - - - - - - - - - - - - - - - - - - -");
end
 
Right, now narrowing down what the cause of the function call error might be. Here's the actual queue class...

Spoiler :
PHP:
--
-- Queue class
--

Queue = { }
 
function Queue:new(o)
    o = o or {};
    setmetatable(o, self);
    self.__index = self;
    return o;
end
 
function Queue:push( value )
    table.insert(self, value);
    if (self:size() >= QUEUE_CAPACITY) then
        self:pop();
    end
end
 
function Queue:pop( )
    if self:size() == 0 then
        return nil;
    end
 
    local val = self[self.first];
    table.remove(self, 1);
    return val;
end
 
function Queue:size( )
    return #self;
end

function Queue:average()
    local sum = 0;
    for id,val in pairs(self) do
        --printDebug(self[i]);
        sum = sum + val;
    end    
    --printDebug("sum = " .. sum);
    --printDebug("self:size() = " .. self:size());
    return math.floor(sum / self:size());
end

And each element of HappinessTable[] is being initialized as an instance of the Queue class:

Spoiler :
PHP:
function doGameInitialization()
	HappinessTable = {};
	for iPlayerID,pPlayer in pairs(Players) do
		HappinessTable[iPlayerID] = Queue:new();
		if (isValidPlayer(pPlayer)) then
			local SaveData = pPlayer:GetScriptData();
			printDebug("Attempting to Load Save Data");
			if SaveData ~= "" then
				printDebug("Loading Save Data");
				printDebug(SaveData);
				SaveData = stringToTable(SaveData, QUEUE_CAPACITY, 1, {","}, 1);
				printContentsOfTable(SaveData);
				HappinessTable[iPlayerID] = SaveData;
			end
		end
	end
end

The problem seems to be this initialization is happening properly from the start of the game until save/load occurs. After loading a game containing previously stored data, the indices of the HappinessTable array appear to not properly be inheriting the Queue class methods, including the push() method...
 
Can you give me the output again, with the fixed debug statements? This output:

Code:
Mod: -21,-16,-7
Mod: --------------------------------------------------
Mod: Table printout for table ID:
Mod: Table index:
Mod: Table index:
Mod: Table index:
Seems to imply 3 values where loaded, which would be correct...
 
Aha! Figured out the problem...

PHP:
function doGameInitialization()
    HappinessTable = {};
    for iPlayerID,pPlayer in pairs(Players) do
>       HappinessTable[iPlayerID] = Queue:new();
        if (isValidPlayer(pPlayer)) then
            local SaveData = pPlayer:GetScriptData();
            printDebug("Attempting to Load Save Data");
            if SaveData ~= "" then
                printDebug("Loading Save Data");
                printDebug(SaveData);
                SaveData = stringToTable(SaveData, QUEUE_CAPACITY, 1, {","}, 1);
                printContentsOfTable(SaveData);
>               HappinessTable[iPlayerID] = SaveData;
            end
        end
    end
end

In the two highlighted lines, I'm initializing each element of the array as a Queue object, but then overriding it with a table that's not a Queue object. It actually ties in well with your point that I should handle the case with no save data separately. I did this and it worked:
PHP:
-- Thank you to Afforess for providing  his code open source. This save/load procedure is based off his work.
function doGameInitialization()
    HappinessTable = {};
    for iPlayerID,pPlayer in pairs(Players) do
        if (isValidPlayer(pPlayer)) then
            local SaveData = pPlayer:GetScriptData();
            printDebug("Attempting to Load Save Data");
            if SaveData == "" then
>               HappinessTable[iPlayerID] = Queue:new();
            else
                printDebug("Loading Save Data");
                printDebug(SaveData);
                SaveData = stringToTable(SaveData, QUEUE_CAPACITY, 1, {","}, 1);
                printContentsOfTable(SaveData);
>               HappinessTable[iPlayerID] = Queue:new(SaveData);
            end
        end
    end
end

Thank you for your help and this invaluable library! :)
 
Back
Top Bottom