Ren'Py Filtering distribution content ?

TDoddery

Member
Apr 28, 2020
175
167
When telling Renpy to build/compile a distribution ZIP, is there a way to have it automatically ignore images which are not actually being used ?

I'm asking because the messy way I work means I have a heap of obsolete image versions sitting in the game/images directory with similar names to ones actually being used, and it would take a long time to delete them manually (because of needing to check against the script which ones are not actually being used).

Thanks for any help.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,964
16,210
When telling Renpy to build/compile a distribution ZIP, is there a way to have it automatically ignore images which are not actually being used ?
Short answer: No, and it will never be possible.


Explained anwer :
It's possible to do that with regularly used images, but for it to works with dynamic images, layered images, images relying on interpolation and "inline expression images", Ren'py would have to :
  • Parse all the code, while keeping the value assigned to every single variables ;
  • Analyze all the images in order to find what variables they use, and build all the possible variations for those images, according to the collected values ;
  • Compare the list of build image name, with the list of available images.
Yet, this would only works for values that are assigned an hard coded value, not for values that are the result of a computation.

If you've this code :
Code:
init python:
    def computedGirlPose( gn ):
       mood = getattr( store.girls, gn ).mood
       if mood == 1: return "angry"
       elif mood == 2: return "annoyed"
       [...]
      else:  return "neutral"

label sarahRoom:
    $ girlName = "sarah"
    $ girlPose = computeGirlPose( girlName )
    show girlDefaultIdle
    [...]
It's possible to build the list of all the values assigned to girlName, but to build the list of all the values assigned to girlPose, Ren'py would have to have it's own internal Python parser and analyzer, which would need as much works than building Ren'py itself.

And yet, even if it was done, Ren'py would still have to analyze the computeGirlPose code in regard of all the possible values for girls.sarah.mood and girls.annie.mood, to ensure that all the values return by the function are effectively possibles.
And who know, perhaps that girls.XXX.mood is in fact a property, and so a computed value. So, Ren'py would have to do again all what it did with computeGirlPose, in order to only keep the effectively possible values.
And so on recursively until it found that there's no more computations.


And this have to be done for every single images used in the game, whatever through the scene and show statements, but also the images added in a screen, the images used for a imagebutton and an imagemap, and the images used in a used defined displayable.

Lets say that it was possible to write the code needed for this, for a game like Super Powered, that massively rely on interpolated images, the process would take hours to perform.


It's your duty as author of the game, to keep your folders clean.
 

sillyrobot

Engaged Member
Apr 22, 2019
2,161
1,881
On a somewhat tangential note, in the following snippet:

Code:
label sarahRoom:
    $ girlName = "sarah"
    $ girlPose = computeGirlPose( girlName )
    show girlDefaultIdle
Is is possible to check if girlDefaultIdle actually resolves to an image that exists or intercept the exception and insert a valid image on failure?
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,964
16,210
Is is possible to check if girlDefaultIdle actually resolves to an image that exists or intercept the exception and insert a valid image on failure?
Yes, but in the same time, no.

There's the configuration variable that can do this. But its use is really limited. It isn't called when the file associated to an image is missing, but when there's no image declared (explicitly or implicitly) with the expected name.
Therefore,
Code:
label start:
    show DoNotExist
will trigger the callback, but
Code:
image IsMissing = "MissingImage.jpg"

label start:
    show IsMissing
will not, and throw an error saying that the file is missing. It's probably the same if the error happen inside a dynamic or layered image, or inside an interpolated image.
And unlike what the documentation lets think, using is of no help here, because it can only tell if the image is loadable or not ; at no time it offer you the possibility to change the name of the file.


As for intercepting the exception... The version 7.3.5 introduced a configuration callback for that, , but it's designed to treat the exception differently, not to change the behavior. So it's not really possible to do something with it.

If the callback return False, Ren'py will handle the exception by itself, and show the traceback. And if the callback return True, Ren'py will ignore the statement... which would be good if the exception wasn't thrown after every single statement, because each time Ren'py try to display the image.
Therefore if you have something like :
Code:
image IsMissing = "DoNotExist1.jpg"
label start:
    show DoNotExist
    "Welcome to my game, please wait an instant, I init the values."
    call initVar
    "Alright, we are ready to go, so first thing first..."
    $ mcName = renpy.input( "Please, what's your name ?" )
    "Glad to encounter you, [mcName]."
    "We are ready to start."
    hide DoNotExist
    [...]
