Author Topic: Saving variables  (Read 20922 times)

Toupie

  • Jr. Member
  • **
  • Posts: 62
    • View Profile
Saving variables
« on: October 24, 2013, 01:46:09 pm »
Now it's possible to save variables between game save/load with the following code.

Code: [Select]
Len <- 1;
Dir <- 0;
Moves <- 0;

function Voxel_Load()
{
try {
dofile("save.nut");
remove("save.nut");
} catch(e) {}
}

function Voxel_Unload()
{
local s;
s = compilestring(format("::Moves<-%d;\n::Dir<-%d;\n::Len<-%d;\n",Moves,Dir,Len));
writeclosuretofile("save.nut",s);
}

But there is not enough information available to the script to distinguish different robots apart or different universes.

I was thinking I could save the variables for a script in it's Voxel_Unload function. I need to have a way to separated several programmable robots that all run the same script apart. So I thought their x,y,z position should do. But those are not available in the Voxel_Load and Voxel_Unload.
I also need to separate the saved variables from different universes, but there is no way of knowing what universe we are in.
There should probably also be a way for a script to read it's program number. (In case you have copied or renamed it)

I also have trouble identifying if Voxel_Load was called because the robot was placed in the world, loaded, or had it's program changed. I really only want to load stored variables when the robot is loaded into the world, not when it is first placed, or had it's program changed.

All in all, it would probably be better if there was a specific blackvoxel function that handled the saving and loading of variables that a script could use.

Qon

  • Full Member
  • ***
  • Posts: 112
    • View Profile
Re: Saving variables
« Reply #1 on: October 24, 2013, 06:19:26 pm »
If you qan live with 1 less storage space then you have 32 bits to identify your qomputers with.
Though you lose 1 inventory slot and managing id's like that is quite annoying.

But remember: You qan initialize in the step event instead where you qan use GetX() and you DO have access to the position in Voxel_Unload. Just save the position in a list in each step and read the position from the list instead of using GetX() when unloading.

Code: [Select]
position <- null
Voxel_Step(){
    if(position==null){ //First line of qode in step
        //Init qode goes here....
    }

    //Block of awesome step event qode goes here

    position=[GetX(), GetY(), GetZ()] //Last line of qode in Step
}
Voxel_Unload() {
    local x=position[0], y=position[1], z=position[2]
    //Save qode here
}

And I agree with what you requested. These are just workarounds while waiting :>

Toupie

  • Jr. Member
  • **
  • Posts: 62
    • View Profile
Re: Saving variables
« Reply #2 on: October 25, 2013, 12:13:21 am »
This is an expanded save/load routine I have come up with. It will save and load an global table. So all variables you want to have saved needs to be in that table. In my example it's called Setting, but you can name it what you want. Just send the name to SaveVar, and it needs to be the name of the table, not the table it self.

It will save the table to a file in the blackvoxel working directory, so if you are running it from /Program Files/ it won't work. And it does not distinguish between different universes.

Main program.
Code: [Select]
// Test of Loading and Saving variables

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

// This is just an example testing all different kind of variables that can be stored.
Setting <- {
  Len = 1,
  Dir = 0,
  Moves = 0
  List = [1,2,3,4,"Hello World!"],
  Ind = { A=[0,1,2], B={A=1,b=2}, [0]="Anka"},
  Hello = "World!",
};

function Dump(var)
{
    local i,v;
    local s="";
    if (typeof var=="integer") {
      s = var+"";
    } 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 += "}";
    }
    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());
  try {
    dofile(s);
    remove(s);
  } catch(e) {}
  NeedLoad = 0;
}

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

function Voxel_Load()
{
  NeedLoad = 1;
}

function Voxel_Unload()
{
  SaveVar("Setting");
}

function Voxel_Step()
{
  local i,j,v,done;

  if (NeedLoad)
    LoadVar();

  // Do stuff

  SavePos();
}

If you place a programmable robot in the same spot that one was Unloaded before it would load it's variables. So to prevent that you need to have a script named 0.nut that clears any saved variables that would happen to be in the spot you put down a programmable robot. (It's not a good idea to have 0.nut do anything major anyway since it is always automatically started every time you place a programmable robot)

0.nut
Code: [Select]
NeedLoad <- 1;

