Skip to content

Commit

Permalink
Merge branch 'main' into mlss-bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesbrq authored Sep 2, 2024
2 parents e40fc8c + 7370129 commit c8ea47e
Show file tree
Hide file tree
Showing 181 changed files with 69,268 additions and 14,126 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
worlds/blasphemous/region_data.py linguist-generated=true
7 changes: 4 additions & 3 deletions .github/workflows/unittests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ jobs:
- {version: '3.9'}
- {version: '3.10'}
- {version: '3.11'}
- {version: '3.12'}
include:
- python: {version: '3.8'} # win7 compat
os: windows-latest
- python: {version: '3.11'} # current
- python: {version: '3.12'} # current
os: windows-latest
- python: {version: '3.11'} # current
- python: {version: '3.12'} # current
os: macos-latest

steps:
Expand Down Expand Up @@ -70,7 +71,7 @@ jobs:
os:
- ubuntu-latest
python:
- {version: '3.11'} # current
- {version: '3.12'} # current

steps:
- uses: actions/checkout@v4
Expand Down
132 changes: 68 additions & 64 deletions BaseClasses.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion CommonClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ def update_game(self, game: str, name_to_id_lookup_table: typing.Dict[str, int])
starting_reconnect_delay: int = 5
current_reconnect_delay: int = starting_reconnect_delay
command_processor: typing.Type[CommandProcessor] = ClientCommandProcessor
ui = None
ui: typing.Optional["kvui.GameManager"] = None
ui_task: typing.Optional["asyncio.Task[None]"] = None
input_task: typing.Optional["asyncio.Task[None]"] = None
keep_alive_task: typing.Optional["asyncio.Task[None]"] = None
Expand Down
14 changes: 7 additions & 7 deletions Fill.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def sweep_from_pool(base_state: CollectionState, itempool: typing.Sequence[Item]
new_state = base_state.copy()
for item in itempool:
new_state.collect(item, True)
new_state.sweep_for_events(locations=locations)
new_state.sweep_for_advancements(locations=locations)
return new_state


Expand Down Expand Up @@ -329,8 +329,8 @@ def accessibility_corrections(multiworld: MultiWorld, state: CollectionState, lo
pool.append(location.item)
state.remove(location.item)
location.item = None
if location in state.events:
state.events.remove(location)
if location in state.advancements:
state.advancements.remove(location)
locations.append(location)
if pool and locations:
locations.sort(key=lambda loc: loc.progress_type != LocationProgressType.PRIORITY)
Expand Down Expand Up @@ -363,7 +363,7 @@ def distribute_early_items(multiworld: MultiWorld,
early_priority_locations: typing.List[Location] = []
loc_indexes_to_remove: typing.Set[int] = set()
base_state = multiworld.state.copy()
base_state.sweep_for_events(locations=(loc for loc in multiworld.get_filled_locations() if loc.address is None))
base_state.sweep_for_advancements(locations=(loc for loc in multiworld.get_filled_locations() if loc.address is None))
for i, loc in enumerate(fill_locations):
if loc.can_reach(base_state):
if loc.progress_type == LocationProgressType.PRIORITY:
Expand Down Expand Up @@ -558,7 +558,7 @@ def flood_items(multiworld: MultiWorld) -> None:
progress_done = False

# sweep once to pick up preplaced items
multiworld.state.sweep_for_events()
multiworld.state.sweep_for_advancements()

# fill multiworld from top of itempool while we can
while not progress_done:
Expand Down Expand Up @@ -746,7 +746,7 @@ def item_percentage(player: int, num: int) -> float:
), items_to_test):
reducing_state.collect(location.item, True, location)

reducing_state.sweep_for_events(locations=locations_to_test)
reducing_state.sweep_for_advancements(locations=locations_to_test)

if multiworld.has_beaten_game(balancing_state):
if not multiworld.has_beaten_game(reducing_state):
Expand Down Expand Up @@ -829,7 +829,7 @@ def failed(warning: str, force: typing.Union[bool, str]) -> None:
warn(warning, force)

swept_state = multiworld.state.copy()
swept_state.sweep_for_events()
swept_state.sweep_for_advancements()
reachable = frozenset(multiworld.get_reachable_locations(swept_state))
early_locations: typing.Dict[int, typing.List[str]] = collections.defaultdict(list)
non_early_locations: typing.Dict[int, typing.List[str]] = collections.defaultdict(list)
Expand Down
2 changes: 1 addition & 1 deletion Generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b
continue
logging.warning(f"{option_key} is not a valid option name for {ret.game} and is not present in triggers.")
if PlandoOptions.items in plando_options:
ret.plando_items = game_weights.get("plando_items", [])
ret.plando_items = copy.deepcopy(game_weights.get("plando_items", []))
if ret.game == "A Link to the Past":
roll_alttp_settings(ret, game_weights)

Expand Down
9 changes: 9 additions & 0 deletions KH1Client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
if __name__ == '__main__':
import ModuleUpdate
ModuleUpdate.update()

import Utils
Utils.init_logging("KH1Client", exception_logger="Client")

from worlds.kh1.Client import launch
launch()
34 changes: 17 additions & 17 deletions LttPAdjuster.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from argparse import Namespace
from concurrent.futures import as_completed, ThreadPoolExecutor
from glob import glob
from tkinter import Tk, Frame, Label, StringVar, Entry, filedialog, messagebox, Button, Radiobutton, LEFT, X, TOP, LabelFrame, \
from tkinter import Tk, Frame, Label, StringVar, Entry, filedialog, messagebox, Button, Radiobutton, LEFT, X, BOTH, TOP, LabelFrame, \
IntVar, Checkbutton, E, W, OptionMenu, Toplevel, BOTTOM, RIGHT, font as font, PhotoImage
from tkinter.constants import DISABLED, NORMAL
from urllib.parse import urlparse
Expand All @@ -29,7 +29,8 @@


GAME_ALTTP = "A Link to the Past"

WINDOW_MIN_HEIGHT = 525
WINDOW_MIN_WIDTH = 425

class AdjusterWorld(object):
def __init__(self, sprite_pool):
Expand Down Expand Up @@ -242,16 +243,17 @@ def adjustGUI():
from argparse import Namespace
from Utils import __version__ as MWVersion
adjustWindow = Tk()
adjustWindow.minsize(WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT)
adjustWindow.wm_title("Archipelago %s LttP Adjuster" % MWVersion)
set_icon(adjustWindow)

rom_options_frame, rom_vars, set_sprite = get_rom_options_frame(adjustWindow)

bottomFrame2 = Frame(adjustWindow)
bottomFrame2 = Frame(adjustWindow, padx=8, pady=2)

romFrame, romVar = get_rom_frame(adjustWindow)

romDialogFrame = Frame(adjustWindow)
romDialogFrame = Frame(adjustWindow, padx=8, pady=2)
baseRomLabel2 = Label(romDialogFrame, text='Rom to adjust')
romVar2 = StringVar()
romEntry2 = Entry(romDialogFrame, textvariable=romVar2)
Expand All @@ -261,9 +263,9 @@ def RomSelect2():
romVar2.set(rom)

romSelectButton2 = Button(romDialogFrame, text='Select Rom', command=RomSelect2)
romDialogFrame.pack(side=TOP, expand=True, fill=X)
baseRomLabel2.pack(side=LEFT)
romEntry2.pack(side=LEFT, expand=True, fill=X)
romDialogFrame.pack(side=TOP, expand=False, fill=X)
baseRomLabel2.pack(side=LEFT, expand=False, fill=X, padx=(0, 8))
romEntry2.pack(side=LEFT, expand=True, fill=BOTH, pady=1)
romSelectButton2.pack(side=LEFT)

def adjustRom():
Expand Down Expand Up @@ -331,12 +333,11 @@ def saveGUISettings():
messagebox.showinfo(title="Success", message="Settings saved to persistent storage")

adjustButton = Button(bottomFrame2, text='Adjust Rom', command=adjustRom)
rom_options_frame.pack(side=TOP)
rom_options_frame.pack(side=TOP, padx=8, pady=8, fill=BOTH, expand=True)
adjustButton.pack(side=LEFT, padx=(5,5))

saveButton = Button(bottomFrame2, text='Save Settings', command=saveGUISettings)
saveButton.pack(side=LEFT, padx=(5,5))

bottomFrame2.pack(side=TOP, pady=(5,5))

tkinter_center_window(adjustWindow)
Expand Down Expand Up @@ -576,7 +577,7 @@ def hide(self):
def get_rom_frame(parent=None):
adjuster_settings = get_adjuster_settings(GAME_ALTTP)

romFrame = Frame(parent)
romFrame = Frame(parent, padx=8, pady=8)
baseRomLabel = Label(romFrame, text='LttP Base Rom: ')
romVar = StringVar(value=adjuster_settings.baserom)
romEntry = Entry(romFrame, textvariable=romVar)
Expand All @@ -596,20 +597,19 @@ def RomSelect():
romSelectButton = Button(romFrame, text='Select Rom', command=RomSelect)

baseRomLabel.pack(side=LEFT)
romEntry.pack(side=LEFT, expand=True, fill=X)
romEntry.pack(side=LEFT, expand=True, fill=BOTH, pady=1)
romSelectButton.pack(side=LEFT)
romFrame.pack(side=TOP, expand=True, fill=X)
romFrame.pack(side=TOP, fill=X)

return romFrame, romVar

def get_rom_options_frame(parent=None):
adjuster_settings = get_adjuster_settings(GAME_ALTTP)

romOptionsFrame = LabelFrame(parent, text="Rom options")
romOptionsFrame.columnconfigure(0, weight=1)
romOptionsFrame.columnconfigure(1, weight=1)
romOptionsFrame = LabelFrame(parent, text="Rom options", padx=8, pady=8)

for i in range(5):
romOptionsFrame.rowconfigure(i, weight=1)
romOptionsFrame.rowconfigure(i, weight=0, pad=4)
vars = Namespace()

vars.MusicVar = IntVar()
Expand Down Expand Up @@ -660,7 +660,7 @@ def SpriteSelect():
spriteSelectButton = Button(spriteDialogFrame, text='...', command=SpriteSelect)

baseSpriteLabel.pack(side=LEFT)
spriteEntry.pack(side=LEFT)
spriteEntry.pack(side=LEFT, expand=True, fill=X)
spriteSelectButton.pack(side=LEFT)

oofDialogFrame = Frame(romOptionsFrame)
Expand Down
2 changes: 1 addition & 1 deletion Main.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
multiworld.early_items[player][item_name] = max(0, early-count)
remaining_count = count-early
if remaining_count > 0:
local_early = multiworld.early_local_items[player].get(item_name, 0)
local_early = multiworld.local_early_items[player].get(item_name, 0)
if local_early:
multiworld.early_items[player][item_name] = max(0, local_early - remaining_count)
del local_early
Expand Down
6 changes: 3 additions & 3 deletions ModuleUpdate.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ def update(yes: bool = False, force: bool = False) -> None:
if not update_ran:
update_ran = True

install_pkg_resources(yes=yes)
import pkg_resources

if force:
update_command()
return

install_pkg_resources(yes=yes)
import pkg_resources

prev = "" # if a line ends in \ we store here and merge later
for req_file in requirements_files:
path = os.path.join(os.path.dirname(sys.argv[0]), req_file)
Expand Down
42 changes: 33 additions & 9 deletions MultiServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@ def update_dict(dictionary, entries):
return dictionary


def queue_gc():
import gc
from threading import Thread

gc_thread: typing.Optional[Thread] = getattr(queue_gc, "_thread", None)
def async_collect():
time.sleep(2)
setattr(queue_gc, "_thread", None)
gc.collect()
if not gc_thread:
gc_thread = Thread(target=async_collect)
setattr(queue_gc, "_thread", gc_thread)
gc_thread.start()


# functions callable on storable data on the server by clients
modify_functions = {
# generic:
Expand Down Expand Up @@ -551,6 +566,9 @@ def get_datetime_second():
self.logger.info(f"Saving failed. Retry in {self.auto_save_interval} seconds.")
else:
self.save_dirty = False
if not atexit_save: # if atexit is used, that keeps a reference anyway
queue_gc()

self.auto_saver_thread = threading.Thread(target=save_regularly, daemon=True)
self.auto_saver_thread.start()

Expand Down Expand Up @@ -991,7 +1009,7 @@ def collect_player(ctx: Context, team: int, slot: int, is_group: bool = False):
collect_player(ctx, team, group, True)


def get_remaining(ctx: Context, team: int, slot: int) -> typing.List[int]:
def get_remaining(ctx: Context, team: int, slot: int) -> typing.List[typing.Tuple[int, int]]:
return ctx.locations.get_remaining(ctx.location_checks, team, slot)


Expand Down Expand Up @@ -1203,6 +1221,10 @@ def _cmd_countdown(self, seconds: str = "10") -> bool:
timer = int(seconds, 10)
except ValueError:
timer = 10
else:
if timer > 60 * 60:
raise ValueError(f"{timer} is invalid. Maximum is 1 hour.")

async_start(countdown(self.ctx, timer))
return True

Expand Down Expand Up @@ -1350,10 +1372,10 @@ def _cmd_collect(self) -> bool:
def _cmd_remaining(self) -> bool:
"""List remaining items in your game, but not their location or recipient"""
if self.ctx.remaining_mode == "enabled":
remaining_item_ids = get_remaining(self.ctx, self.client.team, self.client.slot)
if remaining_item_ids:
self.output("Remaining items: " + ", ".join(self.ctx.item_names[self.ctx.games[self.client.slot]][item_id]
for item_id in remaining_item_ids))
rest_locations = get_remaining(self.ctx, self.client.team, self.client.slot)
if rest_locations:
self.output("Remaining items: " + ", ".join(self.ctx.item_names[self.ctx.games[slot]][item_id]
for slot, item_id in rest_locations))
else:
self.output("No remaining items found.")
return True
Expand All @@ -1363,10 +1385,10 @@ def _cmd_remaining(self) -> bool:
return False
else: # is goal
if self.ctx.client_game_state[self.client.team, self.client.slot] == ClientStatus.CLIENT_GOAL:
remaining_item_ids = get_remaining(self.ctx, self.client.team, self.client.slot)
if remaining_item_ids:
self.output("Remaining items: " + ", ".join(self.ctx.item_names[self.ctx.games[self.client.slot]][item_id]
for item_id in remaining_item_ids))
rest_locations = get_remaining(self.ctx, self.client.team, self.client.slot)
if rest_locations:
self.output("Remaining items: " + ", ".join(self.ctx.item_names[self.ctx.games[slot]][item_id]
for slot, item_id in rest_locations))
else:
self.output("No remaining items found.")
return True
Expand Down Expand Up @@ -2039,6 +2061,8 @@ def _cmd_send_multiple(self, amount: typing.Union[int, str], player_name: str, *
item_name, usable, response = get_intended_text(item_name, names)
if usable:
amount: int = int(amount)
if amount > 100:
raise ValueError(f"{amount} is invalid. Maximum is 100.")
new_items = [NetworkItem(names[item_name], -1, 0) for _ in range(int(amount))]
send_items_to(self.ctx, team, slot, *new_items)

Expand Down
9 changes: 5 additions & 4 deletions NetUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class NetworkItem(typing.NamedTuple):
item: int
location: int
player: int
""" Sending player, except in LocationInfo (from LocationScouts), where it is the receiving player. """
flags: int = 0


Expand Down Expand Up @@ -397,12 +398,12 @@ def get_missing(self, state: typing.Dict[typing.Tuple[int, int], typing.Set[int]
location_id not in checked]

def get_remaining(self, state: typing.Dict[typing.Tuple[int, int], typing.Set[int]], team: int, slot: int
) -> typing.List[int]:
) -> typing.List[typing.Tuple[int, int]]:
checked = state[team, slot]
player_locations = self[slot]
return sorted([player_locations[location_id][0] for
location_id in player_locations if
location_id not in checked])
return sorted([(player_locations[location_id][1], player_locations[location_id][0]) for
location_id in player_locations if
location_id not in checked])


if typing.TYPE_CHECKING: # type-check with pure python implementation until we have a typing stub
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ Currently, the following games are supported:
* Yu-Gi-Oh! Ultimate Masters: World Championship Tournament 2006
* A Hat in Time
* Old School Runescape
* Kingdom Hearts 1
* Mega Man 2
* Yacht Dice

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
Loading

0 comments on commit c8ea47e

Please sign in to comment.