How do you create a patch that changes a line of code in a Ren'py game?

hakarlman

Engaged Member
Jul 30, 2017
2,099
3,274
I'm making a renpy game with code like this:
Code:
$ personTitle = "teacher"
How does one create a patch to change "teacher" to "gardener"?

RPYC files are encrypted.

I want to release a patch for my game that can change this variable.
 

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,496
7,082
I have a ren'py game with code like this:
Code:
$ personTitle = "teacher"
How does one create a patch to change "teacher" to "gardener"?
If you're playing the game and you'd like to make this change within the context of your current play, the best way is to open up the developer console and just execute
Code:
personTitle = "teacher"
That will change the variable in your current game (and the changed value will go into your next save.)

To enable the console, edit "options.rpy" and insert
Code:
config.console = True
(or change False to True if a line like that already exists.)

If you're trying to permanently modify the game so that it will always use "gardener" instead of "teacher", you can't (easily) swap out just one line of code. You'd either have to create a new version of the file that's in that will overwrite the existing file. It's also possible to replace a section of code that (a) starts with a label that's always "jumped to" and (b) ends with a jump to a label by creating a new file with a new label at the beginning and the replaced content, and then use "config.label_overrides" ( ), but that has to be done with some care.

RPYC files are encrypted.
RPYC files are not generally encrypted. They are compiled versions of the original RPY files. There are a number of different tools available that will "decompile" the RPYC files into equivalent RPY files. One such is
 

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,496
7,082
Note - if I make the (probably invalid) assumption that you're just trying to change "teacher" to "gardener", there is one patch technique that is pretty straightforward. Create an rpy file in the game directory containing:

Code:
label after_load:
    try:
        if personTitle is "teacher":
            personTitle = gardener
    except:
        pass
    return
Then, start a game, save it and reload it. The "after_load" label is called after a save is loaded - you can use this technique to alter any variables that are in the save before the game resumes. Of course, this will fail if the game already has an after_load label in it...
 
  • Like
Reactions: t727

hakarlman

Engaged Member
Jul 30, 2017
2,099
3,274
I should've specified that I'm the one actually making the ren'py game, so I have full control of the code.

I want to release a patch for the game after I release it.

I want the patch to look like it's a hack.
 

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,496
7,082
Ah, ok. That's a different matter. You showed code
Code:
$ personTitle = "teacher"
Here's what I'd do. In your "unpatched" code somewhere, have:
Code:
init:
    define personTitleType = "teacher"
and change your line to
Code:
$ personTitle = personTitleType
Then, your patch file would contain
Code:
init 10:
    define personTitleType = "gardener"
The point is that the "init 10" code in your patch will run after the "init" code in your unpatched game, and so it will update personTitleType to the new value. All of that runs before your script ("init" phase comes before "script" phase). Thus, when the patch file is in place, when your
Code:
$ personTitle = personTitleType
runs, personTitleType will have a different value then when the patch file is not in place.

So you're not changing the actual line of code that runs, you're changing the constant it uses.
 
  • Like
Reactions: t727

OhWee

Forum Fanatic
Modder
Game Developer
Jun 17, 2017
5,734
29,119
This is sorta kinda but not really related to the OP question, but it's a tool I use to 'rescue' old saves when a developer adds a new variable check to a game, which ends up 'breaking' old saves.

Normally, if Renpy comes across a variable check for an undefined variable, you get that nasty message. There's a way to 'define' a variable with a default value 'after the fact' in these cases.

Code:
init python:
    try: NewVariableName
    except NameError: NewVariableName = None
As Rich noted above, inits are done (in order) before the rest of the script is executed. In this case, the game checks to see if NewVariableName has been defined/assigned a value. If not, the except NameError fills in the value with None.

Note that "None" could be any value you choose, say "1", "Bobby", you get the idea.

I usually add this init block to the beginning of an .rpy, so that I can find it fairly easily later, should I need to add more of these for more new variables.
Note that it doesn't need to be in script.rpy, it could be in another file. I've inserted this undefined variable check in screens.rpy in my mods before, since screens.rpy isn't changed nearly as much as script.rpy is... or you could create a new. rpy file with just this init python block.

Of course, you still need to decide what an initial value should be if say you end up skipping the part of the game where the variable value is assigned using an old save. One trick I've used is to add a 'question' to the start of a subsequent day, should a player end up skipping something. i.e.
Code:
if NewVariableName == None
    "When this happened yesterday, what did you do?"
    menu:
        "I did option 1":
            $ NewVariableName = 1
        "I did option 2":
            $ NewVariableName = 2
You could also add an 'abbreviated' version of the scene here, i.e. just 'replay' that scene from the previous day, or perhaps jump back to that scene, with a

