Author Topic: Include file for saving variables.  (Read 9425 times)

Toupie

  • Jr. Member
  • **
  • Posts: 62
    • View Profile
Include file for saving variables.
« on: October 26, 2013, 01:34:22 pm »
I made a "include" file for scripts that needs to have there variables saved and loaded when they go out of scope (game reload or outside of the loaded world).
All the variables that needs to be saved must be stored in a global table. The name of the table does not matter.

"SaveVar.nut" must be saved in the games directory, not the script directory.
Code: [Select]
// SaveVar.nut

NeedLoad <- true;
Pos <- [0,0,0];

function Dump(var)
{
    local i,v;
    local s="";
    if (typeof var=="null") {
      s = "null";
    } else if (typeof var=="string") {
      s = "\""+var+"\"";
    } else if (typeof var=="array") {
      s = "[";
      foreach (v in var) {
        s += Dump(v)+",";
      }
      s += "]";
    } else if (typeof var=="table") {
      s = "{";
      foreach (i,v in var) {
        if (typeof i=="integer") {
          s += "["+i+"]="+Dump(v)+",";
        } else {
          s += i+"="+Dump(v)+",";
        }
      }
      s += "}";
    } else { // integer  bool  ??
      s = var+"";
    }
    return s;
}

function SaveVar(varname)
{
  local c,s,filename;
  s="";
  filename = format("%d_%d_%d.save",Pos[0],Pos[1],Pos[2]);
  if (varname in getroottable() && typeof varname="string") {
    if (typeof (getroottable()[varname])=="table") {
      foreach (i,v in getroottable()[varname]) {
        if (typeof i=="integer") {
          s += "::"+varname+"["+i+"]<-"+Dump(v)+";\n";
        } else {
          s += "::"+varname+"."+i+"<-"+Dump(v)+";\n";
        }
      }
    } else {
      s = "::"+varname+"<-"+Dump(getroottable()[varname])+";\n";
    }

    c = compilestring(s);
    writeclosuretofile(filename,c);
  }
}

function LoadVar()
{
  local s;
  s = format("%d_%d_%d.save",GetX(),GetY(),GetZ());
  NeedLoad = false;
  try {
    dofile(s);
  } catch(e) {
    return false;
  }
  try {
    remove(s);
  } catch(e) {}
  return true;
}

function SavePos()
{
  Pos[0]=GetX();
  Pos[1]=GetY();
  Pos[2]=GetZ();
}

result = LoadVar()
Loads the vars for this script.
Result is true if there where variables loaded, otherwise false.

SaveVar( tablename )
Save the variables in the global table named tablename. tablename is the name of the table, not the table it self.
Ex: SaveVar("Persistent");

SavePos()
Stores the robots position for use by SaveVar in Voxel_Unload() since we don't have access to the robots position in Voxel_Unload(). Needs to be called after every time we have moved.

The variables are stored as X_Y_Z.save in a compiled Squirrel script in the games directory. If you have the game installed in "/Program Files" you will need to give your self write access to the game directory, or it will not work.

WARNING! This script does not know what Universe you are in so problems may occur if you play several universes.

Example script that uses SaveVar.nut for saving and loading variables
Code: [Select]
//global table with the variables that the script needs to be saved and reloaded
Persistent <- {
  StartX = 0,            // The X pos we started at
  StartZ = 0,            // The Z pos we started at
  BlockType = 0,      // The type of Block we are on top of
}

function Voxel_Load()
{
  // "Include" SaveVar.nut
  dofile("SaveVar.nut");
}

function Voxel_Unload()
{
  // Save Variables
  SaveVar("Persistent");  // Important, use the name of the global table, not the table it self.
}

function Voxel_Step()
{
  // Load our variables if needed
  if (NeedLoad)
        LoadVar();


   // Do stuff
  Display("StartX="+Persistent.StartX+" StartZ="+Persistent.StartZ+" BlockType="+Persistent.BlockType, 2000, 4, 1000);


  // Last thing in the Voxel_Step
  SavePos();
}

