Page 1 of 1

[SOLVED] How to define persistable objects?

Posted: Wed Apr 03, 2024 10:11 pm
by AVNSnax
As the immortal Lloyd Bridges said in Airplane, looks like I picked the wrong week to quit drinking.

I started down a rathole trying to implement my own achievements system that would wrap the rudimentary Steam-flavored achievement class. Everything was working fine until I tried to persist my list of achievements.

I defined a class called Achievement that has a bunch of features like grouped achievements (achievements that are granted when all member achievements have been earned), flags for visibility of an achievement's title and description, alert and menu support, etc. I'm aware of a couple of commercially available add-ons but wanted to roll my own because, "How hard could it be?"

First, Ren'Py complains that my class can't be pickled. Being the trained developer that I am, I had implemented a class for the achievement itself, and a separate class to manage the achievement groups. Nope, the ancillary class must be hashable. So I implement a __hash__ method. Can't make that work, so I gave in and folded the goals object into Achievement itself. One simple class. Nope: my class "must support equality comparison." So I implemented an __eq__ method. Now, it's telling me "TypeError: Achievement is not safe for use in persistent." without any kind of hint as to why. Can someone point me in the right direction for clues? Or should I just bite the bullet and implement my own persistence method?

Re: How to define persistable objects?

Posted: Thu Apr 04, 2024 12:40 am
by Ocelot
As persistent data is loaded before init python blocks are run, persistent data should only contain types that are native to Python or Ren'Py. Alternatively, classes that are defined in python early blocks can be used, provided those classes can be pickled and implement equality.
My approach in similar cases is to have an immutable class for achievement itself, which would refer to some nestled dictionary in persistent data to store mutable data (like achievement progress, if it was granted, etc.)

Re: How to define persistable objects?

Posted: Thu Apr 04, 2024 1:14 am
by AVNSnax
Ocelot wrote: Thu Apr 04, 2024 12:40 am
As persistent data is loaded before init python blocks are run, persistent data should only contain types that are native to Python or Ren'Py. Alternatively, classes that are defined in python early blocks can be used, provided those classes can be pickled and implement equality.
My approach in similar cases is to have an immutable class for achievement itself, which would refer to some nestled dictionary in persistent data to store mutable data (like achievement progress, if it was granted, etc.)
Hmmm. In the class file, I just changed init python: to python early: and it worked. Almost. Things were fine until I tried to quit and Ren'Py went to write out the persistent data. To be persisted, RevertableSet({Achievement:...}) must support equality comparison.

Re: How to define persistable objects?

Posted: Thu Apr 04, 2024 7:46 am
by jeffster
AVNSnax wrote: Thu Apr 04, 2024 1:14 am
Ocelot wrote: Thu Apr 04, 2024 12:40 am
As persistent data is loaded before init python blocks are run, persistent data should only contain types that are native to Python or Ren'Py. Alternatively, classes that are defined in python early blocks can be used, provided those classes can be pickled and implement equality.
My approach in similar cases is to have an immutable class for achievement itself, which would refer to some nestled dictionary in persistent data to store mutable data (like achievement progress, if it was granted, etc.)
Hmmm. In the class file, I just changed init python: to python early: and it worked. Almost. Things were fine until I tried to quit and Ren'Py went to write out the persistent data. To be persisted, RevertableSet({Achievement:...}) must support equality comparison.
Docs:
https://renpy.org/doc/html/persistent.html
As persistent data is loaded before init python blocks are run, persistent data should only contain types that are native to Python or Ren'Py. Alternatively, classes that are defined in python early blocks can be used, provided those classes can be pickled and implement equality.

Re: How to define persistable objects?

Posted: Thu Apr 04, 2024 5:29 pm
by AVNSnax
So, the final solution was to use plain ol' dictionaries and lists for the achievement data, and relegate the class object to being a mere data handler. The conversion process was pretty painful, wordy, and backwards. There really ought to be a better way to persist objects, especially for operations that don't easily lend themselves to being just lists of dictionaries.