From 318a4ffa5ec24b3a2241f9259e475afc3b43d69a Mon Sep 17 00:00:00 2001 From: qwint Date: Sun, 19 Jul 2026 15:48:14 -0500 Subject: [PATCH 1/8] Build APWorlds: fix moved import (#6343) --- worlds/LauncherComponents.py | 1 - 1 file changed, 1 deletion(-) diff --git a/worlds/LauncherComponents.py b/worlds/LauncherComponents.py index 24910f0e24..51b129a70b 100644 --- a/worlds/LauncherComponents.py +++ b/worlds/LauncherComponents.py @@ -405,7 +405,6 @@ if not is_frozen(): from worlds import AutoWorldRegister from worlds.Files import APWorldContainer - from Launcher import open_folder import argparse parser = argparse.ArgumentParser(prog="Build APWorlds", description="Build script for APWorlds") From 72dbfcc1d367df4dd03681052e845b4e9f5a93af Mon Sep 17 00:00:00 2001 From: Phaneros <31861583+MatthewMarinets@users.noreply.github.com> Date: Mon, 20 Jul 2026 02:49:12 -0700 Subject: [PATCH 2/8] SC2: Disabling UT as it doesn't work (#6346) --- worlds/sc2/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/worlds/sc2/__init__.py b/worlds/sc2/__init__.py index c57f54aa70..be27b5aa90 100644 --- a/worlds/sc2/__init__.py +++ b/worlds/sc2/__init__.py @@ -91,6 +91,7 @@ class SC2World(World): game = "Starcraft 2" web = Starcraft2WebWorld() settings: ClassVar[settings.Starcraft2Settings] + disable_ut = True item_name_to_id = {name: data.code for name, data in get_full_item_list().items()} location_name_to_id = {location.name: location.code for location in DEFAULT_LOCATION_LIST} @@ -433,7 +434,7 @@ def create_and_flag_explicit_item_locks_and_excludes(world: SC2World) -> List[Fi if max_count and count > max_count: return max_count return count - + auto_excludes = Counter({item_name: 1 for item_name in item_groups.legacy_items}) if world.options.exclude_overpowered_items.value == ExcludeOverpoweredItems.option_true: for item_name in item_groups.overpowered_items: @@ -1066,7 +1067,7 @@ def fill_pool_with_kerrigan_levels(world: SC2World, item_pool: List[StarcraftIte or (world.options.grant_story_levels and not kerrigan_build_missions) ): return - + def add_kerrigan_level_items(level_amount: int, item_amount: int): name = f"{level_amount} Kerrigan Level" if level_amount > 1: From abb6f2647e7da44e3529dc9316c683a1d79edde0 Mon Sep 17 00:00:00 2001 From: CodeGorilla Date: Thu, 30 Jul 2026 08:17:49 -0500 Subject: [PATCH 3/8] Tests: Force race mode off in test_generate_yaml (#6349) --- test/programs/test_generate.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/programs/test_generate.py b/test/programs/test_generate.py index 51800a0ec5..66f1a1b016 100644 --- a/test/programs/test_generate.py +++ b/test/programs/test_generate.py @@ -115,6 +115,7 @@ class TestGenerateWeights(TestGenerateMain): settings = get_settings() settings.generator.player_files_path = settings.generator.PlayerFilesPath(self.yaml_input_dir) settings.generator.players = 5 # arbitrary number, should be enough + settings.generator.race = 0 # make sure race mode is disabled so the below seed is actually respected settings._filename = None user_path_backup = user_path.cached_path user_path.cached_path = local_path() From a7abc86eb1398577c2008aa4885819ea56516fc0 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Thu, 30 Jul 2026 15:18:21 +0200 Subject: [PATCH 4/8] Core: update platformdirs (#6328) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3a91b16680..f75b29a03d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ jinja2==3.1.6 schema==0.7.8 kivy==2.3.1 bsdiff4==1.2.6 -platformdirs==4.9.4 +platformdirs==4.10.1 certifi==2026.2.25 cython==3.2.4 cymem==2.0.13 From 26792ef113a3b86ebee177235135be84494b80dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Bolduc?= <16137441+Jouramie@users.noreply.github.com> Date: Thu, 30 Jul 2026 09:19:06 -0400 Subject: [PATCH 5/8] The Messenger: Fix shop price not correctly handled by UT (#6340) --- worlds/messenger/__init__.py | 12 ++++++++++-- worlds/messenger/universal_tracker.py | 10 ++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/worlds/messenger/__init__.py b/worlds/messenger/__init__.py index d1b672db7d..a05ff0aa88 100644 --- a/worlds/messenger/__init__.py +++ b/worlds/messenger/__init__.py @@ -19,7 +19,7 @@ from .rules import MessengerHardRules, MessengerOOBRules, MessengerRules from .shop import FIGURINES, PROG_SHOP_ITEMS, SHOP_ITEMS, USEFUL_SHOP_ITEMS, shuffle_shop_prices from .subclasses import MessengerItem, MessengerRegion, MessengerShopLocation from .transitions import disconnect_entrances, shuffle_transitions -from .universal_tracker import reverse_portal_exits_into_portal_plando, reverse_transitions_into_plando_connections +from .universal_tracker import reverse_portal_exits_into_portal_plando, reverse_shop_prices, reverse_transitions_into_plando_connections components.append( Component( @@ -169,7 +169,11 @@ class MessengerWorld(World): if self.options.early_meditation: self.multiworld.early_items[self.player]["Meditation"] = 1 - self.shop_prices, self.figurine_prices = shuffle_shop_prices(self) + if not hasattr(self.multiworld, "re_gen_passthrough"): + self.shop_prices, self.figurine_prices = shuffle_shop_prices(self) + else: + if slot_data := self.multiworld.re_gen_passthrough.get(self.game): + self.shop_prices, self.figurine_prices = reverse_shop_prices(slot_data["shop"], slot_data["figures"]) starting_portals = ["Autumn Hills", "Howling Grotto", "Glacial Peak", "Riviere Turquoise", "Sunken Shrine", "Searing Crags"] @@ -276,6 +280,10 @@ class MessengerWorld(World): self.multiworld.itempool += filler + if hasattr(self.multiworld, "re_gen_passthrough"): + if slot_data := self.multiworld.re_gen_passthrough.get(self.game): + self.total_shards = slot_data["max_price"] + def set_rules(self) -> None: logic = self.options.logic_level if logic == Logic.option_normal: diff --git a/worlds/messenger/universal_tracker.py b/worlds/messenger/universal_tracker.py index 9d752031bf..9da7af5f8e 100644 --- a/worlds/messenger/universal_tracker.py +++ b/worlds/messenger/universal_tracker.py @@ -1,9 +1,11 @@ from Options import PlandoConnection from .connections import RANDOMIZED_CONNECTIONS from .portals import REGION_ORDER, SHOP_POINTS, CHECKPOINTS +from .shop import FIGURINES, SHOP_ITEMS from .transitions import TRANSITIONS REVERSED_RANDOMIZED_CONNECTIONS = {v: k for k, v in RANDOMIZED_CONNECTIONS.items()} +REVERSED_SHOP_ITEMS = {v.internal_name: k for k, v in (SHOP_ITEMS | FIGURINES).items()} def find_spot(portal_key: int) -> str: @@ -26,6 +28,14 @@ def reverse_portal_exits_into_portal_plando(portal_exits: list[int]) -> list[Pla PlandoConnection("Glacial Peak", find_spot(portal_exits[5]), "both"), ] +def reverse_shop_prices( + shop_prices: dict[str, int], figures_prices: dict[str, int] +) -> tuple[dict[str, int], dict[str, int]]: + return ( + {REVERSED_SHOP_ITEMS[item_internal_name]: price for item_internal_name, price in shop_prices.items()}, + {REVERSED_SHOP_ITEMS[item_internal_name]: price for item_internal_name, price in figures_prices.items()}, + ) + def reverse_transitions_into_plando_connections(transitions: list[list[int]]) -> list[PlandoConnection]: plando_connections = [] From b34b84aa8f4fb96b466596d49f920834c95ca941 Mon Sep 17 00:00:00 2001 From: Severencir <62644089+Severencir@users.noreply.github.com> Date: Thu, 30 Jul 2026 08:25:11 -0500 Subject: [PATCH 6/8] Starcraft 2: item pool reduction overcounting fix (#6318) --- worlds/sc2/pool_filter.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/worlds/sc2/pool_filter.py b/worlds/sc2/pool_filter.py index abecf8264c..3068034b93 100644 --- a/worlds/sc2/pool_filter.py +++ b/worlds/sc2/pool_filter.py @@ -146,7 +146,11 @@ class ValidInventory: return sum(self.logical_inventory.get(item, 0) for item in items) def count_from_list_unique(self, items: Iterable[str], player: int) -> int: - return sum(item in self.logical_inventory for item in items) + result = 0 + for item in items: + if self.logical_inventory[item] > 0: + result += 1 + return result def generate_reduced_inventory(self, inventory_size: int, filler_amount: int, mission_requirements: List[Tuple[str, Callable]]) -> List[StarcraftItem]: """Attempts to generate a reduced inventory that can fulfill the mission requirements.""" From 18c3b19cfa513717f3fc4e1583e5e2bc75e3c670 Mon Sep 17 00:00:00 2001 From: Phaneros <31861583+MatthewMarinets@users.noreply.github.com> Date: Thu, 30 Jul 2026 14:39:51 -0700 Subject: [PATCH 7/8] Starcraft 2: Moving pool filter to use a counter instead of a dict (#6366) --- worlds/sc2/pool_filter.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/worlds/sc2/pool_filter.py b/worlds/sc2/pool_filter.py index 3068034b93..1087364e50 100644 --- a/worlds/sc2/pool_filter.py +++ b/worlds/sc2/pool_filter.py @@ -1,5 +1,6 @@ import logging from typing import Callable, Dict, List, Set, Tuple, TYPE_CHECKING, Iterable +from collections import Counter from BaseClasses import Location, ItemClassification from .item import StarcraftItem, ItemFilterFlags, item_names, item_parents, item_groups @@ -110,7 +111,7 @@ class ValidInventory: self.player = world.player self.world: 'SC2World' = world # Track all Progression items and those with complex rules for filtering - self.logical_inventory: Dict[str, int] = {} + self.logical_inventory: Counter[str] = Counter() for item in item_pool: if not item_table[item.name].is_important_for_filtering(): continue @@ -125,13 +126,13 @@ class ValidInventory: self.item_name_to_child_items.setdefault(parent_item, []).append(item) def has(self, item: str, player: int, count: int = 1) -> bool: - return self.logical_inventory.get(item, 0) >= count + return self.logical_inventory[item] >= count def has_any(self, items: Set[str], player: int) -> bool: - return any(self.logical_inventory.get(item) for item in items) + return any(self.logical_inventory[item] for item in items) def has_all(self, items: Set[str], player: int) -> bool: - return all(self.logical_inventory.get(item) for item in items) + return all(self.logical_inventory[item] for item in items) def has_group(self, item_group: str, player: int, count: int = 1) -> bool: return False # Deliberately fails here, as item pooling is not aware about mission layout @@ -140,17 +141,17 @@ class ValidInventory: return 0 # For item filtering assume no missions are beaten def count(self, item: str, player: int) -> int: - return self.logical_inventory.get(item, 0) + return self.logical_inventory[item] def count_from_list(self, items: Iterable[str], player: int) -> int: - return sum(self.logical_inventory.get(item, 0) for item in items) + return sum(self.logical_inventory[item] for item in items) def count_from_list_unique(self, items: Iterable[str], player: int) -> int: result = 0 for item in items: if self.logical_inventory[item] > 0: result += 1 - return result + return result def generate_reduced_inventory(self, inventory_size: int, filler_amount: int, mission_requirements: List[Tuple[str, Callable]]) -> List[StarcraftItem]: """Attempts to generate a reduced inventory that can fulfill the mission requirements.""" From 53bc3754c0fa50849cba1dc829c9b18a980a3391 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Mon, 3 Aug 2026 17:16:25 +0200 Subject: [PATCH 8/8] Core: remove per_slot_randoms and DeprecateDict (#6186) --- BaseClasses.py | 6 +----- Utils.py | 17 ----------------- worlds/AutoWorld.py | 1 - 3 files changed, 1 insertion(+), 23 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 206e804e87..2629fc6963 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -49,7 +49,7 @@ class ThreadBarrierProxy: return getattr(self.obj, name) else: raise RuntimeError("You are in a threaded context and global random state was removed for your safety. " - "Please use multiworld.per_slot_randoms[player] or randomize ahead of output.") + "Please use world.random or randomize ahead of output.") class HasNameAndPlayer(Protocol): @@ -100,8 +100,6 @@ class MultiWorld(): game: Dict[int, str] random: random.Random - per_slot_randoms: Utils.DeprecateDict[int, random.Random] - """Deprecated. Please use `self.random` instead.""" class AttributeProxy(): def __init__(self, rule): @@ -177,8 +175,6 @@ class MultiWorld(): set_player_attr('game', "Archipelago") set_player_attr('completion_condition', lambda state: True) self.worlds = {} - self.per_slot_randoms = Utils.DeprecateDict("Using per_slot_randoms is now deprecated. Please use the " - "world's random object instead (usually self.random)", True) self.plando_options = PlandoOptions.none def get_all_ids(self) -> Tuple[int, ...]: diff --git a/Utils.py b/Utils.py index 36bff1b32e..2e5b1658ba 100644 --- a/Utils.py +++ b/Utils.py @@ -1012,23 +1012,6 @@ def deprecate(message: str, add_stacklevels: int = 0): warnings.warn(message, stacklevel=2 + add_stacklevels) -class DeprecateDict(dict): - log_message: str - should_error: bool - - def __init__(self, message: str, error: bool = False) -> None: - self.log_message = message - self.should_error = error - super().__init__() - - def __getitem__(self, item: Any) -> Any: - if self.should_error: - deprecate(self.log_message, add_stacklevels=1) - elif __debug__: - warnings.warn(self.log_message, stacklevel=2) - return super().__getitem__(item) - - def _extend_freeze_support() -> None: """Extend multiprocessing.freeze_support() to also work on Non-Windows and without setting spawn method first.""" # original upstream issue: https://github.com/python/cpython/issues/76327 diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py index 6f0d211c1a..99b75f7c7a 100644 --- a/worlds/AutoWorld.py +++ b/worlds/AutoWorld.py @@ -363,7 +363,6 @@ class World(metaclass=AutoWorldRegister): self.multiworld = multiworld self.player = player self.random = Random(multiworld.random.getrandbits(64)) - multiworld.per_slot_randoms[player] = self.random def __getattr__(self, item: str) -> Any: if item == "settings":