Core: APWorld manifest (#4516)

Adds support for a manifest file (archipelago.json) inside an .apworld file. It tells AP the game, minimum core version (optional field), maximum core version (optional field), its own version (used to determine which file to prefer to load only currently)
The file itself is marked as required starting with core 0.7.0, prior, just a warning is printed, with error trace.

Co-authored-by: Doug Hoskisson <beauxq@users.noreply.github.com>
Co-authored-by: qwint <qwint.42@gmail.com>
Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
Co-authored-by: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com>
This commit is contained in:
Fabian Dill
2025-09-24 02:39:19 +02:00
committed by GitHub
parent e256abfdfb
commit a99da85a22
5 changed files with 129 additions and 7 deletions

View File

@@ -371,6 +371,8 @@ class BuildExeCommand(cx_Freeze.command.build_exe.build_exe):
os.makedirs(self.buildfolder / "Players" / "Templates", exist_ok=True)
from Options import generate_yaml_templates
from worlds.AutoWorld import AutoWorldRegister
from worlds.Files import APWorldContainer
from Utils import version
assert not non_apworlds - set(AutoWorldRegister.world_types), \
f"Unknown world {non_apworlds - set(AutoWorldRegister.world_types)} designated for .apworld"
folders_to_remove: list[str] = []
@@ -379,13 +381,26 @@ class BuildExeCommand(cx_Freeze.command.build_exe.build_exe):
if worldname not in non_apworlds:
file_name = os.path.split(os.path.dirname(worldtype.__file__))[1]
world_directory = self.libfolder / "worlds" / file_name
if os.path.isfile(world_directory / "archipelago.json"):
manifest = json.load(open(world_directory / "archipelago.json"))
else:
manifest = {}
# this method creates an apworld that cannot be moved to a different OS or minor python version,
# which should be ok
with zipfile.ZipFile(self.libfolder / "worlds" / (file_name + ".apworld"), "x", zipfile.ZIP_DEFLATED,
zip_path = self.libfolder / "worlds" / (file_name + ".apworld")
apworld = APWorldContainer(str(zip_path))
apworld.minimum_ap_version = version
apworld.maximum_ap_version = version
apworld.game = worldtype.game
manifest.update(apworld.get_manifest())
apworld.manifest_path = f"{file_name}/archipelago.json"
with zipfile.ZipFile(zip_path, "x", zipfile.ZIP_DEFLATED,
compresslevel=9) as zf:
for path in world_directory.rglob("*.*"):
relative_path = os.path.join(*path.parts[path.parts.index("worlds")+1:])
zf.write(path, relative_path)
if not relative_path.endswith("archipelago.json"):
zf.write(path, relative_path)
zf.writestr(apworld.manifest_path, json.dumps(manifest))
folders_to_remove.append(file_name)
shutil.rmtree(world_directory)
shutil.copyfile("meta.yaml", self.buildfolder / "Players" / "Templates" / "meta.yaml")