All the part between show DoNotExist and hide DoNotExist will be skip. It's worse if you use scene, because all the following scene will throw the error and so be ignored...

In top of that, the callback don't have raw arguments, but already formatted ones. So it would be a pain in the ass to identify the effective problem and solve it.
For the code above, the first argument would be :
While loading <'Image' u'DoNotExist1.jpg'>:
File "game/script.rpy", line 4, in script
"Welcome to my game, please wait an instant, I init the values."
IOError: Couldn't find file 'DoNotExist1.jpg'.
[note: yes, the error do not appear at the show line.]
The second would be the full traceback, totally useless in this case. And the last argument would be the path to the traceback file.

Therefore, you have no possibility to know what image triggered the exception, and so no possibility to hide it and save the day.


This said, theoretically there's still a possibility. You need to overwrite renpy.show and renpy.scene. Something like :
[WARNING, totally untested, it's pure theoretical code]
Code:
init python:
    def myShow( name, *args, **kwargs ):
        try:
            showOriginal( name, *args, **kwargs )
        except:
            renpy.hide( name )
            showOriginal( placeholder )

    showOriginal = renpy.show
    renpy.show = myShow

image placeholder = "placeholder.jpg"
I don't remember the Python equivalent scene (which is NOT renpy.scene) but the principle is the same ; you try to display the image, and in case of error you show something else.
 
  • Thinking Face
Reactions: sillyrobot

sillyrobot

Engaged Member
Apr 22, 2019
2,161
1,881
Yes, but in the same time, no.

There's the configuration variable that can do this. But its use is really limited. It isn't called when the file associated to an image is missing, but when there's no image declared (explicitly or implicitly) with the expected name.
Therefore,
Code:
label start:
    show DoNotExist
will trigger the callback, but
Code:
image IsMissing = "MissingImage.jpg"

label start:
    show IsMissing
will not, and throw an error saying that the file is missing. It's probably the same if the error happen inside a dynamic or layered image, or inside an interpolated image.
And unlike what the documentation lets think, using is of no help here, because it can only tell if the image is loadable or not ; at no time it offer you the possibility to change the name of the file.


As for intercepting the exception... The version 7.3.5 introduced a configuration callback for that, , but it's designed to treat the exception differently, not to change the behavior. So it's not really possible to do something with it.

If the callback return False, Ren'py will handle the exception by itself, and show the traceback. And if the callback return True, Ren'py will ignore the statement... which would be good if the exception wasn't thrown after every single statement, because each time Ren'py try to display the image.
Therefore if you have something like :
Code:
image IsMissing = "DoNotExist1.jpg"
label start:
    show DoNotExist
    "Welcome to my game, please wait an instant, I init the values."
    call initVar
    "Alright, we are ready to go, so first thing first..."
    $ mcName = renpy.input( "Please, what's your name ?" )
    "Glad to encounter you, [mcName]."
    "We are ready to start."
    hide DoNotExist
    [...]
All the part between show DoNotExist and hide DoNotExist will be skip. It's worse if you use scene, because all the following scene will throw the error and so be ignored...

In top of that, the callback don't have raw arguments, but already formatted ones. So it would be a pain in the ass to identify the effective problem and solve it.
For the code above, the first argument would be :

[note: yes, the error do not appear at the show line.]
The second would be the full traceback, totally useless in this case. And the last argument would be the path to the traceback file.

Therefore, you have no possibility to know what image triggered the exception, and so no possibility to hide it and save the day.


This said, theoretically there's still a possibility. You need to overwrite renpy.show and renpy.scene. Something like :
[WARNING, totally untested, it's pure theoretical code]
Code:
init python:
    def myShow( name, *args, **kwargs ):
        try:
            showOriginal( name, *args, **kwargs )
        except:
            renpy.hide( name )
            showOriginal( placeholder )

    showOriginal = renpy.show
    renpy.show = myShow

image placeholder = "placeholder.jpg"
I don't remember the Python equivalent scene (which is NOT renpy.scene) but the principle is the same ; you try to display the image, and in case of error you show something else.
Thanks. I was afraid it wasn't easy since I couldn't find any examples in the wild. Now I know why. The theoretical approach looks potentially workable.