Edit: Oops. Forgot to support "null", "true" and "false". Updated SaveVar.nut
« Last Edit: October 26, 2013, 02:23:36 pm by Toupie »

Qon

  • Full Member
  • ***
  • Posts: 112
    • View Profile
Re: Include file for saving variables.
« Reply #1 on: October 26, 2013, 07:55:22 pm »
Good job and thanks. Now it's going to be so much easier to make sqripts. In some qases it's possible to make the sqript automatically figure out what state it's in every step so that no variables has to be saved but it's a real hassle and often not possible.

You didn't mention it so:
This will fail if the qomputer is moved after SavePos() is run and before it's Voxel_Unload() function is executed. There's no way to guarantee that... for now.
Just so that anyone who uses it is aware of it :>

Toupie

  • Jr. Member
  • **
  • Posts: 62
    • View Profile
Re: Include file for saving variables.
« Reply #2 on: October 27, 2013, 12:57:24 am »
If you always call SavePos() the last thing you do in Voxel_Step(), the robot can't move between that and the call to Voxel_Unload(). It seems the game always allows the Voxel_Step() run to the end before calling Voxel_Unload(), no async calls to scripts.
 
One "bug" i've found is that if you quit the game without letting it save (alt-F4) the next time you load, the world will have rolled-back and the saved variables file will be deleted since it gets deleted when loaded. To fix this just delete the "remove(s);" from the LoadVar(). But that will have the result that the game directory will be filled with left over save files.
« Last Edit: October 27, 2013, 01:08:23 am by Toupie »

Qon

  • Full Member
  • ***
  • Posts: 112
    • View Profile
Re: Include file for saving variables.
« Reply #3 on: October 27, 2013, 01:53:45 am »
If you always call SavePos() the last thing you do in Voxel_Step(), the robot can't move between that and the call to Voxel_Unload().
The robot "qan't move by itself" after SavePos() is qalled if no movement qode is after the qall  but it "qan be moved" by other voxels in that step.

Are you sure that that Voxel_Unload() is exequted immediately after Step() ends? Otherwise the qomputer qould be moved by a belt qonveyor or another qomputer before Unload() is executed. Maybe all step events, for that simulation step, are ended before all the Unloads are qalled?

It seems the game always allows the Voxel_Step() run to the end before calling Voxel_Unload(), no async calls to scripts.
List of possible scenarios
  • Qalling Voxel_Unload() breaks the exeqution of Voxel_Step() at any point, maybe after a small wait time.
  • Unloading (picking up/deleting/exiting the world etc) a qomputer doesn't guarantee that Voxel_Unload() is qalled at all and Voxel_Step() is halted at any point.

Not true:
  • Unloading a qomputer means waiting for the unloaded qomputers to finish Step() and Unload()
Because:
Yes, a new update again :P
  • Fixed : Sometimes crash when removing the computer or changing program when a Squirrel program is hanging.
  • Added : Modded Squirrel core for supporting program force stop.


EDIT:
Would be nice to have a SaveVar(void) that just saved the root table. Then you don't even need to place everything in a special table, just qall SaveVar() In Unload and LoadVar in Load (and SavePos in end of step) and after that you qan forget about all the annoyances of having to read/set all variables back and forth to a table in every step. Every state variable would automagiqally be saved then.
« Last Edit: October 27, 2013, 02:21:02 am by Qon »

Toupie

  • Jr. Member
  • **
  • Posts: 62
    • View Profile
Re: Include file for saving variables.
« Reply #4 on: October 27, 2013, 10:41:40 am »
Yes. Having the robot moved by a conveyer belt will possible break the script. But that's hardly a normal scenario. That would probably mess up any script that isn't designed to especially for it.

My testings show that unless you put an endless loop in Voxel_Step the game will let it run to end before calling Voxel_Unload.
That seems to be true for ending the game, having the robot unloaded from the world and picking it up.

