init python: if renpy.android: settings.default("crashdefendersetting", 0) # Attempts at fixing the texture leak plaguing # android devices, mainly running on Android11 & Android 12. class Android11TextureLeakFix(NoRollback): def __init__(self, limit=100): self.statements = 0 self.set_mode(settings.get("crashdefendersetting")) def set_mode(self, mode): if mode == 3: self.limit = 15 elif mode == 2: self.limit = 25 elif mode == 1: self.limit = 55 else: self.limit = 0 def __call__(self, name): if renpy.is_init_phase() or self.limit == 0: return self.statements += 1 if self.statements > self.limit: self.statements = 0 # Big thanks to Andykl (https://github.com/Andykl) # for finding the issue and inventing this workaround. # https://github.com/renpy/renpy/issues/3643 cache = renpy.display.im.cache cache_size = cache.get_total_size() cache_limit = cache.cache_limit * 0.95 if cache_size >= cache_limit: if config.developer: print("Cache limit reached, purging cache... ({}/{})\n{}".format(cache_size, cache_limit, renpy.get_filename_line())) cache.clear() if renpy.game.interface is not None: if config.developer: print("Statements limit reached, cleaning textures... ({})\n{}".format(self.limit, renpy.get_filename_line())) renpy.game.interface.full_redraw = True renpy.game.interface.restart_interaction = True if renpy.display.draw is not None: renpy.display.draw.kill_textures() renpy.display.render.free_memory() crashdefender = Android11TextureLeakFix() config.statement_callbacks.append(crashdefender) python early hide: import functools if renpy.windows: # On windows, Renpy does not support backslashes in some of its functions, # but because the code needs to be platform-independent, # we require to monkey patch those functions in order # to remain compatible with all platforms without losing functionality. @renpy.pure def _loadable(filename): filename = filename.replace("\\", "/") return renpy.loader.loadable(filename) renpy.loadable = _loadable # renpy.list_files does not use cached results, let's fix that. @functools.cache def _list_files(common=False): rv = [fn for dir, fn in renpy.loader.listdirfiles(common) if not fn.startswith("saves/")] rv.sort() return rv renpy.list_files = _list_files # Default focus behaviour restarts the interaction whenever # any element that contains a tooltip is being hovered or unhovered. # Restarting interactions in a short timespan causes massive lag spikes, # in order to fix it, we'll refresh just the tooltip screen and skip the rest. def set_focused(widget, arg, screen): global _tooltip renpy.display.focus.argument = arg renpy.display.focus.screen_of_focused = screen if screen is not None: renpy.display.focus.screen_of_focused_names = { screen.screen_name[0], screen.tag } else: renpy.display.focus.screen_of_focused_names = set() renpy.game.context().scene_lists.focused = widget renpy.display.tts.displayable(widget) # Figure out the tooltip. _tooltip = widget._get_tooltip() if widget else None # setattr(renpy.store, "widget", widget) # DEBUG # if renpy.display.focus.tooltip != new_tooltip: # renpy.display.focus.tooltip = new_tooltip # renpy.display.focus.capture_focus("tooltip") # renpy.exports.restart_interaction() # if renpy.display.focus.tooltip is not None: # renpy.display.focus.last_tooltip = renpy.display.focus.tooltip # renpy.display.focus.screen_of_last_focused_names = renpy.display.focus.screen_of_focused_names renpy.display.focus.set_focused = set_focused