Variables only change the compatibiity of save-games?

Started by bx83, Sun 04/04/2021 13:41:24

Previous topic - Next topic

bx83

I've noticed that if I add or delete a variable, save-games made before that change are no longer compatible with the new .exe file.
Some experimentation has shown that it's the number of variables, not the name or type. Is this true?
What, other than deleting essential rooms or characters, will make a save-game incompatible?

Crimson Wizard

#1
Yes, changing number of any game objects (except for audio clips, I think), as well as total size of bytes in script variables, will break saves.

Adding completely new rooms does not break saves.
Changing managed objects (created with new), such as dynamic arrays, does not break saves as they are fully stored within a save, which makes them good choice for supporting extensions in your game if you need this.
There are few notes on this here: https://github.com/adventuregamestudio/ags-manual/issues/62

bx83

Total size bytes in script vars? Like changing an int to 2048 from 1024?

Crimson Wizard

#3
Quote from: bx83 on Sun 04/04/2021 14:14:37
Total size bytes in script vars? Like changing an int to 2048 from 1024?

Not changing values, but changing total size, like adding extra int, or removing an old int. Changing array size.

Replacing one object or variable with another will let load old save, but may shift values if you put it into different order. Because builtin save system in AGS does not identify objects, it just saves and loads everything in the order.


This all was discussed number of times on forums, there may be other threads with this and more information already.

bx83

When you say object, I’m assuming you only mean variables with graphical entities in game and are written beginning with an ‘o’ ie. oBird.Visible=true? Or you mean a programmatic object?o

Crimson Wizard

#5
Quote from: bx83 on Sun 04/04/2021 15:21:47
When you say object, I’m assuming you only mean variables with graphical entities in game and are written beginning with an ‘o’ ie. oBird.Visible=true? Or you mean a programmatic object?o

Any "static" game objects that you create in the editor and see in the project tree: Characters, GUI, Views, all of them. Room objects too.
Script module number also matters.
The exceptions are resources like AudioClips and Sprites - these don't matter as they don't have anything changed at runtime.
Another exception is anything created at runtime in script, such as DynamicSprites or Overlays - these also don't matter.


Ok, I need to write the article for the manual explaining all this.

bx83

How might I define all my variables in global.h, making them truly global and extensible without causing save game incompatibility? Is it even possible to achieve the same without the Global Variables section?

Crimson Wizard

#7
Quote from: bx83 on Tue 06/04/2021 15:00:13
How might I define all my variables in global.h, making them truly global and extensible without causing save game incompatibility? Is it even possible to achieve the same without the Global Variables section?

It seems to me that there's still misunderstanding. Changing global variables does break the saves. Declaring them in any header does not make them "extensible".
Any variables declared in script, does not matter where, even Global Variables panel (they are also normal global variables) contribute to the saves problem.

Only changing contents of dynamically created objects does not break saves. For example, changing dynamic array's size, extending "managed" structs, Dictionary and Set types (these two since AGS 3.5.0).


There are two main known methods to make extensible set of variables:
1) Create a regular global array of ints (or other type) bigger than you need right now. Then later you may use extra elements from that array without breaking saves (unless you change array size).
2) Use dynamic structs or arrays. In the reply above I linked a page with few tips on how to create extendable data using dynamic arrays and Dictionaries: https://github.com/adventuregamestudio/ags-manual/issues/62

Dave Gilbert once said that in Wadjet Eye they create multiple extra dummy characters, guis and other objects, as well as extra array of many ints in script (as described above) just in case they have to patch their game and would need extra game entities for that. This is an ugly workaround, but kind of works.


Alternatively you may script your own save system and save/restore only data you want. Depending on what and how much do you want to put into saves - this may be relatively simple or very complicated.

Crimson Wizard

#8
Actually, I just realized that changing number of room objects does not break old saves, in the sense that they load. But this may still lead to glitches, as rooms will "think" they have different number of objects.