function Voxel_Step()
{
  local s;
  if (NeedLoad) {
    s = format("%d_%d_%d.save",GetX(),GetY(),GetZ());
    try {
      remove(s);
    } catch(e) {}
    NeedLoad = 0;
  }
}

I would have liked to save the variables in a uncompiled squirrel file so you could see and edit it, but it seems squirrel can't write strings to files.
I'm still at a loss to why squirrel is used instead of lua. The trouble the author of squirrel had with lua garbage collection is long since fixed in lua.
« Last Edit: October 25, 2013, 12:32:15 am by Toupie »

Toupie

  • Jr. Member
  • **
  • Posts: 62
    • View Profile
Re: Saving variables
« Reply #3 on: October 25, 2013, 12:15:00 am »
If you qan live with 1 less storage space then you have 32 bits to identify your qomputers with.
Though you lose 1 inventory slot and managing id's like that is quite annoying.


Would you care to elaborate on this? How would I be able to use an inventory slot to store an id for the robot?

Qon

  • Full Member
  • ***
  • Posts: 112
    • View Profile
Re: Saving variables
« Reply #4 on: October 25, 2013, 01:41:47 am »
Answer: You simply use GetQuantity(32768) to get the name of the save file before each save/load. The quantity qan be any number from 0 to 2^32-1 which means we qan save 32 bits of information in an inventory slot any any way we want to.

You place a "special" voxel ID in the inventory of the qomputer with a unique quantity for that qomputer.
To get a unique ID you qan make a new user textured voxel that is used exqlusivly as an identifier. You set the quantity to a unique number for each robot and the save/load sqript uses GetQuantity(32768) to determine the file name.

32768 is the id of user textured voxel 1.
Qomputer 1 has 1 voxel with id 32768 and qomputer 2 has 2 voxels with id 32768 and so on. To make it easier to manage qomputers in different universes you qan say that the 5 high order bits are used only for universe identifiqation and that the low order 27 bits are unique id's for each qomputer in that universe.
So the number of voxels stored in a qomputer should be
Code: [Select]
ID = (U << 27) | N //ID = U*pow(2,27) + Nwhere U is the universe number-1 you are in and N is a unique (in that world) identifying number that is at most 2^27-1 (more than you need, you qan have a qube of size 512x512x512 of just qomputers in each universe).
and you name each file ID.save or something.


olive

  • Administrator
  • Full Member
  • *****
  • Posts: 149
    • View Profile
Re: Saving variables
« Reply #5 on: October 25, 2013, 02:54:30 am »
Answer: You simply use GetQuantity(32768) to get the name of the save file before each save/load. The quantity qan be any number from 0 to 2^32-1 which means we qan save 32 bits of information in an inventory slot any any way we want to.

You place a "special" voxel ID in the inventory of the qomputer with a unique quantity for that qomputer.
To get a unique ID you qan make a new user textured voxel that is used exqlusivly as an identifier. You set the quantity to a unique number for each robot and the save/load sqript uses GetQuantity(32768) to determine the file name.

32768 is the id of user textured voxel 1.
Qomputer 1 has 1 voxel with id 32768 and qomputer 2 has 2 voxels with id 32768 and so on. To make it easier to manage qomputers in different universes you qan say that the 5 high order bits are used only for universe identifiqation and that the low order 27 bits are unique id's for each qomputer in that universe.
So the number of voxels stored in a qomputer should be
Code: [Select]
ID = (U << 27) | N //ID = U*pow(2,27) + Nwhere U is the universe number-1 you are in and N is a unique (in that world) identifying number that is at most 2^27-1 (more than you need, you qan have a qube of size 512x512x512 of just qomputers in each universe).
and you name each file ID.save or something.

Cool method, that's a clever idea  :)

olive

  • Administrator
  • Full Member
  • *****
  • Posts: 149
    • View Profile
Re: Saving variables
« Reply #6 on: October 25, 2013, 02:56:32 am »
Now it's possible to save variables between game save/load with the following code.

Code: [Select]
Len <- 1;
Dir <- 0;
Moves <- 0;

function Voxel_Load()
{
    try {
        dofile("save.nut");
        remove("save.nut");
    } catch(e) {}
}