'If NewVariableName== "None"
JumpToNextDayWhenDone = "Yes"​
inserted into the code before you make the jump to the previous scene, then add a
If JumpToNextDayWhenDone == "Yes"
Jump back to next day​
At the end of the scene from that previous day where you added the new variable.

This way, players don't have to replay the entire game, or even the entire previous day, when you've added a new variable...

It's not perfect, but this can at least appease players that hate replaying games over and over every time you add a new variable you hadn't contemplated before, which would otherwise 'break' an old save.
 
  • Like
Reactions: t727

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,384
15,293
Code:
init python:
    try: NewVariableName
    except NameError: NewVariableName = None
Sorry, but you got a "nasty message" because you do nasty things ; you don't need to rely on exceptions here. The none nasty way is :
Code:
init python:
    if not "varName" in globals():
        varName = None
Which will works great since Ren'py import the whole default store as global for every Python block or singleton line. But like every variable is in fact an attribute of the store object, it's more clean to do it like this :
Code:
init python:
    if not hasattr( store, "NewVariableName" ):
        NewVariableName = None
Then, like said in the how-to (second part of A-4), if you replace the direct assignation by the use of setattr, it even offer you a way to automatize this availability control, including assignation of the effective default value, with just three lines in the after_load label (that you write then forget), plus one line for each update.
 
  • Like
Reactions: t727

OhWee

Forum Fanatic
Modder
Game Developer
Jun 17, 2017
5,734
29,119
Sorry, but you got a "nasty message" because you do nasty things ; you don't need to rely on exceptions here. The none nasty way is :
Code:
init python:
    if not "varName" in globals():
        varName = None
Which will works great since Ren'py import the whole default store as global for every Python block or singleton line. But like every variable is in fact an attribute of the store object, it's more clean to do it like this :
Code:
init python:
    if not hasattr( store, "NewVariableName" ):
        NewVariableName = None
Then, like said in the how-to (second part of A-4), if you replace the direct assignation by the use of setattr, it even offer you a way to automatize this availability control, including assignation of the effective default value, with just three lines in the after_load label (that you write then forget), plus one line for each update.
Except that I'm NOT getting a nasty message when I use the code to 'cover' for added variables that aren't defined in previous saves. Try it and get back with me, you'll see that it works. I've used this in two separate published mods now, without issue.

Sure, the best thing is to code all your variables up front so that you don't get the 'undefined' error when playing in the first place, but developers add variables to new versions all the time, and then expect people to replay the entire game, rather than 'covering' for their lack of foresight.

My way DOES work, you are just citing other ways to do the same thing.

I'm not going to debate you on which way is better, I'm just pointing out what has worked well for me. The entire point here is to help help developers find ways to resolve such issues, as this has been an ongoing problem.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,384
15,293
Why going all emotional and seeing what is just someone trying to help you improve your coding capabilities, as a personal attack ?


Except that I'm NOT getting a nasty message [...]
It's not because you intercepted it with the except clause, before it reach the exception handler of Ren'py, that you didn't get the nasty message. You don't see it, which is not the same thing than not having it.
You can turn it in all the way you want, the exception is raised, so the nasty message exist.


Try it and get back with me, you'll see that it works.
I didn't said that it wasn't working, just that you don't need to rely on an exception when there's a built-in function wrote especially to do what you want to do.
Exception exist to cover exceptional/unexpected situation. By example I had to add one in my variable viewer because someone found funny to mess with Ren'py translation. It was a problem because Ren'py tell me that there's a translation, but in the same time yell at me if I try to access it. So, intercepted the exception raised to not annoy users and let them still enjoy the tool.
But here it's neither exceptional nor unexpected. In the contrary, you expect your code to fail for anyone using an old save. So exception aren't the way to do, even if they works ; and they works.


but developers add variables to new versions all the time, and then expect people to replay the entire game, rather than 'covering' for their lack of foresight.
Reason why I wrote the how-to I linked above. I have the knowledge, they have the problem... and after reading it, they share this part of my knowledge and don't have the problem anymore.


My way DOES work, you are just citing other ways to do the same thing.
Let me phrase it otherwise: If you wear a 50cm layer of foam all around you, you don't care if a car hit you when you cross a road. Yet, it doesn't mean that it's not easier and better to just look both side before crossing the said road.
The first solution is the fact to rely on exception, the second one is the fact to just use setattr.


The entire point here is to help help developers find ways to resolve such issues, as this has been an ongoing problem.
And so ? Between your answer and mine, they now have three different ways to resolve this issue... You should be happy, not angry.
 

OhWee

Forum Fanatic
Modder
Game Developer
Jun 17, 2017
5,734
29,119
Anne, you seem to be the one taking this personally. I'm just pointing out that my way works. Not saying your way doesn't work.

