saving displayables (plus some stuff about renpy.checkpoint)

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Message
Author
User avatar
PyTom
Ren'Py Creator
Posts: 16096
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

Re: Are displayables saveable?

#16 Post by PyTom »

Aenakume wrote:Uh oh, that might make things difficult. ^_^; i thought that's what renpy.call_in_new_context() did.
call_in_new_context creates a new context, which has its own set of game state. This includes the scene lists that comprise the layers, the call stack, and so on.

Only the outermost context (the default one) can be saved in and rolled-back in, since otherwise, you'd have to be able to start execution in the middle of a block of python code. And that's something we can't do. Code needs to be designed to work around this.

(Loading a game performs a rollback during the load process, so it will always go back at least to the start of a python statement.)

Re: the map engine

Your best bet is to structure it as an event driven system. The displayable has two states: static and dynamic. In the static state, it just shows the map, and doesn't let people interact with it. When the user calls a special function, it switches to the dynamic state, which lets people walk about the screen. This function returns when an "event" occurs... an event could be an encounter with a character or a hotspot or something.

Each event is an object giving the parameters of the encounter. This is returned from the special function, and passed into Ren'Py code that decides what to do with it, jumping to the Ren'Py code for the appropriate encounter. When the encounter is done, it jumps back to a label that calls the special function again, starting the cycle anew.

Each python block should contain at most one logical interaction with the user. These logical interactions could consist of multiple ui.interacts (for example, purchasing an item in a shop could require confirmation), but they should be something the user sees as a single event to roll back over.

Code inside callbacks (and similarly, code run from renpy.Displayables) should be relatively simple. Its job is to decide two things:

- Is this the end of the interaction?
- If so, what should the interaction return?

Once the return value from ui.interact is known, it can be dealt with by normal Ren'Py code, running in the outermost context.
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
Software > Drama • https://www.patreon.com/renpytom

herenvardo
Veteran
Posts: 359
Joined: Sat Feb 25, 2006 11:09 am
Location: Sant Cugat del Vallès (Barcelona, Spain)
Contact:

Re: Are displayables saveable?

#17 Post by herenvardo »

