Crash on reload on versions prior to 8.2

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.
Post Reply
Message
Author
goldo
Regular
Posts: 127
Joined: Mon Jan 23, 2017 8:23 am
Contact:

Crash on reload on versions prior to 8.2

#1 Post by goldo »

Hi all, so for a long while now I have been stuck with a nasty bug which causes a completely silent crash on auto-reload. The game actually reloads, but then it's just a rotating blue circle cursor followed by CTD.

I have tried a lot of things and at this stage the bug is painfully easy to reproduce:

- I delete all Ren'Py SDK files including persistent files and the 'AppData' renpy folder.
- I install a new version of Ren'py.
- I run "Tutorial", press Shit+R on the title screen, and it crashes.

Interestingly, I run into this issue on every Ren'Py version I've tried except the old 7.3.5 and the brand new 8.2. Sadly, my game is not compatible with 8.2 for other reasons, so what I am looking for is a way to at least log that error so that I can finally do something about it.

Obviously the error has to do with something on my PC, otherwise everyone would have it. Off the top of my head, I can only think about a few things:
- Maybe I'm missing something on how to completely uninstall Ren'py and there is some leftover registry entry or whatever that is screwing with my install
- My computer has two drives, C:/ is restricted to my wife as a user and D:/ to me (where I run Ren'py); Windows is installed on C:/ -> Perhaps something is being accessed without the proper rights?
- Something changed in 8.2 that removed the issue; I'd like to figure out what!

Anyway, I guess the first step would be to catch that error. Is there any way to trace what's happening with auto-reload before it crashes?

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Crash on reload on versions prior to 8.2

#2 Post by jeffster »

Look for files "log.txt", "errors.txt" and "traceback.txt".

Also if you run Ren'Py from command line (terminal), there could be some diagnostic messages.

goldo
Regular
Posts: 127
Joined: Mon Jan 23, 2017 8:23 am
Contact:

Re: Crash on reload on versions prior to 8.2

#3 Post by goldo »

Yup, the game generates no traceback and no errors files. When I say it's a silent crash, I mean it's really really silent.

The same happens with a command prompt.

The only error that I can trace is in Windows event viewer, the one mentioned in the original thread:

Code: Select all

Faulting application name: pythonw.exe, version: 0.0.0.0, time stamp: 0x61a6b501
Faulting module name: MMDevAPI.DLL, version: 10.0.19041.1023, time stamp: 0x00c1ffe2
Exception code: 0xc0000005
Fault offset: 0x000000000001b33b
Faulting process id: 0x6d38
Faulting application start time: 0x01d80bff24ddc8d2
Faulting application path: D:\RenPy\renpy-7.4.9-sdk\lib\windows-x86_64\pythonw.exe
Faulting module path: C:\Windows\SYSTEM32\MMDevAPI.DLL
Report Id: 72c56750-4c60-4c3e-840b-68ccbe45cb73
Faulting package full name:
Faulting package-relative application ID:
Every crash generates that one. That's it.

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Crash on reload on versions prior to 8.2

#4 Post by jeffster »

There can be incompatibilities

(1) between different Ren'Py versions,
(2) and between Python executables and used libraries (DLL in Windows).

In this case there's probably the incompatibility between the Python executable
D:\RenPy\renpy-7.4.9-sdk\lib\windows-x86_64\pythonw.exe

and some system library
C:\Windows\SYSTEM32\MMDevAPI.DLL

Theoretically it's possible to find a version of MMDevAPI.DLL that would work well with this Python executable, but as we are talking about DLL in the system folder, replacing it with another versions might lead to other problems (other programs crashing).

You can try to put the necessary version of DLL in some local folder (maybe where the Python executable is?), I'm not sure, but it might work as that local DLL would be used.

But more adequate solution would be either use the old version of Ren'Py that works with your game, or re-write the parts of your game for the latest Ren'Py.

For example, ProportionalScale can be implemented as CDD
https://renpy.org/doc/html/cdd.html

In __init__ you calculate how the image should be scaled (if the width to height ratio is different for the original and the scaled image, then should the original image be cropped to fit, or a part of the target rectangle should be left blank?). You set the zoom factor accordingly.

In render() you just create the render with the target width & height, and blit there the original image with "zoom" property.
renpy.load_image:
https://renpy.org/doc/html/cdd.html#renpy.load_image
zoom:
https://renpy.org/doc/html/cdd.html#renpy.Render.zoom

goldo
Regular
Posts: 127
Joined: Mon Jan 23, 2017 8:23 am
Contact:

Re: Crash on reload on versions prior to 8.2

#5 Post by goldo »

Thank you for the suggestions! Yes, I think my best bet is to replace ProportionalScale with something more long-lasting. I'll look into CDDs although it looks way above my level of understanding for now... :(

goldo
Regular
Posts: 127
Joined: Mon Jan 23, 2017 8:23 am
Contact:

Re: Crash on reload on versions prior to 8.2

#6 Post by goldo »

Here is my attempt to replicate ProportionalScale() using more modern methods:


Edited thanks to jeffster's remarks

Code: Select all

class ProportionalScale(renpy.Displayable):
        '''Resizes a renpy image to fit into the specified width and height.
        The aspect ratio of the image will be conserved.'''
        def __init__(self, imgname, maxwidth=None, maxheight=None, **properties):
            super(ProportionalScale, self).__init__()
            self.width = maxwidth or config.screen_width
            self.height = maxheight or config.screen_height
            if not renpy.exists(imgname):
                imgname = "backgrounds/not_found.webp"
            self.image = Transform(imgname, size=(self.width, self.height), fit="contain", **properties)

        def render(self, width, height, st, at):
            return renpy.render(self.image, self.width, self.height, st, at)

        def visit(self):
            return [ self.image ]

        def per_interact(self):
            renpy.redraw(self, 0)
I'd be lying if I said I understand everything it's doing, but it seems to work well enough. I would welcome any feedback!
Last edited by goldo on Mon Mar 04, 2024 7:44 pm, edited 1 time in total.

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Crash on reload on versions prior to 8.2

#7 Post by jeffster »

It looks close to what I was thinking about, maybe even better.

If you want some nitpicking comments: :-)

