Skip to content

Commit

Permalink
Core: move region and location management to worlds
Browse files Browse the repository at this point in the history
  • Loading branch information
alwaysintreble committed Oct 4, 2024
1 parent 05a6738 commit 3cb64d5
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 43 deletions.
88 changes: 49 additions & 39 deletions BaseClasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,34 +98,42 @@ def __getitem__(self, player) -> bool:
return self.rule(player)

class RegionManager:
region_cache: Dict[int, Dict[str, Region]]
entrance_cache: Dict[int, Dict[str, Entrance]]
location_cache: Dict[int, Dict[str, Location]]

def __init__(self, players: int):
self.region_cache = {player: {} for player in range(1, players+1)}
self.entrance_cache = {player: {} for player in range(1, players+1)}
self.location_cache = {player: {} for player in range(1, players+1)}
region_cache: Union[Dict[int, Dict[str, Region]], Dict[str, Region]]
entrance_cache: Union[Dict[int, Dict[str, Entrance]], Dict[str, Entrance]]
location_cache: Union[Dict[int, Dict[str, Location]], Dict[str, Location]]
multiworld: "MultiWorld"

def __init__(self, multiworld: "Multiworld" = None):
# players is no longer needed. The multiworld is passed in here so we can reference the worlds' caches
# while they continue to use multiworld.regions
# TODO remove later
self.multiworld = multiworld
self.region_cache = {}
self.entrance_cache = {}
self.location_cache = {}

def __iadd__(self, other: Iterable[Region]):
self.extend(other)
return self

def append(self, region: Region):
assert region.name not in self.region_cache[region.player], \
f"{region.name} already exists in region cache."
self.region_cache[region.player][region.name] = region
# TODO
if self.multiworld is not None:
region_cache = self.multiworld.worlds[region.player].regions.region_cache
else:
region_cache = self.region_cache
assert region.name not in region_cache, f"{region.name} already exists in region cache."
region_cache[region.name] = region

def extend(self, regions: Iterable[Region]):
# TODO
if self.multiworld is not None:
region_cache = self.multiworld.worlds[regions[0].player].regions.region_cache
else:
region_cache = self.region_cache
for region in regions:
assert region.name not in self.region_cache[region.player], \
f"{region.name} already exists in region cache."
self.region_cache[region.player][region.name] = region

def add_group(self, new_id: int):
self.region_cache[new_id] = {}
self.entrance_cache[new_id] = {}
self.location_cache[new_id] = {}
assert region.name not in region_cache, f"{region.name} already exists in region cache."
region_cache[region.name] = region

