Ren'Py breakyour[save]

crabsinthekitchen

Well-Known Member
Apr 28, 2020
1,550
8,807
Did you know you can break a lot of RenPy games that let you name your saves, by putting brackets in the save name like, for example, the title of this thread? Well, I didn't know either until someone from Steam told us. Apparently, RenPy thinks that whatever you put in brackets is a variable used in the game and tries to find it
  1. The input for the save name should be on the save screen. If there's another screen where you have to enter the save name, it will work fine
  2. Dev allows square brackets in the save name. But I've only seen a few games that don't do it
  3. You put square brackets with some text between them in the save name
Boom. You get an error screen and now you have to look for the broken save in your save folder and delete it because save and load screens don't work anymore.

Element-174, the game I occasionaly help with Chasing Sunsets ARTEMIS Desert Stalker

Well, if you're a dev and don't want your players to suffer like this, there's actually a simple fix that doesn't require creating a new screen to enter your save names or filtering the user input. And it even works if the user already broke their game

Most likely, you have this line somewhere in your file_slots screen
Python:
text FileSaveName(slot)
If you assign it to a variable first, RenPy will stop trying to be smart and will just show the screen with the save names as player entered them
Python:
$ save_name_text = FileSaveName(slot)
text "[save_name_text]":
Everything works again
Paging Stone Fox Studios, digi.B, and ZetanDS because I reproduced it on your games as well when I tried to verify it's not me being stupid and not knowing how RenPy works for once
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
Another solution is to not allow the characters [ and ] as part of the entered save name (and { and } apparently too).

A lot of games will use some combination of either or the screen language's to allow the player to name the save file. Both those functions have an exclude= parameter. So just exclude "[]{}".

Realistically though, players have probably named literal hundreds of thousands of saves by this point - if not millions. If even a small fraction of those names were causing problems, I can't help but feel it'd have a higher visibility within the community. Yes, it's a game breaking combination. Yes, it's clearly reproducible. It might even be worth reporting as a bug within RenPy. I'm just not so sure people are using those characters within save names.
If I were writing a game using save naming, I'd alter my code to exclude those characters now I know about the potential - I'm just not sure anyone (other that yourself and a few devs on here) would ever notice.
 
Last edited:
  • Like
Reactions: gojira667

crabsinthekitchen

Well-Known Member
Apr 28, 2020
1,550
8,807
Another solution is to not allow the characters [ and ] as part of the entered save name.
Couldn't do that, some guy got the game on Steam and told us he can't play anymore. I mean, we could ask him to delete the broken save from AppData and saves directory but when there's one, there might be others and I'd rather not write a Steam guide about finding broken saves
There's probably a lot of stuff that doesn't happen often enough to be considered a huge bug. A lot of games don't even use save names, or show a separate screen for it, which makes the problem disappear for some reason I'm not entirely sure of
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
Did you know you can break a lot of RenPy games that let you name your saves, by putting brackets in the save name like, for example, the title of this thread?
Known for sure, no, but "assumed", yes. Brackets are used to mark , so obviously they will not deal well with displayed text. And named saves aren't the only time this can happen.


