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
+31 -1
View File
@@ -8,7 +8,8 @@ import os
import threading
from io import BytesIO
from typing import ClassVar, Dict, List, Literal, Tuple, Any, Optional, Union, BinaryIO, overload, Sequence
from typing import (ClassVar, Dict, List, Literal, Tuple, Any, Optional, Union, BinaryIO, overload, Sequence,
TYPE_CHECKING)
import bsdiff4
@@ -16,6 +17,9 @@ semaphore = threading.Semaphore(os.cpu_count() or 4)
del threading
if TYPE_CHECKING:
from Utils import Version
class AutoPatchRegister(abc.ABCMeta):
patch_types: ClassVar[Dict[str, AutoPatchRegister]] = {}
@@ -163,6 +167,32 @@ class APContainer:
}
class APWorldContainer(APContainer):
"""A zipfile containing a world implementation."""
game: str | None = None
world_version: "Version | None" = None
minimum_ap_version: "Version | None" = None
maximum_ap_version: "Version | None" = None
def read_contents(self, opened_zipfile: zipfile.ZipFile) -> Dict[str, Any]:
from Utils import tuplize_version, Version
manifest = super().read_contents(opened_zipfile)
self.game = manifest["game"]
for version_key in ("world_version", "minimum_ap_version", "maximum_ap_version"):
if version_key in manifest:
setattr(self, version_key, Version(*tuplize_version(manifest[version_key])))
return manifest
def get_manifest(self) -> Dict[str, Any]:
manifest = super().get_manifest()
manifest["game"] = self.game
for version_key in ("world_version", "minimum_ap_version", "maximum_ap_version"):
version = getattr(self, version_key)
if version:
manifest[version_key] = version.as_simple_string()
return manifest
class APPlayerContainer(APContainer):
"""A zipfile containing at least archipelago.json meant for a player"""
game: ClassVar[Optional[str]] = None