def __iter__(self) -> Iterator[Region]:
for regions in self.region_cache.values():
Expand All @@ -141,7 +149,7 @@ def __init__(self, players: int):
self.player_types = {player: NetUtils.SlotType.player for player in self.player_ids}
self.algorithm = 'balanced'
self.groups = {}
self.regions = self.RegionManager(players)
self.regions = self.RegionManager(self)
self.shops = []
self.itempool = []
self.seed = None
Expand Down Expand Up @@ -189,7 +197,6 @@ def add_group(self, name: str, game: str, players: AbstractSet[int] = frozenset(
return group_id, group
new_id: int = self.players + len(self.groups) + 1

self.regions.add_group(new_id)
self.game[new_id] = game
self.player_types[new_id] = NetUtils.SlotType.group
world_type = AutoWorld.AutoWorldRegister.world_types[game]
Expand Down Expand Up @@ -417,16 +424,19 @@ def world_name_lookup(self):
return {self.player_name[player_id]: player_id for player_id in self.player_ids}

def get_regions(self, player: Optional[int] = None) -> Collection[Region]:
return self.regions if player is None else self.regions.region_cache[player].values()
if player is not None:
return self.worlds[player].regions.region_cache.values()
return Utils.RepeatableChain(tuple(self.worlds[player].regions.region_cache.values()
for player in self.get_all_ids()))

def get_region(self, region_name: str, player: int) -> Region:
return self.regions.region_cache[player][region_name]
return self.worlds[player].get_region(region_name)

def get_entrance(self, entrance_name: str, player: int) -> Entrance:
return self.regions.entrance_cache[player][entrance_name]
return self.worlds[player].get_entrance(entrance_name)

def get_location(self, location_name: str, player: int) -> Location:
return self.regions.location_cache[player][location_name]
return self.worlds[player].get_location(location_name)

def get_all_state(self, use_cache: bool) -> CollectionState:
cached = getattr(self, "_all_state", None)
Expand Down Expand Up @@ -489,9 +499,9 @@ def push_item(self, location: Location, item: Item, collect: bool = True):

def get_entrances(self, player: Optional[int] = None) -> Iterable[Entrance]:
if player is not None:
return self.regions.entrance_cache[player].values()
return Utils.RepeatableChain(tuple(self.regions.entrance_cache[player].values()
for player in self.regions.entrance_cache))
return self.worlds[player].regions.entrance_cache.values()
return Utils.RepeatableChain(tuple(self.worlds[player].regions.entrance_cache.values()
for player in self.get_all_ids()))

def register_indirect_condition(self, region: Region, entrance: Entrance):
"""Report that access to this Region can result in unlocking this Entrance,
Expand All @@ -500,9 +510,9 @@ def register_indirect_condition(self, region: Region, entrance: Entrance):

def get_locations(self, player: Optional[int] = None) -> Iterable[Location]:
if player is not None:
return self.regions.location_cache[player].values()
return Utils.RepeatableChain(tuple(self.regions.location_cache[player].values()
for player in self.regions.location_cache))
return self.worlds[player].regions.location_cache.values()
return Utils.RepeatableChain(tuple(self.worlds[player].regions.location_cache.values()
for player in self.get_all_ids()))

def get_unfilled_locations(self, player: Optional[int] = None) -> List[Location]:
return [location for location in self.get_locations(player) if location.item is None]
Expand All @@ -524,7 +534,7 @@ def get_unfilled_locations_for_players(self, location_names: List[str], players:
valid_locations = [location.name for location in self.get_unfilled_locations(player)]
else:
valid_locations = location_names
relevant_cache = self.regions.location_cache[player]
relevant_cache = self.worlds[player].regions.location_cache
for location_name in valid_locations:
location = relevant_cache.get(location_name, None)
if location and location.item is None:
Expand Down Expand Up @@ -1007,31 +1017,31 @@ def __delitem__(self, index: int) -> None:
del(self.region_manager.location_cache[location.player][location.name])

def insert(self, index: int, value: Location) -> None:
assert value.name not in self.region_manager.location_cache[value.player], \
assert value.name not in self.region_manager.location_cache, \
f"{value.name} already exists in the location cache."
self._list.insert(index, value)
self.region_manager.location_cache[value.player][value.name] = value
self.region_manager.location_cache[value.name] = value

class EntranceRegister(Register):
def __delitem__(self, index: int) -> None:
entrance: Entrance = self._list.__getitem__(index)
self._list.__delitem__(index)
del(self.region_manager.entrance_cache[entrance.player][entrance.name])
del (self.region_manager.entrance_cache[entrance.name])

def insert(self, index: int, value: Entrance) -> None:
assert value.name not in self.region_manager.entrance_cache[value.player], \
assert value.name not in self.region_manager.entrance_cache, \
f"{value.name} already exists in the entrance cache."
self._list.insert(index, value)
self.region_manager.entrance_cache[value.player][value.name] = value
self.region_manager.entrance_cache[value.name] = value

_locations: LocationRegister[Location]
_exits: EntranceRegister[Entrance]

def __init__(self, name: str, player: int, multiworld: MultiWorld, hint: Optional[str] = None):
self.name = name
self.entrances = []
self._exits = self.EntranceRegister(multiworld.regions)
self._locations = self.LocationRegister(multiworld.regions)
self._exits = self.EntranceRegister(multiworld.worlds[player].regions)
self._locations = self.LocationRegister(multiworld.worlds[player].regions)
self.multiworld = multiworld
self._hint_text = hint
self.player = player
Expand Down
10 changes: 7 additions & 3 deletions worlds/AutoWorld.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@ class World(metaclass=AutoWorldRegister):

origin_region_name: str = "Menu"
"""Name of the Region from which accessibility is tested."""
regions: "MultiWorld.RegionManager"
"""Regions for this world instance. Regions should be added to this, and not override it."""

explicit_indirect_conditions: bool = True
"""If True, the world implementation is supposed to use MultiWorld.register_indirect_condition() correctly.
Expand Down Expand Up @@ -334,6 +336,8 @@ def __init__(self, multiworld: "MultiWorld", player: int):
self.player = player
self.random = Random(multiworld.random.getrandbits(64))
multiworld.per_slot_randoms[player] = self.random
from BaseClasses import MultiWorld
self.regions = MultiWorld.RegionManager()

def __getattr__(self, item: str) -> Any:
if item == "settings":
Expand Down Expand Up @@ -528,13 +532,13 @@ def create_filler(self) -> "Item":

# convenience methods
def get_location(self, location_name: str) -> "Location":
return self.multiworld.get_location(location_name, self.player)
return self.regions.location_cache[location_name]

def get_entrance(self, entrance_name: str) -> "Entrance":
return self.multiworld.get_entrance(entrance_name, self.player)
return self.regions.entrance_cache[entrance_name]

def get_region(self, region_name: str) -> "Region":
return self.multiworld.get_region(region_name, self.player)
return self.regions.region_cache[region_name]

@property
def player_name(self) -> str:
Expand Down
2 changes: 1 addition & 1 deletion worlds/messenger/subclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def __init__(self, name: str, world: "MessengerWorld", parent: Optional[str] = N
loc_dict = {loc: world.location_name_to_id.get(loc, None) for loc in locations}
self.add_locations(loc_dict, MessengerLocation)

self.multiworld.regions.append(self)
world.regions.append(self)


class MessengerLocation(Location):
Expand Down

0 comments on commit 3cb64d5

Please sign in to comment.