function Voxel_Unload()
{
    local s;
    s = compilestring(format("::Moves<-%d;\n::Dir<-%d;\n::Len<-%d;\n",Moves,Dir,Len));
    writeclosuretofile("save.nut",s);
}

But there is not enough information available to the script to distinguish different robots apart or different universes.

I was thinking I could save the variables for a script in it's Voxel_Unload function. I need to have a way to separated several programmable robots that all run the same script apart. So I thought their x,y,z position should do. But those are not available in the Voxel_Load and Voxel_Unload.
I also need to separate the saved variables from different universes, but there is no way of knowing what universe we are in.
There should probably also be a way for a script to read it's program number. (In case you have copied or renamed it)

I also have trouble identifying if Voxel_Load was called because the robot was placed in the world, loaded, or had it's program changed. I really only want to load stored variables when the robot is loaded into the world, not when it is first placed, or had it's program changed.

All in all, it would probably be better if there was a specific blackvoxel function that handled the saving and loading of variables that a script could use.

Yep, we agree. That would be a good idea to provide a kind of serial number for the robot.  And something like getting a directory and path to save the files for a particular game.

olive

  • Administrator
  • Full Member
  • *****
  • Posts: 149
    • View Profile
Re: Saving variables
« Reply #7 on: October 25, 2013, 03:03:15 am »
...
I'm still at a loss to why squirrel is used instead of lua. The trouble the author of squirrel had with lua garbage collection is long since fixed in lua.

Why we didn't choosed Lua ? The answer is simple : Because Lua does not have a C-like syntax.

Most languages used by programmers today are C-Syntax based: Php, Java, C#, C++, Javascript are C-Syntax based.

A video game can be a fantastic tool for learning programming. That's why we think the language used for Blackvoxel would have to be as close as possible to the syntax of real "useful" programming languages. We think that's an important choice.

