Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,20 @@ class RomMeta(TypedDict):
player_name: str


class IncompatiblePatchError(Exception):
"""
Used to report a version mismatch between a patch's world version and
a user's installed world version that is too important to be compatible
"""
pass


def create_rom_file(patch_file: str) -> Tuple[RomMeta, str]:
auto_handler = AutoPatchRegister.get_handler(patch_file)
if auto_handler:
handler: APAutoPatchInterface = auto_handler(patch_file)
handler.read()
handler.verify_version()
target = os.path.splitext(patch_file)[0]+handler.result_file_ending
handler.patch(target)
return {"server": handler.server,
Expand Down
24 changes: 23 additions & 1 deletion worlds/Files.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ def get_manifest(self) -> Dict[str, Any]:
class APPlayerContainer(APContainer):
"""A zipfile containing at least archipelago.json meant for a player"""
game: ClassVar[Optional[str]] = None
world_version: "Version | None" = None
patch_file_ending: str = ""

player: Optional[int]
Expand All @@ -235,10 +236,13 @@ def __init__(self, path: Optional[str] = None, player: Optional[int] = None,
self.server = server

def read_contents(self, opened_zipfile: zipfile.ZipFile) -> Dict[str, Any]:
from Utils import tuplize_version
manifest = super().read_contents(opened_zipfile)
self.player = manifest["player"]
self.server = manifest["server"]
self.player_name = manifest["player_name"]
if "world_version" in manifest:
self.world_version = tuplize_version(manifest["world_version"])
return manifest

def get_manifest(self) -> Dict[str, Any]:
Expand All @@ -250,6 +254,9 @@ def get_manifest(self) -> Dict[str, Any]:
"game": self.game,
"patch_file_ending": self.patch_file_ending,
})
if self.game:
from .AutoWorld import AutoWorldRegister
manifest["world_version"] = AutoWorldRegister.world_types[self.game].world_version.as_simple_string()
return manifest


Expand Down Expand Up @@ -281,6 +288,22 @@ class APAutoPatchInterface(APPatch, abc.ABC, metaclass=AutoPatchRegister):
def patch(self, target: str) -> None:
""" create the output file with the file name `target` """

def verify_version(self) -> None:
"""
Verify compatibility between a game's currently installed
world version and the version used for generation.
Warns the user or raises an IncompatiblePatchError if the versions are too different.
"""
from Utils import messagebox
from .AutoWorld import AutoWorldRegister
game_version = AutoWorldRegister.world_types[self.game].world_version if self.game else None
if game_version and self.world_version and game_version != self.world_version:
info_msg = "This patch was generated with " \
f"{self.game} version {self.world_version.as_simple_string()}, " \
f"but its currently installed version is {game_version.as_simple_string()}. " \
"You may encounter errors while patching or connecting."
messagebox("APWorld version mismatch", info_msg, False)


class APProcedurePatch(APAutoPatchInterface):
"""
Expand Down Expand Up @@ -343,7 +366,6 @@ def write_file(self, file_name: str, file: bytes) -> None:
self.files[file_name] = file

def patch(self, target: str) -> None:
self.read()
base_data = self.get_source_data_with_cache()
patch_extender = AutoPatchExtensionRegister.get_handler(self.game)
assert not isinstance(self.procedure, str), f"{type(self)} must define procedures"
Expand Down
Loading