Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge Main into branch #13

Merged
merged 132 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
132 commits
Select commit Hold shift + click to select a range
701fbab
Core: World: MultiWorld and another deprecated option getter (#3254)
Exempt-Medic May 12, 2024
f38655d
Bumper Stickers and Meritous: Options and world: multiworld fixes (#3…
Exempt-Medic May 12, 2024
77cce68
Zillion: remove deprecated `Logger.warn` (#3295)
beauxq May 13, 2024
9a82edc
World: remove ClassVar typing from topology_present (#3294)
alwaysintreble May 14, 2024
b78781a
Docs: Update advanced yaml guide wording for priority locations (#3298)
ScipioWright May 14, 2024
6576b06
Hylics 2: Remove Random Start option and replace it with Start Locati…
chandler05 May 14, 2024
4da9cdd
CV64: Fix items with weird characters landing on Renon's shop crashin…
LiquidCat64 May 15, 2024
467bbd7
WebHost: Fix setup guide link not working for games with special char…
NewSoupVi May 16, 2024
705cb2e
Wargroove: Switched to options API. (#3306)
FlySniper May 16, 2024
4bd4a2c
Docs: remove obsolete yaml generation info (#3304)
beauxq May 16, 2024
5a2d839
Removing deprecated item_count (#3309)
Exempt-Medic May 17, 2024
6d8ac5d
Core: Remove deprecated get_current_option_name and SpecialRange (#3296)
Exempt-Medic May 17, 2024
88dd27e
The Witness: Use OptionError (#3258)
NewSoupVi May 17, 2024
2447be9
The Messenger: fix generation failure for no portal shuffle with 3 av…
alwaysintreble May 17, 2024
68323b4
Bomb Rush Cyberfunk: Implement new game (#2925)
TRPG0 May 17, 2024
3dbdd04
Core: prevent "Could not find identify Component responsible for None…
Berserker66 May 17, 2024
7900e4c
WebHost: use a limited process pool to run Rooms (#3214)
Berserker66 May 17, 2024
89a2a3c
Aquaria: implement new game (#3197)
tioui May 17, 2024
5fb1d0f
FF1: Switching Options System (#3302)
Exempt-Medic May 17, 2024
539ee1c
Yu-Gi-oh! 2006: implement new game (#2795)
Rensen3 May 17, 2024
b4b79bc
BRCF: Small Fixes (#3314)
Exempt-Medic May 17, 2024
bd18018
The Witness: Fix Mountain Floor 2 Near Row 5 Symbol Requirement (#3212)
NewSoupVi May 17, 2024
9ae7083
Fix Monastery Entry RIght righqeuotghqeougtfgas (#3213)
NewSoupVi May 17, 2024
280b67f
some worlds: some typing in `LocalRom` (#3090)
beauxq May 17, 2024
013862b
Pokemon Emerald: Update changelog (#3317)
Zunawe May 17, 2024
b4c263f
YGO06: add new game yugioh06 to CODEOWNERS inno_setup and readme (#3316)
Rensen3 May 17, 2024
5fb0126
Core: Player name property on world class (#3042)
NewSoupVi May 17, 2024
5e3c5de
WebHost: Massive overhaul of options pages (#2614)
LegendaryLinux May 18, 2024
2bc3455
YGO06: make sure it runs on 3.8 support (#3324)
Rensen3 May 18, 2024
0e89388
MLSS: General bugfixes + Add patch extension to inno_setup.iss (#3286)
jamesbrq May 18, 2024
1b6fb7b
Tests: test that no worlds fail to load (#3318)
Silvris May 18, 2024
230a9e6
Core: move `OptionGroup` definition to `Options.py` (#3325)
beauxq May 19, 2024
663b50b
WebHost: fix AutoLauncher restarting rooms due to race condition (#3333)
Berserker66 May 19, 2024
cf34f12
CustomServer: don't mutate static server data (#3334)
black-sliver May 19, 2024
d3f4ee4
WebHost: re-introduce per-Room Locker (#3337)
Berserker66 May 19, 2024
e97eddc
WebHost: move atexit saving to end of room hosting function (#3339)
Berserker66 May 19, 2024
2801e21
WebHost: fixup WebHostLib/options.py (#3332)
Berserker66 May 19, 2024
8e9a050
Zillion: "item counts" OptionGroup (#3338)
beauxq May 19, 2024
019dfb8
CustomServer: re-add missing Archipelago to data package (#3341)
black-sliver May 19, 2024
e978109
WebHost: properly stop worker threads (#3340)
black-sliver May 19, 2024
14321d6
Factorio: update factorio-rcon (#3198)
black-sliver May 19, 2024
e0b6889
ALTTP: Second attempt to fix Swamp Palace boss logic (#3315)
Alchav May 19, 2024
12cde88
Lingo: Fixed edge case sunwarp shuffle accessibility issue (#3228)
hatkirby May 19, 2024
754fc11
TUNIC: ER Refactor for better plando connections, fewer shops improve…
ScipioWright May 19, 2024
14ffd1c
Subnautica: fix use of _valid_keys were valid_keys should be used. (#…
Berserker66 May 20, 2024
5910b94
Update options pages macros to respect valid_keys for item and locati…
LegendaryLinux May 20, 2024
bfe215d
Use world.web.options_presets directly instead of creating an empty d…
LegendaryLinux May 20, 2024
c792ae7
Aquaria: Adding Aquaria to README and some other minors changes (#3313)
tioui May 20, 2024
fe7bc87
A Hat in Time: Implement New Game (#2640)
CookieCat45 May 20, 2024
461f5db
Customserver: only save on exit if it's in a good state. (#3351)
Berserker66 May 21, 2024
514ad69
Remove logging from validate_rom (#3362)
jamesbrq May 21, 2024
92b1f3c
Bomb Rush Cyberfunk: Update option docstrings (#3358)
TRPG0 May 21, 2024
5c66681
SA2B: Option Groups and Dataclass (#3357)
PoryGone May 21, 2024
9441cc3
Hylics 2: Update option docstrings (#3359)
TRPG0 May 21, 2024
62e68ba
SMW: Option Groups and Presets (#3345)
PoryGone May 21, 2024
e7544d8
TUNIC: Add option groups, fix option descriptions (#3344)
ScipioWright May 21, 2024
61be79b
The Witness: Option Groups & Tooltip formatting (#3342)
NewSoupVi May 21, 2024
a1c2e87
DKC3: Option Groups (#3322)
PoryGone May 21, 2024
20134d3
Celeste 64: Option Groups (#3321)
PoryGone May 21, 2024
0ea20f3
Core: add panic_method setting (#3261)
Berserker66 May 22, 2024
1ae0a9b
WebHost: Fixing default values for LocationSets (#3374)
Exempt-Medic May 22, 2024
b4fec93
Update guide with some linux instructions (#3330)
ScipioWright May 23, 2024
93f63a3
Pokemon Emerald: Fix broken Markdown in spanish setup guide (#3320)
Zunawe May 23, 2024
cd16084
BizHawkClient: Linting/style (#3335)
Zunawe May 23, 2024
02d3fdf
Update options to look better on webhost after update, also give deat…
ScipioWright May 23, 2024
893a157
Lingo: Minor logic fixes (part 2) (#3250)
hatkirby May 23, 2024
92392c0
Update Song List to Muse Dash 4.3.0 (#3216)
DeamonHunter May 23, 2024
a43e294
TUNIC: Add option presets (#3377)
ScipioWright May 23, 2024
56d01f3
CV64: Add option groups (#3360)
LiquidCat64 May 23, 2024
89d0dae
Stardew valley: Create Option Groups (#3376)
agilbert1412 May 23, 2024
8b6eae0
Lingo: Add option groups (#3352)
hatkirby May 23, 2024
e1ff507
WebHost, Core: Move item and location descriptions to `WebWorld` resp…
ThePhar May 23, 2024
3f8c348
AHIT: Fix Your Contract has Expired being placed on the first level w…
CookieCat45 May 23, 2024
860ab10
Generate: remove tag "-" (#3036)
Berserker66 May 23, 2024
d09b214
Core: Utils.py typing (#3064)
beauxq May 23, 2024
8b992cb
Webhost: Disallow empty option groups (#3369)
alwaysintreble May 23, 2024
2a47f03
Docs: Update trigger guide and advanced yaml guide (#3385)
Exempt-Medic May 24, 2024
613e766
CODEOWNERS: Actually link the correct person for Yu Gi Oh (#3389)
NewSoupVi May 24, 2024
8045c87
Webhost: Allow Option Groups to specify whether they start collapsed …
alwaysintreble May 24, 2024
18390ec
Witness: Fix option description (#3396)
Exempt-Medic May 24, 2024
61e8852
Core: Rename "count_exclusive" methods to "count_unique" (#3386)
NewSoupVi May 25, 2024
f249c36
Setup: pin cx_freeze to 7.0.0 (#3406)
black-sliver May 26, 2024
70d97a0
BizHawkClient: Add suggestion when no handler is found (#3375)
Zunawe May 27, 2024
df877a9
Muse Dash: 4.4.0 (#3395)
DeamonHunter May 27, 2024
74aa4ec
MultiServer: make !hint prefer early sphere (#2862)
Berserker66 May 27, 2024
dfc347c
Core: add options to the list of valid names instead of deleting game…
alwaysintreble May 27, 2024
04e9f5c
Migrate Terraria to new options API (#3414)
Seldom-SE May 28, 2024
5b34e06
adds godtuner to prog and requires it for godhome flower quest manual…
qwint May 29, 2024
649ee11
Docs: improve contributing sign posting (#2888)
alwaysintreble May 29, 2024
5275593
Docs, Starcraft 2: Add French documentation for setup and game page (…
neocerber May 29, 2024
e31a709
WebHost: use settings defaults for /api/generate and options -> Singl…
Berserker66 May 29, 2024
34f903e
CODEOWNERS: Remove @jtoyoda as world maintainer for Final Fantasy (#3…
ThePhar May 29, 2024
378af4b
Stardew Valley: Fix magic altar logic (#3417)
Witchybun May 29, 2024
6f6bf3c
CustomServer: properly 'inherit' Archipelago from static_server_data …
black-sliver May 30, 2024
2fe8c43
SC2: Fix Kerrigan Primal Form on Half Completion (#3419)
Salzkorn May 30, 2024
7058575
Hollow Knight: Add missing comma (#3403)
BadMagic100 May 30, 2024
b055a39
PKMN R/B: "J.r" -> "Jr." (#3423)
Exempt-Medic May 31, 2024
15e06e1
Fix TextChoice options sometimes creating a broken YAML (#3390)
LegendaryLinux Jun 1, 2024
f3003ff
Fix options pages sometimes displaying blank values in form fields (#…
LegendaryLinux Jun 1, 2024
5aa6ad6
Core: Remove Universally Unique ID Requirements (Per-Game Data Packag…
ThePhar Jun 1, 2024
2a5de85
Docs: Making option description more readable and accurate (#3426)
Exempt-Medic Jun 1, 2024
f2587d5
Aquatia: Locations name changed due to typo's, grammar, or inconsiste…
tioui Jun 1, 2024
91c8960
YGO06: prevent multiple players affecting each others procedure patch…
Rensen3 Jun 1, 2024
67cd32b
Pokemon Emerald: Use `self.player_name` (#3384)
Zunawe Jun 1, 2024
4cab3b6
The Witness: Put Treehouse Both Orange Bridges EP on the normal EPs e…
NewSoupVi Jun 1, 2024
f40b10d
Pokemon Emerald: Adjust options (#3278)
Zunawe Jun 1, 2024
4e5b6bb
Core: move PlandoConnections and PlandoTexts to the options system (#…
Silvris Jun 1, 2024
97c9c53
PKMN R/B: Fixing Key Items Only + Removed Exp. All (#3420)
Exempt-Medic Jun 1, 2024
1e205f9
Landstalker: Fixed rare generation issues (#3353)
Dinopony Jun 1, 2024
8dbc8d2
Installer: Prevent ALTTP Sprite Download from being Interrupted (#3293)
nicholassaylor Jun 1, 2024
3cb5452
Core: Fix auto-fill in the text client when clicking on a hint sugges…
Ishigh1 Jun 1, 2024
bbc79a5
LttP: allow Triforce Piece as start inventory item (#3292)
Berserker66 Jun 1, 2024
13bc121
Webhost: Sphere Tracker (#3412)
Berserker66 Jun 1, 2024
da33d15
WebHost: update trackers only if they're visible. (#3407)
Berserker66 Jun 1, 2024
e49b1f9
The Witness: Automatic Postgame & Disabled Panels Calculation (#2698)
NewSoupVi Jun 1, 2024
dedabad
APSudoku: take over maintaining hintgame sudoku from bk_sudoku (#3432)
EmilyV99 Jun 2, 2024
6432560
Fix Egg_Shop typo in costsanity (#3447)
qwint Jun 3, 2024
424c8b0
Pokemon RB: Add an item group for each HM to improve hinting (#3311)
remyjette Jun 3, 2024
d9120f0
WebHost: Allowing options that work on WebHost to be used in presets …
Exempt-Medic Jun 3, 2024
70e9ccb
TUNIC: Fix plando connections, seed groups, and UT support (#3429)
ScipioWright Jun 3, 2024
cff7327
Utils: Fix mistake made with `KeyedDefaultDict` from #1933 that broke…
ThePhar Jun 3, 2024
fb2c194
Lingo: Fix Basement access with THE MASTER (#3231)
hatkirby Jun 3, 2024
c7eef13
Accounting for name change (#3449)
Exempt-Medic Jun 3, 2024
06e65c1
WebHost: weighted-options bugfixes (#3448)
LegendaryLinux Jun 3, 2024
0265f4d
BizHawkClient: Reset finished_game if ROM changes (#3246)
Zunawe Jun 4, 2024
c4e0b17
TUNIC: Add ice grapple logic to get to gauntlet (#3459)
ScipioWright Jun 4, 2024
16ae844
AHIT: Fix Death Wish location rules not being added properly (#3455)
CookieCat45 Jun 4, 2024
c457296
KH2: Fixing Start Inventory bug, limiting CustomItemPool keys, fixing…
Exempt-Medic Jun 4, 2024
ee1b13f
Pokemon Emerald: Fix possible dexsanity/legendary hunt softlock (#3443)
Zunawe Jun 4, 2024
f30f2d3
RoR2: Add Support for New Stage (#3436)
kindasneaki Jun 4, 2024
1331675
Muse Dash: Option Groups and Options Rework (#3434)
DeamonHunter Jun 4, 2024
3cc391e
Docs: Add detail on customizing the forced groups (#3371)
alwaysintreble Jun 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 8 additions & 0 deletions AHITClient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from worlds.ahit.Client import launch
import Utils
import ModuleUpdate
ModuleUpdate.update()

if __name__ == "__main__":
Utils.init_logging("AHITClient", exception_logger="Client")
launch()
2 changes: 1 addition & 1 deletion AdventureClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def on_package(self, cmd: str, args: dict):
if ': !' not in msg:
self._set_message(msg, SYSTEM_MESSAGE_ID)
elif cmd == "ReceivedItems":
msg = f"Received {', '.join([self.item_names[item.item] for item in args['items']])}"
msg = f"Received {', '.join([self.item_names.lookup_in_slot(item.item) for item in args['items']])}"
self._set_message(msg, SYSTEM_MESSAGE_ID)
elif cmd == "Retrieved":
if f"adventure_{self.auth}_freeincarnates_used" in args["keys"]:
Expand Down
16 changes: 6 additions & 10 deletions BaseClasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,10 +718,6 @@ def has_any_count(self, item_counts: Mapping[str, int], player: int) -> bool:
def count(self, item: str, player: int) -> int:
return self.prog_items[player][item]

def item_count(self, item: str, player: int) -> int:
Utils.deprecate("Use count instead.")
return self.count(item, player)

def has_from_list(self, items: Iterable[str], player: int, count: int) -> bool:
"""Returns True if the state contains at least `count` items matching any of the item names from a list."""
found: int = 0
Expand All @@ -732,7 +728,7 @@ def has_from_list(self, items: Iterable[str], player: int, count: int) -> bool:
return True
return False

def has_from_list_exclusive(self, items: Iterable[str], player: int, count: int) -> bool:
def has_from_list_unique(self, items: Iterable[str], player: int, count: int) -> bool:
"""Returns True if the state contains at least `count` items matching any of the item names from a list.
Ignores duplicates of the same item."""
found: int = 0
Expand All @@ -747,7 +743,7 @@ def count_from_list(self, items: Iterable[str], player: int) -> int:
"""Returns the cumulative count of items from a list present in state."""
return sum(self.prog_items[player][item_name] for item_name in items)

def count_from_list_exclusive(self, items: Iterable[str], player: int) -> int:
def count_from_list_unique(self, items: Iterable[str], player: int) -> int:
"""Returns the cumulative count of items from a list present in state. Ignores duplicates of the same item."""
return sum(self.prog_items[player][item_name] > 0 for item_name in items)

Expand All @@ -762,7 +758,7 @@ def has_group(self, item_name_group: str, player: int, count: int = 1) -> bool:
return True
return False

def has_group_exclusive(self, item_name_group: str, player: int, count: int = 1) -> bool:
def has_group_unique(self, item_name_group: str, player: int, count: int = 1) -> bool:
"""Returns True if the state contains at least `count` items present in a specified item group.
Ignores duplicates of the same item.
"""
Expand All @@ -782,7 +778,7 @@ def count_group(self, item_name_group: str, player: int) -> int:
for item_name in self.multiworld.worlds[player].item_name_groups[item_name_group]
)

def count_group_exclusive(self, item_name_group: str, player: int) -> int:
def count_group_unique(self, item_name_group: str, player: int) -> int:
"""Returns the cumulative count of items from an item group present in state.
Ignores duplicates of the same item."""
player_prog_items = self.prog_items[player]
Expand Down Expand Up @@ -1050,7 +1046,7 @@ def __init__(self, player: int, name: str = '', address: Optional[int] = None, p
self.parent_region = parent

def can_fill(self, state: CollectionState, item: Item, check_access=True) -> bool:
return ((self.always_allow(state, item) and item.name not in state.multiworld.non_local_items[item.player])
return ((self.always_allow(state, item) and item.name not in state.multiworld.worlds[item.player].options.non_local_items)
or ((self.progress_type != LocationProgressType.EXCLUDED or not (item.advancement or item.useful))
and self.item_rule(item)
and (not check_access or self.can_reach(state))))
Expand Down Expand Up @@ -1246,7 +1242,7 @@ def create_playthrough(self, create_paths: bool = True) -> None:
logging.debug('The following items could not be reached: %s', ['%s (Player %d) at %s (Player %d)' % (
location.item.name, location.item.player, location.name, location.player) for location in
sphere_candidates])
if any([multiworld.accessibility[location.item.player] != 'minimal' for location in sphere_candidates]):
if any([multiworld.worlds[location.item.player].options.accessibility != 'minimal' for location in sphere_candidates]):
raise RuntimeError(f'Not all progression items reachable ({sphere_candidates}). '
f'Something went terribly wrong here.')
else:
Expand Down
91 changes: 79 additions & 12 deletions CommonClient.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import collections
import copy
import logging
import asyncio
Expand All @@ -8,6 +9,7 @@
import typing
import time
import functools
import warnings

import ModuleUpdate
ModuleUpdate.update()
Expand Down Expand Up @@ -173,10 +175,74 @@ class CommonContext:
items_handling: typing.Optional[int] = None
want_slot_data: bool = True # should slot_data be retrieved via Connect

# data package
# Contents in flux until connection to server is made, to download correct data for this multiworld.
item_names: typing.Dict[int, str] = Utils.KeyedDefaultDict(lambda code: f'Unknown item (ID:{code})')
location_names: typing.Dict[int, str] = Utils.KeyedDefaultDict(lambda code: f'Unknown location (ID:{code})')
class NameLookupDict:
"""A specialized dict, with helper methods, for id -> name item/location data package lookups by game."""
def __init__(self, ctx: CommonContext, lookup_type: typing.Literal["item", "location"]):
self.ctx: CommonContext = ctx
self.lookup_type: typing.Literal["item", "location"] = lookup_type
self._unknown_item: typing.Callable[[int], str] = lambda key: f"Unknown {lookup_type} (ID: {key})"
self._archipelago_lookup: typing.Dict[int, str] = {}
self._flat_store: typing.Dict[int, str] = Utils.KeyedDefaultDict(self._unknown_item)
self._game_store: typing.Dict[str, typing.ChainMap[int, str]] = collections.defaultdict(
lambda: collections.ChainMap(self._archipelago_lookup, Utils.KeyedDefaultDict(self._unknown_item)))
self.warned: bool = False

# noinspection PyTypeChecker
def __getitem__(self, key: str) -> typing.Mapping[int, str]:
# TODO: In a future version (0.6.0?) this should be simplified by removing implicit id lookups support.
if isinstance(key, int):
if not self.warned:
# Use warnings instead of logger to avoid deprecation message from appearing on user side.
self.warned = True
warnings.warn(f"Implicit name lookup by id only is deprecated and only supported to maintain "
f"backwards compatibility for now. If multiple games share the same id for a "
f"{self.lookup_type}, name could be incorrect. Please use "
f"`{self.lookup_type}_names.lookup_in_game()` or "
f"`{self.lookup_type}_names.lookup_in_slot()` instead.")
return self._flat_store[key] # type: ignore

return self._game_store[key]

def __len__(self) -> int:
return len(self._game_store)

def __iter__(self) -> typing.Iterator[str]:
return iter(self._game_store)

def __repr__(self) -> str:
return self._game_store.__repr__()

def lookup_in_game(self, code: int, game_name: typing.Optional[str] = None) -> str:
"""Returns the name for an item/location id in the context of a specific game or own game if `game` is
omitted.
"""
if game_name is None:
game_name = self.ctx.game
assert game_name is not None, f"Attempted to lookup {self.lookup_type} with no game name available."

return self._game_store[game_name][code]

def lookup_in_slot(self, code: int, slot: typing.Optional[int] = None) -> str:
"""Returns the name for an item/location id in the context of a specific slot or own slot if `slot` is
omitted.
"""
if slot is None:
slot = self.ctx.slot
assert slot is not None, f"Attempted to lookup {self.lookup_type} with no slot info available."

return self.lookup_in_game(code, self.ctx.slot_info[slot].game)

def update_game(self, game: str, name_to_id_lookup_table: typing.Dict[str, int]) -> None:
"""Overrides existing lookup tables for a particular game."""
id_to_name_lookup_table = Utils.KeyedDefaultDict(self._unknown_item)
id_to_name_lookup_table.update({code: name for name, code in name_to_id_lookup_table.items()})
self._game_store[game] = collections.ChainMap(self._archipelago_lookup, id_to_name_lookup_table)
self._flat_store.update(id_to_name_lookup_table) # Only needed for legacy lookup method.
if game == "Archipelago":
# Keep track of the Archipelago data package separately so if it gets updated in a custom datapackage,
# it updates in all chain maps automatically.
self._archipelago_lookup.clear()
self._archipelago_lookup.update(id_to_name_lookup_table)

# defaults
starting_reconnect_delay: int = 5
Expand Down Expand Up @@ -231,7 +297,7 @@ class CommonContext:
# message box reporting a loss of connection
_messagebox_connection_loss: typing.Optional["kvui.MessageBox"] = None

def __init__(self, server_address: typing.Optional[str], password: typing.Optional[str]) -> None:
def __init__(self, server_address: typing.Optional[str] = None, password: typing.Optional[str] = None) -> None:
# server state
self.server_address = server_address
self.username = None
Expand Down Expand Up @@ -271,6 +337,9 @@ def __init__(self, server_address: typing.Optional[str], password: typing.Option
self.exit_event = asyncio.Event()
self.watcher_event = asyncio.Event()

self.item_names = self.NameLookupDict(self, "item")
self.location_names = self.NameLookupDict(self, "location")

self.jsontotextparser = JSONtoTextParser(self)
self.rawjsontotextparser = RawJSONtoTextParser(self)
self.update_data_package(network_data_package)
Expand Down Expand Up @@ -486,19 +555,17 @@ async def prepare_data_package(self, relevant_games: typing.Set[str],
or remote_checksum != cache_checksum:
needed_updates.add(game)
else:
self.update_game(cached_game)
self.update_game(cached_game, game)
if needed_updates:
await self.send_msgs([{"cmd": "GetDataPackage", "games": [game_name]} for game_name in needed_updates])

def update_game(self, game_package: dict):
for item_name, item_id in game_package["item_name_to_id"].items():
self.item_names[item_id] = item_name
for location_name, location_id in game_package["location_name_to_id"].items():
self.location_names[location_id] = location_name
def update_game(self, game_package: dict, game: str):
self.item_names.update_game(game, game_package["item_name_to_id"])
self.location_names.update_game(game, game_package["location_name_to_id"])

def update_data_package(self, data_package: dict):
for game, game_data in data_package["games"].items():
self.update_game(game_data)
self.update_game(game_data, game)

def consume_network_data_package(self, data_package: dict):
self.update_data_package(data_package)
Expand Down
64 changes: 49 additions & 15 deletions Fill.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ def fill_restrictive(multiworld: MultiWorld, base_state: CollectionState, locati
"""
:param multiworld: Multiworld to be filled.
:param base_state: State assumed before fill.
:param locations: Locations to be filled with item_pool
:param item_pool: Items to fill into the locations
:param locations: Locations to be filled with item_pool, gets mutated by removing locations that get filled.
:param item_pool: Items to fill into the locations, gets mutated by removing items that get placed.
:param single_player_placement: if true, can speed up placement if everything belongs to a single player
:param lock: locations are set to locked as they are filled
:param swap: if true, swaps of already place items are done in the event of a dead end
Expand Down Expand Up @@ -220,7 +220,8 @@ def fill_restrictive(multiworld: MultiWorld, base_state: CollectionState, locati
def remaining_fill(multiworld: MultiWorld,
locations: typing.List[Location],
itempool: typing.List[Item],
name: str = "Remaining") -> None:
name: str = "Remaining",
move_unplaceable_to_start_inventory: bool = False) -> None:
unplaced_items: typing.List[Item] = []
placements: typing.List[Location] = []
swapped_items: typing.Counter[typing.Tuple[int, str]] = Counter()
Expand Down Expand Up @@ -284,13 +285,21 @@ def remaining_fill(multiworld: MultiWorld,

if unplaced_items and locations:
# There are leftover unplaceable items and locations that won't accept them
raise FillError(f"No more spots to place {len(unplaced_items)} items. Remaining locations are invalid.\n"
f"Unplaced items:\n"
f"{', '.join(str(item) for item in unplaced_items)}\n"
f"Unfilled locations:\n"
f"{', '.join(str(location) for location in locations)}\n"
f"Already placed {len(placements)}:\n"
f"{', '.join(str(place) for place in placements)}")
if move_unplaceable_to_start_inventory:
last_batch = []
for item in unplaced_items:
logging.debug(f"Moved {item} to start_inventory to prevent fill failure.")
multiworld.push_precollected(item)
last_batch.append(multiworld.worlds[item.player].create_filler())
remaining_fill(multiworld, locations, unplaced_items, name + " Start Inventory Retry")
else:
raise FillError(f"No more spots to place {len(unplaced_items)} items. Remaining locations are invalid.\n"
f"Unplaced items:\n"
f"{', '.join(str(item) for item in unplaced_items)}\n"
f"Unfilled locations:\n"
f"{', '.join(str(location) for location in locations)}\n"
f"Already placed {len(placements)}:\n"
f"{', '.join(str(place) for place in placements)}")

itempool.extend(unplaced_items)

Expand Down Expand Up @@ -420,7 +429,8 @@ def distribute_early_items(multiworld: MultiWorld,
return fill_locations, itempool


def distribute_items_restrictive(multiworld: MultiWorld) -> None:
def distribute_items_restrictive(multiworld: MultiWorld,
panic_method: typing.Literal["swap", "raise", "start_inventory"] = "swap") -> None:
fill_locations = sorted(multiworld.get_unfilled_locations())
multiworld.random.shuffle(fill_locations)
# get items to distribute
Expand Down Expand Up @@ -470,8 +480,29 @@ def mark_for_locking(location: Location):

if progitempool:
# "advancement/progression fill"
fill_restrictive(multiworld, multiworld.state, defaultlocations, progitempool, single_player_placement=multiworld.players == 1,
name="Progression")
if panic_method == "swap":
fill_restrictive(multiworld, multiworld.state, defaultlocations, progitempool,
swap=True,
on_place=mark_for_locking, name="Progression", single_player_placement=multiworld.players == 1)
elif panic_method == "raise":
fill_restrictive(multiworld, multiworld.state, defaultlocations, progitempool,
swap=False,
on_place=mark_for_locking, name="Progression", single_player_placement=multiworld.players == 1)
elif panic_method == "start_inventory":
fill_restrictive(multiworld, multiworld.state, defaultlocations, progitempool,
swap=False, allow_partial=True,
on_place=mark_for_locking, name="Progression", single_player_placement=multiworld.players == 1)
if progitempool:
for item in progitempool:
logging.debug(f"Moved {item} to start_inventory to prevent fill failure.")
multiworld.push_precollected(item)
filleritempool.append(multiworld.worlds[item.player].create_filler())
logging.warning(f"{len(progitempool)} items moved to start inventory,"
f" due to failure in Progression fill step.")
progitempool[:] = []

else:
raise ValueError(f"Generator Panic Method {panic_method} not recognized.")
if progitempool:
raise FillError(
f"Not enough locations for progression items. "
Expand All @@ -486,7 +517,9 @@ def mark_for_locking(location: Location):

inaccessible_location_rules(multiworld, multiworld.state, defaultlocations)

remaining_fill(multiworld, excludedlocations, filleritempool, "Remaining Excluded")
remaining_fill(multiworld, excludedlocations, filleritempool, "Remaining Excluded",
move_unplaceable_to_start_inventory=panic_method=="start_inventory")

if excludedlocations:
raise FillError(
f"Not enough filler items for excluded locations. "
Expand All @@ -495,7 +528,8 @@ def mark_for_locking(location: Location):

restitempool = filleritempool + usefulitempool

remaining_fill(multiworld, defaultlocations, restitempool)
remaining_fill(multiworld, defaultlocations, restitempool,
move_unplaceable_to_start_inventory=panic_method=="start_inventory")

unplaced = restitempool
unfilled = defaultlocations
Expand Down
Loading
Loading