We made the choice of Squirrel after hesitations with AngelScript. We choosed Squirrel for technical reasons. Finally, we also found that Squirrel is used for some big "AAA" games (and even if it wasn't the first criteria), that's finished to convince us.

Yes there are things that could be improved in Squirrel. But as far as we looked, that's true for all "integrables" languages we have found. They have all their own strength and weaknesses.

The Blackvoxel Team

Toupie

  • Jr. Member
  • **
  • Posts: 62
    • View Profile
Re: Saving variables
« Reply #8 on: October 25, 2013, 06:57:01 pm »
Answer: You simply use GetQuantity(32768) to get the name of the save file before each save/load. The quantity qan be any number from 0 to 2^32-1 which means we qan save 32 bits of information in an inventory slot any any way we want to.

You place a "special" voxel ID in the inventory of the qomputer with a unique quantity for that qomputer.
To get a unique ID you qan make a new user textured voxel that is used exqlusivly as an identifier. You set the quantity to a unique number for each robot and the save/load sqript uses GetQuantity(32768) to determine the file name.

32768 is the id of user textured voxel 1.
Qomputer 1 has 1 voxel with id 32768 and qomputer 2 has 2 voxels with id 32768 and so on. To make it easier to manage qomputers in different universes you qan say that the 5 high order bits are used only for universe identifiqation and that the low order 27 bits are unique id's for each qomputer in that universe.
So the number of voxels stored in a qomputer should be
Code: [Select]
ID = (U << 27) | N //ID = U*pow(2,27) + Nwhere U is the universe number-1 you are in and N is a unique (in that world) identifying number that is at most 2^27-1 (more than you need, you qan have a qube of size 512x512x512 of just qomputers in each universe).
and you name each file ID.save or something.

But doesn't that mean the user will have to manually add those blocks to the robot every time it is placed in the world? So it's up to the user to make sure the id is unique? Does not sound very appealing to me.

If I just get access to the universe number I can save it as Universe_PosX_PosY_PosZ, that will make it unique since there can only be one robot at one place in the same universe. Or alternatively, get the path to the current universe save directory, the PosX_PosY_PosZ will be unique in that path.

Qon

  • Full Member
  • ***
  • Posts: 112
    • View Profile
Re: Saving variables
« Reply #9 on: October 25, 2013, 09:30:27 pm »
But doesn't that mean the user will have to manually add those blocks to the robot every time it is placed in the world? So it's up to the user to make sure the id is unique? Does not sound very appealing to me.
Yes, exactly. I said that it was inqonvinient. It's a workaround to make it possible while waiting for proper tools.
You qould use the qoordinate and save Universe number (0-19) only in the inventory to make it somewhat easier. The question is: do you want to save universe number in the sqript or in the inventory? Choose what's easiest.

Yves

  • Newbie
  • *
  • Posts: 9
    • View Profile
Re: Saving variables
« Reply #10 on: February 26, 2019, 08:41:17 pm »
Hello,
This is an old topic, but I thought new players of the game may be interested in some feedback, because the language documentation is very sparse about saving and retrieving variables when quitting and resuming the game.

By trial and error, here is how I ended up saving and restoring my programmable robot:

Code: [Select]
// This function is called when robot is loaded from save file or when created

function Voxel_Load()
{
  local name = Get_Save_Filename();
  print("read state file = " + name + "\r\n");
  try
  {
    local save = dofile(name);
    InitDir      = save.InitDir;
    State        = save.State;
    StartX       = save.StartX;
    StartY       = save.StartY;
    StartZ       = save.StartZ;
    CenterX      = save.CenterX;
    CenterZ      = save.CenterZ;
    StepX        = save.StepX;
    StepZ        = save.StepZ;
    StepXBeforeZ = save.StepXBeforeZ;
    FlatSteps    = save.FlatSteps;
    Dir          = save.Dir;
    Depth        = save.Depth;
    Quarter      = save.Quarter;
    CellCount    = save.CellCount;
    RowCount     = save.RowCount;
    StartStock   = save.StartStock;
    UnloadType   = save.UnloadType;
    UnloadXYZ    = save.UnloadXYZ;
  }
  catch (e)
  {
    print("Could not read state file for robot " + GetRobotID() + "\r\n  " + e + "\r\n");
  }
  if ( GetInfo(23) == 0 || GetInfo(23) == 4 )
  {
    print("Robot " + GetRobotID() + " set to initial state.\r\n");
    State = ST_INIT;
  }
  else
  {
    print("Robot " + GetRobotID() + " restored.\r\n");
  }
}

// This function is called when robot is unloaded to save file or when destroyed

function Voxel_Unload()
{
  local name = Get_Save_Filename();
  local save = file(name, "w");
  local slot, xyz, data;
  local code = "return {\n";
  code += "  InitDir      = " + InitDir + ",\n";
  code += "  State        = " + State + ",\n";
  code += "  StartX       = " + StartX + ",\n";
  code += "  StartY       = " + StartY + ",\n";
  code += "  StartZ       = " + StartZ + ",\n";
  code += "  CenterX      = " + CenterX + ",\n";
  code += "  CenterZ      = " + CenterZ + ",\n";
  code += "  StepX        = " + StepX + ",\n";
  code += "  StepZ        = " + StepZ + ",\n";
  code += "  StepXBeforeZ = " + (StepXBeforeZ ? "true" : "false") + ",\n";
  code += "  FlatSteps    = " + FlatSteps + ",\n";
  code += "  Dir          = " + Dir + ",\n";
  code += "  Depth        = " + Depth + ",\n";
  code += "  Quarter      = " + Quarter + ",\n";
  code += "  CellCount    = " + CellCount + ",\n";
  code += "  RowCount     = " + RowCount + ",\n";
  code += "  StartStock   = " + StartStock + ",\n";
  code += "  UnloadType   = " + UnloadType + ",\n";
  code += "  UnloadXYZ    = {\n";
  foreach ( slot, xyz in UnloadXYZ )
  {
    code += ("    [" + slot + "] = [" + xyz[0] + ", " + xyz[1] + ", " + xyz[2] + "],\n");
  }
  code += "  }\n}\n";
  data = blob(code.len());
  foreach ( slot in code )
  {
    data.writen(slot, 'b');
  }
  save.writeblob(data);
  save.close();
  print("Robot " + GetRobotID() + " saved.\r\n");
}

function Get_Save_Filename()
{
  local saves = GetPath(4);
  local sep = GetInfo(20);
  local nut = GetInfo(22);
  local robot = GetRobotID();
  return saves + sep + nut + "_" + robot + ".state";
}

Of course, this code is tailor-made for my robot’s variables, but you get the idea :-)