diff --git a/launcher/Visual Studio Code (System).edit.py b/launcher/Visual Studio Code (System).edit.py index 9f235f61..6dc1db23 100644 --- a/launcher/Visual Studio Code (System).edit.py +++ b/launcher/Visual Studio Code (System).edit.py @@ -42,7 +42,7 @@ def get_code(self): elif renpy.arch == "armv7l": arch = "arm" else: - arch = "x86_64" + arch = "x64" code = os.path.join(RENPY_VSCODE, "VSCode-linux-" + arch, "bin", "code") else: diff --git a/launcher/Visual Studio Code.edit.py b/launcher/Visual Studio Code.edit.py index 9f235f61..6dc1db23 100644 --- a/launcher/Visual Studio Code.edit.py +++ b/launcher/Visual Studio Code.edit.py @@ -42,7 +42,7 @@ def get_code(self): elif renpy.arch == "armv7l": arch = "arm" else: - arch = "x86_64" + arch = "x64" code = os.path.join(RENPY_VSCODE, "VSCode-linux-" + arch, "bin", "code") else: diff --git a/launcher/game/ability.rpy b/launcher/game/ability.rpy index 41179e7f..6a99df09 100644 --- a/launcher/game/ability.rpy +++ b/launcher/game/ability.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files diff --git a/launcher/game/about.rpy b/launcher/game/about.rpy index f873c9c4..240e3128 100644 --- a/launcher/game/about.rpy +++ b/launcher/game/about.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -36,13 +36,9 @@ screen about: null height 15 - text _("DDMM [config.version!q]") xalign 0.5 bold True + text _("[version!q]") xalign 0.5 bold True - null height 5 - - text _("Ren'Py " + renpy.version().split()[1]) xalign 0.5 bold False - - null height 15 + null height 20 textbutton _("View license") action interface.OpenLicense() xalign 0.5 diff --git a/launcher/game/add_file.rpy b/launcher/game/add_file.rpy index ec680fd3..f213acc2 100644 --- a/launcher/game/add_file.rpy +++ b/launcher/game/add_file.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files diff --git a/launcher/game/android.rpy b/launcher/game/android.rpy index 6280def8..76969613 100644 --- a/launcher/game/android.rpy +++ b/launcher/game/android.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -31,22 +31,23 @@ init python: ANDROID_NO_BUNDLE = 6 ANDROID_OK = 7 - NO_RAPT_TEXT = _("To build Android packages, please download RAPT, unzip it, and place it into the DDMM directory. Then restart DDMM.") - NO_JDK_TEXT = _("A 64-bit/x64 Java 8 Development Kit is required to build Android packages on Windows. The JDK is different from the JRE, so it's possible you have Java without having the JDK.\n\nPlease {a=https://adoptium.net/temurin/releases/?version=8}download and install the JDK{/a}, then restart DDMM.") + JDK_REQUIREMENT=8 + + NO_RAPT_TEXT = _("To build Android packages, please download RAPT, unzip it, and place it into the Ren'Py directory. Then restart the Ren'Py launcher.") + NO_JDK_TEXT = _("A 64-bit/x64 Java [JDK_REQUIREMENT] Development Kit is required to build Android packages on Windows. The JDK is different from the JRE, so it's possible you have Java without having the JDK.\n\nPlease {a=https://www.renpy.org/jdk/[JDK_REQUIREMENT]}download and install the JDK{/a}, then restart the Ren'Py launcher.") NO_SDK_TEXT = _("RAPT has been installed, but you'll need to install the Android SDK before you can build Android packages. Choose Install SDK to do this.") - NO_KEY_TEXT = _("RAPT has been installed, but a key hasn't been configured. Please create a new key, or restore android.keystore.") - NO_BUNDLE_KEY_TEXT = _("RAPT has been installed, but a bundle key hasn't been configured. Please create a new key, or restore bundle.keystore.") + NO_KEY_TEXT = _("RAPT has been installed, but a key hasn't been configured. Please generate new keys, or copy android.keystore and bundle.keystore to the base directory.") NO_CONFIG_TEXT = _("The current project has not been configured. Use \"Configure\" to configure it before building.") NO_BUNDLE_TEXT = _("Please select if you want a Play Bundle (for Google Play), or a Universal APK (for sideloading and other app stores).") OK_TEXT = _("Choose \"Build\" to build the current project, or attach an Android device and choose \"Build & Install\" to build and install it on the device.") PHONE_TEXT = _("Attempts to emulate an Android phone.\n\nTouch input is emulated through the mouse, but only when the button is held down. Escape is mapped to the menu button, and PageUp is mapped to the back button.") TABLET_TEXT = _("Attempts to emulate an Android tablet.\n\nTouch input is emulated through the mouse, but only when the button is held down. Escape is mapped to the menu button, and PageUp is mapped to the back button.") - OUYA_TEXT = _("Attempts to emulate a televison-based Android console, like the OUYA or Fire TV.\n\nController input is mapped to the arrow keys, Enter is mapped to the select button, Escape is mapped to the menu button, and PageUp is mapped to the back button.") + OUYA_TEXT = _("Attempts to emulate a televison-based Android console.\n\nController input is mapped to the arrow keys, Enter is mapped to the select button, Escape is mapped to the menu button, and PageUp is mapped to the back button.") - INSTALL_SDK_TEXT = _("Downloads and installs the Android SDK and supporting packages. Optionally, generates the keys required to sign the package.") + INSTALL_SDK_TEXT = _("Downloads and installs the Android SDK and supporting packages.") + GENERATE_KEYS_TEXT = _("Generates the keys required to sign the package.") CONFIGURE_TEXT = _("Configures the package name, version, and other information about this project.") - PLAY_KEYS_TEXT = _("Opens the file containing the Google Play keys in the editor.\n\nThis is only needed if the application is using an expansion APK. Read the documentation for more details.") BUILD_TEXT = _("Builds the Android package.") BUILD_AND_INSTALL_TEXT = _("Builds the Android package, and installs it on an Android device connected to your computer.") BUILD_INSTALL_AND_LAUNCH_TEXT = _("Builds the Android package, installs it on an Android device connected to your computer, then launches the app on your device.") @@ -94,6 +95,7 @@ init python: import rapt.install_sdk import rapt.plat import rapt.interface + import rapt.keys rapt.plat.renpy = True rapt.plat.translate = __ @@ -112,11 +114,12 @@ init python: return ANDROID_NO_JDK if not os.path.exists(rapt.plat.adb): return ANDROID_NO_SDK - if not os.path.exists(rapt.plat.path("project/local.properties")): + if not rapt.keys.keys_exist(project.current.path): return ANDROID_NO_KEY - if not os.path.exists(rapt.plat.path("project/bundle.properties")): - return ANDROID_NO_BUNDLE_KEY - if not os.path.exists(os.path.join(project.current.path, ".android.json")): + if not any([ + os.path.exists(os.path.join(project.current.path, "android.json")), + os.path.exists(os.path.join(project.current.path, ".android.json")) + ]): return ANDROID_NO_CONFIG if persistent.android_bundle is None: return ANDROID_NO_BUNDLE @@ -137,8 +140,6 @@ init python: return NO_SDK_TEXT if state == ANDROID_NO_KEY: return NO_KEY_TEXT - if state == ANDROID_NO_BUNDLE_KEY: - return NO_BUNDLE_KEY_TEXT if state == ANDROID_NO_CONFIG: return NO_CONFIG_TEXT if state == ANDROID_NO_BUNDLE: @@ -181,31 +182,23 @@ init python: build = p.dump["build"] - filename = os.path.join(p.path, ".android.json") - - with open(filename, "r") as f: - android_json = json.load(f) - - old_android_json = dict(android_json) + c = rapt.configure.Configuration(p.path) if "google_play_key" in build: - android_json["google_play_key"] = build["google_play_key"] + c.google_play_key = build["google_play_key"] else: - android_json.pop("google_play_key", None) + c.google_play_key = None if "google_play_salt" in build: if len(build["google_play_salt"]) != 20: raise Exception("build.google_play_salt must be exactly 20 bytes long.") - android_json["google_play_salt"] = ", ".join(str(i) for i in build["google_play_salt"]) + c.google_play_salt = ", ".join(str(i) for i in build["google_play_salt"]) else: - android_json.pop("google_play_salt", None) + c.google_play_salt = None - if android_json != old_android_json: - - with open(filename, "w") as f: - json.dump(android_json, f) + c.save(p.path) def android_build(p=None, gui=True, bundle=False, install=False, launch=False, destination=None, opendir=False): @@ -288,7 +281,7 @@ init python: with interface.nolinks(): - rapt.build.build(rapt_interface, dist, bundle=bundle, install=install, launch=launch, finished=finished, permissions=p.dump['build']['android_permissions']) + rapt.build.build(rapt_interface, dist, p.path, bundle=bundle, install=install, launch=launch, finished=finished, permissions=p.dump['build']['android_permissions']) def android_build_argument(cmd): @@ -388,12 +381,16 @@ screen android: frame style "l_indent": has vbox - textbutton _("Install SDK & Create Keys"): + textbutton _("Install SDK"): action AndroidIfState(state, ANDROID_NO_SDK, Jump("android_installsdk")) hovered tt.Action(INSTALL_SDK_TEXT) + textbutton _("Generate Keys"): + action AndroidIfState(state, ANDROID_NO_KEY, Jump("android_keys")) + hovered tt.Action(GENERATE_KEYS_TEXT) + textbutton _("Configure"): - action AndroidIfState(state, ANDROID_NO_CONFIG, Jump("android_configure")) + action AndroidIfState(state, ANDROID_NO_KEY, Jump("android_configure")) hovered tt.Action(CONFIGURE_TEXT) add SPACER @@ -410,15 +407,15 @@ screen android: add SPACER - textbutton _("Build Package"): + textbutton _("Build Mod APK"): action AndroidIfState(state, ANDROID_OK, AndroidBuild("android_build")) hovered tt.Action(BUILD_TEXT) - textbutton _("Build & Install"): + textbutton _("Build Mod APK & Install"): action AndroidIfState(state, ANDROID_OK, AndroidBuild("android_build_and_install")) hovered tt.Action(BUILD_AND_INSTALL_TEXT) - textbutton _("Build, Install & Launch"): + textbutton _("Build Mod APK, Install & Launch"): action AndroidIfState(state, ANDROID_OK, AndroidBuild("android_build_install_and_launch")) hovered tt.Action(BUILD_INSTALL_AND_LAUNCH_TEXT) @@ -474,9 +471,9 @@ screen android: hovered tt.Action(CLEAN_TEXT) textbutton _("Open Android Mod Guide"): - action OpenDirectory(config.basedir + "/templates/Android Mod Guide.pdf") + action OpenDirectory(config.gamedir + "/mmupdater/data/DDLC MT/Documentation/Android Mod Guide.pdf") hovered tt.Action(GUIDE_TEXT) - + add SPACER add SEPARATOR2 @@ -513,6 +510,14 @@ label android_installsdk: jump android +label android_keys: + + python: + rapt.keys.generate_keys(MobileInterface("android"), project.current.path) + + jump android + + label android_configure: python: diff --git a/launcher/game/androidstrings.rpy b/launcher/game/androidstrings.rpy index f38e18d5..4b61c0b1 100644 --- a/launcher/game/androidstrings.rpy +++ b/launcher/game/androidstrings.rpy @@ -1,4 +1,4 @@ - + # This file contains strings used by RAPT, so the Ren'Py translation framework # can find them. It's automatically generated by rapt/update_translations.py, and # hence should not be changed by hand. @@ -11,7 +11,7 @@ init python hide: __("Updating project.") __("Creating assets directory.") __("Packaging internal data.") - __("Building the mod using gradle.") + __("I'm using Gradle to build the package.") __("The build seems to have failed.") __("I'm installing the bundle.") __("Installing the bundle appears to have failed.") @@ -29,37 +29,46 @@ init python hide: __("{} is a Java keyword, and can't be used as part of a package name.") __("What is the application's version?\n\nThis should be the human-readable version that you would present to a person. It must contain only numbers and dots.") __("The version number must contain only numbers and dots.") - __("How much RAM do you want to allocate to Gradle?\n\nThis must be a positive integer number.") - __("The RAM size must contain only numbers.") + __("How much RAM (in GB) do you want to allocate to Gradle?\nThis must be a positive integer number.") + __("The RAM size must contain only numbers and be positive.") __("How would you like your application to be displayed?") __("In landscape orientation.") __("In portrait orientation.") __("In the user's preferred orientation.") + __("Which app store would you like to support in-app purchasing through?") + __("Google Play.") + __("Amazon App Store.") + __("Both, in one app.") + __("Neither.") __("Do you want to automatically update the Java source code?") __("Yes. This is the best choice for most projects.") __("No. This may require manual updates when Ren'Py or the project configuration changes.") __("Unknown configuration variable: {}") - __("Compiling a short test program, to see if you have a working JDK on your system.") - __("Unable to use javac to compile a test file. If you haven't installed the Java Development Kit yet, please download it from:\n\n{a=https://adoptium.net/?variant=openjdk8}https://adoptium.net/?variant=openjdk8{/a}\n\nThe JDK is different from the JRE, so it's possible you have Java without having the JDK. Please make sure you installed the 'JavaSoft (Oracle) registry keys'.\n\nWithout a working JDK, I can't continue.") + __("I'm compiling a short test program, to see if you have a working JDK on your system.") + __("I was unable to use javac to compile a test file. If you haven't installed the Java Development Kit yet, please download it from:\n\n{a=https://adoptium.net/?variant=openjdk8}https://adoptium.net/?variant=openjdk8{/a}\n\nThe JDK is different from the JRE, so it's possible you have Java without having the JDK. Please make sure you installed the 'JavaSoft (Oracle) registry keys'.\n\nWithout a working JDK, I can't continue.") __("The version of Java on your computer does not appear to be JDK 8, which is the only version supported by the Android SDK. If you need to install JDK 8, you can download it from:\n\n{a=https://adoptium.net/?variant=openjdk8}https://adoptium.net/?variant=openjdk8{/a}\n\nYou can also set the JAVA_HOME environment variable to use a different version of Java.") __("The JDK is present and working. Good!") __("The Android SDK has already been unpacked.") __("Do you accept the Android SDK Terms and Conditions?") - __("Downloading the Android SDK. This might take a while.") - __("Extracting the Android SDK.") - __("Finished unpacking the Android SDK.") - __("Downloading and installing the required Android packages. This might take a while.") - __("Unable to accept the Android licenses.") - __("Unable to accept the Android licenses.") - __("Unable to install the required Android packages.") - __("Finished installing the required Android packages.") + __("I'm downloading the Android SDK. This might take a while.") + __("I'm extracting the Android SDK.") + __("I've finished unpacking the Android SDK.") + __("I'm about to download and install the required Android packages. This might take a while.") + __("I was unable to accept the Android licenses.") + __("I was unable to accept the Android licenses.") + __("I was unable to install the required Android packages.") + __("I've finished installing the required Android packages.") + __("It looks like you're ready to start packaging games.") __("Please enter your name or the name of your organization.") + __("I found an android.keystore file in the rapt directory. Do you want to use this file?") __("I can create an application signing key for you. This key is required to create Universal APK for sideloading and stores other than Google Play.\n\nDo you want to create a key?") __("I will create the key in the android.keystore file.\n\nYou need to back this file up. If you lose it, you will not be able to upgrade your application.\n\nYou also need to keep the key safe. If evil people get this file, they could make fake versions of your application, and potentially steal your users' data.\n\nWill you make a backup of android.keystore, and keep it in a safe place?") + __("\n\nSaying 'No' will prevent key creation.") __("Could not create android.keystore. Is keytool in your path?") - __("Finished creating android.keystore. Please back it up, and keep it in a safe place.") + __("I've finished creating android.keystore. Please back it up, and keep it in a safe place.") + __("I found a bundle.keystore file in the rapt directory. Do you want to use this file?") __("I can create a bundle signing key for you. This key is required to build an Android App Bundle (AAB) for upload to Google Play.\n\nDo you want to create a key?") __("I will create the key in the bundle.keystore file.\n\nYou need to back this file up. If you lose it, you will not be able to upgrade your application.\n\nYou also need to keep the key safe. If evil people get this file, they could make fake versions of your application, and potentially steal your users' data.\n\nWill you make a backup of bundle.keystore, and keep it in a safe place?") + __("\n\nSaying 'No' will prevent key creation.") __("Could not create bundle.keystore. Is keytool in your path?") __("I've opened the directory containing android.keystore and bundle.keystore. Please back them up, and keep them in a safe place.") - __("It looks like you're ready to start packaging games.") diff --git a/launcher/game/archiver.rpy b/launcher/game/archiver.rpy index a6fda122..2f359a31 100644 --- a/launcher/game/archiver.rpy +++ b/launcher/game/archiver.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files diff --git a/launcher/game/cache/shaders.txt b/launcher/game/cache/shaders.txt index 8011f8fe..5ea12fe1 100644 --- a/launcher/game/cache/shaders.txt +++ b/launcher/game/cache/shaders.txt @@ -1,7 +1,7 @@ -renpy.ftl -renpy.alpha renpy.texture -renpy.geometry renpy.texture -renpy.texture -renpy.alpha renpy.geometry renpy.texture -renpy.geometry renpy.solid -renpy.solid +renpy.ftl +renpy.geometry renpy.texture +renpy.alpha renpy.texture +renpy.solid +renpy.texture +renpy.geometry renpy.solid +renpy.alpha renpy.geometry renpy.texture diff --git a/launcher/game/change_icon.py b/launcher/game/change_icon.py index 64983260..f9931c19 100644 --- a/launcher/game/change_icon.py +++ b/launcher/game/change_icon.py @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files diff --git a/launcher/game/choose_directory.rpy b/launcher/game/choose_directory.rpy index 8918b1a1..fbbdd881 100644 --- a/launcher/game/choose_directory.rpy +++ b/launcher/game/choose_directory.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -41,7 +41,7 @@ init python: except Exception: return False - def choose_directory(path): + def choose_directory(default_path): """ Pops up a directory chooser. @@ -54,38 +54,36 @@ init python: rather than user choice. """ - if path: - default_path = path - path = None - else: - try: - default_path = os.path.dirname(os.path.abspath(config.renpy_base)) - except Exception: - default_path = os.path.abspath(config.renpy_base) - if _renpytfd: path = _renpytfd.selectFolderDialog(__("Select Projects Directory"), default_path) + else: + path = None - is_default = False + if default_path is None: + try: + default_path = os.path.dirname(os.path.abspath(config.renpy_base)) + except Exception: + default_path = os.path.abspath(config.renpy_base) # Path being None or "" means nothing was selected. if not path: - path = default_path - is_default = True - path = renpy.fsdecode(path) + if default_path is None or not os.path.isdir(default_path) or not directory_is_writable(default_path): + interface.error(_("No directory was selected, but one is required.")) + + return default_path, True - if (not os.path.isdir(path)) or (not directory_is_writable(path)): - interface.error(_("The selected projects directory is not writable.")) - path = default_path - is_default = True + # Apply more thorough checks to an explicit path. + path = renpy.fsdecode(path) - if is_default and (not directory_is_writable(path)): - path = os.path.expanduser("~") + if not os.path.isdir(path): + interface.error(_("The selected directory does not exist.")) + elif not directory_is_writable(path): + interface.error(_("The selected directory is not writable.")) - return path, is_default + return path, False - def choose_file(path, bracket): + def choose_file(default_path, bracket): """ Pops up a file chooser. @@ -93,30 +91,24 @@ init python: The directory that is selected by default. If None, config.renpy_base is selected. - Returns a (path, is_default) tuple, where path is the chosen directory, + Returns a (path, is_default) tuple, where path is the chosen file, and is_default is true if and only if it was chosen by default mechanism rather than user choice. """ - if path: - default_path = path - path = None - else: - try: - default_path = os.path.dirname(os.path.abspath(config.renpy_base)) - except Exception: - default_path = os.path.abspath(config.renpy_base) - - if _renpytfd: - path = _renpytfd.openFileDialog(__("Select"), default_path, bracket, None) - - is_default = False + path = _renpytfd.openFileDialog(__("Select"), default_path, bracket, None) # Path being None or "" means nothing was selected. if not path: - path = default_path - is_default = True + interface.error(_("No file was selected, but one is required.")) + + return default_path, True + + # Apply more thorough checks to an explicit path. path = renpy.fsdecode(path) - return path, is_default \ No newline at end of file + if not os.path.isfile(path): + interface.error(_("The selected file does not exist.")) + + return path, False \ No newline at end of file diff --git a/launcher/game/consolecommand.rpy b/launcher/game/consolecommand.rpy index fc97b13a..230dc126 100644 --- a/launcher/game/consolecommand.rpy +++ b/launcher/game/consolecommand.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files diff --git a/launcher/game/distribute.rpy b/launcher/game/distribute.rpy index 5002aecf..878a26f7 100644 --- a/launcher/game/distribute.rpy +++ b/launcher/game/distribute.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -368,13 +368,15 @@ change_renpy_executable() duplicate is set. """ + prefix = py("lib/py{major}-mac-universal") + for f in list(self): if f.name.startswith("lib/python") and (not duplicate): name = app + "/Contents/Resources/" + f.name - elif f.name.startswith(py("lib/py{major}-mac-x86_64")): - name = app + "/Contents/MacOS/" + f.name[19:] + elif f.name.startswith(prefix): + name = app + "/Contents/MacOS/" + f.name[len(prefix)+1:] else: continue @@ -399,7 +401,10 @@ change_renpy_executable() for f in sorted(self, key=lambda a : a.name): f.hash(sha, distributor) - return sha.hexdigest() + if PY2: + return sha.hexdigest().decode("utf-8") + else: + return sha.hexdigest() def split_by_prefix(self, prefix): """ @@ -436,7 +441,7 @@ change_renpy_executable() This manages the process of building distributions. """ - def __init__(self, project, destination=None, reporter=None, packages=None, build_update=True, open_directory=False, noarchive=False, packagedest=None, report_success=True, scan=True, macapp=None, force_format=None): + def __init__(self, project, destination=None, reporter=None, packages=None, build_update=True, open_directory=False, noarchive=False, packagedest=None, report_success=True, scan=True, macapp=None, force_format=None, files_filter=None): """ Distributes `project`. @@ -474,6 +479,11 @@ change_renpy_executable() `force_format` If given, forces the format of the distribution to be this. + + `files_filter` + If given, use this object to decide which files must be included. + The object must contains the `filter(file, variant, format)` + method which must return True is the file must be included. """ # A map from a package to a unique update version hash. @@ -541,7 +551,10 @@ change_renpy_executable() self.pretty_version = build['version'] if (" " in self.base_name) or (":" in self.base_name) or (";" in self.base_name): - reporter.info(_("Building distributions failed:\n\nThe build.directory_name variable may not include the space, colon, or semicolon characters."), pause=True) + reporter.info( + _("Building distributions failed:\n\nThe build.directory_name variable may not include the space, colon, or semicolon characters."), + submessage=_("This may be derived from build.name and config.version or build.version."), + pause=True) self.log.close() return @@ -646,6 +659,8 @@ change_renpy_executable() # The time of the update version. self.update_version = int(time.time()) + self.files_filter = files_filter + for p in build_packages: formats = p["formats"] @@ -708,29 +723,37 @@ change_renpy_executable() is_dir = os.path.isdir(path) if is_dir: - match_name = name + "/" + match_names = [ name + "/", name ] else: - match_name = name + match_names = [ name ] for pattern, file_list in patterns: - if match(match_name, pattern): + matched = False - # When we have ('test/**', None), avoid excluding test. - if (not file_list) and is_dir: - new_pattern = pattern.rstrip("*") - if (pattern != new_pattern) and match(match_name, new_pattern): - continue + for match_name in match_names: + if match(match_name, pattern): + + # When we have ('test/**', None), avoid excluding test. + if (not file_list) and is_dir: + new_pattern = pattern.rstrip("*") + if (pattern != new_pattern) and match(match_name, new_pattern): + continue + + matched = True + break + + if matched: break else: - print(str(match_name), "doesn't match anything.", file=self.log) + print(str(match_names[0]), "doesn't match anything.", file=self.log) pattern = None file_list = None - print(str(match_name), "matches", str(pattern), "(" + str(file_list) + ").", file=self.log) + print(str(match_names[0]), "matches", str(pattern), "(" + str(file_list) + ").", file=self.log) if file_list is None: return @@ -960,6 +983,7 @@ change_renpy_executable() CFBundleDisplayName=display_name, CFBundleExecutable=executable_name, CFBundleIconFile="icon", + CFBundleIdentifier="com.domain.game", CFBundleInfoDictionaryVersion="6.0", CFBundleName=display_name, CFBundlePackageType="APPL", @@ -1051,8 +1075,8 @@ change_renpy_executable() self.add_file( mac, - prefix + "mac-x86_64/" + self.executable_name, - os.path.join(config.renpy_base, prefix + "mac-x86_64/renpy"), + prefix + "mac-universal/" + self.executable_name, + os.path.join(config.renpy_base, prefix + "mac-universal/renpy"), True) def add_mac_files(self): @@ -1076,7 +1100,7 @@ change_renpy_executable() self.add_file(filelist, contents + "/MacOS/" + self.executable_name, - os.path.join(config.renpy_base, py("lib/py{major}-mac-x86_64/renpy"))) + os.path.join(config.renpy_base, py("lib/py{major}-mac-universal/renpy"))) custom_fn = os.path.join(self.project.path, "icon.icns") @@ -1094,7 +1118,7 @@ change_renpy_executable() if not self.build['renpy']: self.add_directory(filelist, contents + "/MacOS/lib") - self.add_directory(filelist, contents + py("/MacOS/lib/py{major}-mac-x86_64")) + self.add_directory(filelist, contents + py("/MacOS/lib/py{major}-mac-universal")) self.add_directory(filelist, contents + py("/Resources/lib/python{major}.{minor}")) self.file_lists[filelist].mac_lib_transform(self.app, self.build['renpy']) @@ -1308,13 +1332,13 @@ change_renpy_executable() def workaround_mac_notarization(self, fl): """ This works around mac notarization by compressing the unsigned, - un-notarized, binaries in lib/py3-mac-x86_64. + un-notarized, binaries in lib/py3-mac-universal. """ fl = fl.copy() for f in fl: - if py("/lib/py{major}-mac-x86_64/") in f.name: + if py("/lib/py{major}-mac-universal/") in f.name: with open(f.path, "rb") as inf: data = inf.read() @@ -1447,7 +1471,7 @@ change_renpy_executable() if self.include_update and (variant not in [ 'ios', 'android', 'source']) and (not format.startswith("app-")): - with open(update_fn, "w") as f: + with open(update_fn, "wb" if PY2 else "w") as f: json.dump(update, f, indent=2) if (not dlc) or (format == "update"): @@ -1501,10 +1525,10 @@ change_renpy_executable() elif format == "update": pkg = UpdatePackage(path, filename, self.destination) elif format == "zip" or format == "app-zip" or format == "bare-zip": - if self.build['renpy']: - pkg = ExternalZipPackage(path) - else: - pkg = ZipPackage(path) + #if self.build['renpy']: + #pkg = ExternalZipPackage(path) + #else: + pkg = ZipPackage(path) elif dmg: def make_dmg(): @@ -1529,6 +1553,9 @@ change_renpy_executable() if f.directory: pkg.add_directory(f.name, f.path) else: + if self.files_filter is not None and not self.files_filter.filter(f, variant, format): + # Ignore file + continue pkg.add_file(f.name, f.path, f.executable) self.reporter.progress_done() @@ -1584,7 +1611,7 @@ change_renpy_executable() add_variant(p["name"]) fn = renpy.fsencode(os.path.join(self.destination, "updates.json")) - with open(fn, "w") as f: + with open(fn, "wb" if PY2 else "w") as f: json.dump(index, f, indent=2) diff --git a/launcher/game/distribute_gui.rpy b/launcher/game/distribute_gui.rpy index f100484a..8cb83f6a 100644 --- a/launcher/game/distribute_gui.rpy +++ b/launcher/game/distribute_gui.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -219,7 +219,13 @@ screen build_distributions: for pkg in packages: if not pkg["hidden"]: $ description = pkg["description"] - textbutton "[description!q]" action PackageToggle(pkg["name"]) style "l_checkbox" + button: + action PackageToggle(pkg["name"]) style "l_checkbox" + hbox: + spacing 3 + text "[description!q]" + if pkg["dlc"]: + text _("(DLC)") add SPACER add HALF_SPACER @@ -276,12 +282,12 @@ label build_update_dump: label build_distributions: python: - f = readVersion() - if f is None or f == -1: - if project.current.name != "launcher": + mod_version = renpy_version_compatible(project.current.path) + if project.current.name != "launcher": + if mod_version["missing"]: interface.error(_("`renpy-version.txt` missing or corrupt."), _("Check if this file exists or attempt to compile guess."),) - elif f < 8: - interface.error(_("You are trying to compile a Ren'Py 6/7 DDLC mod in Ren'Py 8."), _("Please use DDMM 6 or 7 in order to comile your Ren'Py 6/7 mod respectively."),) + elif mod_version["incorrect"]: + interface.error(_("You are trying to compile a Ren'Py") + mod_version["version"] + _("DDLC mod in Ren'Py") + renpy.version_tuple[0] + _("."), _("Please use DDMM 6 or 7 in order to comile your Ren'Py 6/7 mod respectively."),) call build_update_dump diff --git a/launcher/game/dmgcheck.rpy b/launcher/game/dmgcheck.rpy deleted file mode 100644 index 73db89d8..00000000 --- a/launcher/game/dmgcheck.rpy +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2004-2022 Tom Rothamel -# -# 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. - -# This file checks to see if we're inside a dmg, and displays an error -# message to the player if we are. - - -init python: - - def dmgcheck(): - - if not renpy.macintosh: - return - - fn = os.path.join(config.renpy_base, "dmgcheck.txt") - - try: - if os.path.exists(fn): - os.unlink(fn) - - with open(fn, "w") as f: - f.write("Testing to see if we're in a DMG.\r\n") - - os.unlink(fn) - return - - except Exception: - - # If we're here, we didn't make it through the checks safely. So - # put up a warning message. - - interface.info( - message=_("Ren'Py is running from a read only folder. Some functionality will not work."), - submessage=_("This is probably because Ren'Py is running directly from a Macintosh drive image. To fix this, quit this launcher, copy the entire %s folder somewhere else on your computer, and run Ren'Py again.") % (os.path.basename(config.renpy_base)), - ) diff --git a/launcher/game/download.rpy b/launcher/game/download.rpy index 0def3161..b410213c 100644 --- a/launcher/game/download.rpy +++ b/launcher/game/download.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files diff --git a/launcher/game/editor.rpy b/launcher/game/editor.rpy index cf6323f2..76e595a3 100644 --- a/launcher/game/editor.rpy +++ b/launcher/game/editor.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -162,7 +162,7 @@ init 1 python in editor: elif renpy.arch == "armv7l": arch = "arm" else: - arch = "x86_64" + arch = "x64" installed = os.path.exists(os.path.join(config.renpy_base, "vscode/VSCode-linux-" + arch)) @@ -197,7 +197,7 @@ init 1 python in editor: 1, _("Atom"), AD, - dlc, + "extension:atom", _("Up to 150 MB download required."), None) @@ -205,7 +205,7 @@ init 1 python in editor: fei.append(e) - # jEdit + # jEdit - Only present if it exists on system. if os.path.exists(os.path.join(config.renpy_base, "jedit")): fei.append(FancyEditorInfo( diff --git a/launcher/game/extractor.py b/launcher/game/extractor.py index 1a61285b..496ac6f3 100644 --- a/launcher/game/extractor.py +++ b/launcher/game/extractor.py @@ -59,7 +59,3 @@ def game_installation(self, filePath, modFolder, copy=False): if not copy: shutil.rmtree(game_dir) - - def installation(self, filePath, modFolder): - with ZipFile(filePath, "r") as z: - z.extractall(modFolder) diff --git a/launcher/game/front_page.rpy b/launcher/game/front_page.rpy index b632c8c8..e8cfdf8b 100644 --- a/launcher/game/front_page.rpy +++ b/launcher/game/front_page.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -25,35 +25,57 @@ init python: import datetime import shutil + import re # Used for testing. def Relaunch(): renpy.quit(relaunch=True) - def readVersion(): - old_txt_path = os.path.join(project.current.path, 'renpy-version.txt').replace("\\", "/") - new_txt_path = os.path.join(project.current.path, 'game/renpy-version.txt').replace("\\", "/") - - if os.path.exists(old_txt_path): - shutil.move(old_txt_path, new_txt_path) + def read_version(d): + mod_ver_file = os.path.join(d, "game/renpy-version.txt") try: - with open(new_txt_path) as f: - file_ver = int(f.readline().strip()) - - if file_ver >= 6 and file_ver <= 8: - return file_ver - else: - return -1 + with open(mod_ver_file, "r") as f: + version_line = f.readline() except IOError: return None - except ValueError: - return -1 + + match = re.match(r"^\d$", version_line) + if match: + return int(match.group(0)) + else: + return None + + def renpy_version_compatible(d): + mod_version = read_version(d) + + check_result = { + "compatible": None, + "version": None, + "missing": False, + "range": False, + "incorrect": False, + } + + check_result["version"] = mod_version + + if mod_version is None: + check_result["compatible"] = False + check_result["missing"] = True + elif mod_version < 6 or mod_version > 8: + check_result["compatible"] = False + check_result["range"] = True + elif mod_version != renpy.version_tuple[0]: + check_result["compatible"] = False + check_result["incorrect"] = True + else: + check_result["compatible"] = True + + return check_result # Adds backwards compat between 4.1.0+ and older templates def NewEditorOpen(path): - base = persistent.projects_directory if persistent.projects_directory is not None else config.basedir - if os.path.exists(os.path.join(base, project.current.name, path)): + if os.path.exists(os.path.join(project.current.path, path)): return editor.Edit(path, check=True) else: old_path = path.split("/") @@ -126,19 +148,22 @@ screen front_page: if project.current is not None: python: - launch = readVersion() - - if launch == 6: - textbutton _("DDMM 6 Needed") action NullAction() style "l_unavail_button" - elif launch == 7: - textbutton _("DDMM 7 Needed") action NullAction() style "l_unavail_button" - elif launch == 8 or project.current.name == "launcher": + checks = renpy_version_compatible(project.current.path) + + if project.current.name != "launcher": + if not checks["compatible"]: + if checks["missing"]: + textbutton _("Missing Version Data") action NullAction() style "l_unavail_button" + elif checks["range"]: + textbutton _("Invalid Ren'Py Version") action NullAction() style "l_unavail_button" + elif checks["incorrect"]: + textbutton _("Incorrect DDMM SDK Version") action NullAction() style "l_unavail_button" + else: + textbutton _("Launch Mod") action project.Launch() style "l_right_button" + key "K_F5" action project.Launch() + else: textbutton _("Launch Mod") action project.Launch() style "l_right_button" key "K_F5" action project.Launch() - else: - textbutton _("Cannot Determine Version") action Jump('version_error') style "l_unavail_button" - - # This is used by front_page to display the list of known projects on the screen. screen front_page_project_list: @@ -163,11 +188,19 @@ screen front_page_project_list: for p in projects: - textbutton "[p.name!q]": + python: + mod_version = renpy_version_compatible(p.path) + mod_text_ver = mod_version["version"] + if mod_text_ver is None or mod_version["range"]: + mod_text_ver = "Unknown" + button: action project.Select(p) - alt _("Select project [text].") style "l_list" + vbox: + text "[p.name!q]" style "l_list_text" + text "(Ren'Py [mod_text_ver] Mod)" style "l_list_text" size 12 xpos 5 + null height 12 # This is used for the right side of the screen, which is where the project-specific @@ -243,16 +276,16 @@ screen front_page_project: frame style "l_indent": has vbox - textbutton _("Install a Tool") action Jump("tool_install") + textbutton _("Tool Installer") action Jump("mmtoolinstaller") if ability.can_distribute: textbutton _("Build Mod") action Jump("build_distributions") if project.current.name != "launcher": python: - launch = readVersion() + checks = renpy_version_compatible(project.current.path) - if launch == 8: + if checks["compatible"]: textbutton _("Build Mod for Android") action Jump("android") else: textbutton _("Android Unavailable") action Jump("no_android") @@ -273,33 +306,23 @@ label start: default persistent.has_chosen_language = False default persistent.has_update = False -define update_notified = False label front_page: - if persistent.zip_directory is not None: - if persistent.zip_directory.endswith("ddlc-mac") or persistent.zip_directory.endswith("ddlc-mac.zip"): - $ persistent.zip_directory = None - if (not persistent.has_chosen_language) or ("RENPY_CHOOSE_LANGUAGE" in os.environ): - if _preferences.language is None: + if (_preferences.language is None) or ("RENPY_CHOOSE_LANGUAGE" in os.environ): hide screen bottom_info call choose_language show screen bottom_info $ persistent.has_chosen_language = True - if persistent.daily_update_check and ((not persistent.last_update_check) or (datetime.date.today() > persistent.last_update_check)): - python hide: - persistent.last_update_check = datetime.date.today() - persistent.update_available = False - renpy.invoke_in_thread(fetch_ddmm_updates, update_json=True) - renpy.invoke_in_thread(fetch_ddmm_updates, mt=True, update_json=True) - - if not update_notified and persistent.update_available: - $ update_notified = True - $ renpy.notify("Updates are available.") + if not os.path.exists(config.basedir + "/update/third_party.json"): + python: + f = open(config.basedir + "/update/third_party.json", "w") + f.write("{}") + f.close() call screen front_page jump front_page @@ -336,55 +359,26 @@ label force_recompile: jump front_page -label version_error: - python: - interface.info(_("This project cannot launch in DDMM as this is either a non-DDLC mod or is missing 'renpy-version.txt'"), _("Please check if 'renpy-version.txt' exists."),) - renpy.jump('front_page') - -label no_android: - python: - interface.info(_("This project cannot be built for Android as it's either in Ren'Py 6/7 mode or is missing 'renpy-version.txt'"), _("Please check if 'renpy-version.txt' exists or change the version of your project."),) - renpy.jump('front_page') - label set_version: python: - x = readVersion() - if x is None: - try: - ver_path = os.path.join(persistent.projects_directory, project.current.name, "game/renpy-version.txt") - if not os.path.exists(ver_path): - ver_path = os.path.join(config.basedir, project.current.name, "game/renpy-version.txt") - except TypeError: - ver_path = os.path.join(config.basedir, project.current.name, "game/renpy-version.txt") + mod_version = renpy_version_compatible(project.current.path) + if mod_version["missing"]: + ver_path = os.path.join(project.current.gamedir, "renpy-version.txt") with open(ver_path, "w") as f: - f.write("8") + f.write(str(renpy.version_tuple[0])) interface.info(_("A file named `renpy-version.txt` has been created in your projects' game directory."), _("Do not delete this file as it is needed to determine which version of Ren'Py it uses for building your mod.")) renpy.jump("front_page") - - prompt = False - if x == 6: - prompt = True - response_text = _("This mod is set to Ren'Py 6 Mode. ") - elif x == 7: - prompt = True - response_text = _("This mod is set to Ren'Py 7 Mode. ") - if prompt: - confirm_delete = False - delete_response = interface.yesno( + if mod_version["incorrect"]: + edit_response = interface.yesno( label=_("Warning"), - message=response_text + _("If you change this, it may result in a improperly packaged mod.\nAre you sure you want to proceed? Type either Yes or No."), - yes=SetScreenVariable(confirm_delete, True), - no=Return(), - cancel=Jump("front_page")) + message=_("This mod is set to Ren'Py ") + str(mod_version["version"]) + _(" Mode. If you change this, it may result in a improperly packaged mod.\nAre you sure you want to proceed? Type either Yes or No."), + no=Jump("front_page")) - if not confirm_delete: - renpy.jump("front_page") - else: - with open(os.path.join(persistent.projects_directory, project.current.name, "game/renpy-version.txt"), "w") as f: - f.write("8") - interface.info(_("Set the Ren'Py mode version to Ren'Py 8.")) + with open(os.path.join(project.current.gamedir, "renpy-version.txt"), "w") as f: + f.write(str(renpy.version_tuple[0])) + interface.info(_("Set the Ren'Py mode version to Ren'Py {}.".format(str(renpy.version_tuple[0])))) else: - interface.info(_("The Ren'Py mode version is already set to Ren'Py 8.")) + interface.info(_("The Ren'Py mode version is already set to Ren'Py {}.".format(str(renpy.version_tuple[0])))) jump front_page \ No newline at end of file diff --git a/launcher/game/gui7/__init__.py b/launcher/game/gui7/__init__.py index e664de70..16f69b30 100644 --- a/launcher/game/gui7/__init__.py +++ b/launcher/game/gui7/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files diff --git a/launcher/game/gui7/code.py b/launcher/game/gui7/code.py index c5d3b1f2..37a09188 100644 --- a/launcher/game/gui7/code.py +++ b/launcher/game/gui7/code.py @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -214,7 +214,8 @@ def update_gui_defines(self): 'gui.insensitive_color' : repr(self.p.insensitive_color.hexcode), 'gui.text_color' : repr(self.p.text_color.hexcode), 'gui.interface_text_color' : repr(self.p.text_color.hexcode), - 'gui.choice_text_color' : repr(self.p.choice_color.hexcode), + 'gui.choice_button_text_idle_color' : repr(self.p.idle_color.hexcode), + 'gui.choice_button_text_insensitive_color' : repr(self.p.insensitive_color.hexcode), } self.update_defines(replacements, language_defines[self.p.language]) diff --git a/launcher/game/gui7/images.py b/launcher/game/gui7/images.py index f96c92a2..473eba3e 100644 --- a/launcher/game/gui7/images.py +++ b/launcher/game/gui7/images.py @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -404,6 +404,21 @@ def generate_menus(self): self.save(s, "main_menu", overwrite=False) self.save(s, "game_menu", overwrite=False) + def generate_bubble(self): + + import shutil + + for fn in [ "bubble.png", "thoughtbubble.png" ]: + + source = os.path.join(config.renpy_base, "gui", "game", "gui", fn) + dest = os.path.join(self.prefix, fn) + + if source == dest: + return + + shutil.copyfile(source, dest) + + def generate_all(self): self.generate_textbox() self.generate_choice_button() @@ -415,6 +430,7 @@ def generate_all(self): self.generate_notify() self.generate_menus() self.generate_icon() + self.generate_bubble() if __name__ == "__main__": diff --git a/launcher/game/gui7/parameters.py b/launcher/game/gui7/parameters.py index 88c52cfb..eddfbeab 100644 --- a/launcher/game/gui7/parameters.py +++ b/launcher/game/gui7/parameters.py @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -67,8 +67,8 @@ def __init__(self, prefix, template, width, height, accent, boring, light, langu if light: self.selected_color = Color("#555555") - self.idle_color = Color("#aaaaaa") - self.idle_small_color = Color("#888888") + self.idle_color = Color("#707070") + self.idle_small_color = Color("#606060") self.text_color = Color("#404040") self.choice_color = Color("#cccccc") diff --git a/launcher/game/install.rpy b/launcher/game/install.rpy index d5d0a51c..0668eff5 100644 --- a/launcher/game/install.rpy +++ b/launcher/game/install.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -81,7 +81,6 @@ init python: label install_live2d: python hide: - if PY2: _prefix = r"lib/py2-" else: @@ -90,17 +89,19 @@ label install_live2d: patterns = [ (r".*/Core/dll/linux/x86_64/(libLive2DCubismCore.so)", _prefix + r"linux-x86_64/\1"), (r".*/Core/dll/windows/x86_64/(Live2DCubismCore.dll)", _prefix + r"windows-x86_64/\1"), - (r".*/Core/dll/windows/x86/(Live2DCubismCore.dll)", _prefix + r"windows-i686/\1"), - (r".*/Core/dll/macos/(libLive2DCubismCore.dylib)", _prefix + r"mac-x86_64/\1"), + (r".*/Core/dll/macos/(libLive2DCubismCore.dylib)", _prefix + r"mac-universal/\1"), (r".*/Core/dll/experimental/rpi/(libLive2DCubismCore.so)", _prefix + r"linux-armv7l/\1"), (r".*/Core/dll/android/(armeabi-v7a/libLive2DCubismCore.so)", r"rapt/prototype/renpyandroid/src/main/jniLibs/\1"), (r".*/Core/dll/android/(arm64-v8a/libLive2DCubismCore.so)", r"rapt/prototype/renpyandroid/src/main/jniLibs/\1"), - - # This doesn't exist yet. - # (r".*/Core/dll/android/(x86_64/libLive2DCubismCore.so)", r"rapt/prototype/renpyandroid/src/main/jniLibs/\1"), + (r".*/Core/dll/android/(x86_64/libLive2DCubismCore.so)", r"rapt/prototype/renpyandroid/src/main/jniLibs/\1"), ] + if PY2: + patterns.extend([ + (r".*/Core/dll/windows/x86/(Live2DCubismCore.dll)", _prefix + r"windows-i686/\1"), + ]) + install_from_zip("Live2D Cubism SDK for Native", "CubismSdkForNative-4-*.zip", patterns) jump front_page @@ -159,7 +160,7 @@ screen install_live2d(): add SPACER - text _("Live2D in Ren'Py doesn't support the Web, Android x86_64 (including emulators and Chrome OS), and must be added to iOS projects manually. Live2D must be reinstalled after upgrading Ren'Py or installing Android support. You must enable {i}config.gl2{/i} in your mod in order for it to work in {u}renpy_patches.txt{/u}.") + text _("Live2D in Ren'Py doesn't support the Web, Android x86_64 (including emulators and Chrome OS), and must be added to iOS projects manually. Live2D must be reinstalled after upgrading Ren'Py or installing Android support.") diff --git a/launcher/game/installer.py b/launcher/game/installer.py index a52928d1..c223a2d9 100644 --- a/launcher/game/installer.py +++ b/launcher/game/installer.py @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files diff --git a/launcher/game/installer.rpy b/launcher/game/installer.rpy index fa5b7dbc..7bcc41f0 100644 --- a/launcher/game/installer.rpy +++ b/launcher/game/installer.rpy @@ -1,4 +1,4 @@ - + # This file imports the extensions API into the default store, and makes it # also contains the strings used by the extensions API, so the Ren'Py translation # framework can find them. diff --git a/launcher/game/interface.rpy b/launcher/game/interface.rpy index 1526b638..cc5208f8 100644 --- a/launcher/game/interface.rpy +++ b/launcher/game/interface.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -66,6 +66,16 @@ init python in interface: else: return OpenURL(LICENSE_URL) + def get_sponsor_url(): + """ + Returns the URL to the sponsors page. + """ + + return "https://www.renpy.org/sponsors.html?version={}&language={}".format( + renpy.version_only, + _preferences.language or "english" + ) + # Should we display the bottom links? links = True @@ -133,6 +143,8 @@ screen bottom_info: textbutton _("Settings") style "l_link" action Jump("preferences") textbutton _("Quit") style "l_link" action Quit(confirm=False) + + screen common: default complete = None @@ -152,7 +164,7 @@ screen common: has vbox text message: - text_align 0.5 + textalign 0.5 xalign 0.5 layout "subtitle" @@ -182,7 +194,15 @@ screen common: add SPACER for v, l in choices: - textbutton l action SetScreenVariable("selected", v) + textbutton l: + action SetScreenVariable("selected", v) + selected_background REVERSE_IDLE + selected_hover_background REVERSE_HOVER + xpadding 20 + size_group "choice" + text_selected_idle_color REVERSE_TEXT + text_selected_hover_color REVERSE_TEXT + text_xalign 0.5 if selected is not None: $ continue_ = Return(selected) @@ -193,7 +213,7 @@ screen common: add SPACER text submessage: - text_align 0.5 + textalign 0.5 xalign 0.5 layout "subtitle" @@ -232,7 +252,7 @@ screen launcher_input: has vbox text message: - text_align 0.5 + textalign 0.5 xalign 0.5 layout "subtitle" diff --git a/launcher/game/mac.rpy b/launcher/game/mac.rpy index 9e00112d..7f8aa053 100644 --- a/launcher/game/mac.rpy +++ b/launcher/game/mac.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -23,7 +23,7 @@ init python hide: import os macapp = os.path.join(config.renpy_base, "renpy.app/Contents/MacOS") - maclib = os.path.join(config.renpy_base, distribute.py("lib/py{major}-mac-x86_64")) + maclib = os.path.join(config.renpy_base, distribute.py("lib/py{major}-mac-universal")) if os.path.exists(maclib): diff --git a/launcher/game/mmupdater.rpy b/launcher/game/mmupdater.rpy index 4f12c017..fe6994a0 100644 --- a/launcher/game/mmupdater.rpy +++ b/launcher/game/mmupdater.rpy @@ -1,105 +1,26 @@ init python: - import requests - import glob + import mmupdater + mmupdate = mmupdater.MMakerUpdater() + import os - import zipfile import json - import datetime - - TEMPLATE_URL = "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/latest" - DDMM_URL = "https://api.github.com/repos/GanstaKingofSA/DDLC-ModMaker/releases/latest" - TEMPLATE_JSON_PATH = config.basedir + "/update/template_current.json" - DDMM_JSON_PATH = config.basedir + "/update/mmaker_current.json" - - def get_git_template_version(template_json): - return tuple(int(num) for num in template_json["tag_name"].split(".")) - - def get_installed_template_version(string=False): - if string: - num = glob.glob(config.basedir + "/templates/DDLCModTemplate-*-Py3.zip") - return num[-1].replace("-Py3.zip", "").split("-")[-1] or None - return tuple(int(num) for num in glob.glob(config.basedir + "/templates/DDLCModTemplate-*-Py3.zip")[-1].replace("-Py3.zip", "").split("-")[-1].split(".")) - - def get_git_ddmm_version(mmaker_json_file): - return tuple(int(num) for num in mmaker_json_file["tag_name"].split(".")) - - def get_installed_ddmm_version(): - return tuple(int(num) for num in config.version.split(".")) - - def fetch_ddmm_updates(quiet=True, mt=False, update_json=True): - if not quiet: - process_text = "" - if mt: - process_text += "Mod Template" - else: - process_text += "DDMM" - interface.processing(_("Checking for {} updates").format(process_text)) - - if not quiet: - with interface.error_handling(_("Downloading the latest JSON information")): - if mt: - channels = requests.get(TEMPLATE_URL).json() - if update_json or not os.path.exists(TEMPLATE_JSON_PATH): - with open(TEMPLATE_JSON_PATH, "w") as template_json_file: - json.dump(channels, template_json_file) - else: - channels = requests.get(DDMM_URL).json() - if update_json or not os.path.exists(DDMM_JSON_PATH): - with open(DDMM_JSON_PATH, "w") as mmaker_json_file: - json.dump(channels, mmaker_json_file) - else: - if mt: - channels = requests.get(TEMPLATE_URL).json() - if update_json or not os.path.exists(TEMPLATE_JSON_PATH): - with open(TEMPLATE_JSON_PATH, "w") as template_json_file: - json.dump(channels, template_json_file) - else: - channels = requests.get(DDMM_URL).json() - if update_json or not os.path.exists(DDMM_JSON_PATH): - with open(DDMM_JSON_PATH, "w") as mmaker_json_file: - json.dump(channels, mmaker_json_file) - - if mt: - persistent.template_update = False - with open(TEMPLATE_JSON_PATH, "r") as tf: - template_json = json.load(tf) - - try: - template_ver = get_installed_template_version() - except: - template_ver = None - - if template_ver == None or template_ver < get_git_template_version(template_json): - if not persistent.disable_mt_update: - persistent.update_available = True - persistent.template_update = True - - else: - persistent.mmaker_update = False - with open(DDMM_JSON_PATH, "r") as mf: - mmaker_json = json.load(mf) - - if get_installed_ddmm_version() < get_git_ddmm_version(mmaker_json): - if not persistent.disable_mm_update: - persistent.update_available = True - persistent.mmaker_update = True - - return channels label mmupdater: python: if not os.path.exists(config.basedir + "/update"): os.makedirs(config.basedir + "/update") - $ persistent.update_available = False - $ ddmm_chan = fetch_ddmm_updates(False) - $ ddmt_chan = fetch_ddmm_updates(False, True) - call screen ddmmupdate(ddmm_chan, ddmt_chan) + call screen new_ddmmupdate jump front_page -screen ddmmupdate(ddmm_chan, ddmt_chan): +screen new_ddmmupdate(): + + default updates = {} + default no_updates = {} + default first_party_json = None + default third_party_json = None frame: style_group "l" @@ -124,122 +45,112 @@ screen ddmmupdate(ddmm_chan, ddmt_chan): has vbox - if persistent.update_available: - text _("A update for DDMM or the DDLC Mod Template is now available. Select the update you want to install to proceed.") - else: - text _("No Updates Are Available.") + python: + with open(config.basedir + '/update/first_party.json') as fp: + first_party_json = json.load(fp) + + if os.path.exists(config.basedir + '/update/third_party.json'): + with open(config.basedir + '/update/third_party.json') as tp: + third_party_json = json.load(tp) + + for i, cls in mmupdate.all_modules.items(): + module_name = eval(cls) + acronym = module_name.ModTool().acronym + if module_name.ModTool().check_for_updates(): + updates[acronym] = {} + updates[acronym]['cls'] = module_name.ModTool() + + try: + first_party_json[acronym] + updates[acronym]['third_party'] = False + except KeyError: + updates[acronym]['third_party'] = True + else: + no_updates[acronym] = {} + no_updates[acronym]['cls'] = module_name.ModTool() + + try: + first_party_json[acronym] + no_updates[acronym]['third_party'] = False + except KeyError: + no_updates[acronym]['third_party'] = True + + text "Pending Updates" size 18 add SPACER - if persistent.mmaker_update: - textbutton "Doki Doki Mod Maker (DDMM/DDMMaker) for Ren'Py 8 - Update Available": - action [Call("install_ddmm_update_script", ddmm_chan), Jump("mmupdater")] - else: - if persistent.disable_mm_update: - textbutton "Doki Doki Mod Maker (DDMM/DDMMaker) for Ren'Py 8 - Updates Disabled": - action NullAction() - else: - textbutton "Doki Doki Mod Maker (DDMM/DDMMaker) for Ren'Py 8": - action NullAction() - text "Version Installed: {} | Latest Version: {}".format(config.version, ddmm_chan["tag_name"]) - text "This is the program you are running to build DDLC mods on! Self-explanatory already." - text "{a=" + ddmm_chan["html_url"] + "}What's new for DDMM/DDMMaker?{/a}" + if updates: + for acc, tool in updates.items(): + python: + third_party = False + if tool['third_party']: + tool_ver = third_party_json[acc]['version'] + third_party = True + else: + tool_ver = first_party_json[acc]['version'] - add SPACER - - if persistent.template_update: - textbutton "The DDLC Mod Template 2.0 (Python 3 Edition) - Update Available": - action [Call("install_ddmt_update_script", ddmt_chan), Jump("mmupdater")] - else: - if persistent.disable_mt_update: - textbutton "The DDLC Mod Template 2.0 (Python 3 Edition) - Updates Disabled": - action NullAction() - else: - textbutton "The DDLC Mod Template 2.0 (Python 3 Edition)": - action NullAction() - text "Version Installed: {}-Py3 | Latest Version: {}-Py3".format(get_installed_template_version(True), ddmt_chan["tag_name"]) - text "The DDLC Mod Template 2.0 is the latest template for DDLC that allows modders to easily mod DDLC to their hearts content. {u}This is the Python 3 Edition of the template and is not compatible with DDMM 6-7/Ren'Py 6-7{/u}." - text "{a=" + ddmt_chan["html_url"] + "}What's new for the DDLC Mod Template - Version 2.0 (Python 3 Edition)?{/a}" - - textbutton _("Return") action Jump("front_page") style "l_left_button" + textbutton tool['cls'].name action Call("update_script", tool['cls'], tool['third_party']) + text "Creator: {}".format(tool['cls'].creator) size 14 + text "Type: {} | Identifier: {} | Version {} -> {}".format("{b}Third Party{/b}" if tool['third_party'] else "{b}First Party{/b}", acc, tool_ver, tool['cls'].get_remote_tool_version()) size 14 + text "\n" + tool['cls'].desc size 14 -label install_ddmt_update_script(ddmt_chan): + add SPACER - python hide: - with interface.error_handling("Updating the DDLC Mod Template"): - interface.processing("Updating the DDLC Mod Template. Please wait...") + else: + + text "No updates available." - for endpart in ["-Py3", "-Py3Extras"]: - zipContent = requests.get("https://github.com/GanstaKingofSA/DDLCModTemplate2.0/releases/download/" + ddmt_chan["tag_name"] + "/DDLCModTemplate-" + ddmt_chan["tag_name"] + endpart + ".zip") - filename = "DDLCModTemplate-" + ddmt_chan["tag_name"] + endpart + ".zip" + add SPACER - try: - for x in glob.glob(config.basedir + "/templates/DDLCModTemplate-*" + endpart + ".zip"): - os.remove(x) - except: pass + add SEPARATOR + add SPACER - with open(config.basedir + "/templates/" + filename, "wb") as newTemplate: - newTemplate.write(zipContent.content) + text "Current Updates" size 18 - if endpart == "-Py3": - with zipfile.ZipFile(config.basedir + "/templates/" + filename) as newTemplate: - try: - newTemplate.extract("guide.pdf", config.basedir + "/templates") - os.rename(config.basedir + "/templates/guide.pdf", config.basedir + "/templates/Android Mod Guide.pdf") - except: - newTemplate.extract("Documentation/Android Mod Guide.pdf", config.basedir + "/templates") - shutil.move(config.basedir + "/templates/Documentation/Android Mod Guide.pdf", config.basedir + "/templates/Android Mod Guide.pdf") - shutil.rmtree(config.basedir + "/templates/Documentation") + add SPACER - persistent.update_available = False - interface.info("The update has been complete.") + for acc, tool in no_updates.items(): + python: + if tool['third_party']: + tool_ver = third_party_json[acc]['version'] + else: + tool_ver = first_party_json[acc]['version'] - jump mmupdater + textbutton tool['cls'].name action NullAction() + text "Creator: {}".format(tool['cls'].creator) size 14 + text "Type: {} | Identifier: {} | Version {}".format("{b}Third Party{/b}" if tool['third_party'] else "{b}First Party{/b}", acc, tool_ver) size 14 + text "\n" + tool['cls'].desc size 14 -label install_ddmm_update_script(ddmm_chan): + add SPACER - python hide: - - with interface.error_handling("Updating DDMM/DDMMaker"): - interface.processing("Updating DDMM/DDMMaker. Please wait...") + textbutton _("Return") action Jump("front_page") style "l_left_button" - zipContent = requests.get("https://github.com/GanstaKingofSA/DDLC-ModMaker/releases/download/" + ddmm_chan["tag_name"] + "/" + build.directory_name.split("-")[0] + "-" + ddmm_chan["tag_name"] + "-sdk.zip") - filename = build.directory_name.split("-")[0] + "-" + ddmm_chan["tag_name"] + "-sdk.zip" +label update_script(pkg, third_party): + python: + with interface.error_handling(_("Updating " + pkg.name)): + interface.processing(_("Updating " + pkg.name + ". Please wait...")) + pkg.update() - with open(config.basedir + "/" + filename, "wb") as newMMaker: - newMMaker.write(zipContent.content) + if third_party: + with open(config.basedir + '/update/third_party.json') as tp: + json_data = json.load(tp) - with zipfile.ZipFile(filename, "r") as z: - z.extractall(config.basedir) - - for update_src, dirs, files in os.walk(config.basedir + "/" + filename.replace(".zip", "")): - dst_dir = update_src.replace(config.basedir + "/" + filename.replace(".zip", ""), config.basedir) - - if not os.path.exists(dst_dir): - os.makedirs(dst_dir) - - for f in files: - update_file = os.path.join(update_src, f) - dst_file = os.path.join(dst_dir, f) - - if os.path.exists(dst_file): - if renpy.windows: - if os.stat(update_file) == os.stat(dst_file): - continue - else: - if os.path.samefile(update_file, dst_file): - continue - - os.remove(dst_file) - - shutil.move(update_file, dst_file) - - shutil.rmtree(filename.replace(".zip", "")) - os.remove(filename) - - persistent.update_available = False - - interface.info("The update has been complete. DDMM will now restart.") + json_data[pkg.acronym]['version'] = pkg.get_remote_tool_version() + + with open(config.basedir + '/update/third_party.json', "w") as tp: + json.dump(json_data, tp) + else: + with open(config.basedir + '/update/first_party.json') as tp: + json_data = json.load(tp) + + json_data[pkg.acronym]['version'] = pkg.get_remote_tool_version() + + with open(config.basedir + '/update/first_party.json', "w") as tp: + json.dump(json_data, tp) + + if not pkg.restart: + interface.info(_(pkg.name + " has been successfully updated to the latest version.")) + renpy.jump("mmupdater") + else: + interface.info(_(pkg.name + " has been successfully updated to the latest version.\nYou must restart DDMM for to apply the update.")) renpy.quit(True) - - jump mmupdater \ No newline at end of file diff --git a/launcher/game/mmupdater/__init__.py b/launcher/game/mmupdater/__init__.py new file mode 100644 index 00000000..8499991d --- /dev/null +++ b/launcher/game/mmupdater/__init__.py @@ -0,0 +1,153 @@ +import abc +import datetime +import importlib +import os +import requests +import json +from renpy import config, store + +class MMakerTool(abc.ABC): + def __init__(self, name: str, acronym: str, creator: str, desc: str, restart=False, tool=True): + for x in (name, acronym, creator, desc): + if not isinstance(x, str): + raise Exception("A argurment provided is not type string") + + self.name = name + self.acronym = acronym + self.creator = creator + self.desc = desc + self.restart = restart + self.install_dir = config.gamedir + "/mmupdater/data/{}".format(acronym) + self.tool = tool + + os.makedirs(config.gamedir + "/mmupdater/data/{}".format(acronym), exist_ok=True) + + @abc.abstractmethod + def install(self, mod_folder): + """Installs the tool.""" + pass + + @abc.abstractmethod + def get_updates(self): + """Gets the updates for the tool.""" + pass + + @abc.abstractmethod + def update(self, *args): + """Updates the tool to the latest version.""" + pass + + @abc.abstractmethod + def check_for_updates(self) -> bool: + """Checks for updates for the tool.""" + pass + + def check_if_installed(self) -> bool: + """Checks if the tool is installed on the system.""" + return os.path.exists(self.install_dir) + + def get_local_tool_version(self): + version = None + + for x in ("first_party", "third_party"): + with open(config.basedir + "/update/{}.json".format(x)) as v: + pkgs = json.load(v) + try: + version = pkgs[self.acronym]['version'] + except KeyError: + pass + + return version + + @abc.abstractmethod + def get_remote_tool_version(self): + pass + + @abc.abstractmethod + def install_package(self): + pass + +class MMakerGithubTool(MMakerTool): + def __init__(self, github_name: str, github_branch: str, package_name: str, name: str = None, acronym: str = None, creator: str = None, desc: str = None, restart=False, tool=True, extra=False): + if not isinstance(github_name, str): + raise Exception("Github Repo Name must be provided.") + + if len(github_name.split("/")) != 2: + raise Exception("Github Repo Name must begin with 'GithubUsername/GithubRepo' e.g. 'bronya_rand/a-ddlc-repo'.") + if github_branch is None: + raise Exception("Github Branch Name must be provided.") + if extra and (name is None or acronym is None or desc is None): + raise Exception("Name/Acronym/Description of Extra must be provided if this is a Github Extra Mod Package.") + if not isinstance(package_name, str): + raise Exception("package name is not a type string.") + + self.github_name = github_name + + gh_json = requests.get("https://raw.githubusercontent.com/{}/{}/ddmm_metadata.json".format(github_name, github_branch)) + if gh_json.status_code in (400, 404): + super().__init__(name, acronym, creator, desc, restart, tool) + else: + try: + gh_data = gh_json.json() + except json.JSONDecodeError: + raise Exception(gh_json.status_code) + if extra: + super().__init__(name, acronym, gh_data["creator"], desc, gh_data['restart'], tool) + else: + super().__init__(gh_data['name'], gh_data['acronym'], gh_data["creator"], gh_data["description"], gh_data['restart'], tool) + + self.package_url = "https://github.com/{}/releases/download/".format(github_name) + self.package_file = package_name + + def check_if_installed(self) -> bool: + return os.path.exists(self.install_dir + "/{}.zip".format(self.package_file)) + + def get_updates(self): + package_update_data = requests.get("https://api.github.com/repos/{}/releases/latest".format(self.github_name)).json() + with open(self.install_dir + "/update.json", "w") as pud: + json.dump(package_update_data, pud) + + def get_remote_tool_version(self): + with open(self.install_dir + "/update.json", "r") as pud: + temp = json.load(pud) + return temp["tag_name"] + + def check_for_updates(self) -> bool: + if not self.check_if_installed(): + return False + + if store.persistent.daily_update_check and ((not store.persistent.last_update_check) or (datetime.date.today() > store.persistent.last_update_check)): + self.get_updates() + store.persistent.last_update_check = datetime.date.today() + + local_ver = self.get_local_tool_version() + if local_ver is None: + return True + local_ver_tuple = tuple(int(num) for num in local_ver.split(".")) + + remote_ver = self.get_remote_tool_version() + remote_ver_tuple = tuple(int(num) for num in remote_ver.split(".")) + + if local_ver_tuple < remote_ver_tuple: + return True + + return False + +class MMakerUpdater(object): + def __init__(self): + self.all_modules = {} + directory = "{}/{}".format(config.gamedir, "mmupdater") + # Import all Python files in the directory + i = 0 + + for filename in os.listdir(directory): + if "__init__" in filename: + continue + + if filename.endswith(".py"): + module_name = filename[:-3] + full_module_name = "mmupdater.{}".format(module_name) + + importlib.import_module(full_module_name) + self.all_modules[i] = full_module_name + i += 1 \ No newline at end of file diff --git a/launcher/game/mmupdater/data/DDLC MT/DDLCModTemplate.zip b/launcher/game/mmupdater/data/DDLC MT/DDLCModTemplate.zip new file mode 100644 index 00000000..b81855e4 Binary files /dev/null and b/launcher/game/mmupdater/data/DDLC MT/DDLCModTemplate.zip differ diff --git a/templates/Android Mod Guide.pdf b/launcher/game/mmupdater/data/DDLC MT/Documentation/Android Mod Guide.pdf similarity index 100% rename from templates/Android Mod Guide.pdf rename to launcher/game/mmupdater/data/DDLC MT/Documentation/Android Mod Guide.pdf diff --git a/launcher/game/mmupdater/data/DDLC MT/update.json b/launcher/game/mmupdater/data/DDLC MT/update.json new file mode 100644 index 00000000..66bcfe28 --- /dev/null +++ b/launcher/game/mmupdater/data/DDLC MT/update.json @@ -0,0 +1 @@ +{"url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/122901229", "assets_url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/122901229/assets", "upload_url": "https://uploads.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/122901229/assets{?name,label}", "html_url": "https://github.com/GanstaKingofSA/DDLCModTemplate2.0/releases/tag/4.2.3", "id": 122901229, "author": {"login": "GanstaKingofSA", "id": 40864681, "node_id": "MDQ6VXNlcjQwODY0Njgx", "avatar_url": "https://avatars.githubusercontent.com/u/40864681?v=4", "gravatar_id": "", "url": "https://api.github.com/users/GanstaKingofSA", "html_url": "https://github.com/GanstaKingofSA", "followers_url": "https://api.github.com/users/GanstaKingofSA/followers", "following_url": "https://api.github.com/users/GanstaKingofSA/following{/other_user}", "gists_url": "https://api.github.com/users/GanstaKingofSA/gists{/gist_id}", "starred_url": "https://api.github.com/users/GanstaKingofSA/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/GanstaKingofSA/subscriptions", "organizations_url": "https://api.github.com/users/GanstaKingofSA/orgs", "repos_url": "https://api.github.com/users/GanstaKingofSA/repos", "events_url": "https://api.github.com/users/GanstaKingofSA/events{/privacy}", "received_events_url": "https://api.github.com/users/GanstaKingofSA/received_events", "type": "User", "site_admin": false}, "node_id": "RE_kwDOC2MP_c4HU1Lt", "tag_name": "4.2.3", "target_commitish": "python-2", "name": "DDLC Mod Template 4.2.3 - Foreseen, Foreknown", "draft": false, "prerelease": false, "created_at": "2023-09-28T07:48:32Z", "published_at": "2023-09-28T07:59:49Z", "assets": [{"url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/assets/134803329", "id": 134803329, "node_id": "RA_kwDOC2MP_c4ICO-B", "name": "DDLCModTemplate-4.2.3-Extras.zip", "label": null, "uploader": {"login": "GanstaKingofSA", "id": 40864681, "node_id": "MDQ6VXNlcjQwODY0Njgx", "avatar_url": "https://avatars.githubusercontent.com/u/40864681?v=4", "gravatar_id": "", "url": "https://api.github.com/users/GanstaKingofSA", "html_url": "https://github.com/GanstaKingofSA", "followers_url": "https://api.github.com/users/GanstaKingofSA/followers", "following_url": "https://api.github.com/users/GanstaKingofSA/following{/other_user}", "gists_url": "https://api.github.com/users/GanstaKingofSA/gists{/gist_id}", "starred_url": "https://api.github.com/users/GanstaKingofSA/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/GanstaKingofSA/subscriptions", "organizations_url": "https://api.github.com/users/GanstaKingofSA/orgs", "repos_url": "https://api.github.com/users/GanstaKingofSA/repos", "events_url": "https://api.github.com/users/GanstaKingofSA/events{/privacy}", "received_events_url": "https://api.github.com/users/GanstaKingofSA/received_events", "type": "User", "site_admin": false}, "content_type": "application/x-zip-compressed", "state": "uploaded", "size": 13062, "download_count": 4, "created_at": "2023-11-09T22:35:22Z", "updated_at": "2023-11-09T22:35:23Z", "browser_download_url": "https://github.com/GanstaKingofSA/DDLCModTemplate2.0/releases/download/4.2.3/DDLCModTemplate-4.2.3-Extras.zip"}, {"url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/assets/128104167", "id": 128104167, "node_id": "RA_kwDOC2MP_c4Horbn", "name": "DDLCModTemplate-4.2.3-Py3.zip", "label": null, "uploader": {"login": "GanstaKingofSA", "id": 40864681, "node_id": "MDQ6VXNlcjQwODY0Njgx", "avatar_url": "https://avatars.githubusercontent.com/u/40864681?v=4", "gravatar_id": "", "url": "https://api.github.com/users/GanstaKingofSA", "html_url": "https://github.com/GanstaKingofSA", "followers_url": "https://api.github.com/users/GanstaKingofSA/followers", "following_url": "https://api.github.com/users/GanstaKingofSA/following{/other_user}", "gists_url": "https://api.github.com/users/GanstaKingofSA/gists{/gist_id}", "starred_url": "https://api.github.com/users/GanstaKingofSA/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/GanstaKingofSA/subscriptions", "organizations_url": "https://api.github.com/users/GanstaKingofSA/orgs", "repos_url": "https://api.github.com/users/GanstaKingofSA/repos", "events_url": "https://api.github.com/users/GanstaKingofSA/events{/privacy}", "received_events_url": "https://api.github.com/users/GanstaKingofSA/received_events", "type": "User", "site_admin": false}, "content_type": "application/x-zip-compressed", "state": "uploaded", "size": 2256523, "download_count": 149, "created_at": "2023-09-28T07:57:46Z", "updated_at": "2023-09-28T07:57:47Z", "browser_download_url": "https://github.com/GanstaKingofSA/DDLCModTemplate2.0/releases/download/4.2.3/DDLCModTemplate-4.2.3-Py3.zip"}, {"url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/assets/134803328", "id": 134803328, "node_id": "RA_kwDOC2MP_c4ICO-A", "name": "DDLCModTemplate-4.2.3-Py3Extras.zip", "label": null, "uploader": {"login": "GanstaKingofSA", "id": 40864681, "node_id": "MDQ6VXNlcjQwODY0Njgx", "avatar_url": "https://avatars.githubusercontent.com/u/40864681?v=4", "gravatar_id": "", "url": "https://api.github.com/users/GanstaKingofSA", "html_url": "https://github.com/GanstaKingofSA", "followers_url": "https://api.github.com/users/GanstaKingofSA/followers", "following_url": "https://api.github.com/users/GanstaKingofSA/following{/other_user}", "gists_url": "https://api.github.com/users/GanstaKingofSA/gists{/gist_id}", "starred_url": "https://api.github.com/users/GanstaKingofSA/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/GanstaKingofSA/subscriptions", "organizations_url": "https://api.github.com/users/GanstaKingofSA/orgs", "repos_url": "https://api.github.com/users/GanstaKingofSA/repos", "events_url": "https://api.github.com/users/GanstaKingofSA/events{/privacy}", "received_events_url": "https://api.github.com/users/GanstaKingofSA/received_events", "type": "User", "site_admin": false}, "content_type": "application/x-zip-compressed", "state": "uploaded", "size": 19310, "download_count": 1, "created_at": "2023-11-09T22:35:22Z", "updated_at": "2023-11-09T22:35:22Z", "browser_download_url": "https://github.com/GanstaKingofSA/DDLCModTemplate2.0/releases/download/4.2.3/DDLCModTemplate-4.2.3-Py3Extras.zip"}, {"url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/assets/128104163", "id": 128104163, "node_id": "RA_kwDOC2MP_c4Horbj", "name": "DDLCModTemplate-4.2.3.zip", "label": null, "uploader": {"login": "GanstaKingofSA", "id": 40864681, "node_id": "MDQ6VXNlcjQwODY0Njgx", "avatar_url": "https://avatars.githubusercontent.com/u/40864681?v=4", "gravatar_id": "", "url": "https://api.github.com/users/GanstaKingofSA", "html_url": "https://github.com/GanstaKingofSA", "followers_url": "https://api.github.com/users/GanstaKingofSA/followers", "following_url": "https://api.github.com/users/GanstaKingofSA/following{/other_user}", "gists_url": "https://api.github.com/users/GanstaKingofSA/gists{/gist_id}", "starred_url": "https://api.github.com/users/GanstaKingofSA/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/GanstaKingofSA/subscriptions", "organizations_url": "https://api.github.com/users/GanstaKingofSA/orgs", "repos_url": "https://api.github.com/users/GanstaKingofSA/repos", "events_url": "https://api.github.com/users/GanstaKingofSA/events{/privacy}", "received_events_url": "https://api.github.com/users/GanstaKingofSA/received_events", "type": "User", "site_admin": false}, "content_type": "application/x-zip-compressed", "state": "uploaded", "size": 2061967, "download_count": 259, "created_at": "2023-09-28T07:57:44Z", "updated_at": "2023-09-28T07:57:45Z", "browser_download_url": "https://github.com/GanstaKingofSA/DDLCModTemplate2.0/releases/download/4.2.3/DDLCModTemplate-4.2.3.zip"}], "tarball_url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/tarball/4.2.3", "zipball_url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/zipball/4.2.3", "body": "# Welcome to the DDLC Mod Template\r\n\r\nThis minor update for the mod template does the following.\r\n\r\n## Fixes\r\n- **(Python 3)** Allow Tearing to be Saved [PR #73]\r\n\r\n## Changes\r\n- Changed init timings for Achievements\r\n\r\n# Selecting the right mod template\r\n- If you are modding DDLC for the first time, download the `DDLCModTemplate-4.2.3.zip` file.\r\n > It is recommended to mod DDLC on **Ren'Py 7** if you use this version of the mod template. Ren'Py 6 is **NOT** recommended to make your DDLC mods anymore. [more info](https://www.reddit.com/r/DDLCMods/wiki/notices/#wiki_why_is_the_megathread_and_other_users_recommending_me_to_create_my_mod_in_ren.27py_7.3F)\r\n\r\n- If you are modding DDLC on Ren'Py 8, download the `DDLCModTemplate-4.2.3-Py3.zip` file. \r\n\r\nInstallation of both mod template versions are the same as all template installation steps.\r\n\r\nIf you are interested in downloading the Extra tools for the mod template, download either `DDLCModTemplate-4.2.3-Extras.zip` for normal versions of the mod template or `DDLCModTemplate-4.2.3-Py3Extras.zip` for Ren'Py 8 versions of the mod template.\r\n## Read the README file before installing anything and backup your mod."} \ No newline at end of file diff --git a/launcher/game/mmupdater/data/DDMM/update.json b/launcher/game/mmupdater/data/DDMM/update.json new file mode 100644 index 00000000..eab14432 --- /dev/null +++ b/launcher/game/mmupdater/data/DDMM/update.json @@ -0,0 +1 @@ +{"url": "https://api.github.com/repos/GanstaKingofSA/DDLC-ModMaker/releases/90382426", "assets_url": "https://api.github.com/repos/GanstaKingofSA/DDLC-ModMaker/releases/90382426/assets", "upload_url": "https://uploads.github.com/repos/GanstaKingofSA/DDLC-ModMaker/releases/90382426/assets{?name,label}", "html_url": "https://github.com/GanstaKingofSA/DDLC-ModMaker/releases/tag/1.5.22", "id": 90382426, "author": {"login": "GanstaKingofSA", "id": 40864681, "node_id": "MDQ6VXNlcjQwODY0Njgx", "avatar_url": "https://avatars.githubusercontent.com/u/40864681?v=4", "gravatar_id": "", "url": "https://api.github.com/users/GanstaKingofSA", "html_url": "https://github.com/GanstaKingofSA", "followers_url": "https://api.github.com/users/GanstaKingofSA/followers", "following_url": "https://api.github.com/users/GanstaKingofSA/following{/other_user}", "gists_url": "https://api.github.com/users/GanstaKingofSA/gists{/gist_id}", "starred_url": "https://api.github.com/users/GanstaKingofSA/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/GanstaKingofSA/subscriptions", "organizations_url": "https://api.github.com/users/GanstaKingofSA/orgs", "repos_url": "https://api.github.com/users/GanstaKingofSA/repos", "events_url": "https://api.github.com/users/GanstaKingofSA/events{/privacy}", "received_events_url": "https://api.github.com/users/GanstaKingofSA/received_events", "type": "User", "site_admin": false}, "node_id": "RE_kwDODPsvss4FYyBa", "tag_name": "1.5.22", "target_commitish": "mmaker-7.4", "name": "DDMM 1.5.22 - Astute Voyage 2 Part I.II", "draft": false, "prerelease": false, "created_at": "2023-01-27T05:52:16Z", "published_at": "2023-01-27T06:03:32Z", "assets": [{"url": "https://api.github.com/repos/GanstaKingofSA/DDLC-ModMaker/releases/assets/93213191", "id": 93213191, "node_id": "RA_kwDODPsvss4FjlIH", "name": "DDMMaker6-1.5.22-sdk.zip", "label": null, "uploader": {"login": "GanstaKingofSA", "id": 40864681, "node_id": "MDQ6VXNlcjQwODY0Njgx", "avatar_url": "https://avatars.githubusercontent.com/u/40864681?v=4", "gravatar_id": "", "url": "https://api.github.com/users/GanstaKingofSA", "html_url": "https://github.com/GanstaKingofSA", "followers_url": "https://api.github.com/users/GanstaKingofSA/followers", "following_url": "https://api.github.com/users/GanstaKingofSA/following{/other_user}", "gists_url": "https://api.github.com/users/GanstaKingofSA/gists{/gist_id}", "starred_url": "https://api.github.com/users/GanstaKingofSA/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/GanstaKingofSA/subscriptions", "organizations_url": "https://api.github.com/users/GanstaKingofSA/orgs", "repos_url": "https://api.github.com/users/GanstaKingofSA/repos", "events_url": "https://api.github.com/users/GanstaKingofSA/events{/privacy}", "received_events_url": "https://api.github.com/users/GanstaKingofSA/received_events", "type": "User", "site_admin": false}, "content_type": "application/zip", "state": "uploaded", "size": 52020548, "download_count": 391, "created_at": "2023-01-27T06:02:35Z", "updated_at": "2023-01-27T06:02:40Z", "browser_download_url": "https://github.com/GanstaKingofSA/DDLC-ModMaker/releases/download/1.5.22/DDMMaker6-1.5.22-sdk.zip"}, {"url": "https://api.github.com/repos/GanstaKingofSA/DDLC-ModMaker/releases/assets/95274584", "id": 95274584, "node_id": "RA_kwDODPsvss4FrcZY", "name": "DDMMaker7.4-1.5.22-sdk.zip", "label": null, "uploader": {"login": "GanstaKingofSA", "id": 40864681, "node_id": "MDQ6VXNlcjQwODY0Njgx", "avatar_url": "https://avatars.githubusercontent.com/u/40864681?v=4", "gravatar_id": "", "url": "https://api.github.com/users/GanstaKingofSA", "html_url": "https://github.com/GanstaKingofSA", "followers_url": "https://api.github.com/users/GanstaKingofSA/followers", "following_url": "https://api.github.com/users/GanstaKingofSA/following{/other_user}", "gists_url": "https://api.github.com/users/GanstaKingofSA/gists{/gist_id}", "starred_url": "https://api.github.com/users/GanstaKingofSA/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/GanstaKingofSA/subscriptions", "organizations_url": "https://api.github.com/users/GanstaKingofSA/orgs", "repos_url": "https://api.github.com/users/GanstaKingofSA/repos", "events_url": "https://api.github.com/users/GanstaKingofSA/events{/privacy}", "received_events_url": "https://api.github.com/users/GanstaKingofSA/received_events", "type": "User", "site_admin": false}, "content_type": "application/zip", "state": "uploaded", "size": 77868427, "download_count": 671, "created_at": "2023-02-12T01:14:38Z", "updated_at": "2023-02-12T01:15:01Z", "browser_download_url": "https://github.com/GanstaKingofSA/DDLC-ModMaker/releases/download/1.5.22/DDMMaker7.4-1.5.22-sdk.zip"}, {"url": "https://api.github.com/repos/GanstaKingofSA/DDLC-ModMaker/releases/assets/95274637", "id": 95274637, "node_id": "RA_kwDODPsvss4FrcaN", "name": "DDMMaker8-1.5.22-sdk.zip", "label": null, "uploader": {"login": "GanstaKingofSA", "id": 40864681, "node_id": "MDQ6VXNlcjQwODY0Njgx", "avatar_url": "https://avatars.githubusercontent.com/u/40864681?v=4", "gravatar_id": "", "url": "https://api.github.com/users/GanstaKingofSA", "html_url": "https://github.com/GanstaKingofSA", "followers_url": "https://api.github.com/users/GanstaKingofSA/followers", "following_url": "https://api.github.com/users/GanstaKingofSA/following{/other_user}", "gists_url": "https://api.github.com/users/GanstaKingofSA/gists{/gist_id}", "starred_url": "https://api.github.com/users/GanstaKingofSA/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/GanstaKingofSA/subscriptions", "organizations_url": "https://api.github.com/users/GanstaKingofSA/orgs", "repos_url": "https://api.github.com/users/GanstaKingofSA/repos", "events_url": "https://api.github.com/users/GanstaKingofSA/events{/privacy}", "received_events_url": "https://api.github.com/users/GanstaKingofSA/received_events", "type": "User", "site_admin": false}, "content_type": "application/zip", "state": "uploaded", "size": 62995407, "download_count": 315, "created_at": "2023-02-12T01:15:03Z", "updated_at": "2023-02-12T01:15:17Z", "browser_download_url": "https://github.com/GanstaKingofSA/DDLC-ModMaker/releases/download/1.5.22/DDMMaker8-1.5.22-sdk.zip"}], "tarball_url": "https://api.github.com/repos/GanstaKingofSA/DDLC-ModMaker/tarball/1.5.22", "zipball_url": "https://api.github.com/repos/GanstaKingofSA/DDLC-ModMaker/zipball/1.5.22", "body": "# Welcome to Doki Doki Mod Maker\r\n\r\nThis update is a hotfix for 1.5.2 that fixes a major bug.\r\n\r\n- Fixed a bug where the readVersion function fails outside of the project directory.\r\n\r\n## Depreciation Warning\r\nDDMM 6 is being put into question for future updates due to a lack of demand for Ren'Py 6 mods, and projects along with keeping up with the current years' development lifecycle. DDMM 6 may no longer be updated after 1.5.2 or any later versions and this post may change to state the final release of DDMM 6.\r\n\r\n## Selecting the version to download\r\n**(NOT RECOMMENDED)** If you do not want to use Ren'Py 7 tools/features, download `DDMMaker6-1.5.22-sdk.zip`.\r\nIf you are new to DDLC modding to modding or want to use Ren'Py 7 tools/features, download `DDMMaker7.4-1.5.22-sdk.zip`.\r\nIf you want Ren'Py 8 support (#BeOnTheBleedingEdge), download `DDMMaker8-1.5.22-sdk.zip`."} \ No newline at end of file diff --git a/launcher/game/mmupdater/data/DDMT-Extras/DDLCModTemplate-Py3Extras.zip b/launcher/game/mmupdater/data/DDMT-Extras/DDLCModTemplate-Py3Extras.zip new file mode 100644 index 00000000..bf7d38b3 Binary files /dev/null and b/launcher/game/mmupdater/data/DDMT-Extras/DDLCModTemplate-Py3Extras.zip differ diff --git a/launcher/game/mmupdater/data/DDMT-Extras/update.json b/launcher/game/mmupdater/data/DDMT-Extras/update.json new file mode 100644 index 00000000..66bcfe28 --- /dev/null +++ b/launcher/game/mmupdater/data/DDMT-Extras/update.json @@ -0,0 +1 @@ +{"url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/122901229", "assets_url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/122901229/assets", "upload_url": "https://uploads.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/122901229/assets{?name,label}", "html_url": "https://github.com/GanstaKingofSA/DDLCModTemplate2.0/releases/tag/4.2.3", "id": 122901229, "author": {"login": "GanstaKingofSA", "id": 40864681, "node_id": "MDQ6VXNlcjQwODY0Njgx", "avatar_url": "https://avatars.githubusercontent.com/u/40864681?v=4", "gravatar_id": "", "url": "https://api.github.com/users/GanstaKingofSA", "html_url": "https://github.com/GanstaKingofSA", "followers_url": "https://api.github.com/users/GanstaKingofSA/followers", "following_url": "https://api.github.com/users/GanstaKingofSA/following{/other_user}", "gists_url": "https://api.github.com/users/GanstaKingofSA/gists{/gist_id}", "starred_url": "https://api.github.com/users/GanstaKingofSA/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/GanstaKingofSA/subscriptions", "organizations_url": "https://api.github.com/users/GanstaKingofSA/orgs", "repos_url": "https://api.github.com/users/GanstaKingofSA/repos", "events_url": "https://api.github.com/users/GanstaKingofSA/events{/privacy}", "received_events_url": "https://api.github.com/users/GanstaKingofSA/received_events", "type": "User", "site_admin": false}, "node_id": "RE_kwDOC2MP_c4HU1Lt", "tag_name": "4.2.3", "target_commitish": "python-2", "name": "DDLC Mod Template 4.2.3 - Foreseen, Foreknown", "draft": false, "prerelease": false, "created_at": "2023-09-28T07:48:32Z", "published_at": "2023-09-28T07:59:49Z", "assets": [{"url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/assets/134803329", "id": 134803329, "node_id": "RA_kwDOC2MP_c4ICO-B", "name": "DDLCModTemplate-4.2.3-Extras.zip", "label": null, "uploader": {"login": "GanstaKingofSA", "id": 40864681, "node_id": "MDQ6VXNlcjQwODY0Njgx", "avatar_url": "https://avatars.githubusercontent.com/u/40864681?v=4", "gravatar_id": "", "url": "https://api.github.com/users/GanstaKingofSA", "html_url": "https://github.com/GanstaKingofSA", "followers_url": "https://api.github.com/users/GanstaKingofSA/followers", "following_url": "https://api.github.com/users/GanstaKingofSA/following{/other_user}", "gists_url": "https://api.github.com/users/GanstaKingofSA/gists{/gist_id}", "starred_url": "https://api.github.com/users/GanstaKingofSA/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/GanstaKingofSA/subscriptions", "organizations_url": "https://api.github.com/users/GanstaKingofSA/orgs", "repos_url": "https://api.github.com/users/GanstaKingofSA/repos", "events_url": "https://api.github.com/users/GanstaKingofSA/events{/privacy}", "received_events_url": "https://api.github.com/users/GanstaKingofSA/received_events", "type": "User", "site_admin": false}, "content_type": "application/x-zip-compressed", "state": "uploaded", "size": 13062, "download_count": 4, "created_at": "2023-11-09T22:35:22Z", "updated_at": "2023-11-09T22:35:23Z", "browser_download_url": "https://github.com/GanstaKingofSA/DDLCModTemplate2.0/releases/download/4.2.3/DDLCModTemplate-4.2.3-Extras.zip"}, {"url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/assets/128104167", "id": 128104167, "node_id": "RA_kwDOC2MP_c4Horbn", "name": "DDLCModTemplate-4.2.3-Py3.zip", "label": null, "uploader": {"login": "GanstaKingofSA", "id": 40864681, "node_id": "MDQ6VXNlcjQwODY0Njgx", "avatar_url": "https://avatars.githubusercontent.com/u/40864681?v=4", "gravatar_id": "", "url": "https://api.github.com/users/GanstaKingofSA", "html_url": "https://github.com/GanstaKingofSA", "followers_url": "https://api.github.com/users/GanstaKingofSA/followers", "following_url": "https://api.github.com/users/GanstaKingofSA/following{/other_user}", "gists_url": "https://api.github.com/users/GanstaKingofSA/gists{/gist_id}", "starred_url": "https://api.github.com/users/GanstaKingofSA/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/GanstaKingofSA/subscriptions", "organizations_url": "https://api.github.com/users/GanstaKingofSA/orgs", "repos_url": "https://api.github.com/users/GanstaKingofSA/repos", "events_url": "https://api.github.com/users/GanstaKingofSA/events{/privacy}", "received_events_url": "https://api.github.com/users/GanstaKingofSA/received_events", "type": "User", "site_admin": false}, "content_type": "application/x-zip-compressed", "state": "uploaded", "size": 2256523, "download_count": 149, "created_at": "2023-09-28T07:57:46Z", "updated_at": "2023-09-28T07:57:47Z", "browser_download_url": "https://github.com/GanstaKingofSA/DDLCModTemplate2.0/releases/download/4.2.3/DDLCModTemplate-4.2.3-Py3.zip"}, {"url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/assets/134803328", "id": 134803328, "node_id": "RA_kwDOC2MP_c4ICO-A", "name": "DDLCModTemplate-4.2.3-Py3Extras.zip", "label": null, "uploader": {"login": "GanstaKingofSA", "id": 40864681, "node_id": "MDQ6VXNlcjQwODY0Njgx", "avatar_url": "https://avatars.githubusercontent.com/u/40864681?v=4", "gravatar_id": "", "url": "https://api.github.com/users/GanstaKingofSA", "html_url": "https://github.com/GanstaKingofSA", "followers_url": "https://api.github.com/users/GanstaKingofSA/followers", "following_url": "https://api.github.com/users/GanstaKingofSA/following{/other_user}", "gists_url": "https://api.github.com/users/GanstaKingofSA/gists{/gist_id}", "starred_url": "https://api.github.com/users/GanstaKingofSA/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/GanstaKingofSA/subscriptions", "organizations_url": "https://api.github.com/users/GanstaKingofSA/orgs", "repos_url": "https://api.github.com/users/GanstaKingofSA/repos", "events_url": "https://api.github.com/users/GanstaKingofSA/events{/privacy}", "received_events_url": "https://api.github.com/users/GanstaKingofSA/received_events", "type": "User", "site_admin": false}, "content_type": "application/x-zip-compressed", "state": "uploaded", "size": 19310, "download_count": 1, "created_at": "2023-11-09T22:35:22Z", "updated_at": "2023-11-09T22:35:22Z", "browser_download_url": "https://github.com/GanstaKingofSA/DDLCModTemplate2.0/releases/download/4.2.3/DDLCModTemplate-4.2.3-Py3Extras.zip"}, {"url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/releases/assets/128104163", "id": 128104163, "node_id": "RA_kwDOC2MP_c4Horbj", "name": "DDLCModTemplate-4.2.3.zip", "label": null, "uploader": {"login": "GanstaKingofSA", "id": 40864681, "node_id": "MDQ6VXNlcjQwODY0Njgx", "avatar_url": "https://avatars.githubusercontent.com/u/40864681?v=4", "gravatar_id": "", "url": "https://api.github.com/users/GanstaKingofSA", "html_url": "https://github.com/GanstaKingofSA", "followers_url": "https://api.github.com/users/GanstaKingofSA/followers", "following_url": "https://api.github.com/users/GanstaKingofSA/following{/other_user}", "gists_url": "https://api.github.com/users/GanstaKingofSA/gists{/gist_id}", "starred_url": "https://api.github.com/users/GanstaKingofSA/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/GanstaKingofSA/subscriptions", "organizations_url": "https://api.github.com/users/GanstaKingofSA/orgs", "repos_url": "https://api.github.com/users/GanstaKingofSA/repos", "events_url": "https://api.github.com/users/GanstaKingofSA/events{/privacy}", "received_events_url": "https://api.github.com/users/GanstaKingofSA/received_events", "type": "User", "site_admin": false}, "content_type": "application/x-zip-compressed", "state": "uploaded", "size": 2061967, "download_count": 259, "created_at": "2023-09-28T07:57:44Z", "updated_at": "2023-09-28T07:57:45Z", "browser_download_url": "https://github.com/GanstaKingofSA/DDLCModTemplate2.0/releases/download/4.2.3/DDLCModTemplate-4.2.3.zip"}], "tarball_url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/tarball/4.2.3", "zipball_url": "https://api.github.com/repos/GanstaKingofSA/DDLCModTemplate2.0/zipball/4.2.3", "body": "# Welcome to the DDLC Mod Template\r\n\r\nThis minor update for the mod template does the following.\r\n\r\n## Fixes\r\n- **(Python 3)** Allow Tearing to be Saved [PR #73]\r\n\r\n## Changes\r\n- Changed init timings for Achievements\r\n\r\n# Selecting the right mod template\r\n- If you are modding DDLC for the first time, download the `DDLCModTemplate-4.2.3.zip` file.\r\n > It is recommended to mod DDLC on **Ren'Py 7** if you use this version of the mod template. Ren'Py 6 is **NOT** recommended to make your DDLC mods anymore. [more info](https://www.reddit.com/r/DDLCMods/wiki/notices/#wiki_why_is_the_megathread_and_other_users_recommending_me_to_create_my_mod_in_ren.27py_7.3F)\r\n\r\n- If you are modding DDLC on Ren'Py 8, download the `DDLCModTemplate-4.2.3-Py3.zip` file. \r\n\r\nInstallation of both mod template versions are the same as all template installation steps.\r\n\r\nIf you are interested in downloading the Extra tools for the mod template, download either `DDLCModTemplate-4.2.3-Extras.zip` for normal versions of the mod template or `DDLCModTemplate-4.2.3-Py3Extras.zip` for Ren'Py 8 versions of the mod template.\r\n## Read the README file before installing anything and backup your mod."} \ No newline at end of file diff --git a/launcher/game/mmupdater/mod_maker.py b/launcher/game/mmupdater/mod_maker.py new file mode 100644 index 00000000..e7a73d70 --- /dev/null +++ b/launcher/game/mmupdater/mod_maker.py @@ -0,0 +1,73 @@ +from . import MMakerGithubTool +import requests +import os +from zipfile import ZipFile +from renpy import config +import renpy +import shutil + +class ModTool(MMakerGithubTool): + + def __init__(self): + super().__init__("Bronya-Rand/DDLC-ModMaker", + "mmaker-7.4", + "DDMMaker", + "Doki Doki Mod Maker", + "DDMM", + "bronya_rand", + "DDMM is a Ren'Py SDK Modification that makes DDLC Modding easier and efficient with better wording, auto-tool install, and more! This tool adheres to the Team Salvato IP Guidelines and RenpyTom Guidelines for DDLC and Ren'Py SDK modding.", + True, False) + + def update(self): + git_ver = self.get_remote_tool_version() + rpy_num = renpy.version_tuple[0] # Easier said than done + pkg_url = self.package_url + self.package_file + "{}-{}-sdk.zip".format(rpy_num, git_ver) + + zipContent = requests.get(pkg_url) + filepath = "{}/{}".format(self.install_dir, self.package_file) + + try: + os.remove(filepath) + except: pass + + with open(filepath, "wb") as newTemplate: + newTemplate.write(zipContent.content) + + with ZipFile(filepath, "r") as z: + z.extractall("{}".format(self.install_dir)) + + ddmm_file = "{}/DDMMaker.zip".format(self.install_dir) + + for update_src, dirs, files in os.walk(ddmm_file): + dst_dir = update_src.replace(ddmm_file, config.basedir) + + if not os.path.exists(dst_dir): + os.makedirs(dst_dir) + + for f in files: + update_file = os.path.join(update_src, f) + dst_file = os.path.join(dst_dir, f) + + if os.path.exists(dst_file): + if renpy.windows: + if os.stat(update_file) == os.stat(dst_file): + continue + else: + if os.path.samefile(update_file, dst_file): + continue + + os.remove(dst_file) + + shutil.move(update_file, dst_file) + + os.remove("{}/temp".format(self.install_dir)) + os.remove("{}/{}".format(self.install_dir, self.package_file)) + + def install_package(self): + # We don't use this for DDMM + pass + + def install(self, mod_folder): + # We don't use this for DDMM + pass + \ No newline at end of file diff --git a/launcher/game/mmupdater/mod_template.py b/launcher/game/mmupdater/mod_template.py new file mode 100644 index 00000000..7495d0d6 --- /dev/null +++ b/launcher/game/mmupdater/mod_template.py @@ -0,0 +1,38 @@ +from . import MMakerGithubTool +import requests +from zipfile import ZipFile +from renpy import PY2 + +class ModTool(MMakerGithubTool): + + def __init__(self): + super().__init__("Bronya-Rand/DDLCModTemplate2.0", "python-2", "DDLCModTemplate", tool=False) + + def update(self): + git_ver = self.get_remote_tool_version() + if PY2: + pkg_url = self.package_url + "{}/{}-{}.zip".format(git_ver, self.package_file, git_ver) + else: + pkg_url = self.package_url + "{}/{}-{}-Py3.zip".format(git_ver, self.package_file, git_ver) + + zipContent = requests.get(pkg_url) + filepath = "{}/{}.zip".format(self.install_dir, self.package_file) + + with open(filepath, "wb") as newTemplate: + newTemplate.write(zipContent.content) + + with ZipFile(filepath) as newTemplate: + newTemplate.extract("Documentation/Android Mod Guide.pdf", self.install_dir) + + def install_package(self): + self.update() + + def install(self, mod_folder): + if not self.check_if_installed(): + self.install_package() + + filepath = "{}/{}.zip".format(self.install_dir, self.package_file) + + with ZipFile(filepath, "r") as z: + z.extractall(mod_folder) + \ No newline at end of file diff --git a/launcher/game/mmupdater/mod_template_extras.py b/launcher/game/mmupdater/mod_template_extras.py new file mode 100644 index 00000000..be00f93e --- /dev/null +++ b/launcher/game/mmupdater/mod_template_extras.py @@ -0,0 +1,43 @@ +from . import MMakerGithubTool +import requests +from zipfile import ZipFile +from renpy import PY2 + +class ModTool(MMakerGithubTool): + + def __init__(self): + super().__init__( + github_name="Bronya-Rand/DDLCModTemplate2.0", + github_branch="python-2", + name="DDLC Mod Template 2.0 - Extras", + acronym="DDMT-Extras", + desc="An extension to the DDLC Mod Template 2.0 that adds additional features to the mod template. {u}This is the Python 3 Edition of the mod template extras and is not compatible with DDMM 6-7/Ren'Py 6-7{/u}.", + package_name="DDLCModTemplate-Py3Extras" if not PY2 else "DDLCModTemplate-Py2Extras", + restart=False, extra=True + ) + + def update(self): + git_ver = self.get_remote_tool_version() + if PY2: + pkg_url = self.package_url + "{}/DDLCModTemplate-{}-Extras.zip".format(git_ver, git_ver) + else: + pkg_url = self.package_url + "{}/DDLCModTemplate-{}-Py3Extras.zip".format(git_ver, git_ver) + + zipContent = requests.get(pkg_url) + filepath = "{}/{}.zip".format(self.install_dir, self.package_file) + + with open(filepath, "wb") as newTemplate: + newTemplate.write(zipContent.content) + + def install_package(self): + self.update() + + def install(self, mod_folder): + if not self.check_if_installed(): + self.install_package() + + filepath = "{}/{}.zip".format(self.install_dir, self.package_file) + + with ZipFile(filepath, "r") as z: + z.extractall(mod_folder) + \ No newline at end of file diff --git a/launcher/game/mobilebuild.rpy b/launcher/game/mobilebuild.rpy index 2addff23..0ced2272 100644 --- a/launcher/game/mobilebuild.rpy +++ b/launcher/game/mobilebuild.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files diff --git a/launcher/game/navigation.rpy b/launcher/game/navigation.rpy index 4d5d3943..41117d89 100644 --- a/launcher/game/navigation.rpy +++ b/launcher/game/navigation.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -249,7 +249,7 @@ screen navigation: if persistent.navigation == "todo": text _("No TODO comments found.\n\nTo create one, include \"# TODO\" in your script."): - text_align 0.5 + textalign 0.5 xalign 0.5 yalign 0.5 @@ -279,4 +279,3 @@ label navigation_loop: label navigation_refresh: $ project.current.update_dump(True) jump navigation_loop - diff --git a/launcher/game/new_project.rpy b/launcher/game/new_project.rpy index 553333c1..93cf289c 100644 --- a/launcher/game/new_project.rpy +++ b/launcher/game/new_project.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -78,16 +78,11 @@ label new_project: if persistent.zip_directory is None: $ interface.error(_("The DDLC ZIP directory could not be set. Giving up.")) - if not glob.glob(config.basedir + "/templates/DDLCModTemplate-*-Py3.zip"): - $ interface.error(_("The DDLC Mod Template ZIP file is missing in the templates folder. Check for updates, add the template to the templates folder, or reinstall DDMM.")) - - $ template = glob.glob(config.basedir + "/templates/DDLCModTemplate-*-Py3.zip")[-1] - - if persistent.install_mod_extras: - if not glob.glob(config.basedir + "/templates/DDLCModTemplate-*-Py3Extras.zip"): - $ interface.error(_("The DDLC Mod Template ZIP file is missing in the templates folder. Check for updates, add the template to the templates folder, or reinstall DDMM.")) - - $ template_extras = glob.glob(config.basedir + "/templates/DDLCModTemplate-*-Py3Extras.zip")[-1] + python: + try: + mmupdater.mod_template + except AttributeError: + interface.error(_("The DDLC Mod Template 2.0 package is missing on this copy of DDMM. Reinstall it using the Tool Installer and try again.")) python: while True: @@ -122,12 +117,7 @@ label new_project: interface.processing(_("Installing Template Files...")) with interface.error_handling(_("Extracting the DDLC Mod Template")): - extract.installation(template, project_dir) - - if persistent.install_mod_extras: - interface.processing(_("Installing Extra Template Files...")) - with interface.error_handling(_("Extracting Mod Extras for the Mod Template")): - extract.installation(template_extras, project_dir) + mmupdater.mod_template.ModTool().install(project_dir) interface.processing(_("Installing DDLC...")) if persistent.safari == True and renpy.macintosh: @@ -138,7 +128,7 @@ label new_project: extract.game_installation(persistent.zip_directory, project_dir) with open(project_dir + '/game/renpy-version.txt', 'w') as f: - f.write("8") + f.write(str(renpy.version_tuple[0])) # Easier for future ref interface.info(_("A file named `renpy-version.txt` has been created in your projects' game directory."), _("Do not delete this file as it is needed to determine which version of Ren'Py it uses for building your mod.")) @@ -148,86 +138,3 @@ label new_project: break return - -screen select_template: - - default result = project.manager.get("english") - - frame: - style_group "l" - style "l_root" - - window: - - has vbox - - label _("Choose Project Template") - - hbox: - - frame: - style "l_indent" - xmaximum ONETHIRD - - viewport: - scrollbars "vertical" - vbox: - for p in project.manager.templates: - textbutton "[p.name!q]" action SetScreenVariable("result", p) style "l_list" - - frame: - style "l_indent" - xmaximum TWOTHIRDS - - text _("Please select a template to use for your new project. The template sets the default font and the user interface language. If your language is not supported, choose 'english'.") - - - textbutton _("Return") action Jump("front_page") style "l_left_button" - textbutton _("Continue") action Return(result) style "l_right_button" - - -label new_theme_project: - - python hide: - if len(project.manager.templates) == 1: - template = project.manager.templates[0] - else: - template = renpy.call_screen("select_template") - - template_path = template.path - - with interface.error_handling(_("creating a new project")): - shutil.copytree(template_path, project_dir, symlinks=False) - - # Delete the tmp directory, if it exists. - if os.path.isdir(os.path.join(project_dir, "tmp")): - shutil.rmtree(os.path.join(project_dir, "tmp")) - - # Delete project.json, which must exist. - os.unlink(os.path.join(project_dir, "project.json")) - - # Change the save directory in options.rpy - fn = os.path.join(project_dir, "game/options.rpy") - with open(fn, "rb") as f: - options = f.read().decode("utf-8") - - options = options.replace("PROJECT_NAME", project_name) - options = options.replace("UNIQUE", str(int(time.time()))) - - with open(fn, "wb") as f: - f.write(options.encode("utf-8")) - - font = template.data.get("font", None) - if font is not None: - src = os.path.join(config.gamedir, "fonts", font) - dst = os.path.join(project_dir, "game", "tl", "None", font) - shutil.copy(src, dst) - - # Activate the project. - with interface.error_handling(_("activating the new project")): - project.manager.scan() - project.Select(project.manager.get(project_name))() - - call choose_theme_callable - - jump front_page diff --git a/launcher/game/options.rpy b/launcher/game/options.rpy index 4453414b..7cf3f5dc 100644 --- a/launcher/game/options.rpy +++ b/launcher/game/options.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -52,7 +52,7 @@ init -1 python hide: # These control the name and version of the game, that are reported # with tracebacks and other debugging logs. config.name = "Doki Doki Mod Maker" - config.version = "1.5.3" + config.version = "1.6.0" ##################### # Themes @@ -270,22 +270,8 @@ init python: build.classify_renpy("**/thumbs.db", None) build.classify_renpy("**/.*", None) - # Atom rules. These have to be very early, since Atom uses names like - # tmp for packages. - build.classify_renpy("atom/", "atom-all") - build.classify_renpy("atom/default-dot-atom/**", "atom-all") - build.classify_renpy("atom/atom-windows/**", "atom-windows") - build.classify_renpy("atom/Atom.app/**", "atom-mac") - build.classify_renpy("atom/atom-linux**", "atom-linux") - - try: - with open(os.path.join(config.renpy_base, "atom", "executable.txt")) as f: - for l in f: - build.executable(l.strip()) - except Exception: - pass - build.classify_renpy("rapt/**/libLive2DCubismCore.so", None) + build.classify_renpy("rapt/symbols/", None) build.classify_renpy("rapt/**", "rapt") build.executable("rapt/prototype/gradlew") @@ -351,16 +337,26 @@ init python: build.classify_renpy(pattern + "/**.rpyc", binary) build.classify_renpy(pattern + "/**.rpymc", binary) + + build.classify_renpy(pattern + "/**/" + renpy.script.BYTECODE_FILE, binary) + build.classify_renpy(pattern + "/**/cache/bytecode-*.rpyb", None) + build.classify_renpy(pattern + "/**/cache/*", binary) build.classify_renpy(pattern + "/**", source) + build.classify_renpy("renpy.py", "binary") + build.classify_renpy("renpy.sh", "binary") source_and_binary("renpy") # games. build.classify_renpy("launcher/game/theme/", None) - build.classify_renpy("gui/game/gui/", None) + + build.classify_renpy("gui/game/gui/", "source") + build.classify_renpy("gui/game/gui/bubble.png", "source") + build.classify_renpy("gui/game/gui/thoughtbubble.png", "source") + build.classify_renpy("gui/game/gui/*", None) source_and_binary("launcher", py=False) source_and_binary("gui", binary=None, py=False) @@ -401,6 +397,7 @@ init python: build.classify_renpy("module/pysdlsound/*.pyx", "source") build.classify_renpy("module/fribidi-src/**", "source") build.classify_renpy("module/tinyfiledialogs/**", "source") + build.classify_renpy("module/libhydrogen/**", "source") # all-platforms binary. build.classify_renpy("lib/**/*steam_api*", "steam") @@ -421,11 +418,6 @@ init python: build.classify_renpy("lib/", "binary") - # renpy.app is now built from scratch from distribute.rpy. - - # jedit rules. - build.classify_renpy("jedit/**", "jedit") - # Packages. build.packages = [ ] @@ -434,16 +426,12 @@ init python: build.package("source", "tar.bz2", "source source_only", update=False) build.package("steam", "zip", "steam", dlc=True) - build.package("jedit", "zip", "jedit", dlc=True) - - build.package("atom-linux", "tar.bz2", "atom-all atom-linux", dlc=True) - build.package("atom-mac", "zip", "atom-all atom-mac", dlc=True) - build.package("atom-windows", "zip", "atom-all atom-windows", dlc=True) - build.package("rapt", "zip", "rapt", dlc=True) build.package("renios", "zip", "renios", dlc=True) build.package("web", "zip", "web", dlc=True) +# The identifier for the SDK. +define build.mac_info_plist["CFBundleIdentifier"] = "org.renpy.sdk" # Enable the special launcher translation mode. define config.translate_launcher = True @@ -464,3 +452,6 @@ python early: # Since the launcher can be run directly or can be run from the SDK directory, # uneliding files needs to be handled slightly differently. define config.alternate_unelide_path = os.path.join(config.basedir, "launcher") + +# Disable skipping. +define config.allow_skipping = False diff --git a/launcher/game/package_formats.rpy b/launcher/game/package_formats.rpy index 74632430..d808c7de 100644 --- a/launcher/game/package_formats.rpy +++ b/launcher/game/package_formats.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -31,12 +31,11 @@ init python in distribute: import sys import threading - from renpy import six from zipfile import crc32 # Since the long type doesn't exist on py3, define it here - if six.PY3: + if not PY2: long = int zlib.Z_DEFAULT_COMPRESSION = 5 diff --git a/launcher/game/preferences.rpy b/launcher/game/preferences.rpy index 386c374b..abe0c7c5 100644 --- a/launcher/game/preferences.rpy +++ b/launcher/game/preferences.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -278,7 +278,7 @@ screen preferences(): #textbutton _("Sponsor message") style "l_checkbox" action ToggleField(persistent, "sponsor_message") #if ability.can_update: - #textbutton _("Check for Updates Daily") style "l_checkbox" action [ToggleField(persistent, "daily_update_check"), SetField(persistent, "last_update_check", None)] selected persistent.daily_update_check + #textbutton _("Daily check for update") style "l_checkbox" action [ToggleField(persistent, "daily_update_check"), SetField(persistent, "last_update_check", None)] selected persistent.daily_update_check elif preference_tab == "theme": @@ -302,8 +302,8 @@ screen preferences(): add HALF_SPACER textbutton _("Default theme") style "l_checkbox" action [SetField(persistent, "theme", None), RestartAtPreferences() ] - textbutton _("Dark theme") style "l_checkbox" action [SetField(persistent, "theme", "dark", None), RestartAtPreferences()] - textbutton _("Custom theme") style "l_checkbox" action [SetField(persistent, "theme", "custom", None), RestartAtPreferences()] + textbutton _("Dark theme") style "l_checkbox" action [SetField(persistent, "theme", "dark"), RestartAtPreferences()] + textbutton _("Custom theme") style "l_checkbox" action [SetField(persistent, "theme", "custom"), RestartAtPreferences()] add SPACER @@ -354,10 +354,6 @@ screen preferences(): textbutton _("Reset window size") style "l_nonbox" action Preference("display", 1.0) textbutton _("Clean temporary files") style "l_nonbox" action Jump("clean_tmp") - textbutton _("Disable Mod Template Updates") style "l_checkbox" action [ToggleField(persistent, "disable_mt_update"), Function(fetch_ddmm_updates), Function(fetch_ddmm_updates, mt=True)] - textbutton _("Disable Mod Maker Updates") style "l_checkbox" action [ToggleField(persistent, "disable_mm_update"), Function(fetch_ddmm_updates), Function(fetch_ddmm_updates, mt=True)] - textbutton _("Install Extra Template Content") style "l_checkbox" action ToggleField(persistent, "install_mod_extras") - if renpy.macintosh: textbutton _("Change Extract Settings") style "l_nonbox" action Jump("auto_extract") @@ -403,7 +399,7 @@ screen choose_language(): style "l_label_text" size 36 - text_align .5 + textalign .5 layout "subtitle" add SPACER @@ -416,7 +412,7 @@ screen choose_language(): for tlid, tlname in tran: textbutton tlname: xmaximum (TWOTHIRDS//3) - action SetScreenVariable("chosen_lang", tlid) + action [ SetScreenVariable("chosen_lang", tlid), Language(tlid), project.SelectTutorial(True), Return() ] hovered SetScreenVariable("local_lang", tlid) unhovered SetScreenVariable("local_lang", chosen_lang) style "l_list" @@ -437,7 +433,7 @@ screen choose_language(): text_style "l_default" text_size 30 - text_text_align .5 + text_textalign .5 text_layout "subtitle" diff --git a/launcher/game/project.rpy b/launcher/game/project.rpy index 67e1af95..282df33c 100644 --- a/launcher/game/project.rpy +++ b/launcher/game/project.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -50,6 +50,7 @@ init python in project: _("Press shift+O (the letter) to access the console."), _("Press shift+D to access the developer menu."), _("Have you backed up your projects recently?"), + _("Lint checks your game for potential mistakes, and gives you statistics."), ] class Project(object): @@ -262,6 +263,10 @@ init python in project: environ["RENPY_LAUNCHER_LANGUAGE"] = _preferences.language or "english" environ.update(env) + # Filter out system PYTHON* environment variables. + if hasattr(sys, "renpy_executable"): + environ = { k : v for k, v in environ.items() if not k.startswith("PYTHON") } + encoded_environ = { } for k, v in environ.items(): @@ -278,6 +283,8 @@ init python in project: if wait: if p.wait(): + print("Launch failed. command={!r}, returncode={!r}".format(cmd, p.returncode)) + if args and not self.is_writeable(): interface.error(_("Launching the project failed."), _("This may be because the project is not writeable.")) else: @@ -389,9 +396,18 @@ init python in project: can be included in the project. """ + def is_script(fn): + fn = fn.lower() + + for i in [ ".rpy", ".rpym", "_ren.py" ]: + if fn.endswith(i): + return True + + return False + rv = [ ] rv.extend(i for i, isdir in util.walk(self.path) - if (not isdir) and (i.endswith(".rpy") or i.endswith(".rpym")) and (not i.startswith("tmp/")) ) + if (not isdir) and is_script(i) and (not i.startswith("tmp/")) ) return rv @@ -858,24 +874,19 @@ label auto_extract: label delete_folder: python: - confirm_delete = False interface.yesno( label=_("Deleting a Project"), message=_("Are you sure you want to delete '[project.current.name!q]'?"), filename=False, - yes=[SetVariable("confirm_delete", True), Return()], - no=Return(), + no=Jump("front_page"), cancel=Jump("front_page")) + + interface.processing(_("Deleting [project.current.name]...")) + + with interface.error_handling(_("deleting mod.")): + modman.delete_mod(persistent.projects_directory, project.current.name) - if not confirm_delete: - renpy.jump("front_page") - else: - interface.processing(_("Deleting [project.current.name]...")) - - with interface.error_handling(_("deleting mod.")): - modman.delete_mod(persistent.projects_directory, project.current.name) - - interface.info("[project.current.name] has been deleted from the projects folder.") + interface.info("[project.current.name] has been deleted from the projects folder.") project.manager.scan() @@ -908,3 +919,24 @@ init python: return False renpy.arguments.register_command("get_projects_directory", get_projects_directory_command) + + def set_project_command(): + ap = renpy.arguments.ArgumentParser() + ap.add_argument("project", help="The full path to the project to select.") + + args = ap.parse_args() + + projects = os.path.dirname(os.path.abspath(args.project)) + name = os.path.basename(args.project) + + persistent.projects_directory = renpy.fsdecode(projects) + project.multipersistent.projects_directory = persistent.projects_directory + + persistent.active_project = name + + project.multipersistent.save() + renpy.save_persistent() + + return False + + renpy.arguments.register_command("set_project", set_project_command) diff --git a/launcher/game/rapt_hash.txt b/launcher/game/rapt_hash.txt index d627081e..1e29cb7b 100644 --- a/launcher/game/rapt_hash.txt +++ b/launcher/game/rapt_hash.txt @@ -1 +1 @@ -8ee4d3ffb0f3ffdd2177556d941c373a7b05a1e71ea740a63f7da88e8086cda8 \ No newline at end of file +6d13e19b246f0f61d4bf850a70bf4befc52fdcbd3b3198ac4c572d02eed41961 \ No newline at end of file diff --git a/launcher/game/renios_hash.txt b/launcher/game/renios_hash.txt index 58e20160..001eaa5f 100644 --- a/launcher/game/renios_hash.txt +++ b/launcher/game/renios_hash.txt @@ -1 +1 @@ -0db6c7fc9ec51ebc33434546a4ea4b593a4225254671e74d40c5afab73c7d876 \ No newline at end of file +c06f1cdc661440acffd06781377c64848c59836e7c1a47a56cc526c7cd1ab9f8 \ No newline at end of file diff --git a/launcher/game/style.rpy b/launcher/game/style.rpy index 7738ddd7..3bc42f6d 100644 --- a/launcher/game/style.rpy +++ b/launcher/game/style.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -353,7 +353,7 @@ style l_unavail_button is l_right_button style l_unavail_button_text is l_right_button_text: size size(24) - + # The root frame. This contains everything but the bottom navigation, # and buttons. style l_root is l_default: @@ -406,7 +406,7 @@ style l_alternate is l_default: style l_alternate_text is l_default: size size(14) font light_font() - text_align 1.0 + textalign 1.0 style l_small_button is l_button @@ -492,7 +492,7 @@ style l_info_button is l_button: xmargin 50 style l_info_button_text is l_button_text: - text_align 0.5 + textalign 0.5 layout "subtitle" # Progress bar. diff --git a/launcher/game/tail.rpy b/launcher/game/tail.rpy index ca0b42bd..60d11bda 100644 --- a/launcher/game/tail.rpy +++ b/launcher/game/tail.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -22,6 +22,7 @@ init python: + class FileTail(object): def __init__(self, filename, lines=8): @@ -31,6 +32,11 @@ init python: def update(self): + def filter_text(s): + if "Unknown chunk type '200'" in s: + return False + return True + try: with open(self.filename) as f: text = f.read() @@ -49,6 +55,9 @@ init python: if "\r" in l: _head, _sep, l = l.rpartition("\r") + if not filter_text(l): + continue + while l: newtext.append(l[:100]) l = l[100:] diff --git a/launcher/game/theme_data.rpy b/launcher/game/theme_data.rpy index c1f3137f..67d1e32f 100644 --- a/launcher/game/theme_data.rpy +++ b/launcher/game/theme_data.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files diff --git a/launcher/game/tool_install.rpy b/launcher/game/tool_install.rpy index 75d3f759..d0e00e56 100644 --- a/launcher/game/tool_install.rpy +++ b/launcher/game/tool_install.rpy @@ -5,98 +5,106 @@ init python: from zipfile import ZipFile import tempfile -label tool_install: +screen mmtoolinstaller(): - python: - gamedir = False - confirm_install = False - - project_dir = os.path.join(persistent.projects_directory, project.current.name) - - interface.info("This installer is in beta.\nNot all mod tools will install properly and may require changes before launch.\nMake sure to backup your project if anything fails.") - - if renpy.macintosh and persistent.safari: - interface.interaction(_("Tool Folder"), _("Please select the tool folder you wish to install."),) - - path, is_default = choose_directory(None) - else: - interface.interaction(_("Tool ZIP File"), _("Please select the tool ZIP file you wish to install."),) - - path, is_default = choose_file(None, bracket=["DDLC Tool ZIP File", "*.zip"]) - - if is_default: - interface.error(_("The operation has been cancelled.")) - renpy.jump("front_page") - - interface.yesno( - label=_("Install Tool"), - message=_("Are you sure you install {file}".format(file=path.replace("\\", "/").split("/")[-1] + "?")), - filename=False, - yes=[SetVariable("confirm_install", True), Return()], - no=Return(), - cancel=Jump("front_page")) - - if not confirm_install: - renpy.jump("front_page") - else: - interface.processing("Installing Tool. Please wait...") - - with interface.error_handling("extracting user tool"): - if renpy.macintosh and persistent.safari: - td = path - else: - td = tempfile.mkdtemp(prefix="DDMM_",suffix="_TempTool") - with ZipFile(path, "r") as z: - z.extractall(td) - - tool_dir = None - for src, dirs, files in os.walk(td): - for d in dirs: - if d in ["game", "mod_assets", "python-packages"]: - if "game" in d: - tool_dir = os.path.join(src, d) - gamedir = True - else: - tool_dir = os.path.join(src, d).replace("\\" + d, "") - gamedir = True - break - if tool_dir is None: - # Assume the best is the src + subfolder itself - tool_dir = os.path.join(td, os.listdir(td)[-1]) - - with interface.error_handling("extracting user tool pt2"): - for tool_src, dirs, files in os.walk(tool_dir): - if gamedir: - dst_dir = tool_src.replace(tool_dir, project_dir + "/game") - else: - dst_dir = tool_src.replace(tool_dir, project_dir + "/game/mod_assets") - - for d in dirs: - if not os.path.exists(os.path.join(dst_dir, d)): - os.makedirs(os.path.join(dst_dir, d)) - - for f in files: - temp_file = os.path.join(tool_src, f) - dst_file = os.path.join(dst_dir, f) - - if os.path.exists(dst_file): - if renpy.windows: - if os.stat(temp_file) == os.stat(dst_file): - continue - else: - if os.path.samefile(temp_file, dst_file): - continue - - os.remove(dst_file) - - if renpy.macintosh and persistent.safari: - shutil.copy2(temp_file, dst_file) - else: - shutil.move(temp_file, dst_file) - - if not renpy.macintosh and not persistent.safari: - shutil.rmtree(td) + default tools = {} + default have_third_party = False + + frame: + style_group "l" + style "l_root" + + window: + + has viewport: + scrollbars "vertical" + mousewheel True + + has vbox + + label _("DDMM Tool Installer") - interface.info("DDMM successfully installed the selected tool to [project.current.name].") - + add HALF_SPACER + + hbox: + frame: + style "l_indent" + xfill True + + has vbox + + text _("Installing to: {}".format(project.current.name)) size 15 + + add SPACER + + python: + with open(config.basedir + '/update/first_party.json') as fp: + first_party_json = json.load(fp) + + if os.path.exists(config.basedir + '/update/third_party.json'): + with open(config.basedir + '/update/third_party.json') as tp: + third_party_json = json.load(tp) + + for i, cls in mmupdate.all_modules.items(): + module_name = eval(cls) + acronym = module_name.ModTool().acronym + if module_name.ModTool().tool: + tools[acronym] = {} + tools[acronym]['cls'] = module_name.ModTool() + + try: + first_party_json[acronym] + tools[acronym]['third_party'] = False + except KeyError: + have_third_party = True + tools[acronym]['third_party'] = True + + text "First Party" size 18 + + add SPACER + + for acc, tool in tools.items(): + if not tool['third_party']: + + textbutton tool['cls'].name action Call("install_script", tool['cls'], project.current.path) + text "Creator: {}".format(tool['cls'].creator) size 14 + text "Identifier: {} | Downloaded: {}".format(acc, "Yes" if tool['cls'].check_if_installed() else "No") size 14 + text "\n" + tool['cls'].desc size 14 + + add SPACER + + add SEPARATOR + add SPACER + + text "Third Party" size 18 + + add SPACER + + if have_third_party: + for acc, tool in tools.items(): + if tool['third_party']: + + textbutton tool['cls'].name action Call("install_script", tool['cls'], project.current.path) + text "Creator: {}".format(tool['cls'].creator) size 14 + text "Identifier: {} | Downloaded: {}".format(acc, "Yes" if tool['cls'].check_if_installed else "No") size 14 + text "\n" + tool['cls'].desc size 14 + + add SPACER + else: + + text "No third-party tools available." + + textbutton _("Return") action Jump("front_page") style "l_left_button" + +label install_script(pkg, directory): + python: + with interface.error_handling(_("Installing " + pkg.name)): + interface.processing(_("Installing " + pkg.name + ". Please wait...")) + pkg.install(directory) + + interface.info(_(pkg.name + " has been successfully installed onto your mod.")) + renpy.jump("mmtoolinstaller") + +label mmtoolinstaller: + call screen mmtoolinstaller jump front_page \ No newline at end of file diff --git a/launcher/game/translations.rpy b/launcher/game/translations.rpy index 63dbe90c..33054341 100644 --- a/launcher/game/translations.rpy +++ b/launcher/game/translations.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files diff --git a/launcher/game/updater.rpy b/launcher/game/updater.rpy index 8f5a9c97..88f64e9c 100644 --- a/launcher/game/updater.rpy +++ b/launcher/game/updater.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -52,7 +52,7 @@ init python: if state is not None: base_name = state.get("sdk", {}).get('base_name', '') - if "-nightly-" in base_name: + if "+nightly" in base_name: dlc_url = "http://nightly.renpy.org/{}/updates.json".format(base_name[6:]) return renpy.invoke_in_new_context(updater.update, dlc_url, add=[name], public_key=PUBLIC_KEY, simulate=UPDATE_SIMULATE, restart=restart) @@ -73,11 +73,17 @@ init python: _("Experimental") _("Experimental versions of Ren'Py. You shouldn't select this channel unless asked by a Ren'Py developer.") + _("Nightly Fix") + _("Nightly Fix (Ren'Py 8, Python 3)") + _("Nightly Fix (Ren'Py 7, Python 2)") + _("A nightly build of fixes to the release version of Ren'Py.") + _("Nightly") _("Nightly (Ren'Py 8, Python 3)") _("Nightly (Ren'Py 7, Python 2)") _("The bleeding edge of Ren'Py development. This may have the latest features, or might not run at all.") +default allow_repair_update = False screen update_channel(channels): @@ -108,8 +114,8 @@ screen update_channel(channels): for c in channels: - if c["split_version"] != list(renpy.version_tuple): - $ action = [SetField(persistent, "has_update", None), SetField(persistent, "last_update_check", None), updater.Update(c["url"], simulate=UPDATE_SIMULATE, public_key=PUBLIC_KEY, confirm=False)] + if c["split_version"] != list(renpy.version_tuple) or allow_repair_update: + $ action = [SetField(persistent, "has_update", None), SetField(persistent, "last_update_check", None), updater.Update(c["url"], simulate=UPDATE_SIMULATE, public_key=PUBLIC_KEY, confirm=False, force=allow_repair_update)] if c["channel"].startswith("Release"): $ current = _("• {a=https://www.renpy.org/doc/html/changelog.html}View change log{/a}") diff --git a/launcher/game/util.rpy b/launcher/game/util.rpy index 4bb1b2fb..37ba6b41 100644 --- a/launcher/game/util.rpy +++ b/launcher/game/util.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files diff --git a/launcher/skin/skin.rpy b/launcher/skin/skin.rpy new file mode 100644 index 00000000..fbbe1756 --- /dev/null +++ b/launcher/skin/skin.rpy @@ -0,0 +1,45 @@ +init -2 python: + + # The color of non-interactive text. + custom_text = "#545454" + + # Colors for buttons in various states. + custom_idle = "#42637b" + custom_hover = "#d86b45" + custom_disable = "#808080" + + # Colors for reversed text buttons (selected list entries). + custom_reverse_idle = "#78a5c5" + reverse_hover = "#d86b45" + custom_reverse_text = "#ffffff" + + # Colors for the scrollbar thumb. + custom_scrollbar_idle = "#dfdfdf" + custom_scrollbar_hover = "#d86b45" + + # An image used as a separator pattern. + custom_pattern = "images/pattern.png" + + # A displayable used for the background of everything. + custom_background = "skin/skin_background.jpg" + + # A displayable used for the background of the projects list. + custom_projects_window = Null() + + # A displayable used the background of information boxes. + custom_info_window = "#f9f9f9c0" + + # Colors for the titles of information boxes. + custom_error_color = "#d15353" + custom_info_color = "#545454" + custom_interaction_color = "#d19753" + custom_question_color = "#d19753" + + # The color of input text. + custom_input_color = "#d86b45" + + # A displayable used for the background of windows + # containing commands, preferences, and navigation info. + + # custom_window = Frame(Fixed(Solid(custom_reverse_idle, xsize=4, xalign=0), Solid(custom_info_window, xsize=794, xalign=1.0), xsize=800, ysize=600), 0, 0, tile=True) + custom_window = Null() diff --git a/launcher/skin/skin_background.jpg b/launcher/skin/skin_background.jpg new file mode 100644 index 00000000..1b6579fb Binary files /dev/null and b/launcher/skin/skin_background.jpg differ diff --git a/renpy/common/00build.rpy b/renpy/common/00build.rpy index b5b2bba5..e67ab3ae 100644 --- a/renpy/common/00build.rpy +++ b/renpy/common/00build.rpy @@ -1,4 +1,4 @@ -# Copyright 2004-2022 Tom Rothamel +# Copyright 2004-2023 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -143,7 +143,6 @@ init -1500 python in build: ])) - def classify_renpy(pattern, groups): """ Classifies files in the Ren'Py base directory according to pattern. @@ -159,6 +158,7 @@ init -1500 python in build: ("*.app/", None), ("*.dll", None), ("*.manifest", None), + ("*.keystore", None), ("lib/", None), ("renpy/", None), @@ -194,6 +194,7 @@ init -1500 python in build: ("game/presplash*.*", "all"), + ("android.json", "android"), (".android.json", "android"), ("android-*.png", "android"), ("android-*.jpg", "android"), @@ -210,7 +211,11 @@ init -1500 python in build: ("steam_appid.txt", None), - ]) + ("game/" + renpy.script.BYTECODE_FILE, "all"), + ("game/cache/bytecode-311.rpyb", "web"), + ("game/cache/bytecode-*.rpyb", None), + ]) + base_patterns = [ ] @@ -340,11 +345,14 @@ init -1500 python in build: dmg A Macintosh DMG containing the files. app-zip - A zip file containing a macintosh application. + A zip file containing a macintosh application. This format + doesn't support the Ren'Py updater. app-directory - A directory containing the mac app. + A directory containing the mac app. This format + doesn't support the Ren'Py updater. app-dmg - A macintosh drive image containing a dmg. (Mac only.) + A macintosh drive image containing a dmg. (Mac only.) This format + doesn't support the Ren'Py updater. bare-zip A zip file without :var:`build.directory_name` prepended. @@ -399,15 +407,15 @@ init -1500 python in build: packages.append(d) - # package("pc", "zip", "windows linux renpy all", "PC: Windows and Linux") - # package("linux", "tar.bz2", "linux linux_arm renpy all", "Linux") - # package("mac", "app-zip app-dmg", "mac renpy all", "Macintosh") - # package("win", "zip", "windows renpy all", "Windows") - # package("market", "bare-zip", "windows linux mac renpy all", "Windows, Mac, Linux for Markets") - # package("steam", "zip", "windows linux mac renpy all", hidden=True) + #package("pc", "zip", "windows linux renpy all", "PC: Windows and Linux") + #package("linux", "tar.bz2", "linux linux_arm renpy all", "Linux") + #package("mac", "app-zip app-dmg", "mac renpy all", "Macintosh") + #package("win", "zip", "windows renpy all", "Windows") + #package("market", "bare-zip", "windows linux mac renpy all", "Windows, Mac, Linux for Markets") + #package("steam", "zip", "windows linux mac renpy all", hidden=True) package("android", "directory", "android all", hidden=True, update=False, dlc=True) - # package("ios", "directory", "ios all", hidden=True, update=False, dlc=True) - # package("web", "zip", "web all", hidden=True, update=False, dlc=True) + #package("ios", "directory", "ios all", hidden=True, update=False, dlc=True) + #package("web", "zip", "web renpy all", hidden=True, update=False, dlc=True) package("Renpy8-DDLCMod", "zip", "windows linux mac renpy mod", "(DDMM) Ren'Py 8 DDLC Compliant Mod") # Data that we expect the user to set. @@ -452,6 +460,17 @@ init -1500 python in build: # The itch.io project name. itch_project = None + # Maps from files to itch.io channels. + itch_channels = [ + ( "*-all.zip", "win-osx-linux" ), + ( "*-market.zip", "win-osx-linux" ), + ( "*-pc.zip", "win-linux" ), + ( "*-win.zip", "win" ), + ( "*-mac.zip", "osx" ), + ( "*-linux.tar.bz2", "linux" ), + ( "*-release.apk", "android" ), + ] + # Should we include the old Ren'Py themes? include_old_themes = True @@ -548,6 +567,8 @@ init -1500 python in build: if itch_project: rv["itch_project"] = itch_project + rv["itch_channels"] = itch_channels + if mac_identity: rv["mac_identity"] = mac_identity rv["mac_codesign_command"] = mac_codesign_command diff --git a/templates/DDLCModTemplate-4.2.0-Py3.zip b/templates/DDLCModTemplate-4.2.0-Py3.zip deleted file mode 100644 index 430cf79c..00000000 Binary files a/templates/DDLCModTemplate-4.2.0-Py3.zip and /dev/null differ diff --git a/templates/Discord RPC Guide.pdf b/templates/Discord RPC Guide.pdf deleted file mode 100644 index 6123c7a8..00000000 Binary files a/templates/Discord RPC Guide.pdf and /dev/null differ