A module system that allows save-game compatibility for global variable changes!

Users who are viewing this thread

Mordachai

Squire
I'm wicked stoked!

I always hated that modifying code runs the huge risk of adding new globals, or (even more likely) changing the order in which they're first referenced, which then breaks saves.

So, I figured there must be a way to force the order of reference, outside of the module code itself, so that changing ones module_*.py files doesn't risk your saves... and there IS! :cool:

The module system compiles a list of global variables and writes it out to variables.txt.  This file defines the order of the globals in a saved game.  So when you open the save later, the order things were saved at that time need to match up with the order that the variables.txt file claims it is now.

So, the trick is to keep that order consistent.  What I've done is to have the process_global_variables.py read the old variables.txt file before starting processing your module files, which means that the order of the variables will be forced to be the same as last time, and any new variables you added will go on the end, and any change in order of reference won't alter the order they're read from the save file (or written).  Viola!  Save game compatible!

To add this for yourself, you need to modify the end of the process_global_variables.py

Replace the final two lines, which normally look like:
Code:
compile_all_global_vars(variables, variable_uses, triggers, dialogs, game_menus, mission_templates, scripts, simple_triggers)
save_variables(export_dir, variables, variable_uses)
With these lines instead:
Code:
#MORDACHAI - Preserve previous global variable order, for save-game compatibility...
try:
  file = open("variables.txt","r")
  var_list = file.readlines()
  file.close()
  for v in var_list:
    vv = string.strip(v)
    if vv:
      variables.append(vv)
      variable_uses.append(0)
except:
  print "Variables.txt not found. No attempt to maintain save game compatibility will be made for this build."

compile_all_global_vars(variables, variable_uses, triggers, dialogs, game_menus, mission_templates, scripts, simple_triggers)
save_variables(export_dir, variables, variable_uses)

#MORDACHAI - write out the new version of variables.txt to our module system folder, so we can maintain compatibility with it...
file = open("variables.txt","w")
for i in xrange(len(variables)):
  file.write("%s\n"%variables[i])
file.close()
The first time you compile, this code will copy the resulting variables.txt into your module system folder.  That file will be overwritten every time you build after that. 

And since this seems to be neglected often, I realized that its quite easy to also automatically have your module.ini update when you build your code (and it makes it easy to keep your source in a source control system, with all of the source properly stored in your build folder). 
Just add this at the end of process_global_variables.py file (after the above changes):
Code:
#MORDACHAI - copy our module.ini to the target folder too (if there is one)
try:
  file = open("module.ini","r")
  contents = file.readlines()
  file.close()
  file = open(export_dir + "module.ini","w")
  for i in xrange(len(contents)):
    file.write("%s"%contents[i])
  file.close()
  print "Module.ini updated."
except:
  print "Module.ini not found.  Skipping..."

Finally, I wanted to add another layer of protection, so I could see when / if I added globals, so I made a few changes to build_module.bat to force it to backup to 10 files deep a copy of variables.txt.  This is not strictly necessary, but I feel better having it (place at the top of the file, just after the @echo off):
Code:
@echo backing up our previous variables.txt...
if not exist "variables.txt" goto nobackup
if exist "variables backup 9.txt" del "variables backup 9.txt" > nul
if exist "variables backup 8.txt" ren "variables backup 8.txt" "variables backup 9.txt" > nul
if exist "variables backup 7.txt" ren "variables backup 7.txt" "variables backup 8.txt" > nul
if exist "variables backup 6.txt" ren "variables backup 6.txt" "variables backup 7.txt" > nul
if exist "variables backup 5.txt" ren "variables backup 5.txt" "variables backup 6.txt" > nul
if exist "variables backup 4.txt" ren "variables backup 4.txt" "variables backup 5.txt" > nul
if exist "variables backup 3.txt" ren "variables backup 3.txt" "variables backup 4.txt" > nul
if exist "variables backup 2.txt" ren "variables backup 2.txt" "variables backup 3.txt" > nul
if exist "variables backup 1.txt" ren "variables backup 1.txt" "variables backup 2.txt" > nul
@rem I want to ensure that the file time stamp is what it was, not now...
ren "variables.txt" "variables backup 1.txt" > nul
copy "variables backup 1.txt" "variables.txt" > nul
:nobackup

Note: Adding presentations does NOT break save compatibility. 

I hope that this brings a whole new level of save-game compatibility to the whole modding community! :wink:
 
hehe nice find!

On a side note, another thing that will break saves is simple triggers. If you add one, your savegame won't load anymore as it will choke on the amount of simple triggers. Not sure if the same problem exists with normal triggers, since I usually just use the simple ones.

But I guess it's a great help already to be able to add global variables without breaking saves
 
The simplest answer to that is to simply add some dummy triggers to the end of your simple_triggers.py file.

SoD does this:
Code:
#x############################################
#SOD RESERVED
#x############################################
#116
(999,
	[
	]),

They have about 5 of those.  Then you just change the contents of one if you need it, and you don't need to break your saves.  Changing the definition of a trigger doesn't break the save, apparently.  So add 5 or 10 of those, and use them over the course of a release, and I expect you'll be fine. :smile:
 
Back
Top Bottom