Skip to content

Commit

Permalink
Merge branch 'refs/heads/main' into per_game_datapackage
Browse files Browse the repository at this point in the history
  • Loading branch information
ThePhar committed May 22, 2024
2 parents f197be5 + 0ea20f3 commit 0e56de4
Show file tree
Hide file tree
Showing 59 changed files with 6,604 additions and 388 deletions.
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()
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
4 changes: 2 additions & 2 deletions Main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from BaseClasses import CollectionState, Item, Location, LocationProgressType, MultiWorld, Region
from Fill import balance_multiworld_progression, distribute_items_restrictive, distribute_planned, flood_items
from Options import StartInventoryPool
from Utils import __version__, output_path, version_tuple
from Utils import __version__, output_path, version_tuple, get_settings
from settings import get_settings
from worlds import AutoWorld
from worlds.generic.Rules import exclusion_rules, locality_rules
Expand Down Expand Up @@ -272,7 +272,7 @@ def find_common_pool(players: Set[int], shared_pool: Set[str]) -> Tuple[
if multiworld.algorithm == 'flood':
flood_items(multiworld) # different algo, biased towards early game progress items
elif multiworld.algorithm == 'balanced':
distribute_items_restrictive(multiworld)
distribute_items_restrictive(multiworld, get_settings().generator.panic_method)

AutoWorld.call_all(multiworld, 'post_fill')

Expand Down
1 change: 1 addition & 0 deletions Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,7 @@ def from_text(cls, text: str) -> Range:

class FreezeValidKeys(AssembleOptions):
def __new__(mcs, name, bases, attrs):
assert not "_valid_keys" in attrs, "'_valid_keys' gets set by FreezeValidKeys, define 'valid_keys' instead."
if "valid_keys" in attrs:
attrs["_valid_keys"] = frozenset(attrs["valid_keys"])
return super(FreezeValidKeys, mcs).__new__(mcs, name, bases, attrs)
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ Currently, the following games are supported:
* Yoshi's Island
* Mario & Luigi: Superstar Saga
* Bomb Rush Cyberfunk
* Aquaria
* Yu-Gi-Oh! Ultimate Masters: World Championship Tournament 2006
* A Hat in Time

For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled
Expand Down
10 changes: 7 additions & 3 deletions WebHostLib/customserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,15 +270,19 @@ async def start_room(room_id):
await ctx.shutdown_task

except (KeyboardInterrupt, SystemExit):
pass
except Exception:
if ctx.saving:
ctx._save()
except Exception as e:
with db_session:
room = Room.get(id=room_id)
room.last_port = -1
logger.exception(e)
raise
else:
if ctx.saving:
ctx._save()
finally:
try:
ctx._save()
with (db_session):
# ensure the Room does not spin up again on its own, minute of safety buffer
room = Room.get(id=room_id)
Expand Down
6 changes: 1 addition & 5 deletions WebHostLib/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,6 @@ def test_ordered(obj):
@cache.cached()
def option_presets(game: str) -> Response:
world = AutoWorldRegister.world_types[game]
presets = {}

if world.web.options_presets:
presets = presets | world.web.options_presets

class SetEncoder(json.JSONEncoder):
def default(self, obj):
Expand All @@ -91,7 +87,7 @@ def default(self, obj):
return list(obj)
return json.JSONEncoder.default(self, obj)

json_data = json.dumps(presets, cls=SetEncoder)
json_data = json.dumps(world.web.options_presets, cls=SetEncoder)
response = Response(json_data)
response.headers["Content-Type"] = "application/json"
return response
Expand Down
6 changes: 3 additions & 3 deletions WebHostLib/templates/playerOptions/macros.html
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
{% macro ItemDict(option_name, option, world) %}
{{ OptionTitle(option_name, option) }}
<div class="option-container">
{% for item_name in world.item_names|sort %}
{% for item_name in (option.valid_keys|sort if (option.valid_keys|length > 0) else world.item_names|sort) %}
<div class="option-entry">
<label for="{{ option_name }}-{{ item_name }}-qty">{{ item_name }}</label>
<input type="number" id="{{ option_name }}-{{ item_name }}-qty" name="{{ option_name }}||{{ item_name }}||qty" value="{{ option.default[item_name]|default("0") }}" data-option-name="{{ option_name }}" data-item-name="{{ item_name }}" />
Expand Down Expand Up @@ -149,7 +149,7 @@
{% if world.location_name_groups.keys()|length > 1 %}
<div class="option-divider">&nbsp;</div>
{% endif %}
{% for location_name in world.location_names|sort %}
{% for location_name in (option.valid_keys|sort if (option.valid_keys|length > 0) else world.location_names|sort) %}
<div class="option-entry">
<input type="checkbox" id="{{ option_name }}-{{ location_name }}" name="{{ option_name }}" value="{{ location_name }}" {{ "checked" if location_name in option.default }} />
<label for="{{ option_name }}-{{ location_name }}">{{ location_name }}</label>
Expand All @@ -172,7 +172,7 @@
{% if world.item_name_groups.keys()|length > 1 %}
<div class="option-divider">&nbsp;</div>
{% endif %}
{% for item_name in world.item_names|sort %}
{% for item_name in (option.valid_keys|sort if (option.valid_keys|length > 0) else world.item_names|sort) %}
<div class="option-entry">
<input type="checkbox" id="{{ option_name }}-{{ item_name }}" name="{{ option_name }}" value="{{ item_name }}" {{ "checked" if item_name in option.default }} />
<label for="{{ option_name }}-{{ item_name }}">{{ item_name }}</label>
Expand Down
6 changes: 3 additions & 3 deletions WebHostLib/templates/weightedOptions/macros.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@

{% macro ItemDict(option_name, option, world) %}
<div class="dict-container">
{% for item_name in world.item_names|sort %}
{% for item_name in (option.valid_keys|sort if (option.valid_keys|length > 0) else world.item_names|sort) %}
<div class="dict-entry">
<label for="{{ option_name }}-{{ item_name }}-qty">{{ item_name }}</label>
<input
Expand Down Expand Up @@ -150,7 +150,7 @@
{% if world.location_name_groups.keys()|length > 1 %}
<div class="divider">&nbsp;</div>
{% endif %}
{% for location_name in world.location_names|sort %}
{% for location_name in (option.valid_keys|sort if (option.valid_keys|length > 0) else world.location_names|sort) %}
<div class="set-entry">
<input type="checkbox" id="{{ option_name }}-{{ location_name }}" name="{{ option_name }}||{{ location_name }}" value="1" {{ "checked" if location_name in option.default }} />
<label for="{{ option_name }}-{{ location_name }}">{{ location_name }}</label>
Expand All @@ -172,7 +172,7 @@
{% if world.item_name_groups.keys()|length > 1 %}
<div class="set-divider">&nbsp;</div>
{% endif %}
{% for item_name in world.item_names|sort %}
{% for item_name in (option.valid_keys|sort if (option.valid_keys|length > 0) else world.item_names|sort) %}
<div class="set-entry">
<input type="checkbox" id="{{ option_name }}-{{ item_name }}" name="{{ option_name }}||{{ item_name }}" value="1" {{ "checked" if item_name in option.default }} />
<label for="{{ option_name }}-{{ item_name }}">{{ item_name }}</label>
Expand Down
Binary file added data/yatta.ico
Binary file not shown.
Binary file added data/yatta.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
# Adventure
/worlds/adventure/ @JusticePS

# A Hat in Time
/worlds/ahit/ @CookieCat45

# A Link to the Past
/worlds/alttp/ @Berserker66

Expand Down
9 changes: 9 additions & 0 deletions settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,14 @@ class Race(IntEnum):
OFF = 0
ON = 1

class PanicMethod(str):
"""
What to do if the current item placements appear unsolvable.
raise -> Raise an exception and abort.
swap -> Attempt to fix it by swapping prior placements around. (Default)
start_inventory -> Move remaining items to start_inventory, generate additional filler items to fill locations.
"""

enemizer_path: EnemizerPath = EnemizerPath("EnemizerCLI/EnemizerCLI.Core") # + ".exe" is implied on Windows
player_files_path: PlayerFilesPath = PlayerFilesPath("Players")
players: Players = Players(0)
Expand All @@ -673,6 +681,7 @@ class Race(IntEnum):
spoiler: Spoiler = Spoiler(3)
race: Race = Race(0)
plando_options: PlandoOptions = PlandoOptions("bosses, connections, texts")
panic_method: PanicMethod = PanicMethod("swap")


class SNIOptions(Group):
Expand Down
Loading

0 comments on commit 0e56de4

Please sign in to comment.