core: clarify usage of classmethods in World class (#1449)

This commit is contained in:
el-u 2023-02-16 00:28:02 +01:00 committed by GitHub
parent b20be3ccec
commit ad4846cedd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 28 additions and 20 deletions

View File

@ -433,7 +433,7 @@ In addition, the following methods can be implemented and attributes can be set
* `required_client_version: Tuple(int, int, int)` * `required_client_version: Tuple(int, int, int)`
Client version as tuple of 3 ints to make sure the client is compatible to Client version as tuple of 3 ints to make sure the client is compatible to
this world (e.g. implements all required features) when connecting. this world (e.g. implements all required features) when connecting.
* `assert_generate(cls, world)` is a class method called at the start of * `stage_assert_generate(cls, multiworld)` is a class method called at the start of
generation to check the existence of prerequisite files, usually a ROM for generation to check the existence of prerequisite files, usually a ROM for
games which require one. games which require one.

View File

@ -22,3 +22,12 @@ class TestImplemented(unittest.TestCase):
for region in multiworld.regions: for region in multiworld.regions:
for exit in region.exits: for exit in region.exits:
self.assertEqual(exit.parent_region, region) self.assertEqual(exit.parent_region, region)
def testStageMethods(self):
"""Tests that worlds don't try to implement certain steps that are only ever called as stage."""
for game_name, world_type in AutoWorldRegister.world_types.items():
if not world_type.hidden:
with self.subTest(game_name):
for method in ("assert_generate",):
self.assertFalse(hasattr(world_type, method),
f"{method} must be implemented as a @classmethod named stage_{method}.")

View File

@ -188,10 +188,11 @@ class World(metaclass=AutoWorldRegister):
# can also be implemented as a classmethod and called "stage_<original_name>", # can also be implemented as a classmethod and called "stage_<original_name>",
# in that case the MultiWorld object is passed as an argument and it gets called once for the entire multiworld. # in that case the MultiWorld object is passed as an argument and it gets called once for the entire multiworld.
# An example of this can be found in alttp as stage_pre_fill # An example of this can be found in alttp as stage_pre_fill
@classmethod @classmethod
def assert_generate(cls) -> None: def stage_assert_generate(cls, multiworld: "MultiWorld") -> None:
"""Checks that a game is capable of generating, usually checks for some base file like a ROM. """Checks that a game is capable of generating, usually checks for some base file like a ROM.
Not run for unittests since they don't produce output""" This gets called once per present world type. Not run for unittests since they don't produce output"""
pass pass
def generate_early(self) -> None: def generate_early(self) -> None:
@ -213,14 +214,12 @@ class World(metaclass=AutoWorldRegister):
"""Optional method that is supposed to be used for special fill stages. This is run *after* plando.""" """Optional method that is supposed to be used for special fill stages. This is run *after* plando."""
pass pass
@classmethod def fill_hook(self,
def fill_hook(cls,
progitempool: List["Item"], progitempool: List["Item"],
usefulitempool: List["Item"], usefulitempool: List["Item"],
filleritempool: List["Item"], filleritempool: List["Item"],
fill_locations: List["Location"]) -> None: fill_locations: List["Location"]) -> None:
"""Special method that gets called as part of distribute_items_restrictive (main fill). """Special method that gets called as part of distribute_items_restrictive (main fill)."""
This gets called once per present world type."""
pass pass
def post_fill(self) -> None: def post_fill(self) -> None:

View File

@ -29,5 +29,5 @@ class Bk_SudokuWorld(World):
location_name_to_id: Dict[str, int] = {} location_name_to_id: Dict[str, int] = {}
@classmethod @classmethod
def stage_assert_generate(cls, world): def stage_assert_generate(cls, multiworld):
raise Exception("BK Sudoku cannot be used for generating worlds, the client can instead connect to any other world") raise Exception("BK Sudoku cannot be used for generating worlds, the client can instead connect to any other world")

View File

@ -55,7 +55,7 @@ class DKC3World(World):
super().__init__(world, player) super().__init__(world, player)
@classmethod @classmethod
def stage_assert_generate(cls, world): def stage_assert_generate(cls, multiworld: MultiWorld):
rom_file = get_base_rom_path() rom_file = get_base_rom_path()
if not os.path.exists(rom_file): if not os.path.exists(rom_file):
raise FileNotFoundError(rom_file) raise FileNotFoundError(rom_file)

View File

@ -80,7 +80,7 @@ class L2ACWorld(World):
shuffle_party_members: Optional[ShufflePartyMembers] shuffle_party_members: Optional[ShufflePartyMembers]
@classmethod @classmethod
def stage_assert_generate(cls, _multiworld: MultiWorld) -> None: def stage_assert_generate(cls, multiworld: MultiWorld) -> None:
rom_file: str = get_base_rom_path() rom_file: str = get_base_rom_path()
if not os.path.exists(rom_file): if not os.path.exists(rom_file):
raise FileNotFoundError(f"Could not find base ROM for {cls.game}: {rom_file}") raise FileNotFoundError(f"Could not find base ROM for {cls.game}: {rom_file}")

View File

@ -140,7 +140,7 @@ class OOTWorld(World):
super(OOTWorld, self).__init__(world, player) super(OOTWorld, self).__init__(world, player)
@classmethod @classmethod
def stage_assert_generate(cls, world: MultiWorld): def stage_assert_generate(cls, multiworld: MultiWorld):
rom = Rom(file=get_options()['oot_options']['rom_file']) rom = Rom(file=get_options()['oot_options']['rom_file'])
def generate_early(self): def generate_early(self):

View File

@ -65,11 +65,11 @@ class PokemonRedBlueWorld(World):
self.traps = None self.traps = None
@classmethod @classmethod
def stage_assert_generate(cls, world): def stage_assert_generate(cls, multiworld: MultiWorld):
versions = set() versions = set()
for player in world.player_ids: for player in multiworld.player_ids:
if world.worlds[player].game == "Pokemon Red and Blue": if multiworld.worlds[player].game == "Pokemon Red and Blue":
versions.add(world.game_version[player].current_key) versions.add(multiworld.game_version[player].current_key)
for version in versions: for version in versions:
if not os.path.exists(get_base_rom_path(version)): if not os.path.exists(get_base_rom_path(version)):
raise FileNotFoundError(get_base_rom_path(version)) raise FileNotFoundError(get_base_rom_path(version))

View File

@ -107,7 +107,7 @@ class SMWorld(World):
super().__init__(world, player) super().__init__(world, player)
@classmethod @classmethod
def stage_assert_generate(cls, world): def stage_assert_generate(cls, multiworld: MultiWorld):
rom_file = get_base_rom_path() rom_file = get_base_rom_path()
if not os.path.exists(rom_file): if not os.path.exists(rom_file):
raise FileNotFoundError(rom_file) raise FileNotFoundError(rom_file)

View File

@ -55,7 +55,7 @@ class SMWWorld(World):
super().__init__(world, player) super().__init__(world, player)
@classmethod @classmethod
def stage_assert_generate(cls, world): def stage_assert_generate(cls, multiworld: MultiWorld):
rom_file = get_base_rom_path() rom_file = get_base_rom_path()
if not os.path.exists(rom_file): if not os.path.exists(rom_file):
raise FileNotFoundError(rom_file) raise FileNotFoundError(rom_file)

View File

@ -181,7 +181,7 @@ class SMZ3World(World):
return itemType in progressionTypes return itemType in progressionTypes
@classmethod @classmethod
def stage_assert_generate(cls, world): def stage_assert_generate(cls, multiworld: MultiWorld):
base_combined_rom = get_base_rom_bytes() base_combined_rom = get_base_rom_bytes()
def generate_early(self): def generate_early(self):

View File

@ -201,7 +201,7 @@ class SoEWorld(World):
return SoEItem(item.name, classification, self.item_name_to_id[item.name], self.player) return SoEItem(item.name, classification, self.item_name_to_id[item.name], self.player)
@classmethod @classmethod
def stage_assert_generate(cls, world): def stage_assert_generate(cls, multiworld):
rom_file = get_base_rom_path() rom_file = get_base_rom_path()
if not os.path.exists(rom_file): if not os.path.exists(rom_file):
raise FileNotFoundError(rom_file) raise FileNotFoundError(rom_file)

View File

@ -108,7 +108,7 @@ class ZillionWorld(World):
self.id_to_zz_item = id_to_zz_item self.id_to_zz_item = id_to_zz_item
@classmethod @classmethod
def stage_assert_generate(cls, world: MultiWorld) -> None: def stage_assert_generate(cls, multiworld: MultiWorld) -> None:
"""Checks that a game is capable of generating, usually checks for some base file like a ROM. """Checks that a game is capable of generating, usually checks for some base file like a ROM.
Not run for unittests since they don't produce output""" Not run for unittests since they don't produce output"""
rom_file = get_base_rom_path() rom_file = get_base_rom_path()