From 4ae87edf371869d43e18b04999d8915852456d5e Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Wed, 24 Sep 2025 23:25:46 +0200 Subject: [PATCH] Core: apworld manifest launcher component (#5340) adds a launcher component that builds all apworlds on top of #4516 --------- Co-authored-by: Doug Hoskisson Co-authored-by: qwint Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/LauncherComponents.py | 38 +++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/worlds/LauncherComponents.py b/worlds/LauncherComponents.py index 7bd47d0b..b2187298 100644 --- a/worlds/LauncherComponents.py +++ b/worlds/LauncherComponents.py @@ -5,7 +5,7 @@ import weakref from enum import Enum, auto from typing import Optional, Callable, List, Iterable, Tuple -from Utils import local_path, open_filename +from Utils import local_path, open_filename, is_frozen class Type(Enum): @@ -243,3 +243,39 @@ icon_paths = { 'icon': local_path('data', 'icon.png'), 'discord': local_path('data', 'discord-mark-blue.png'), } + +if not is_frozen(): + def _build_apworlds(): + import json + import os + import zipfile + + from worlds import AutoWorldRegister + from worlds.Files import APWorldContainer + + apworlds_folder = os.path.join("build", "apworlds") + os.makedirs(apworlds_folder, exist_ok=True) + for worldname, worldtype in AutoWorldRegister.world_types.items(): + file_name = os.path.split(os.path.dirname(worldtype.__file__))[1] + world_directory = os.path.join("worlds", file_name) + if os.path.isfile(os.path.join(world_directory, "archipelago.json")): + manifest = json.load(open(os.path.join(world_directory, "archipelago.json"))) + else: + manifest = {} + + zip_path = os.path.join(apworlds_folder, file_name + ".apworld") + apworld = APWorldContainer(str(zip_path)) + apworld.game = worldtype.game + manifest.update(apworld.get_manifest()) + apworld.manifest_path = f"{file_name}/archipelago.json" + with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED, + compresslevel=9) as zf: + for path in pathlib.Path(world_directory).rglob("*.*"): + relative_path = os.path.join(*path.parts[path.parts.index("worlds") + 1:]) + if "__MACOSX" in relative_path or ".DS_STORE" in relative_path or "__pycache__" in relative_path: + continue + if not relative_path.endswith("archipelago.json"): + zf.write(path, relative_path) + zf.writestr(apworld.manifest_path, json.dumps(manifest)) + + components.append(Component('Build apworlds', func=_build_apworlds, cli=True,))