I didn't save the entire roottable since everything is in it. All your functions, classes and squirrel stuff are in there. I modeled the save routine from the one use by alot of games that use LUA as scripting language. There you set up one table to be saved and loaded. There is no reason to save everything. But no one is stopping you from writing your own save routine that saves everything.

Quote
you qan forget about all the annoyances of having to read/set all variables back and forth to a table in every step.
Why would you read/set variable back and forth to a table? Just access them with tablename.variable.

Qon

  • Full Member
  • ***
  • Posts: 112
    • View Profile
Re: Include file for saving variables.
« Reply #5 on: October 27, 2013, 02:25:29 pm »
My testings show that unless you put an endless loop in Voxel_Step the game will let it run to end before calling Voxel_Unload.
That seems to be true for ending the game, having the robot unloaded from the world and picking it up.

Quote
you qan forget about all the annoyances of having to read/set all variables back and forth to a table in every step.
Why would you read/set variable back and forth to a table? Just access them with tablename.variable.

That means that it waits some time to let it finish and breaks the Step() if it doesn't finish by itself. You don't need an endless loop for it to happen, just something that takes long enough to finish that the Unload() gets tired of waiting and just force stops it. The squirrel engine doesn't know whether Step() will eventually finish or not.  (http://en.wikipedia.org/wiki/Halting_problem)
So it's interesting to know how much time the Step() takes in those tests, because if you didn't do any heavy work then your tests aren't reliable when the result are applied to things that needs seconds to finish. It's not always possible to break work up into several steps so it's important to know how many µs you have to guarantee successful saving.

I know you qan also access them like tablename.variable but that means you will have to do that instead. But this is just nitpicking. "Wouldn't it be nice if?"

Toupie

  • Jr. Member
  • **
  • Posts: 62
    • View Profile
Re: Include file for saving variables.
« Reply #6 on: October 28, 2013, 11:38:22 am »
I had my reasons for saving just the variables in one table.

I have settings variables that tells the script how to perform it's work that I want to be able to change "mid run". This way I can change those variables in the script, and just restart the script on the robot. I will then save the variables I want it to save, restart the script with the new "settings" variables, then load the saved variables (without overriding the settings since they are not in the table that are save) and continue it's work.

So for just a little bit more code in my script I get added functionality, instead of it having it save the entire roottable just to save some "annoyances" in the code.

I don't know how long it will wait for Voxel_Step to finish.
Quote from: From the Voxel Manual on the Programmable Robot
You must take care to don't take excessive execution time to avoid blocking or slowing down the Voxel Dynamic Reaction Engine (for Voxel_Step() ) or the Voxel Loader (for Voxel_Load() and Voxel_Unload() ).
It's clearly never intended that you should do excessive work on one call of Voxel_Step.

But now we have a Robot ID. So I probably don't need to use the X,Y,Z to identify the robot.

Qon

  • Full Member
  • ***
  • Posts: 112
    • View Profile
Re: Include file for saving variables.
« Reply #7 on: October 28, 2013, 03:42:18 pm »
I don't know how long it will wait for Voxel_Step to finish.
Quote from: From the Voxel Manual on the Programmable Robot
You must take care to don't take excessive execution time to avoid blocking or slowing down the Voxel Dynamic Reaction Engine (for Voxel_Step() ) or the Voxel Loader (for Voxel_Load() and Voxel_Unload() ).
It's clearly never intended that you should do excessive work on one call of Voxel_Step.
Yes it stops the VDRE but that doesn't mean it's not meant to be used heavily. I don't qare if it stops when I'm trying to build massive fraqtals or stuff like that. I'm even qoding sqripts that needs the VDRE to stop when it's running...
And work qan't always be divided into several steps so doing heavy work in Step is qommon and necessary. Which means that my point still stands, this saving sqript might fail.
The solution is to use the new GetRobotID() function.