/
uvifera
/
MyFirstRepo
Обзор
Документация
Войти
/
uvifera
/
MyFirstRepo
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/renpy/main.py
716 строк
22 KB
Акимова Ангелина
Добавлена документация визуальной новеллы и проект
23 май 2026, 19:21
23 май 2026, 19:21
8141a8d
Код
Авторство
О чём код?
# Copyright 2004-2023 Tom Rothamel <pytom@bishoujo.us> # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import division, absolute_import, with_statement, print_function, unicode_literals from renpy.compat import PY2, basestring, bchr, bord, chr, open, pystr, range, round, str, tobytes, unicode # * from typing import Tuple, List, Dict, Set, Optional, Iterable, Any import os import sys import time import zipfile import gc import linecache import renpy import renpy.game as game last_clock = time.time() def log_clock(s): global last_clock now = time.time() s = "{} took {:.2f}s".format(s, now - last_clock) renpy.display.log.write(s) if renpy.android and not renpy.config.log_to_stdout: print(s) # Pump the presplash window to prevent marking # our process as unresponsive by OS renpy.display.presplash.pump_window() last_clock = now def reset_clock(): global last_clock last_clock = time.time() def run(restart): """ This is called during a single run of the script. Restarting the script will cause this to change. """ reset_clock() # Reset the store to a clean version of itself. renpy.python.clean_stores() log_clock("Cleaning stores") # Init translation. renpy.translation.init_translation() log_clock("Init translation") # Rebuild the various style caches. renpy.style.build_styles() # @UndefinedVariable log_clock("Build styles") renpy.sl2.slast.load_cache() log_clock("Load screen analysis") # Analyze the screens. renpy.display.screen.analyze_screens() log_clock("Analyze screens") if not restart: renpy.sl2.slast.save_cache() log_clock("Save screen analysis") # Prepare the screens. renpy.display.screen.prepare_screens() log_clock("Prepare screens") if not restart: renpy.pyanalysis.save_cache() log_clock("Save pyanalysis.") renpy.game.script.save_bytecode() log_clock("Save bytecode.") # Handle arguments and commands. if not renpy.arguments.post_init(): # We use 'exception' instead of exports.quit # to not call quit label since it's not nessesary. raise renpy.game.QuitException() if renpy.config.clear_lines: renpy.scriptedit.lines.clear() # Sleep to finish the presplash. renpy.display.presplash.sleep() # Re-Initialize the log. game.log = renpy.python.RollbackLog() # Switch contexts, begin logging. game.contexts = [ renpy.execution.Context(True) ] # Jump to an appropriate start label. if game.script.has_label("_start"): start_label = '_start' else: start_label = 'start' game.context().goto_label(start_label) try: renpy.exports.log("--- " + time.ctime()) renpy.exports.log("") except Exception: pass # Note if this is a restart. renpy.store._restart = restart # We run until we get an exception. renpy.display.interface.enter_context() log_clock("Running {}".format(start_label)) renpy.execution.run_context(True) def load_rpe(fn): with zipfile.ZipFile(fn) as zfn: autorun = zfn.read("autorun.py") if fn in sys.path: sys.path.remove(fn) sys.path.insert(0, fn) exec(autorun, {'__file__': os.path.join(fn, "autorun.py")}) def choose_variants(): if "RENPY_VARIANT" in os.environ: renpy.config.variants = list(os.environ["RENPY_VARIANT"].split()) + [ None ] # type: ignore renpy.display.emulator.early_init_emulator() return renpy.config.variants = [ None ] if renpy.android: # @UndefinedVariable renpy.config.variants.insert(0, 'mobile') # type: ignore renpy.config.variants.insert(0, 'android') # type: ignore import android # type: ignore import math import pygame_sdl2 as pygame from jnius import autoclass # type: ignore # Manufacturer/Model-specific variants. try: Build = autoclass("android.os.Build") manufacturer = Build.MANUFACTURER model = Build.MODEL print("Manufacturer", manufacturer, "model", model) if manufacturer == "Amazon" and model.startswith("AFT"): print("Running on a Fire TV.") renpy.config.variants.insert(0, "firetv") # type: ignore except Exception: pass # Are we running on OUYA or Google TV or something similar? package_manager = android.activity.getPackageManager() if package_manager.hasSystemFeature("android.hardware.type.television"): print("Running on a television.") renpy.config.variants.insert(0, "tv") # type: ignore renpy.config.variants.insert(0, "small") # type: ignore return # Running on a chromebook. try: PythonSDLActivity = autoclass("org.renpy.android.PythonSDLActivity") if PythonSDLActivity.isChromebook(): print("Running on ChromeOS.") renpy.config.variants.insert(0, 'chromeos') # type: ignore except Exception: pass # Otherwise, a phone or tablet. renpy.config.variants.insert(0, 'touch') # type: ignore pygame.display.init() info = renpy.display.get_info() diag = math.hypot(info.current_w, info.current_h) / android.get_dpi() # type: ignore print("Screen diagonal is", diag, "inches.") if diag >= 6: renpy.config.variants.insert(0, 'tablet') # type: ignore renpy.config.variants.insert(0, 'medium') # type: ignore else: renpy.config.variants.insert(0, 'phone') # type: ignore renpy.config.variants.insert(0, 'small') # type: ignore elif renpy.ios: renpy.config.variants.insert(0, 'mobile') # type: ignore renpy.config.variants.insert(0, 'ios') # type: ignore renpy.config.variants.insert(0, 'touch') # type: ignore from pyobjus import autoclass # type: ignore UIDevice = autoclass("UIDevice") idiom = UIDevice.currentDevice().userInterfaceIdiom print("iOS device idiom", idiom) # idiom 0 is iPhone, 1 is iPad. We assume any bigger idiom will # be tablet-like. if idiom >= 1: renpy.config.variants.insert(0, 'tablet') # type: ignore renpy.config.variants.insert(0, 'medium') # type: ignore else: renpy.config.variants.insert(0, 'phone') # type: ignore renpy.config.variants.insert(0, 'small') # type: ignore elif renpy.emscripten: import emscripten # type: ignore import re # web renpy.config.variants.insert(0, 'web') # type: ignore # mobile userAgent = emscripten.run_script_string(r'''navigator.userAgent''') mobile = re.search('Mobile|Android|iPad|iPhone', userAgent) if mobile: renpy.config.variants.insert(0, 'mobile') # type: ignore # Reserve android/ios for when the OS API is exposed # if re.search('Android', userAgent): # renpy.config.variants.insert(0, 'android') # if re.search('iPad|iPhone', userAgent): # renpy.config.variants.insert(0, 'ios') # touch touch = emscripten.run_script_int(r''' ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)''') if touch == 1: # mitigate hybrids (e.g. ms surface) by restricting touch to mobile if mobile: renpy.config.variants.insert(0, 'touch') # type: ignore # large/medium/small # tablet/phone # screen.width/height is auto-adjusted by browser, # so it can be used as a physical sizereference # (see also window.devicePixelRatio) # e.g. Galaxy S5: # - physical / OpenGL: 1080x1920 # - web screen: 360x640 w/ devicePixelRatio=3 ref_width = emscripten.run_script_int(r'''screen.width''') ref_height = emscripten.run_script_int(r'''screen.height''') # medium reference point: ipad 1024x768, ipad pro 1336x1024 (browser "pixels") if mobile: if (ref_width < 768 or ref_height < 768): renpy.config.variants.insert(0, 'small') # type: ignore renpy.config.variants.insert(0, 'phone') # type: ignore else: renpy.config.variants.insert(0, 'medium') # type: ignore renpy.config.variants.insert(0, 'tablet') # type: ignore else: renpy.config.variants.insert(0, 'large') # type: ignore else: renpy.config.variants.insert(0, 'pc') # type: ignore renpy.config.variants.insert(0, 'large') # type: ignore def android_searchpath(): """ Determines the searchpath on Android. """ # The default gamedir, in private. renpy.config.searchpath = [ renpy.config.gamedir ] # The public android directory. if "ANDROID_PUBLIC" in os.environ: android_game = os.path.join(os.environ["ANDROID_PUBLIC"], "game") if os.path.exists(android_game): renpy.config.searchpath.insert(0, android_game) # Asset packs. packs = [ "ANDROID_PACK_FF1", "ANDROID_PACK_FF2", "ANDROID_PACK_FF3", "ANDROID_PACK_FF4", ] for i in packs: if i not in os.environ: continue assets = os.environ[i] for i in [ "renpy/common", "game" ]: dn = os.path.join(assets, i) if os.path.isdir(dn): renpy.config.searchpath.append(dn) print("Android search paths:" , " ".join(renpy.config.searchpath)) def main(): gc.set_threshold(*renpy.config.gc_thresholds) log_clock("Bootstrap to the start of init.init.") renpy.game.exception_info = 'Before loading the script.' # Clear the line cache, since the script may have changed. linecache.clearcache() # Get ready to accept new arguments. renpy.arguments.pre_init() # Init the screen language parser. renpy.sl2.slparser.init() # Init the config after load. renpy.config.init() # Reset live2d if it exists. try: renpy.gl2.live2d.reset() except Exception: pass # Set up variants. choose_variants() renpy.display.touch = "touch" in renpy.config.variants if (renpy.android or renpy.ios) and not renpy.config.log_to_stdout: print("Version:", renpy.version) log_clock("Early init.") # Note the game directory. game.basepath = renpy.config.gamedir renpy.config.searchpath = [ renpy.config.gamedir ] # Find the common directory. commondir = renpy.__main__.path_to_common(renpy.config.renpy_base) # E1101 @UndefinedVariable if os.path.isdir(commondir): renpy.config.searchpath.append(commondir) renpy.config.commondir = commondir else: renpy.config.commondir = None # Add path from env variable, if any if "RENPY_SEARCHPATH" in os.environ: renpy.config.searchpath.extend(os.environ["RENPY_SEARCHPATH"].split("::")) if renpy.android: renpy.config.commondir = None android_searchpath() # Load Ren'Py extensions. for dir in renpy.config.searchpath: # @ReservedAssignment for fn in sorted(os.listdir(dir)): if fn.lower().endswith(".rpe"): load_rpe(dir + "/" + fn) # Generate a list of extensions for each archive handler. archive_extensions = [ ] for handler in renpy.loader.archive_handlers: for ext in handler.get_supported_extensions(): if not (ext in archive_extensions): archive_extensions.append(ext) # Find archives. for dn in renpy.config.searchpath: if not os.path.isdir(dn): continue for i in sorted(os.listdir(dn)): base, ext = os.path.splitext(i) # Check if the archive does not have any of the extensions in archive_extensions if not (ext in archive_extensions): continue renpy.config.archives.append(base) renpy.config.archives.reverse() # Initialize archives. renpy.loader.index_archives() # Start auto-loading. renpy.loader.auto_init() log_clock("Loader init.") # Initialize the log. game.log = renpy.python.RollbackLog() # Initialize the store. renpy.store.store = sys.modules['store'] # type: ignore # Set up styles. game.style = renpy.style.StyleManager() # @UndefinedVariable renpy.store.style = game.style # Run init code in its own context. (Don't log.) game.contexts = [ renpy.execution.Context(False) ] game.contexts[0].init_phase = True renpy.execution.not_infinite_loop(60) # Load the script. renpy.game.exception_info = 'While loading the script.' renpy.game.script = renpy.script.Script() if renpy.session.get("compile", False): renpy.game.args.compile = True # type: ignore # Set up error handling. renpy.exports.load_module("_errorhandling") if renpy.exports.loadable("tl/None/common.rpym") or renpy.exports.loadable("tl/None/common.rpymc"): renpy.exports.load_module("tl/None/common") renpy.config.init_system_styles() renpy.style.build_styles() # @UndefinedVariable log_clock("Loading error handling.") # If recompiling everything, remove orphan .rpyc files. # Otherwise, will fail in case orphan .rpyc have same # labels as in other scripts (usually happens on script rename). if (renpy.game.args.command == 'compile') and not (renpy.game.args.keep_orphan_rpyc): # type: ignore for (fn, dn) in renpy.game.script.script_files: if dn is None: continue if not os.path.isfile(os.path.join(dn, fn + ".rpy")) and not os.path.isfile(os.path.join(dn, fn + "_ren.py")): try: name = os.path.join(dn, fn + ".rpyc") os.rename(name, name + ".bak") except OSError: # This perhaps shouldn't happen since either .rpy or .rpyc should exist pass # Update script files list, so that it doesn't contain removed .rpyc's renpy.loader.cleardirfiles() renpy.game.script.scan_script_files() # Load all .rpy files. renpy.game.script.load_script() # sets renpy.game.script. log_clock("Loading script.") if renpy.game.args.command == 'load-test': # type: ignore start = time.time() for i in range(5): print(i) renpy.game.script = renpy.script.Script() renpy.game.script.load_script() print(time.time() - start) sys.exit(0) renpy.game.exception_info = 'After loading the script.' # Find the save directory. if renpy.config.savedir is None: renpy.config.savedir = renpy.__main__.path_to_saves(renpy.config.gamedir) # E1101 @UndefinedVariable if renpy.game.args.savedir: # type: ignore renpy.config.savedir = renpy.game.args.savedir # type: ignore # Init the save token system. renpy.savetoken.init() # Init preferences. game.persistent = renpy.persistent.init() game.preferences = game.persistent._preferences for i in renpy.game.persistent._seen_translates: # type: ignore if i in renpy.game.script.translator.default_translates: renpy.game.seen_translates_count += 1 if game.persistent._virtual_size: renpy.config.screen_width, renpy.config.screen_height = game.persistent._virtual_size # Init save locations and loadsave. renpy.savelocation.init() try: # Init save slots and save tokens. renpy.loadsave.init() renpy.savetoken.upgrade_all_savefiles() log_clock("Loading save slot metadata.") # Load persistent data from all save locations. renpy.persistent.update() game.preferences = game.persistent._preferences log_clock("Loading persistent.") # Clear the list of seen statements in this game. game.seen_session = { } # Initialize persistent variables. renpy.store.persistent = game.persistent # type: ignore renpy.store._preferences = game.preferences # type: ignore renpy.store._test = renpy.test.testast._test # type: ignore if renpy.parser.report_parse_errors(): raise renpy.game.ParseErrorException() renpy.game.exception_info = 'While executing init code:' for id_, (_prio, node) in enumerate(game.script.initcode): renpy.game.initcode_ast_id = id_ if isinstance(node, renpy.ast.Node): node_start = time.time() renpy.game.context().run(node) node_duration = time.time() - node_start if node_duration > renpy.config.profile_init: renpy.display.log.write(" - Init at %s:%d took %.5f s.", node.filename, node.linenumber, node_duration) else: # An init function. node() renpy.game.exception_info = 'After initialization, but before game start.' # Check if we should simulate android. renpy.android = renpy.android or renpy.config.simulate_android # @UndefinedVariable # Re-set up the logging. renpy.log.post_init() # Run the post init code, if any. for i in renpy.game.post_init: i() renpy.game.script.report_duplicate_labels() # Sort the images. renpy.display.image.image_names.sort() game.persistent._virtual_size = renpy.config.screen_width, renpy.config.screen_height # type: ignore log_clock("Running init code.") renpy.pyanalysis.load_cache() log_clock("Loading analysis data.") # Analyze the script and compile ATL. renpy.game.script.analyze() renpy.atl.compile_all() log_clock("Analyze and compile ATL.") renpy.savelocation.init() renpy.loadsave.init() log_clock("Reloading save slot metadata.") # Index the archive files. We should not have loaded an image # before this point. (As pygame will not have been initialized.) # We need to do this again because the list of known archives # may have changed. renpy.loader.index_archives() log_clock("Index archives") # Check some environment variables. renpy.game.less_memory = "RENPY_LESS_MEMORY" in os.environ renpy.game.less_mouse = "RENPY_LESS_MOUSE" in os.environ renpy.game.less_updates = "RENPY_LESS_UPDATES" in os.environ renpy.dump.dump(False) renpy.game.script.make_backups() log_clock("Dump and make backups.") # Initialize image cache. renpy.display.im.cache.init() log_clock("Cleaning cache.") # Make a clean copy of the store. renpy.python.make_clean_stores() log_clock("Making clean stores.") gc.collect(2) if gc.garbage: del gc.garbage[:] if renpy.config.manage_gc: gc.set_threshold(*renpy.config.gc_thresholds) gc_debug = int(os.environ.get("RENPY_GC_DEBUG", 0)) if renpy.config.gc_print_unreachable: gc_debug |= gc.DEBUG_SAVEALL gc.set_debug(gc_debug) else: gc.set_threshold(700, 10, 10) log_clock("Initial gc.") # Start debugging file opens. renpy.debug.init_main_thread_open() # (Perhaps) Initialize graphics. if not game.interface: renpy.display.core.Interface() log_clock("Creating interface object.") # Start things running. restart = None while True: if restart: renpy.display.screen.before_restart() try: try: run(restart) finally: restart = (renpy.config.end_game_transition, "_invoke_main_menu", "_main_menu") except renpy.game.QuitException: renpy.audio.audio.fadeout_all() raise except game.FullRestartException as e: restart = e.reason finally: renpy.persistent.update(True) renpy.persistent.save_on_quit_MP() # Reset live2d if it exists. try: renpy.gl2.live2d.reset_states() except Exception: pass # Flush any pending interface work. renpy.display.interface.finish_pending() # Give Ren'Py a couple of seconds to finish saving. renpy.loadsave.autosave_not_running.wait(3.0) # Run the at exit callbacks. for cb in renpy.config.at_exit_callbacks: cb() finally: gc.set_debug(0) for i in renpy.config.quit_callbacks: i() renpy.loader.auto_quit() renpy.savelocation.quit() renpy.translation.write_updated_strings() # This is stuff we do on a normal, non-error return. if not renpy.display.error.error_handled: renpy.display.render.check_at_shutdown()