It was 5 a.m. when I posted my last reply... after having enough sleep, I realized I was over-complicating the issue for my Shop code. The only thing the function does is to modify some objects (the player's inventory, the money, and sometimes the gear). I don't really care about whether the function restarts on rollback or save-load, as long as the variables make sense.
So, what I'd really need to know is what will happen with my variables. To make things simple, let's see this:

Code: Select all

def __call__(whatever...):
    # We may refer to the (relevant) variables' state at this point as "initial"
    # here goes my pre-processing stuff (basically, filtering out useless items from the shop's stock)
    while (res is not None):
        # the hearth of the method: there is another loop inside here displaying an UI
        # the inner loop is broken when the player buys or sell something, but not on other interactions (like "hovered")
        renpy.checkpoint() # Let's refer to the state at this point as "checkpoint n" (where n would be the iteration count)
    # And let's call the state here "final", although should be the same as the last checkpoint
Now, let's assume the user goes into the shop, performs four transactions (that's 4 iterations of the while loop, hence 4 calls to renpy.checkpoint()), and then rolls back one step (one hit of Page Up or one wheel-step in the mouse): what would be the state of my variables at that point? The initial one (as the flow recedes to whatever ren'py statement that had called the Shop object)? The state after the 3rd transaction (if checkpoint() saves the state in some way, which would be ideal on this case)? The final state (ie: rollback doesn't alter the variables)? Something else (if so, it'd be interesting to know what and why)? In essence, if the state is correct, there is no difference between re-starting the function or going on from where it was, so it wouldn't be an issue when the program flow re-enters it. It's all about state.

Even if this fails, I'm already brewing up a backup plan which is absurd enough to work :P But before I start nesting functions at several depths :? I'd rather be sure that I need to do so.
I have failed to meet my deadlines so many times I'm not announcing my projects anymore. Whatever I'm working on, it'll be released when it is ready :P

User avatar
PyTom
Ren'Py Creator
Posts: 16096
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

Re: saving displayables (plus some stuff about renpy.checkpoint)

#18 Post by PyTom »

When the user hits rollback, it will go back to the start of the current python block. Calling renpy.checkpoint() more than once is irrelevant... all it does is flag the current block as something that we might be interested in rolling back to.

Again, nested python functions is almost certainly the problem, rather than the solution. The solution is to do more in Ren'Py code, rather than python code.
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
Software > Drama • https://www.patreon.com/renpytom

herenvardo
Veteran
Posts: 359
Joined: Sat Feb 25, 2006 11:09 am
Location: Sant Cugat del Vallès (Barcelona, Spain)
Contact:

Re: saving displayables (plus some stuff about renpy.checkpoint)

#19 Post by herenvardo »

PyTom wrote:When the user hits rollback, it will go back to the start of the current python block. Calling renpy.checkpoint() more than once is irrelevant... all it does is flag the current block as something that we might be interested in rolling back to.

Again, nested python functions is almost certainly the problem, rather than the solution. The solution is to do more in Ren'Py code, rather than python code.
Well, the actual problem is that I need rollback in a piece of python code that I can't reliably turn into ren'py code: all the ways I've come up to switch it (you can review my previous posts in this thread) seem to break in one way or another... The solution here can't be to switch to ren'py code, simply because, for this case, that's not doable. So, my last resource is to get Ren'py's rollback out of the way and mimic its behavior for that piece of code. That sounds quite weird, let me explain:
(Note: I'm not implementing this yet, so some details might be a bit vague).
First of all, at the start of my __call__ function, I would define some key-mappings, in principle for the mouse wheel and the Page Up / Page Down keys (actually, I'll retrieve the actual keys to bind from config, so if the user has customized them my code will react properly :D ). I'll have to define a function to be called by such keymaps, and here is where I'm going to nest functions (so the handler has access to the stuff inside __call__). This gets rollback "out of the way".
Next, I'll have to mimic rollback (from the "key-mapped" function). Fortunatelly, I've an ace on my sleeve here ^^. The __call__ method is keeping track of all the "transactions" (buying/selling actions, and even attempts to buy something that fail due to lack of money) being performed through the shopping UI, which I can trivially convert into a stack. And, once I have a stack with all the stuff that has happened, implementing a undo/redo mechanism (which, in essence, is the same as rollback/roll forward) is something I've done quite often before, so it shouldn't pose no challenge :D.
I'll have to check some stuff in the docs every now and then; but as long as Ren'py honors my key mappings (I remember chronoluminaire having some issues with that when he was working on his unit & tile engine), I see no way in which this approach might fail. Whatever happens (now my pride is speaking for me), I'll get rollback working the way I want :twisted:
I have failed to meet my deadlines so many times I'm not announcing my projects anymore. Whatever I'm working on, it'll be released when it is ready :P

User avatar
DaFool
Lemma-Class Veteran
Posts: 4171
Joined: Tue Aug 01, 2006 12:39 pm
Contact:

Re: saving displayables (plus some stuff about renpy.checkpoint)

#20 Post by DaFool »

I'm just skimming this since I don't understand much of it, but is it possible to make the inventory / shopkeeping a separate framework that can be shared like the RPG battle engine and walkabout engine?

On topic, you might want to dig around in these forums for monele's Magical Boutique source codes. AFAIK there was a rollback problem which was taken care of... (something to do with curries or new contexts or other things I don't quit get). There should also be a simple events framework for MB2 lying about as well.

herenvardo
Veteran
Posts: 359
Joined: Sat Feb 25, 2006 11:09 am
Location: Sant Cugat del Vallès (Barcelona, Spain)
Contact:

Re: saving displayables (plus some stuff about renpy.checkpoint)

#21 Post by herenvardo »

DaFool wrote:I'm just skimming this since I don't understand much of it, but is it possible to make the inventory / shopkeeping a separate framework that can be shared like the RPG battle engine and walkabout engine?
All the code (although not the artwork) of my project (including the inventory / shops stuff, but also my own RPGish combat system, the quest-tracking subengine, the Chat & Emotional AI Engine, and so on) will be heavily documented and made publicly available once it is done and working. I still have to decide under which license will I put it, but its going to be either LGPL or something even softer (most probably a Ren'py-like license).
DaFool wrote:On topic, you might want to dig around in these forums for monele's Magical Boutique source codes. AFAIK there was a rollback problem which was taken care of... (something to do with curries or new contexts or other things I don't quit get). There should also be a simple events framework for MB2 lying about as well.
I'll try to dig that out, but I still think that overriding the rollback keys with my shop-aware rollback-like behavior will be the sanest solution. I'm not sure, however, what will be the best for Aenakume's walkabout engine.
I have failed to meet my deadlines so many times I'm not announcing my projects anymore. Whatever I'm working on, it'll be released when it is ready :P

Post Reply

Who is online

Users browsing this forum: Alex, Ocelot