Ren'Py Saves compatibility

c_n

Newbie
Game Developer
Oct 8, 2018
36
321
Hi every ones,
I already read others posts about saves compatibility problems. I will not say that I understood everythings :(
I undestand that I can change some variable or text without any problems
But I don't know where is the point where it cause a crash.
I mean I change some variables but not so much and any save crash. I try with old version save of the game, new version save of my game and I have the same crash not ablle to find a place to rollback...
The question is how to find where is the problem with the tracback file ? (please make simple answer my english is not as good as I need it :( )
You don't have permission to view the spoiler content. Log in or register now.
And here when I click on BACK
You don't have permission to view the spoiler content. Log in or register now.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,959
16,194
The question is how to find where is the problem with the tracback file ? (please make simple answer my english is not as good as I need it :( )
The answer is simple, you can not find where is the problem. Simply because the problem is that, in regard of how Ren'py see it, a part of the game have disappeared.
As said in one of the "others posts about saves compatibility problems" (precisely this one):

Problems can only come when you start to do more challenging operations ; like deleting the .rpyc file, moving the label, and its content, to another file, or renaming the label. But you must understand that the problems will effectively happen only if you do at least two of this at the same time. Therefore :

  • You can safely delete the .rpyc file, as long as you don't rename a label in this file ;
  • You can safely rename a label, as long as you don't delete the .rpyc file ;
  • You can safely move a label, and its content, to another file, as long as you don't rename it.
It isn't explicitly said (should it be ?) but obviously, if you delete the label, there's few hopes.

The error you got is typical of this situation. Ren'py can not find where he have to restart the game, because the place where the save was made isn't anymore part of the game as Ren'py see it.

Usually it's because the label where the save was made have been removed from the game.
 

c_n

Newbie
Game Developer
Oct 8, 2018
36
321
Thanks for the answer.
Ok, now I see where is my mistake... nothing that I can change :/
I use a "jump" where I tell to save... but when I make an update I delete this jump so the save is no longer available...
Is there a possibility to tell to renpy to save before the jump ? Or do I have to tell to save before the screen I tell it ?
 

the66

beware, the germans are cumming
Modder
Donor
Respected User
Jan 27, 2017
7,808
24,388
Python:
.
.
.
label endChapter1:
    "Save now bitch!"
    return
and in you next release you just remove the say and return statement.
Python:
.
.
.
label endChapter1:

label startChapter2:
.
.
.
or if you have separate files for your chapters
Python:
.
.
.
label endChapter1:
    jump startChapter2

label startChapter2:
.
.
.
 

yihman1

Knockout Master
May 11, 2017
3,109
10,937
Inevitably you are going to run into some incompatibility if your game is more advanced and has various variables especially if you add new ones later on. There are a number of workarounds for this.

1) Jump at chapter X at the start (don't forget to put correct variables in when starting at this chapter)
2) Include a functional save file

If it changed significantly people probably want a fresh playthrough anyways.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,959
16,194
Inevitably you are going to run into some incompatibility if your game is more advanced and has various variables especially if you add new ones later on.
Not at all, it's absolutely not inevitable.

You can add all the variables you want without leading to save incompatibility, whatever how complex and advanced your game can be, you just need to do it correctly with the statement.
You can even retroactively take in count the addition/substraction of points, significant change in the meaning of a variable, move or rename the labels, and many other things, without breaking the compatibility of the save files. The only things possibly (but still not always) inevitable is if you make a big change in the whole game mechanism.


I should write about this one day... Oh, wait...
 
  • Like
Reactions: 79flavors

yihman1

Knockout Master
May 11, 2017
3,109
10,937
Not at all, it's absolutely not inevitable.

You can add all the variables you want without leading to save incompatibility, whatever how complex and advanced your game can be, you just need to do it correctly with the statement.
You can even retroactively take in count the addition/substraction of points, significant change in the meaning of a variable, move or rename the labels, and many other things, without breaking the compatibility of the save files. The only things possibly (but still not always) inevitable is if you make a big change in the whole game mechanism.


I should write about this one day... Oh, wait...

Alright look at it like this. Suppose I have a character. Lets call them Sue. In update version 0.2 I have the option to kiss sue on the mouth, or get more frisky and grab her boobs. It's just a menu option with 0 variables added at this point. Later on in version 0.5 sue comes back who has been absent for a few updates now I decide hey maybe I want two different scenes later on in 0.5 depending on if I grabbed her boobs or kissed her because she will say "remember when you kissed me?" or "remember when you grabbed my boobs?" If I did not put in "define grabsueboobs = true/false" and "define kisssue = true/false" way long ago when I made 0.2 that is now more advanced no longer will know if I grabbed her boobs or if I kissed her or if I engaged in any actions with her at all. Of course I could automatically set it to true or false for either or both but only 1 of these options was possible on 0.2.

So yes I suppose it is not "inevitable" hell nothing is inevitable if you want to get technical. I could simply omit the later "remember when" lines altogether to dodge it, or I could have thought WAY ahead several updates back and gave her the kiss/boob definitions and variables, but ya a lot of times when you make an improvement it's going to make the save not run properly.

What however likely will happen in a case such as the one I mentioned is you simply get an error message and it says "ignore" and you skip that line and it's not "game breaking" but for it to go on bug free you need that fresh start.

It's all about how far you planned ahead or if you are happy with things and went back and changed anything. Kissing a girl or grabbing her tits is not something I would call a "big change in game mechanism" as you put it but still even little things like that will fuck a new version into you gotta restart it if you want the game mechanics to all to perform properly.

ANY choice you make may later on become a variable you did not put in yet. Simply picking to wear a red dress instead of a blue dress on day 1 can later on effect a character on day 7 who saw you in that red or blue dress, a character you haven't even thought up on day 1 yet.

I suppose I could code EVERY possible choice to be "default reddress = 0" & defalut bluedress = 0" and go with $ reddress =+ 1 from the start putting in any potential variable making tons and tons of extra lines of code confusing myself because it does NOTHING yet...

So, ya that's why it's pretty much inevitable. It's a choice between writing tons of lines of code that do nothing yet or not.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,959
16,194
Kissing a girl or grabbing her tits is not something I would call a "big change in game mechanism" as you put it but still even little things like that will fuck a new version into you gotta restart it if you want the game mechanics to all to perform properly.
And because, like you said, it's not a big change in the game mechanism, you have many options on your hands to not break the save compatibility...

Option 1 - You knew nothing about save compatibility before :
Code:
default kisssue = False
default grabsueboobs = False

label after_load:
   if kisssue is False and grabsueboobs is False:
       "Sorry to bother you with this, I forgot to set this before."
       menu :
          "In update 0.2, what have you did with Sue ?"
          "I kissed her":
              $ kisssue = True
          "I grabbed her boobs":
              $ grabsueboobs = True
A little annoying for the player, but way less than seeing the game crash when loading a save and having to restart the game.


Option 2 - You prepared your game for your change of mind, using a label tracker (see the "save compatibility how-to" for more on this):
Code:
default kisssue = False
default grabsueboobs = False

label after_load:
   if kisssue is False and grabsueboobs is False:
        if "kissSueDate1" in seenLabels:
            kisssue = True
        elif "grabBoobsSueDate1" in seenLabels:
            grabsueboobs = True
As far as I remember, your game use a shit tons of labels, this one would be a perfect solution.


Option 3 - You have a gallery :
Code:
default kisssue = False
default grabsueboobs = False

label after_load:
   if kisssue is False and grabsueboobs is False:
        if unlocked( "kissSueDate1" ):
            kisssue = True
        elif unlocked ( "grabBoobsSueDate1" ):
            grabsueboobs = True
        fixedSueFirstDate = True
Well, this one don't concern you, still it's a solution for others.


And there's others options, depending of how the game is made. Some being more suitable, others being not applicable.



So, ya that's why it's pretty much inevitable.
Well, the problem is that I wrote a long (and full of practical examples) how-to that prove you wrong on that.
 
  • Like
Reactions: 79flavors

yihman1

Knockout Master
May 11, 2017
3,109
10,937
And because, like you said, it's not a big change in the game mechanism, you have many options on your hands to not break the save compatibility...

Option 1 - You knew nothing about save compatibility before :
Code:
default kisssue = False
default grabsueboobs = False

label after_load:
   if kisssue is False and grabsueboobs is False:
       "Sorry to bother you with this, I forgot to set this before."
       menu :
          "In update 0.2, what have you did with Sue ?"
          "I kissed her":
              $ kisssue = True
          "I grabbed her boobs":
              $ grabsueboobs = True
A little annoying for the player, but way less than seeing the game crash when loading a save and having to restart the game.


Option 2 - You prepared your game for your change of mind, using a label tracker (see the "save compatibility how-to" for more on this):
Code:
default kisssue = False
default grabsueboobs = False

label after_load:
   if kisssue is False and grabsueboobs is False:
        if "kissSueDate1" in seenLabels:
            kisssue = True
        elif "grabBoobsSueDate1" in seenLabels:
            grabsueboobs = True
As far as I remember, your game use a shit tons of labels, this one would be a perfect solution.


Option 3 - You have a gallery :
Code:
default kisssue = False
default grabsueboobs = False

label after_load:
   if kisssue is False and grabsueboobs is False:
        if unlocked( "kissSueDate1" ):
            kisssue = True
        elif unlocked ( "grabBoobsSueDate1" ):
            grabsueboobs = True
        fixedSueFirstDate = True
Well, this one don't concern you, still it's a solution for others.


And there's others options, depending of how the game is made. Some being more suitable, others being not applicable.





Well, the problem is that I wrote a long (and full of practical examples) how-to that prove you wrong on that.
Option 1 as you said would simply annoy players, although I saw this once done before. It's a fix, but it feels less smooth, and if it were done on a number of occasions it would grow to be very annoying to a player that even really loved the game before.

Option 2 If no variable was put there in the first place then you are going back to option 1 as option 2 is not practicable without that foresight or again you must do a "big change in game mechanism"

Option 3 Most games lack a gallery mode, and would be like you described before a "big change in game mechanism" to add one.

-All these solutions that you offer are silly and over complicated if the only thing a developer would need to do is include save files for the various variables.
-Losing ability to use an old save file is only significant if the game is already very deep into the game with a lot of play. Losing a save file 3 hours in sucks ass, but if you are 15 minutes in so what?
-Players can always skip to new content by using the skip button
-Fixes could be made from the console

It is sometimes necessary for the player to start over. Yes, sometimes there are work arounds as we both mentioned, but in the end... They may be missing some content anyways. Suppose you went back to version 0.1 and fixed some spelling and grammar or changed a line here and there or edited some pics or made a little change in pacing or simply added more dialouge.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,959
16,194
-All these solutions that you offer are silly and over complicated if the only thing a developer would need to do is include save files for the various variables.
If you say so...
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
I use a "jump" where I tell to save... but when I make an update I delete this jump so the save is no longer available...
My solution to this very same problem was to rely on labels I knew would be in the next release - but aren't there yet.

The game I was messing with was released in chapters. When I started modifying it, I made sure that every chapter started with a label chapterXX-start: and right at the end was a label endof_chapterXX:.
Any time the game decided it was time for the next chapters... it would jump endof_chapterXX and then I would have the following code:

Python:
label endof_chapter12:

    if renpy.has_label("chapter13_start"):
        jump chapter13_start
    else:
        jump thank_you_for_playing

thank_you_for_playing was a label that never changed and was there since chapter 1 and it basically had the whole "thanks for playing. please support me on patreon... your support means the world to me... blah, blah, blah".

You could modify it to have something like...

Python:
label endof_chapter12:

    "<<< END OF CHAPTER 12. PLEASE SAVE NOW !!! >>>"

    if renpy.has_label("chapter13_start"):
        jump chapter13_start
    else:
        jump thank_you_for_playing

So what would happen is that when the player sees the save reminder... they save (or don't).
When they load that save... RenPy continues from the line it save saved on (or at least tries to... if it can't, it goes back through it's line history trying to find a line that matches what it expects to be there until it finds one or gives up).

If Chapter 13 has been released... things continue into Chapter 13.
If Chapter 13 hasn't been released yet... control passes to the "thanks for playing" blurb.

In theory, someone could even save during the "thanks for playing" bullshit. Load that save... Hit [back] a few times till they're before the check to see if the label exists and then continue forward. I wouldn't like to guarantee it would work every time though.

Obviously this primarily works because it's a chapter oriented release. An open-world style VN generally never ends anyway - so it's much harder to tell the player they've already experienced all the content in the current release (and therefore need to save).

Maybe this solution could work for you?
It's certainly simple enough to implement as long as you keep to those standard label names (or your version of them).
 
Last edited:

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
As for the other issue of a keeping variables set correctly, even if those variables didn't exist when you played the game the first time around, there is no easy answer.

1st simple solution: Don't have variables. Seriously - write a kinetic novel with zero choices. Never have to worry about this shit.

2nd simple solution: Tell the players to suck it up buttercup - your save is incompatible with the current release.


Okay, okay... I'm being deliberately flippant here. No user really wants to hear they've got to play the game again from scratch because the dev decided to go rewrite chapter 2 while releasing chapter 18. But maybe that's what it takes.

Failing that... you're into complex and messy solutions using label after_load:. "Complex", not least because after_load: is invoked every time you load a save game. So whatever solution you end up with has to work not just the first time the player loads their saved game after a major revision of the game... but also all those other times too.

There are no good choices here... just least bad ones.

One solution I tried was based roughly on this:
Python:
default my_new_variable = None

# blah, blah... lots of code

label after_load:
    if my_new_variable == None:
        my_new_variable = True      # or 6 or whatever you want the value to be.

    return

# blah, blah, more code

When I absolutely had to add in a variable after the fact, I made figuring out it hadn't been set yet easy by using a value of None... and if it wasn't set, I would pick a value I would expect "the majority" of players to have picked. To those players who wanted to remain a virgin by the end of the game... well, fuck 'em if they can't take a joke.

Which isn't to say you can't be cleverer about "upgrading" your list of variables after some major upheaval.

You can check other variables from the players "other" decisions and try to do your best to guess what "that sort of player" would have picked.
You can check if certain images have been seen by the player... or certain music has been played (same way some unlock-able galleries are controlled). This isn't ideal, because it's a global check against all previous play sessions. If the player has picked BOTH routes through a section of your game before - you're back to picking the route "most" players would have picked.

Another solution I tried at one point was to keep track of the version of the game when the save was done.
I had code that looked something like this:

Python:
default savefile_version = "0.0"
default grabbed_her_boob = False

label start:

    $ savefile_version = config.version  # set as soon as the player starts.

# blah, blah. Lots of code.

label after_load:

    if config.version == savefile_version:  # no point upgrading variables if we're already on the right release.
        return

    if savefile_version == "0.1":
        call upgrade_from_01_to_02


    if savefile_version == "0.2":
        call upgrade_from_02_to_03

    # you get the idea.

    $ savefile_version = config.version    # now we've finished the upgrades... let's make sure everything matches.
                                           # in fairness, it should already be the correct version number.
                                           # This is just me removing all doubt.

    return

label upgrade_from_01_to_02:

    # actually... do nothing this time. There weren't any variable changes in 0.1 -> 0.2.
    # but we still need to temporarily set the savefile_version to 0.2 so upgrades can be done from 0.1 -> 0.2 -> 0.3 -> 0.4, etc.
    # not moving the savefile_version forward by one release might mean a needed variable upgrade being missed because
    # the player just downloaded version 0.9, but hasn't played since 0.1 or some other random shit like that.

    $ savefile_version = "0.2"
    return

label upgrade_from_02_to_03:

    $ grabbed_her_boob = True

    $ savefile_version = "0.3"
    return

# etc, etc, etc.

If you do try something like this... be careful about maintaining an upgrade path for each and every release. Even fix releases like "0.6a" or "0.6.1" (if you use that naming style).
Alternatively, don't use config.version and instead have something like:

Python:
default savefile_version = "0.0"      # "default" variables are stored in save files.
default grabbed_her_boob = False

define currentsave_version = "0.8"    # "defined" variables aren't stored in save files. They are effectively constants for runtime only.

# etc

This would allow you to not have to do the whole "0.6a" thing. Instead maintaining a sort of meta savefile version number that doesn't change as often as the main game versioning. Maybe you only change this string when you know you're introducing new variables that might break people saved games.

Again... none of this is ideal. It's just you need to do something - and these are "some" of the choices you could go with.
Don't like it? Fine... do something else... or do nothing.

Simple programming solutions tend to only work for simple programming problems. Save compatibility isn't simple. It isn't massively complex either. It just requires you understand WHY things might be a problem in the future.
 
  • Like
Reactions: anne O'nymous

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
3rd and final post - and a short(ish) one this time.

Keep in mind that save compatibility isn't just about variables... it's also about code structure and changes.

When you load a saved game... as I understand it, you're loading a log of the each state of the game going back "x" number of lines. It isn't a simple line number solution either. There's clever stuff that I like to think of as "voodoo".

In an ideal world... you load the save... it is able to match the last statement in the save with the same line in your current version of the game... and everything continues without issue.
... and the vast majority of the time, this is exactly what happens.

The "voodoo" means that simply changing the indenting or other minor changes don't matter. It still works.

Where the load routine can't match the exact line - it then starts working it's way backward through the history stored in the save file trying to find a line it can be sure DOES match. If it finds one... it continues from there (including rolling back variables if needs be).
If it can't find a match - RenPy crashes... as c_n described.

In this case... the code has changed so much it can't piece things back together in a way that could be made to work.

You're only solution to this is to not hack and slash your code around unless you absolutely need to after release.
If a certain section of code is "in the wrong place" now and would be better organized if it were moved nearer to similar code... DON'T TOUCH IT. It's only your OCD talking and RenPy doesn't care where the code is stored except when it tries to load a saved game. Keep your life simple... leave it alone.

Other than that, most changes shouldn't be a problem.
If you change the indenting... "voodoo" will take care of it.
If you add some extra lines of code, even near the save point... no problem... "voodoo" and history matching are your friends.

My only last piece of advice is to leave the box on the RenPy launcher build menu that says "Add from clauses to calls" ticked and if you ever see something like call do_something_clever from do_something_clever6 in your code... Either delete the whole line or leave it the fuck alone. That "from {unique_identifier}" is part of the voodoo and will keep your game compatible with some previous releases even when you've been hacking and slashing code about to some extent. Again, no solution is perfect... but that one definitely helps as long as you let it do all the hard work and don't interfere.

Good luck.
 
  • Like
Reactions: anne O'nymous

c_n

Newbie
Game Developer
Oct 8, 2018
36
321
Thanks for all this explanations
My game is not an advanced one ^^
I use a file for all the variable where I put and add all my variables each time I need to add some. Easiest to find but maybe not the best solution, I don't know.
Maybe it's better to put them in the beginning of the chapter ?
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,959
16,194
My solution to this very same problem was to rely on labels I knew would be in the next release - but aren't there yet.
Since you're talking about it, when I saw it today while playing the last update, I thought about something.

Like the "save now" message is in thank_you_for_playing, the player have to rollback (whatever before saving, or after loading), which isn't really a problem, I agree with this. But why not :
Python:
label endof_chapter12:

    if renpy.has_label("chapter13_start"):
        jump chapter13_start
    else:
        call thank_you_for_playing   # <- changed
        if renpy.has_label("chapter13_start"):
            jump chapter13_start
        return
This way, even if the player save directly when asked, and not before, it's less a problem.
With the jump he have to rollback after the load. But with the call he can just continue, he will be directly branched to the next update.
And with the return you keep the original behavior if he want to pass through the save point before the next update.
But well, I remember what you said in PM times ago. So, if you can't get it made, it's not a problem.


In theory, someone could even save during the "thanks for playing" bullshit. Load that save... Hit [back] a few times till they're before the check to see if the label exists and then continue forward. I wouldn't like to guarantee it would work every time though.
I have passed the label just once, but as far as I remember there's not enough content to saturate the rollback stack. So Ren'py wouldn't have a problem to fallback on the right "endof" label.
Four/five lines are generally enough for this kind of labels, and it's also few enough to be sure that the rollback will lead back to the game.


Another solution I tried at one point was to keep track of the version of the game when the save was done.
I had code that looked something like this:
For the record, it's, more or less, how Super Powered handle the updates ; years without real complaints about save compatibility, for a massively played game, is a good reference.


Again... none of this is ideal. It's just you need to do something - and these are "some" of the choices you could go with.
Don't like it? Fine... do something else... or do nothing.
The advantage of Ren'py is that it offer many way to do the same thing, which permit to everyone to choose the one he feel the more at ease with.


When you load a saved game... as I understand it, [...]
You understood it correctly.


My only last piece of advice is to leave the box on the RenPy launcher build menu that says "Add from clauses to calls" ticked and if you ever see something like call do_something_clever from do_something_clever6 in your code...
Just one word on this. Do not, under absolutely no circumstances , delete the rpyc files when you do this. Else, Ren'py will add the from again... but with new numbers, what will remove all the interest of the said from.

tech note: The from in fact create a virtual label, using what's follow to name it. And this label will be used to return from the called label. Therefore, if you rename it...
Technically Ren'py is robust enough to deal with this, but it's adding a problem when trying to solve one.
 
  • Like
Reactions: 79flavors

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
I use a file for all the variable where I put and add all my variables each time I need to add some.
Sounds as good a solution as any.

Easiest to find but maybe not the best solution, I don't know.
No... It's good.
RenPy doesn't care about whether you have 1 file called script.rpy or 200 files each with half a dozen lines of code each.

Maybe it's better to put them in the beginning of the chapter ?
Likewise RenPy doesn't care what each of those files are called... so if you have a file called something like variables.rpy that contains a list of all your variables... that's no different to RenPy than having the variables at the top of some other file.

Just make sure each of those variables are created using default and I'll be happy :whistle:
 
  • Like
Reactions: anne O'nymous

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,959
16,194
Likewise RenPy doesn't care what each of those files are called... so if you have a file called something like variables.rpy that contains a list of all your variables... that's no different to RenPy than having the variables at the top of some other file.
You can even have a "[whatever]/game/some/folders/nested/again/and/again/variables.rpy", Ren'py will not care more. There's even some games that put rpy files into the "[whatever]/game/images" folder, so...
 
  • Thinking Face
Reactions: 79flavors