Blackvoxel > Programming with Blackvoxel

Include file for saving variables.

(1/2) > >>

Toupie:
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: ---// 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();
}
--- End code ---

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: ---//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();
}

--- End code ---

Edit: Oops. Forgot to support "null", "true" and "false". Updated SaveVar.nut

Qon:
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:
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.

Qon:

--- Quote from: Toupie 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().
--- End quote ---
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?


--- Quote from: Toupie on October 27, 2013, 12:57:24 am ---It seems the game always allows the Voxel_Step() run to the end before calling Voxel_Unload(), no async calls to scripts.

--- End quote ---
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:

--- Quote from: Enigma on October 25, 2013, 03:32:44 am ---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.
--- End quote ---


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.

Toupie:
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.
--- End quote ---
Why would you read/set variable back and forth to a table? Just access them with tablename.variable.

Navigation

[0] Message Index

[#] Next page

Go to full version