TableSaverLoader, for persisting Lua table data through game save/load

(I've edited the previous post before your answer)

I'm trying to make it work, nothing fancy.

Here are the important parts of the files:
MasterLuaL5R.lua
Code:
include("TableSaverLoader016.lua")
include("L5RDataStorage.lua")

MapModData.gT = MapModData.gT or {}
gT = MapModData.gT
gT.L5RTable = gT.L5RTable or {}
L5RTable = gT.L5RTable

L5RDataStorage:
Code:
function OnModLoaded() --called from end of last mod file to load

	local bNewGame = not TableLoad(gT, "BaneL5R")

	-- < mod-specific init code here depending on bNewGame >

	TableSave(gT, "BaneL5R")  --before the initial autosave that runs for both new and loaded games
end

function OnEnterGame()   --Runs when Begin or Continue Your Journey pressed
	print("Player entering game ...")
	ContextPtr:LookUpControl("/InGame/GameMenu/SaveGameButton"):RegisterCallback(Mouse.eLClick, SaveGameIntercept)
	ContextPtr:LookUpControl("/InGame/GameMenu/QuickSaveButton"):RegisterCallback(Mouse.eLClick, QuickSaveIntercept)
end
Events.LoadScreenClose.Add(OnEnterGame)

function SaveGameIntercept()	--overrides Civ5 code when player presses Save Game from Game Menu or Cntr-s
	TableSave(gT, "BaneL5R")
	UIManager:QueuePopup(ContextPtr:LookUpControl("/InGame/GameMenu/SaveMenu"), PopupPriority.SaveMenu)
end

function QuickSaveIntercept()	--overrides Civ5 code when player presses Quick Save from Game Menu or F11
	TableSave(gT, "BaneL5R")
	UI.QuickSave()
end

local autoSaveFreq = OptionsManager.GetTurnsBetweenAutosave_Cached()
function OnGameOptionsChanged()
	autoSaveFreq = OptionsManager.GetTurnsBetweenAutosave_Cached()
end
Events.GameOptionsChanged.Add(OnGameOptionsChanged)

function OnAIProcessingEndedForPlayer(iPlayer)
	if iPlayer == 63 then					--runs on barb turn AFTER barb unit moves (very close to the regular autosave)
		if Game.GetGameTurn() % autoSaveFreq == 0 then	--only need to do on autosave turns
			TableSave(gT, "BaneL5R")
		end
	end
end
Events.AIProcessingEndedForPlayer.Add(OnAIProcessingEndedForPlayer)

function InputHandler(uiMsg, wParam, lParam)
	if uiMsg == KeyEvents.KeyDown then
		if wParam == Keys.VK_F11 then
			QuickSaveIntercept()		--F11 Quicksave
        		return true
		elseif wParam == Keys.S and UIManager:GetControl() then
			SaveGameIntercept()			--ctrl-s
			return true
		end
	end
end
ContextPtr:SetInputHandler(InputHandler)


OnModLoaded() --add at bottom of whatever file loads last for your mod
 
Is that the order shown in your code snippet of MasterLuaL5R.lua exactly as it appears?

The Lua would go line-by-line, so it'd first include everything from TableSaverLoader016.lua, then move on to L5RDataStorage.lua.

This is fine, except in that particular file you have the OnModLoaded() function running at the end, so it will start trying to do a TableLoad(), which may or may not work, but would simply return a true or false. Following that, it will try to do TableSave(), which is where there may be a problem.

It's set to TableSave() a table called gT, but at this point in the code, gT has not yet been defined. You start defining the tables, and by extension, gT after the function has already attempted to save it.

You need to move L5RDataStorage.lua so that it sits after your block defining the tables.

Edit:
I would also recommend not using MapModData.gT -- since Pazyryk offered gT as a simple default placeholder, if many modders use this as their main table, you will end up saving all of their data along with yours whenever you run TableSave(). Also, if any players run multiple mods which use TableSaverLoader, data loss may occur if they don't have autosaves set to run every turn.
 
That should be fine, although now gT will be a simple Lua table -- is this okay for your usage?
This means that other mods or contexts will be unable to "share" that data. It should be fine if you don't plan on having to share the data at all, and TSL will have no problem saving it.
 
Yeah, MapModData is a global table provided by the game (meant to track Map data, so I'm told) but is accessible by all contexts. In this way, multiple discrete mods (contexts) can access the same "shared" table, despite technically being cut off from one another.
 
Would it be possible to load this mod early enough that it could be used during setup? Or is there another method that I could use to save external tables during setup? Or at least change existing tables?
 
I am having serious issues with this tool (yet again), and I'd appreciate help/confirmation from other users.

After some extensive testing, it seems as though TSL always loads the db from the latest absolute turn ever played in a game. For example, if I play 40 turns, then decide to reload from a save on turn 10, the saved data from turn 40 is loaded into the game. This seems to happen even if I never save (or autosave) a game at that most advanced turn. For example I play 40 turns and never save after turn 10, then reload turn 10, TSL STILL loads in the turn 40 data.

Has anybody else experienced this? If not, then I wonder if anybody has suggestions for overcoming my obvious error in understanding how this mod works.
 
I am having serious issues with this tool (yet again), and I'd appreciate help/confirmation from other users.

After some extensive testing, it seems as though TSL always loads the db from the latest absolute turn ever played in a game. For example, if I play 40 turns, then decide to reload from a save on turn 10, the saved data from turn 40 is loaded into the game. This seems to happen even if I never save (or autosave) a game at that most advanced turn. For example I play 40 turns and never save after turn 10, then reload turn 10, TSL STILL loads in the turn 40 data.

Has anybody else experienced this? If not, then I wonder if anybody has suggestions for overcoming my obvious error in understanding how this mod works.
I use it in my Civ-Linked Great General Names mod, and run that mod pretty much all the time now when I play. So far I have not seen any 'wrong' behavior based on loading game saves from previous turns, but I currently don't have any saves handy to double-check this since I haven't actually been looking for it in quite some time, and I just cleared-out all my save-game files in order to start a new play-through as an extended test of another mod I am working on. I can check that over the next couple of days once I get things going again with play-testing, and see what I get. Once I have anything to report, I'll post further response as to what I am seeing with my mod.

Also, I always use DarkScythe's Add-on for this system, which cures the issues inherent in TableSaver/Loader to do with more than one mod at a time trying to make use of this system.
 
I am having serious issues with this tool (yet again), and I'd appreciate help/confirmation from other users.

After some extensive testing, it seems as though TSL always loads the db from the latest absolute turn ever played in a game. For example, if I play 40 turns, then decide to reload from a save on turn 10, the saved data from turn 40 is loaded into the game. This seems to happen even if I never save (or autosave) a game at that most advanced turn. For example I play 40 turns and never save after turn 10, then reload turn 10, TSL STILL loads in the turn 40 data.

Has anybody else experienced this? If not, then I wonder if anybody has suggestions for overcoming my obvious error in understanding how this mod works.



Empty the table maintained by TSL as soon as your mod loads, then call TableLoad on it to load it from that save file's database. This happens because MapModData maintains its information about the map itself, not the game state, so it persists across game loads regardless of which turn it is.
 
Empty the table maintained by TSL as soon as your mod loads, then call TableLoad on it to load it from that save file's database. This happens because MapModData maintains its information about the map itself, not the game state, so it persists across game loads regardless of which turn it is.

Okay, thank you for the reply. But I really have no idea what this means. Do you have an example? I have set up the mod exactly as Pazyryk lays it out for v.16 in top post. I define gT like this at the top of every file -- including base game UI overwrites:

Code:
MapModData.gT = MapModData.gT or {}
gT = MapModData.gT

If gT isn't defined EXACTLY like that in the mod's main file, I get a load error with other lua states. So, again, I don't know exactly how I would "empty" the gT table because I never define gT other than as a subtable of MapModData.
 
Okay, thank you for the reply. But I really have no idea what this means. Do you have an example? I have set up the mod exactly as Pazyryk lays it out for v.16 in top post. I define gT like this at the top of every file -- including base game UI overwrites:

Code:
MapModData.gT = MapModData.gT or {}
gT = MapModData.gT

If gT isn't defined EXACTLY like that in the mod's main file, I get a load error with other lua states. So, again, I don't know exactly how I would "empty" the gT table because I never define gT other than as a subtable of MapModData.

I'll use code from my latest Civ as an example. It uses "HDNMod" as its table root rather than "gT."

Code:
MapModData.HDNMod = {} -- empty the table

       --Define sub-tables as empty tables so that data from TSL can be loaded into them
	MapModData.HDNMod.Shares = {}
	MapModData.HDNMod.GreatGameDevProgress = {}
	MapModData.HDNMod.GreatGameDevProgressNeeded = {}
	MapModData.HDNMod.UsedCityHeaders = {}
	MapModData.HDNMod.TransformedTurn = {}
	MapModData.HDNMod.ExtraShareSources = {}
	MapModData.HDNMod.NepgearCitiesTurn = {}
	MapModData.HDNMod.UniGotFreeSpy = {}
	MapModData.HDNMod.CulDivInit = nil
	HDNMod = MapModData.HDNMod


	include("TableSaverLoader016.lua");
	include("TSLSerializerV3Neptunia.lua");

	TableLoad(HDNMod, "HDNMod")
	TableSave(HDNMod, "HDNMod")
	HDNMod = MapModData.HDNMod

Basically, you're making your root table completely empty by defining it as a blank table, defining any sub-tables it uses as blank tables as well, then loading the contents of the table from TSL. This ensures that any "future" data from later game saves is wiped out and only what was saved in TSL from the specific save the player loaded is what is put into your table.
 
Basically, you're making your root table completely empty by defining it as a blank table, defining any sub-tables it uses as blank tables as well, then loading the contents of the table from TSL. This ensures that any "future" data from later game saves is wiped out and only what was saved in TSL from the specific save the player loaded is what is put into your table.

Thanks for the reply and example. I had no idea that MapModData persisted on hotload -- that certainly explains everything. And indeed, if you quit out of the game and reload from a save, data is restored as expected. I wish Pazyryk had documented this aspect of his mod.

For other users, you don't actually have to reset your persisting global table, and in fact this will probably force errors if your global table must be accessed from other lua states (e.g. CityView). All you have to do is reset any subtables prior to calling TableLoad.
 
Thanks for the reply and example. I had no idea that MapModData persisted on hotload -- that certainly explains everything. And indeed, if you quit out of the game and reload from a save, data is restored as expected. I wish Pazyryk had documented this aspect of his mod.

For other users, you don't actually have to reset your persisting global table, and in fact this will probably force errors if your global table must be accessed from other lua states (e.g. CityView). All you have to do is reset any subtables prior to calling TableLoad.

I've been away for a couple months, and I'm glad your issues were resolved quickly, but yes -- MapModData table information is persisted across in-game loads, so your code must take care to clear subtables when loading to avoid future data. However, MapModData I believe is cleared with a simple "Exit to main menu" just as well as exiting the game entirely.

With that said, other modders may not need to clear their tables if they do not rely on MapModData. If they are totally self-contained and use Lua tables specific to their own Lua contexts, then these tables are cleared when the scripts are reloaded. The only time you need to handle the subtable clearing is if you rely on MapModData (and even then, you should typically only need to do this if you need to access data across contexts, as I don't believe it offers any other advantage otherwise.)
 
Sorry but it's the first time for me to try to save something into the database. Quite confused.
I have run into a trouble that once I have anything in table 'TiData' (replace for gT), it becomes unable for me to click 'Save Game', nor 'Quick Save'.

Here are the important part of my file, i managed to use only one lua file.

Code:
include("TableSaverLoader016.lua")
include("PlotIterators") -- I don't know whether it bothers

TiData = TiData or {} -- empty the table
TiData.MapTable = TiData.MapTable or {}
MapTable = TiData.MapTable


Code:
function OnModLoaded() --called from end of last mod file to load
	local bNewGame = not TableLoad(TiData, "TokataImprovement")

	TableSave(TiData, "TokataImprovement")	--I talk about this more below
end

function OnEnterGame()   --Runs when Begin or Continue Your Journey pressed
	print("Player entering game ...")
	ContextPtr:LookUpControl("/InGame/GameMenu/SaveGameButton"):RegisterCallback(Mouse.eLClick, SaveGameIntercept)
	ContextPtr:LookUpControl("/InGame/GameMenu/QuickSaveButton"):RegisterCallback(Mouse.eLClick, QuickSaveIntercept)
end
Events.LoadScreenClose.Add(OnEnterGame)

function SaveGameIntercept()	--overrides Civ5 code when player presses Save Game from Game Menu or Cntr-s
	TableSave(TiData, "TokataImprovement")
	UIManager:QueuePopup(ContextPtr:LookUpControl("/InGame/GameMenu/SaveMenu"), PopupPriority.SaveMenu)
end

function QuickSaveIntercept()	--overrides Civ5 code when player presses Quick Save from Game Menu or F11
	TableSave(TiData, "TokataImprovement")
	UI.QuickSave()
end

local autoSaveFreq = OptionsManager.GetTurnsBetweenAutosave_Cached()
function OnGameOptionsChanged()
	autoSaveFreq = OptionsManager.GetTurnsBetweenAutosave_Cached()
end
Events.GameOptionsChanged.Add(OnGameOptionsChanged)

function OnAIProcessingEndedForPlayer(iPlayer)
	if iPlayer == 63 then					--runs on barb turn AFTER barb unit moves (very close to the regular autosave)
		if Game.GetGameTurn() % autoSaveFreq == 0 then	--only need to do on autosave turns
			TableSave(TiData, "TokataImprovement")
		end
	end
end
Events.AIProcessingEndedForPlayer.Add(OnAIProcessingEndedForPlayer)

function InputHandler(uiMsg, wParam, lParam)
	if uiMsg == KeyEvents.KeyDown then
		if wParam == Keys.VK_F11 then
			QuickSaveIntercept()		--F11 Quicksave
        		return true
		elseif wParam == Keys.S and UIManager:GetControl() then
			SaveGameIntercept()			--ctrl-s
			return true
		end
	end
end
ContextPtr:SetInputHandler(InputHandler)

OnModLoaded() -- Right in the end
 
Back
Top Bottom