I'm not sure if you need methods "visit" and "per_interact".
One simple picture might work without them.
But if they help prediction (I don't know), then maybe it's OK to have them.

Do you need the conversions to int: "int(maxwidth), int(maxheight)"?
"maxwidth" and "maxheight" are probably integers already.

You don't use "self.imgname" anywhere except in __init__, hence it doesn't have to be saved as the object property ("self.").

You don't need to check "if renpy.exists(imgname)", instead it should be a bit more efficient to use "try-except" feature:

Code: Select all

            try:
                self.image = Transform(imgname, size=(maxwidth, maxheight), fit="contain", **properties)
            except:
                self.image = Transform("backgrounds/not_found.webp", size=(maxwidth, maxheight), fit="contain", **properties)
(If "try" block causes an error (i.e. an exception), then "except" block is executed).

Using value "if not None" instead of this:

Code: Select all

if not maxwidth: maxwidth = config.screen_width
...
maxwidth
can be done with or operator:

Code: Select all

maxwidth or config.screen_width
You don't need to get "self.width, self.height" in render() because you already set the resulting width & height (as maxwidth & maxheight), right?

And you never use "bilinear" (you probably don't need to, in modern RenPy).

All that together:

Code: Select all

init python:
    class ProportionalScale(renpy.Displayable):
            '''Resizes a renpy image to fit into the specified width and height.
            The aspect ratio of the image will be preserved.'''
            def __init__(self, img, maxwidth=None, maxheight=None, **properties):
                super(ProportionalScale, self).__init__()
                self.w = maxwidth or config.screen_width
                self.h = maxheight or config.screen_height
                try:
                    self.image = Transform(img, size=(self.w, self.h),
                                            fit="contain", **properties)
                except:
                    self.image = Transform("backgrounds/not_found.webp", size=(self.w, self.h),
                                            fit="contain", **properties)

            def render(self, width, height, st, at):
                return renpy.render(self.image, self.w, self.h, st, at)

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Crash on reload on versions prior to 8.2

#8 Post by jeffster »

...And you probably can use just Transform() with size parameter (instead of a custom displayable). :-)

goldo
Regular
Posts: 127
Joined: Mon Jan 23, 2017 8:23 am
Contact:

Re: Crash on reload on versions prior to 8.2

#9 Post by goldo »

jeffster wrote: Mon Mar 04, 2024 10:18 am If you want some nitpicking comments: :-)
Absolutely!
I'm not sure if you need methods "visit" and "per_interact".
One simple picture might work without them.
But if they help prediction (I don't know), then maybe it's OK to have them.
According to the documentation, 'visit()' is needed for prediction, and 'per_interact()' for rollback. I haven't tried without though.
Do you need the conversions to int: "int(maxwidth), int(maxheight)"?
"maxwidth" and "maxheight" are probably integers already.
It's just to make this thing foolproof in case I accidentally feeds it a calculation like '0.7* button_size' or something...
You don't use "self.imgname" anywhere except in __init__, hence it doesn't have to be saved as the object property ("self.").
That's true, originally the exist check was in the render method so that's why I was saving it but now there is no reason to. I will edit it out.
You don't need to check "if renpy.exists(imgname)", instead it should be a bit more efficient to use "try-except" feature:

Code: Select all

            try:
                self.image = Transform(imgname, size=(maxwidth, maxheight), fit="contain", **properties)
            except:
                self.image = Transform("backgrounds/not_found.webp", size=(maxwidth, maxheight), fit="contain", **properties)
Sadly 'Transform("this file doesn't even exist", size=(maxwidth, maxheight), fit="contain", **properties)' is a valid instruction so no error will be thrown in that case. I've tried to put the try... except... check in render() but it didn't work, another error was coming up when predicting.
Using value "if not None" instead of this:

Code: Select all

if not maxwidth: maxwidth = config.screen_width
...
maxwidth
can be done with or operator:

Code: Select all

maxwidth or config.screen_width
Nice, I didn't know that trick!
You don't need to get "self.width, self.height" in render() because you already set the resulting width & height (as maxwidth & maxheight), right?
But then I need to save maxwidth and maxheight as attributes, right?
And you never use "bilinear" (you probably don't need to, in modern RenPy).
Thanks, I will remove it.
All that together:

Code: Select all

init python:
    class ProportionalScale(renpy.Displayable):
            '''Resizes a renpy image to fit into the specified width and height.
            The aspect ratio of the image will be preserved.'''
            def __init__(self, img, maxwidth=None, maxheight=None, **properties):
                super(ProportionalScale, self).__init__()
                self.w = maxwidth or config.screen_width
                self.h = maxheight or config.screen_height
                try:
                    self.image = Transform(img, size=(self.w, self.h),
                                            fit="contain", **properties)
                except:
                    self.image = Transform("backgrounds/not_found.webp", size=(self.w, self.h),
                                            fit="contain", **properties)

            def render(self, width, height, st, at):
                return renpy.render(self.image, self.w, self.h, st, at)
I'll edit the code above to reflect the changes I will keep. Thanks a lot!

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Crash on reload on versions prior to 8.2

#10 Post by jeffster »

You are welcome.

I just tested and it appears that Transform(...size) can take floats. No manual :-) int() conversion is necessary.

About prediction:
I never experienced performance problems, using CDDs without visit(), even with slow (storage-class) HDDs.
When it's just the same image, the screen gets predicted and probably the image gets loaded when necessary.
And then the displayable could be reused:
https://renpy.org/doc/html/screen_optim ... able-reuse

I tested Transform() with non-existing displayable, and indeed it just gives blank result (white or maybe zero-size).
However, isn't it OK to have just white surface instead of some "not found" image?
I would do that instead of an extra check for existence. But you do you of course.
Just note that Transform() can take not only a file name, but other displayables (existing as well), and that might come useful.

Hence my version:

Code: Select all

init python:
    class ProportionalScale(renpy.Displayable):
            def __init__(self, img, maxwidth=None, maxheight=None, **properties):
                super(ProportionalScale, self).__init__()
                self.w = maxwidth or config.screen_width
                self.h = maxheight or config.screen_height
                self.image = Transform(img, size=(self.w, self.h), fit="contain", **properties)

            def render(self, width, height, st, at):
                return renpy.render(self.image, self.w, self.h, st, at)

show ProportionalScale("smile.jpg", 766.667, 800)
Or maybe

Code: Select all

show Transform("smile.jpg", size=(766.667, 800), fit="contain")
...Or maybe subclass Transform()...

Anyway, good luck with new versions, updated for latest RenPy. :-)