Python:
$ save_name_text = FileSaveName(slot)
text "[save_name_text]":
Before you discover it on your own, this is as broke as what you used originally. Put a left brace ({) in your save_name_text and look at your game breaking again.


One should always validate player's inputs . Either :
  • Directly during the , by using the exclude parameter, as said by 79flavors , or the allow parameter ;
  • Right after the input, by varName = varName.replace( "[", "[[" ).replace( "{", "{{" ) ;
  • Or during the display, by using a combination between text interpolation and the !q conversion flag presented in the text interpolation part of the doc (link above), text "[varName!q]".

Edit:
Couldn't do that, some guy got the game on Steam and told us he can't play anymore. I mean, we could ask him to delete the broken save from AppData and saves directory but when there's one, there might be others and I'd rather not write a Steam guide about finding broken saves
Or you could:
If you display the text before loading the save file, fix the name before displaying it by using the replace method presented above.
Then when he load the save file, use the label to definitively fix the name (still with replace) for this play.
 
Last edited:

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
Yeah. A game crashes any time you try to show either the save or load screens (which is pretty much 99% of the time) when an erroneously formatted save name is found.

My gut reaction of "when there's one, there might be others" is that while it is still technically true, it remains largely a remote possibility.

The solution I put forward would correct future saves... and if it were a new game, at least avoid the situation ever arising. But your code works around even those situations where a corrupt save name already exists. I could argue prevention is better than cure, but honestly I'd just be defending my own idea for the sake of itself.

There is also the nuclear option of forcing the deletion of corrupted saves.
This code seemed to work for me:

Python:
init -1 python:

    for i in renpy.list_saved_games():
        if "[" in i[1]:
            renpy.unlink_save(i[0])

Where i[0] is the save slot filename.
And i[1] would be the value of save_name for that slot.

I'm sure someone with a bit more imagination could probably strip the "[" out of the corrupted save_name without deleting the save. But this was my quick and dirty solution based on my limited knowledge of python.

I did consider , but assumed that the game would crash on the load screen before after_load() could fix things. I settled on using during the game startup instead.
 
Last edited:
  • Like
Reactions: crabsinthekitchen

Deleted member 2808342

Cliffhanger Vendor
Game Developer
Sep 4, 2020
276
2,124
Thanks for the heads up...I will test a fix on this today.

Since save pages can be renamed by default, I suspect this issue is present there also. If so, I would think nearly every Ren'Py game, even those without renameable saves has the potential to run afoul of the user inputting interpolation characters into an input field.

EDIT

Yeah, the exclude switch works great for input fields:

Code:
exclude '-=<>|/,.!?@#$%^&*()_+"№;:~`}{][\\'

Thanks 79flavors.

Cheers!
 
Last edited:

Meushi

Well-Known Member
Aug 4, 2017
1,146
12,727
Code:
exclude '-=<>|/,.!?@#$%^&*()_+"№;:~`}{][\\'
I'd suggest not excluding all those special characters from your page/save input (though perhaps you weren't but just demonstrating).

I've regularly used - ? _ in page and save names for instance, and was frustrated in the few games I've encountered that didn't allow them.

One introduced the restriction midway through development, which also caused the existing saves not to load, so you'd still need AONs after_load solution to sort old saves in that case.

Better to limit your exclusions to the minimum set likely to cause problems I would think?

And whatever you decide to exclude should be documented/publicised so the players know of it. This hasn't always been the case in my experience.
 
  • Like
Reactions: gojira667

Deleted member 2808342

Cliffhanger Vendor
Game Developer
Sep 4, 2020
276
2,124
I'd suggest not excluding all those special characters from your page/save input (though perhaps you weren't but just demonstrating).

I've regularly used - ? _ in page and save names for instance, and was frustrated in the few games I've encountered that didn't allow them.

One introduced the restriction midway through development, which also caused the existing saves not to load, so you'd still need AONs after_load solution to sort old saves in that case.

Better to limit your exclusions to the minimum set likely to cause problems I would think?

And whatever you decide to exclude should be documented/publicised so the players know of it. This hasn't always been the case in my experience.
Was mostly demonstrating. Was planning to experiment with all of them to see what truly breaks saves. Hopefuly it's just brackets.
 

SomeGuyWithNoImagination

❤️ Keep comfy. ❤️
Game Developer
May 31, 2019
615
2,290
I did a bit more digging and I see as Anne said, that the method given by crabs doesn't work for {}. However, we all missed the correct solution.

The correct solution as the Renpy docs suggest and use in some other screens is directly below the escaping characters section that Anne linked.

"The !q conversion flag ensures that text tags are properly quoted, so that displaying a string will not introduce unwanted formatting constructs."

Just do text "[save_name_text!q]": and {} no longer cause issues.

Crabs was most of the way there but two characters from it being perfect

There is no need to manually escape strings with string replacement or to limit the characters used. It seems to be perfect from my testing.
 
Last edited:

0x52

Ren'Py Magician
Modder
Donor
Game Developer
May 23, 2019
1,615
6,170
Uhm... there's a very easy fix for this. Because you can just disable "subtitutions" on a text displayable.

Just replace:
Python:
text FileSaveName(slot)
With:
Python:
text FileSaveName(slot) substitute False
This means Ren'Py fill not apply any translation or replace variables in this text

To be fair... I can imagine people don't know about this feature, because I'm unable to find this in the documentation. But I know it has been possible since at least Ren'Py 6.99

Edit:
I said this prevents it from applying styling, but is doesn't.
 
Last edited: