Problem with rollback and inventory.

Epadder

Programmer
Game Developer
Oct 25, 2016
568
1,061
It's something more complex than that. If it was only due to the nesting, then the bug would happen even with other values, but you said that it happen only when it pass from 1 to 0 ; it's surely more from X to 0, whatever the value of X.

But honestly my brain isn't working enough, I totally miss what could be the cause :(
Well two things happen in the old code from the same method.

It subtracted the amount of x item in the inventory.

If that item was below 0 it then removes it.

When testing if you 'watch backpack.inventory[0].amount', you get an 'eval failed' after you choose to ask on the date, because it's been removed from the inventory.

Once you roll back from that point though, the item comes back, but it's amount remained 0.

That's why I thought to work around that by only removing the item if the amount would fall below 0 and not changing the known amount.

*shrug* :)
 
  • Like
Reactions: drKlauz

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,382
15,290
When testing if you 'watch backpack.inventory[0].amount', you get an 'eval failed' after you choose to ask on the date, because it's been removed from the inventory.
Yes, yes, yes... That's what I wasn't able to put my finger on...

Like the item is created directly when added into the list, there's only one copy of it, the one in the list. Therefore, when it's removed from the list, it also disappear, and it's what make Ren'py goes out of track.

[Note: It's really a quick summary of the process ; the intent is to explain the problem, not Ren'py mechanism]
With each interaction, Ren'py look at the known entities, and for each one it keep the actual value, this in order to restore it in case of rollback. This lead to Ren'py to duplicate the objects ; therefore, an object isn't really deleted, it's just the value of the variable that become null.
[in the console]
Code:
a = "string"
id( a )
b = a
id( a ) == id( b )
delattr( store, "a" )
a
b
a = b
a
id( a )
It's, globally, the same principle than with the rollback process.
The variable a is duplicated ; they don't have the same value, but point to the same memory space. Then a is deleted, but b continue to exist. In the end, we give back it's value to a, and it's still the same memory space like its id show.

But here, when the object is removed from the list, it stop to be a "known entity", because it only exist as a duplicated copy in Ren'py rollback stack. Therefore, Ren'py can't take a new copy of its new value ; it don't exist, Ren'py don't know that something have changed.
Then, when you rollback, the inventory list retrieve its value before the deletion. Among those value, there's the pointer to the object... an object that haven't been included in the last rollback preparation, so that will not be restored to its value and still have amount at 0.

I assume that if you do something like :
  1. amount = 2
  2. amount = 1
  3. amount = 0
  4. whatever.
when you rollback to 3, you'll have the value of 0, and the value will still be 0 if you rollback to 2. But once you'll rollback to 1, the value will suddenly become 2, because at this time the object was still a "known entity", and Ren'py correctly took care of it during the rollback preparation.


As I imply previously, my brain isn't at its best actually, still hope that my explanation are understandable.
 

Vanderer

Active Member
Game Developer
Dec 8, 2017
626
2,005
So if i understand correctly.

The rollback feature save only when there is an interaction.

Now, i am not sure what renpy consider an interaction but from my understanding, it is when the user interact with the game, like a click to pass to the following line of text, or inputing a name, or choosing a menu choice.

At each interaction, renpy create a copy of the values in memory. Like variables and lists.

The thing I am not sure about:

Is the problem is because the copy is made after the code is resolved, and so the object is deleted, and since at that moment the object do not exist anymore, cannot save it's value?

But then, is renpy did not save the object and it's value in the rollback stack just before the choice is made? After all, there was an interaction just before choosing the menu choice.

I suspect that it has something to do with the rollback not making actual copy the data separatly but pointing to the object in memory who doesn't exist anymore, but my brain cannot get around it fully.
 
Last edited:

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,382
15,290
Now, i am not sure what renpy consider an interaction but from my understanding, it is when the user interact with the game, like a click to pass to the following line of text, or inputing a name, or choosing a menu choice.
Perhaps still a little too confusing, but here's .
This said, you can just consider that every time there's a Ren'py statement, there's also an interaction. It's a little more complex than that, but unless you start to intensively use Python, or user defined statements and displayable, you don't really need to understand it more.


At each interaction, renpy create a copy of the values in memory. Like variables and lists.
With some limits, but yes, it's how the rollback works. There's a floating list with X entries (with X being the maximal number of step for the rollback), and the X last interactions have an entry associated with them in this list.
Rich once wrote a good explanation regarding the rollback mechanism, but I failed to retrieve it.


Is the problem is because the copy is made after the code is resolved, and so the object is deleted, and since at that moment the object do not exist anymore, cannot save it's value?
Basically yes, but it's a little more complicated.
Normally when an object is deleted, Ren'py use a special value to represent that, what permit it to have a rollback that works correctly even in this case. But here, the object was stored in a list, therefore Ren'py have no place where to put this "it's deleted" value.
It's the combination between the deletion and the fact that the object was existing only in a list, that led to the problem you had.

But there's no real way to correct this, just ways to adapt the code for it to works. The obvious answer would be to have a copy of the object outside of the list, but it would result in an useless pollution of the store.
The solution gave by Epadder works perfectly and feel way better. Just remember to update the screen where the inventory is displayed with something like :
Code:
screen whatever():

    for e in backpack.inventory:
        if e.amount != 0:
            text "[e.amount] [e.name]"
It will display only the elements that exist at least once in the inventory.
This way, you can keep the object when the amount reach 0, what will solve the rollback problem, but they'll not be shown in the inventory, which offer a cleaner view for the player.


But then, is renpy did not save the object and it's value in the rollback stack just before the choice is made? After all, there was an interaction just before choosing the menu choice.
It did it, but when you'll rollback at this step, for a strange reason Ren'py clearly don't know that it should restore the value. It restore the content of the list, which make the flower object reappear, but it don't restore this object itself, keeping it to its last value, so 0.
 
  • Like
Reactions: Vanderer

Vanderer

Active Member
Game Developer
Dec 8, 2017
626
2,005
It will display only the elements that exist at least once in the inventory.
This way, you can keep the object when the amount reach 0, what will solve the rollback problem, but they'll not be shown in the inventory, which offer a cleaner view for the player.
I will, even tough it seems to work without the "if e.amount != 0:"

For the rest, nicely explained, thank you. It help me understand what's going.