goldo
Regular
Posts: 127
Joined: Mon Jan 23, 2017 8:23 am
Contact:

Re: Crash on reload on versions prior to 8.2

#11 Post by goldo »

Thanks! I was under the impression that floats would behave as multiples of the available width and height (as it does when using xsize 0.5 or ysize 0.8 in plain ATL). Maybe only if lower than 1.0? But that would be weirdly inconsistent.

AnthonyOlson
Newbie
Posts: 4
Joined: Mon Jul 03, 2023 6:28 am
Contact:

Re: Crash on reload on versions prior to 8.2

#12 Post by AnthonyOlson »

All good now?
Last edited by AnthonyOlson on Mon Apr 08, 2024 2:37 am, edited 2 times in total.

goldo
Regular
Posts: 127
Joined: Mon Jan 23, 2017 8:23 am
Contact:

Re: Crash on reload on versions prior to 8.2

#13 Post by goldo »

Yes, although I hesitate to mark the topic as solved since I still have the crashes on reload in prior versions - although I took the rather painful step of porting the whole UI to 8.2 to work around it...

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Crash on reload on versions prior to 8.2

#14 Post by jeffster »

goldo wrote: Thu Mar 07, 2024 7:44 am Thanks! I was under the impression that floats would behave as multiples of the available width and height (as it does when using xsize 0.5 or ysize 0.8 in plain ATL). Maybe only if lower than 1.0? But that would be weirdly inconsistent.
No, the documentation doesn't mention such duality, so I think this "size" differs from that "xysize" in this regard.
goldo wrote: Yes, although I hesitate to mark the topic as solved since I still have the crashes on reload in prior versions - although I took the rather painful step of porting the whole UI to 8.2 to work around it...
Why do you care about old versions?
If you distribute a Ren'Py game, the engine gets packed inside, so players will use the version you have packed.

goldo
Regular
Posts: 127
Joined: Mon Jan 23, 2017 8:23 am
Contact:

Re: Crash on reload on versions prior to 8.2

#15 Post by goldo »

jeffster wrote: Fri Mar 08, 2024 10:23 am Why do you care about old versions?
If you distribute a Ren'Py game, the engine gets packed inside, so players will use the version you have packed.
I used to care very much because the whole game was made with one of the legacy UI themes and before the switch to Python 3, so every version update has been very painful in terms of refactoring. In general, Ren'Py is not the best for retrocompatibility.

However, because of this latest problem I decided to redo the code and the whole UI using the new GUI system, so now I don't care as much, but I would have been happy not to have to do that. Basically a week of intensive work to end up with the game UI more or less where it used to be, or even a little worse in some places.

Post Reply

Who is online

Users browsing this forum: Bing [Bot], decocloud, Google [Bot], Li yuanlin