You are trying to get me to debate you on this. I won't. Peace out.
 

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,496
7,082
Interestingly enough, the "do an explicit 'if' test" versus "catch an exception" debate also splits the modern Python world. There are those that insist that using the exception approach is "more Python-like." The main argument for that is that, historically, many of the "if it exists" tests used the exception mechanism under the hood. Odd, perhaps, but true. So the "just use the exception directly" people argued that their approach was more efficient, since the other approaches were incurring both the exception penalty as well as some additional overhead.

Suffice it to say that both approaches are completely valid. And with modern Python (and modern machines) the difference is negligible unless you were somehow doing this a LOT, which isn't the case in a Ren'py game.

One argument that I can see for @anne O'nymous 's approach is that for a Python newbie, it probably reads a bit more cleanly - someone without much Python experience can look at the code and will probably figure out what it's doing. For people with more Python experience, as soon as they see NameError in @OhWee 's code, they know exactly what's being tested for, since that's an incredibly common Python-ism. But for someone new to Python, I can see that it's a bit less obvious as to what's going on.

But, at the end of the day, when you're writing code, write it in the way that makes the most sense to YOU. After all, you're the one that's going to be maintaining it over time. I'm an old fart - I've been programming since the early 1970's. (Yes, really.) I can't tell you the number of times that I've written something slightly tricky for "performance reasons," and then come back to it 6 months or a year later and said "wtf does this code do and how does it do it?" It's amazing how long code you didn't expect to linger can end up lingering. So pick whatever mechanism is the most obvious to you, and we can skip the rest of the religious debate. XD
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,384
15,293
Interestingly enough, the "do an explicit 'if' test" versus "catch an exception" debate also splits the modern Python world.[...]
Suffice it to say that both approaches are completely valid.[...]
Apparently I really didn't expressed my thoughts clearly enough. Despite the, probably unavoidable, paradigm bias between " " and " ", it wasn't at all what I (tried and apparently failed to) talk about.

What I reproach to the exception use made by @OhWee is not the paradigm behind it, but the fact that it's a closed and hard coded approach ; you need to address the variable literally. This mean that even if the structure stay the same, you have to repeat it for every single variable you'll add.
You've one decade more of experience than me, so you surely know how, while writing code, repeating yourself can quickly become boring and discouraging. Without talking about the mess that a bunch of copy/paste can become. And, alas, that's where lead the exception use.
There's authors who base their whole game on flags and so add tens of variables with each updates. They will perhaps try it one time, but most of them will never try it again. Between the forgot variables and the one tested while the assignation in the except clause stay like it was in the copy/paste (and so address another variable), the more variables they'll have to deal with, the more their attempt will have risk to fail. In the end their update risk to stay incompatible with previous saves, so why bother with it in this case ?

At the opposite you've two other ways to do the same things, but ways that don't need you to literally address the variables. Instead of saying, "I want to try this variable", you're saying, "I want to try the variable named like this". This indirect approach mean that you can simplify your code, reusing the same test/assignation couple inside a loop.
I didn't wrote it at first and linked to it instead, but it lead to something like this :
Code:
    for varName in [ "newVar1", "newVar2", "newVar3", ... ]:
        if hasattr( store, varName ) is False:
            setattr( store, varName, False )
The author will still need to build the list, but it will be less discouraging than repeating the same piece of code over and over. It also have less possibility of error than a bunch of copy/paste.
And starting with this, if really they'll have to add a bunch of new variables with each updates, they'll probably figure by themselves how to insert the loop in another one ; using a list of variables list. What will offer them support to any old save, whatever how many updates occurred since the player played for the last time.

That's why I tried to say, and nothing else. You (generic) code your game like you want. I said it more that once, whatever can say "good practices" and paradigms, the only good practice is to write your code like you feel it. It's only this way that you'll enjoy doing it, remember more easily why you wrote things like this, and so write a reliable code.
There's exceptions to this, when your way is really too messy, but they are exceptions, and a correct use of exception handling clearly isn't part of this.


And with modern Python (and modern machines) the difference is negligible unless you were somehow doing this a LOT, which isn't the case in a Ren'py game.
As proof that it's negligible effect, Ren'py itself use more than ten exceptions to control its own process. jump and call are, beyond other things, performed by the raise of an exception.
 
  • Like
Reactions: t727 and Rich

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,496
7,082
All valid points - apologies for misinterpreting. Suffice it to say there are multiple ways of doing things. ESPECIALLY with Ren'py. LOL
 
  • Like
Reactions: t727

random.person

Active Member
Aug 11, 2017
802
1,292
A programming discussion on a forum for adult games.
This is heaven!

Your discussion was pretty interesting, both as a way to know more about Ren'Py and as miscellaneous information on coding practices. I didn't know people held strong preferences over using if or exceptions, lol.
Well, I guess it was to be expected since the only thing I'm decent at is some C. Damn you OOP...
And damn you Python. Not having to declare types should be a crime!