Also, apparently, changing number of custom properties also does not break saves. If you remove existing custom properties and they were in savegame, their values will load, but will be useless (yet don't break anything).

PS. I'm writing an article for the manual about this now, will post a link here later.


UPDATE:
https://github.com/adventuregamestudio/ags-manual/wiki/GameSavesCompatibility

Tried to explain everything I know, may adjust later if remember anything else, or find mistakes.

bx83

Quote
There are two main known methods to make extensible set of variables:
1) Create a regular global array of ints (or other type) bigger than you need right now. Then later you may use extra elements from that array without breaking saves (unless you change array size).

If I were to do this, how would I write this array so it’s global? In global.asc? Just as:

globalint=new dynamic array[999];
int globalvtar[]=, globalvar[]=, etc?

Crimson Wizard

#10
Quote from: bx83 on Fri 09/04/2021 06:32:08
Quote
There are two main known methods to make extensible set of variables:
1) Create a regular global array of ints (or other type) bigger than you need right now. Then later you may use extra elements from that array without breaking saves (unless you change array size).

If I were to do this, how would I write this array so it’s global? In global.asc? Just as:


If you want the array to be accessible in all the scripts in game, then you have to put it in the topmost script, because in AGS global variables and functions can be only accessed in the script they are declared and scripts below.
You may even create a new script specially for this array and other global variables, and move it to the top of the script list - then any variables declared there will be accessible in any other script.
You just need to declare exports/imports properly.


Quote from: bx83 on Fri 09/04/2021 06:32:08
globalint=new dynamic array[999];
int globalvtar[]=, globalvar[]=, etc?

Regular arrays are allocated as
Code: ags

int globalvar[999];


Dynamic arrays are first declared as a pointer to array:
Code: ags

int dynarr[];

And then you have to allocate it with "new" command in some function, for example in game_start:
Code: ags

function game_start() {
    dynarr = new int[999];
}



bx83

Okay, I've come up with this in a new script which always appears at the top:

GlobalVars.asc:
Code: ags
int                           GVInt[];
String                        GVStr[];
float                         GVFloat[];
bool                          GVBool[];
AudioChannel                  GVAChan[];
AudioClip                     GVAClip[];
Button                        GVButton[];
Camera                        GVCam[];
Character                     GVCharacter[];
DateTime                      GVDateTime[];
Dialog                        GVDialog[];
DialogOptionsRenderingInfo    GVDialogRenderInfo[];
Dictionary                    GVDict[];
DrawingSurface                GVDrawingSurface[];
DynamicSprite                 GVDynamicSpr[];
File                          GVFile[];
GUI                           GVGui[];
GUIControl                    GVGuiControl[]; 
Hotspot                       GVHotspot[];
InventoryItem                 GVInvItem[];
InvWindow                     GVInvWindow[];
Label                         GVLabel[];
ListBox                       GVListBox[];
Object                        GVObj[];
Overlay                       GVOverlay[];
Point                         GVPoint[];
Region                        GVRegion[];
Set                           GVSet[];
Slider                        GVSlider[]
TextBox                       GVTextBox
TextWindowGUI                 GVexWindowGUI[];
ViewFrame                     GVViewFrame[];
Viewport                      GVViewport[];

function game_start()
{
  GVInt = new int[999];
  GVStr = new String[999];
  GVFloat = new float[999];
  GVBool = new bool[999];
  GVAChan = new AudioChannel[999];
  GVAClip = new AudioClip[999];
  GVButton = new Button[999];
  GVCam = new Camera[999];
  GVCharacter = new Character[999];
  GVDateTime = new DateTime[999];
  GVDialog = new Dialog[999];
  GVDialogRenderInfo = new DialogOptionsRenderingInfo[999];
  GVDict = new Dictionary[999];
  GVDrawingSurface = new DrawingSurface[999];
  GVDynamicSpr = new DynamicSprite[999];
  GVFile = new File[999];
  GVGui = new GUI[999];
  GVGuiControl = new GUIControl[999];
  GVHotspot = new Hotspot[999];
  GVInvItem = new InventoryItem[999];
  GVInvWindow = new InvWindow[999];
  GVLabel = new Label[999];
  GVListBox = new ListBox[999];
  GVObj = new Object[999];
  GVOverlay = new Overlay[999];
  GVPoint = new Point[999];
  GVRegion = new Region[999];
  GVSet = new Set[999];
  GVSlider = new Slider[999];
  GVTextBox = new TextBox[999];
  GVexWindowGUI = new TextWindowGUI[999];
  GVViewFrame = new ViewFrame[999];
  GVViewport = new Viewport[999];
  
  export GVInt[];
  
  GVInt[NewGlobalIntVar]=5;  
}



GlobalVars.ash:
Code: ags
import int NewGlobalIntVar;


NewGlobalIntVar on the last line of GlobalVars.asc is me making a new int, which will be global and accessible by all other scripts. I can create as many of these as I like without breaking save-game compatibility. Is this correct?

However, it gives an error of:
GlobalVars.asc(7): Error (line 7): Cannot declare local instance of managed type

for line 7 of GlobalVar.asc:
AudioChannel                  GVAChan[];

I try putting
AudioChannel*                  GVAChan[];
but no cigar.

What am I doing wrong?


Crimson Wizard

#12
1.
"export" statement should be done outside of all functions and does not include "[]". Maybe manual still is not clear about this.

2.
"AudioChannel* GVAChan[];" would be correct. Same for all other objects such as Characters, Overlays etc.
I don't know what "no cigar" means. Was there an error and what it was?

3.
Some of these arrays are probably not necessary.
DialogOptionsRenderingInfo is especially not needed, because there's literally 1 object of that kind that is passed to dialog option callbacks, and you should not be storing these.
DrawingSurface - is not recommended to store outside a function... same for File.

Also, to clarify, if you are preparing this in case you'd have to add these objects later, then these arrays are not enough, you need to actually create dummy objects in the editor.

Crimson Wizard

Quote from: bx83 on Sun 11/04/2021 22:57:11
NewGlobalIntVar on the last line of GlobalVars.asc is me making a new int, which will be global and accessible by all other scripts. I can create as many of these as I like without breaking save-game compatibility. Is this correct?

I don't understand this question. Could you give an example of what you intend to do?

bx83

'no cigar' is en English phrase which means "you tried, but unfortunately it didn't go that way. Try an alternate stratagem."

Basically I just want this:

anyType GlobalVars[] = new Dictionary;

GlobalVars[VariableName]=5;
GlobalVars[aFloatVar]=7.2;
GlobalVars[StringVar]="hello";

export GlobalVars;

New, VariableName (or GlobalVars[VariableName], etc.) is a global variable, capable of being accessed by everyone. Adding new indexes to GlobalVars does not break save-game compatibility. That's it. Not sure how to do this - have been experimenting for an hour.

I have this now:

script:
Code: ags
// new module script

int                           GVInt[];
String                        GVStr[];
float                         GVFloat[];
bool                          GVBool[];
AudioChannel                  *GVAChan[];
AudioClip                     *GVAClip[];
Button                        *GVButton[];
Camera                        *GVCam[];
Character                     *GVCharacter[];
DateTime                      *GVDateTime[];
Dialog                        *GVDialog[];
Dictionary                    *GVDict;
DynamicSprite                 *GVDynamicSpr[];
File                          *GVFile[];
GUI                           *GVGui[];
GUIControl                    *GVGuiControl[]; 
Hotspot                       *GVHotspot[];
InventoryItem                 *GVInvItem[];
InvWindow                     *GVInvWindow[];
Label                         *GVLabel[];
ListBox                       *GVListBox[];
Object                        *GVObj[];
Overlay                       *GVOverlay[];
Point                         *GVPoint[];
Region                        *GVRegion[];
Set                           *GVSet[];
Slider                        *GVSlider[];
TextBox                       *GVTextBox[];
TextWindowGUI                 *GVexWindowGUI[];
ViewFrame                     *GVViewFrame[];
Viewport                      *GVViewport[];


function game_start()
{
  GVInt = new int[999];
  GVStr = new String[999];
  GVFloat = new float[999];
  GVBool = new bool[999];
  GVAChan = new AudioChannel[999];
  GVAClip = new AudioClip[999];
  GVButton = new Button[999];
  GVCam = new Camera[999];
  GVCharacter = new Character[999];
  GVDateTime = new DateTime[999];
  GVDialog = new Dialog[999];
  GVDict =  Dictionary.Create();
  GVDynamicSpr = new DynamicSprite[999];
  GVFile = new File[999];
  GVGui = new GUI[999];
  GVGuiControl = new GUIControl[999];
  GVHotspot = new Hotspot[999];
  GVInvItem = new InventoryItem[999];
  GVInvWindow = new InvWindow[999];
  GVLabel = new Label[999];
  GVListBox = new ListBox[999];
  GVObj = new Object[999];
  GVOverlay = new Overlay[999];
  GVPoint = new Point[999];
  GVRegion = new Region[999];
  GVSet = new Set[999];
  GVSlider = new Slider[999];
  GVTextBox = new TextBox[999];
  GVexWindowGUI = new TextWindowGUI[999];
  GVViewFrame = new ViewFrame[999];
  GVViewport = new Viewport[999];
  
  GVDict[TestVariable]="hello";
  GVDict[AnotherVariable]=5;
  
  int NewGlobalIntVar=5;
  GVInt[1]=NewGlobalIntVar;
  
  int SomeOtherVariable=7;
  GVInt[2]=SomeOtherVariable;
  
  
}


export GVInt;
export GVDict;


header:
Code: ags
import int GVInt[];
import Dictionary GVDict[];


error:
GlobalVars.asc(14): Error (line 14): Attributes of identifier do not match prototype
on this line in script:
Dictionary                    *GVDict;

????

I give up :/

Crimson Wizard

#15
Quote from: bx83 on Sun 11/04/2021 23:38:43
error:
GlobalVars.asc(14): Error (line 14): Attributes of identifier do not match prototype
on this line in script:
Dictionary                    *GVDict;


When doing import you must give same type... you changed it in variable but not in import
https://adventuregamestudio.github.io/ags-manual/ImportingFunctionsAndVariables.html

I was going to link this manual article in previous comments, but assumed you might have read it...


Quote
anyType GlobalVars[] = new Dictionary;

GlobalVars[VariableName]=5;
GlobalVars[aFloatVar]=7.2;
GlobalVars[StringVar]="hello";

This is not exactly how Dictionary works...
https://adventuregamestudio.github.io/ags-manual/Dictionary.html

Regarding whole idea, not completely sure about details, but the general direction seem right.

bx83

Is there anything that will save an array of 'anyType' or you have to have an array for each individual type?
How can I more simply set global variables array up?

bx83

Rather than:
Code: ags
int NewGlobalIntVar=5;    GVInt[1]=NewGlobalIntVar;
int SomeOtherVariable=7;  GVInt[2]=SomeOtherVariable;

Crimson Wizard

Quote from: bx83 on Mon 12/04/2021 00:26:33
Is there anything that will save an array of 'anyType' or you have to have an array for each individual type?

No, AGS only supports variables and arrays of exact types.

Quote from: bx83 on Mon 12/04/2021 00:26:33
How can I more simply set global variables array up?

Rather than:
Code: ags
int NewGlobalIntVar=5;    GVInt[1]=NewGlobalIntVar;
int SomeOtherVariable=7;  GVInt[2]=SomeOtherVariable;


I'm confused by this question, or rather by script example. Maybe I'm missing something. Is there a reason you are using these NewGlobalIntVar variables, and not just setting array element directly?
Code: ags

GVInt[1] = 5;
GVInt[2] = 7;

bx83

I just want a named variable - as in, create ThisIsAVar, then use it later. indexes are a bit impersonal and I'll likely make a mistake.

Is there any way to have named global variables? (other than named arrays with just pure indices)

SMF spam blocked by CleanTalk