mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-29 00:43:20 -07:00
Compare commits
1 Commits
core_manif
...
48a59247d9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48a59247d9 |
@@ -32,8 +32,6 @@ If the APWorld is a folder, the only required field is "game":
|
||||
There are also the following optional fields:
|
||||
* `minimum_ap_version` and `maximum_ap_version` - which if present will each be compared against the current
|
||||
Archipelago version respectively to filter those files from being loaded.
|
||||
* `platforms` - a list of strings indicating the `sys.platform`(s) the world can run on.
|
||||
If empty or not set, it is assumed to be any that python itself can run on.
|
||||
* `world_version` - an arbitrary version for that world in order to only load the newest valid world.
|
||||
An APWorld without a world_version is always treated as older than one with a version
|
||||
(**Must** use exactly the format `"major.minor.build"`, e.g. `1.0.0`)
|
||||
|
||||
1
setup.py
1
setup.py
@@ -409,7 +409,6 @@ class BuildExeCommand(cx_Freeze.command.build_exe.build_exe):
|
||||
apworld = APWorldContainer(str(zip_path))
|
||||
apworld.minimum_ap_version = version_tuple
|
||||
apworld.maximum_ap_version = version_tuple
|
||||
apworld.platforms = [sys.platform]
|
||||
apworld.game = worldtype.game
|
||||
manifest.update(apworld.get_manifest())
|
||||
apworld.manifest_path = f"{file_name}/archipelago.json"
|
||||
|
||||
@@ -13,7 +13,7 @@ from typing import (Any, ClassVar, Dict, FrozenSet, List, Optional, Self, Set, T
|
||||
from Options import item_and_loc_options, ItemsAccessibility, OptionGroup, PerGameCommonOptions
|
||||
from BaseClasses import CollectionState, Entrance
|
||||
from rule_builder.rules import CustomRuleRegister, Rule
|
||||
from Utils import Version
|
||||
from Utils import Version, tuplize_version, version_tuple
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from BaseClasses import CollectionRule, Item, Location, MultiWorld, Region, Tutorial
|
||||
@@ -33,6 +33,7 @@ class AutoWorldRegister(type):
|
||||
zip_path: Optional[str]
|
||||
settings_key: str
|
||||
__settings: Any
|
||||
__manifest: Any
|
||||
|
||||
@property
|
||||
def settings(cls) -> Any: # actual type is defined in World
|
||||
@@ -45,6 +46,38 @@ class AutoWorldRegister(type):
|
||||
return None
|
||||
return cls.__settings
|
||||
|
||||
@property
|
||||
def _manifest(cls) -> Dict[str, Any]:
|
||||
if cls.__manifest is None:
|
||||
if cls.zip_path:
|
||||
import zipfile
|
||||
from .Files import APWorldContainer
|
||||
container = APWorldContainer(str(cls.zip_path))
|
||||
with zipfile.ZipFile(container.path, "r") as zf:
|
||||
cls.__manifest = container.read_contents(zf)
|
||||
else:
|
||||
import json
|
||||
import os
|
||||
# look for manifest
|
||||
manifest_path = None
|
||||
world_dir = pathlib.Path(cls.__file__).parent
|
||||
for dirpath, dirnames, filenames in os.walk(world_dir):
|
||||
for file in filenames:
|
||||
if file.endswith("archipelago.json"):
|
||||
manifest_path = os.path.join(dirpath, file)
|
||||
break
|
||||
if manifest_path:
|
||||
break
|
||||
|
||||
if manifest_path:
|
||||
with open(manifest_path, "r", encoding="utf-8-sig") as f:
|
||||
cls.__manifest = json.load(f)
|
||||
elif version_tuple < (0, 7, 0):
|
||||
cls.__manifest = {}
|
||||
else:
|
||||
raise RuntimeError(f"Could not find manifest for {cls.__name__} in {world_dir}.")
|
||||
return cls.__manifest
|
||||
|
||||
def __new__(mcs, name: str, bases: Tuple[type, ...], dct: Dict[str, Any]) -> AutoWorldRegister:
|
||||
if "web" in dct:
|
||||
assert isinstance(dct["web"], WebWorld), "WebWorld has to be instantiated."
|
||||
@@ -101,6 +134,7 @@ class AutoWorldRegister(type):
|
||||
world_folder_name = mod_name[7:].lower() if mod_name.startswith("worlds.") else mod_name.lower()
|
||||
new_class.settings_key = world_folder_name + "_options"
|
||||
new_class.__settings = None
|
||||
new_class.__manifest = None
|
||||
return new_class
|
||||
|
||||
|
||||
@@ -353,8 +387,6 @@ class World(metaclass=AutoWorldRegister):
|
||||
"""path it was loaded from"""
|
||||
world_version: ClassVar[Version] = Version(0, 0, 0)
|
||||
"""Optional world version loaded from archipelago.json"""
|
||||
platforms: ClassVar[Optional[List[str]]] = None
|
||||
"""Optional platforms loaded from archipelago.json"""
|
||||
|
||||
def __init__(self, multiworld: "MultiWorld", player: int):
|
||||
assert multiworld is not None
|
||||
@@ -364,10 +396,15 @@ class World(metaclass=AutoWorldRegister):
|
||||
multiworld.per_slot_randoms[player] = self.random
|
||||
|
||||
def __getattr__(self, item: str) -> Any:
|
||||
if item == "settings":
|
||||
return self.__class__.settings
|
||||
if item in ("settings", "_manifest"):
|
||||
return getattr(self.__class__, item)
|
||||
raise AttributeError
|
||||
|
||||
@property
|
||||
def version(self) -> Version:
|
||||
"""World version loaded from archipelago.json"""
|
||||
return tuplize_version(self._manifest.get("world_version", "0.0.0"))
|
||||
|
||||
# overridable methods that get called by Main.py, sorted by execution order
|
||||
# can also be implemented as a classmethod and called "stage_<original_name>",
|
||||
# in that case the MultiWorld object is passed as the first argument, and it gets called once for the entire multiworld.
|
||||
|
||||
@@ -197,7 +197,6 @@ class APWorldContainer(APContainer):
|
||||
world_version: "Version | None" = None
|
||||
minimum_ap_version: "Version | None" = None
|
||||
maximum_ap_version: "Version | None" = None
|
||||
platforms: Optional[List[str]] = None
|
||||
|
||||
def read_contents(self, opened_zipfile: zipfile.ZipFile) -> Dict[str, Any]:
|
||||
from Utils import tuplize_version
|
||||
@@ -206,7 +205,6 @@ class APWorldContainer(APContainer):
|
||||
for version_key in ("world_version", "minimum_ap_version", "maximum_ap_version"):
|
||||
if version_key in manifest:
|
||||
setattr(self, version_key, tuplize_version(manifest[version_key]))
|
||||
self.platforms = manifest.get("platforms")
|
||||
return manifest
|
||||
|
||||
def get_manifest(self) -> Dict[str, Any]:
|
||||
@@ -217,8 +215,6 @@ class APWorldContainer(APContainer):
|
||||
version = getattr(self, version_key)
|
||||
if version:
|
||||
manifest[version_key] = version.as_simple_string()
|
||||
if self.platforms:
|
||||
manifest["platforms"] = self.platforms
|
||||
return manifest
|
||||
|
||||
|
||||
|
||||
@@ -289,12 +289,6 @@ if not is_frozen():
|
||||
if not worldtype:
|
||||
logging.error(f"Requested APWorld \"{worldname}\" does not exist.")
|
||||
continue
|
||||
|
||||
assert worldtype.platforms != [], (
|
||||
f"World {worldname} has an empty list for platforms. "
|
||||
"Use None or omit the attribute for 'any platform'."
|
||||
)
|
||||
|
||||
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")):
|
||||
|
||||
@@ -118,7 +118,6 @@ for world_source in world_sources:
|
||||
game = manifest.get("game")
|
||||
if game in AutoWorldRegister.world_types:
|
||||
AutoWorldRegister.world_types[game].world_version = tuplize_version(manifest.get("world_version", "0.0.0"))
|
||||
AutoWorldRegister.world_types[game].platforms = manifest.get("platforms")
|
||||
|
||||
if apworlds:
|
||||
# encapsulation for namespace / gc purposes
|
||||
@@ -166,11 +165,6 @@ if apworlds:
|
||||
f"Did not load {apworld_source.path} "
|
||||
f"as its maximum core version {apworld.maximum_ap_version} "
|
||||
f"is lower than current core version {version_tuple}.")
|
||||
elif apworld.platforms and sys.platform not in apworld.platforms:
|
||||
fail_world(apworld.game,
|
||||
f"Did not load {apworld_source.path} "
|
||||
f"as it is not compatible with current platform {sys.platform}. "
|
||||
f"Supported platforms: {', '.join(apworld.platforms)}")
|
||||
else:
|
||||
core_compatible.append((apworld_source, apworld))
|
||||
# load highest version first
|
||||
@@ -205,8 +199,6 @@ if apworlds:
|
||||
# world could fail to load at this point
|
||||
if apworld.world_version:
|
||||
AutoWorldRegister.world_types[apworld.game].world_version = apworld.world_version
|
||||
if apworld.platforms:
|
||||
AutoWorldRegister.world_types[apworld.game].platforms = apworld.platforms
|
||||
load_apworlds()
|
||||
del load_apworlds
|
||||
|
||||
|
||||
@@ -1699,8 +1699,7 @@ def patch_rom(multiworld: MultiWorld, rom: LocalRom, player: int, enemized: bool
|
||||
|
||||
# set rom name
|
||||
# 21 bytes
|
||||
from Utils import __version__
|
||||
rom.name = bytearray(f'AP{__version__.replace(".", "")[0:3]}_{player}_{multiworld.seed:11}\0', 'utf8')[:21]
|
||||
rom.name = bytearray(f'AP{local_world.version.as_simple_string().replace(".", "")[0:3]}_{player}_{multiworld.seed:11}\0', 'utf8')[:21]
|
||||
rom.name.extend([0] * (21 - len(rom.name)))
|
||||
rom.write_bytes(0x7FC0, rom.name)
|
||||
|
||||
|
||||
6
worlds/alttp/archipelago.json
Normal file
6
worlds/alttp/archipelago.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"game": "A Link to the Past",
|
||||
"minimum_ap_version": "0.6.6",
|
||||
"world_version": "5.1.0",
|
||||
"authors": ["Berserker"]
|
||||
}
|
||||
Reference in New Issue
Block a user