Jokes apart, to reply to the OP, the by far easiest solution would be to put the title declaration in a separate script and to distribute an alternative version of the script that has the altered title. I mean, I don't think it could get any simpler than simply copypasting an edited script.
 
  • Like
Reactions: t727

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,496
7,082
A programming discussion on a forum for adult games
Hey, this is the "Programming & Development" forum on F95, after all... LOL

I didn't know people held strong preferences over using if or exceptions, lol.
Well, I guess it was to be expected since the only thing I'm decent at is some C.
With a background in C, you've probably seen (or heard about) the flame wars about where braces should go. Every language has its schisms... LOL

This is just a guess (more like a wild-ass guess), but some of that may have come from preconceived biases of people rooted in other languages where exceptions are a lot more expensive, and thus rarely used. I think they're much lighter-weight in Python than in Java or C#, for example, and MUCH lighter-weight than in C++. So to "native Pythoners," their common use may be less the, um, exception than the rule. (Sorry - I just couldn't resist! XD)
 

random.person

Active Member
Aug 11, 2017
802
1,292
Hey, this is the "Programming & Development" forum on F95, after all... LOL



With a background in C, you've probably seen (or heard about) the flame wars about where braces should go. Every language has its schisms... LOL

This is just a guess (more like a wild-ass guess), but some of that may have come from preconceived biases of people rooted in other languages where exceptions are a lot more expensive, and thus rarely used. I think they're much lighter-weight in Python than in Java or C#, for example, and MUCH lighter-weight than in C++. So to "native Pythoners," their common use may be less the, um, exception than the rule. (Sorry - I just couldn't resist! XD)
You're absolutely right. And were programmers not lazy thus using default automatic indentation in IDEs we'd still be probably discussing the one true way to indent code, lol.
Indeed I get salty every time I have to edit again a Ren'Py script and relaunch the game because they decided that tabs are not allowed.
Another reason why I won't ever get into python: this tab discrimination in 2018 is not acceptable! =P
 
  • Like
Reactions: t727

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,496
7,082
You're absolutely right. And were programmers not lazy thus using default automatic indentation in IDEs we'd still be probably discussing the one true way to indent code, lol.
So now the debate has just shifted to "what should be the settings for the automatic indentor in the IDE. :)
Another reason why I won't ever get into python: this tab discrimination in 2018 is not acceptable! =P
At least this was a decision I understood, as it removed any of the problems of ambiguity as to "how many spaces equal a tab" if you had a file with mixed "tab indenting" and "space indenting," which would definitely have happened.

If you use an editor that "understands" Python, you can hit the tab and shift-tab key and it spaces and un-spaces for you. (Atom is very good at that.)
 

random.person

Active Member
Aug 11, 2017
802
1,292
So now the debate has just shifted to "what should be the settings for the automatic indentor in the IDE. :)

At least this was a decision I understood, as it removed any of the problems of ambiguity as to "how many spaces equal a tab" if you had a file with mixed "tab indenting" and "space indenting," which would definitely have happened.

If you use an editor that "understands" Python, you can hit the tab and shift-tab key and it spaces and un-spaces for you. (Atom is very good at that.)
For this stuff I usually run Notepad++ and I could set it to use whitespaces instead of tabs, but I don't know if I can for specific file extensions only. I surely don't want that in general.

In my humble opinion, it was a stupid choice. It's useful only to avoid people mixing whitespaces and tabs. But, guess what, it's their problem if they do. Those who use one rarely even think of using the other and if many people need to work on the same file they just need to agree on which style to use or, you know, just turn spaces to tabs and viceversa with the editor every time they need.
Besides I hate wasting resources, why using 3+ spaces (actual number according to preference) to do the job of a single \t character? Why should I press left or right arrow 3+ time when I could press it only once per indentation level?
(I know, we're not in the 70s and we have memory to waste but it's my pet peeve nonetheless)

But I'm digressing, so I'll stop here, I think the OP has gotten more than what he/she signed up for =P
 
  • Like
Reactions: t727

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,384
15,293
This is just a guess (more like a wild-ass guess), but some of that may have come from preconceived biases of people rooted in other languages where exceptions are a lot more expensive, and thus rarely used.
Not even talking about older people and their, "exceptions ? You mean like when the program crash and you need to pass a whole week finding which 'if' you need to add to prevent it ?"


I think they're much lighter-weight in Python than in Java or C#, for example, and MUCH lighter-weight than in C++.
Not sure that there's a language where they are as much light-weighted than in Python. I mean, Python internally use an exception to tell itself that it have reached the end of an iteration, or to warn "hasattr" and "getattr" that there's no attribute with the given name. And in Python, both iterations and these two built-in functions are massively used...
 
  • Like
Reactions: t727 and Rich