diff --git a/.run/Archipelago Unittests.run.xml b/.run/Archipelago Unittests.run.xml new file mode 100644 index 0000000000..24fea0f73f --- /dev/null +++ b/.run/Archipelago Unittests.run.xml @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file diff --git a/CommonClient.py b/CommonClient.py index c4d80f3416..736cf4922f 100644 --- a/CommonClient.py +++ b/CommonClient.py @@ -460,7 +460,7 @@ class CommonContext: else: self.update_game(cached_game) if needed_updates: - await self.send_msgs([{"cmd": "GetDataPackage", "games": list(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(): @@ -477,6 +477,7 @@ class CommonContext: current_cache = Utils.persistent_load().get("datapackage", {}).get("games", {}) current_cache.update(data_package["games"]) Utils.persistent_store("datapackage", "games", current_cache) + logger.info(f"Got new ID/Name DataPackage for {', '.join(data_package['games'])}") for game, game_data in data_package["games"].items(): Utils.store_data_package_for_checksum(game, game_data) @@ -727,7 +728,6 @@ async def process_server_cmd(ctx: CommonContext, args: dict): await ctx.server_auth(args['password']) elif cmd == 'DataPackage': - logger.info("Got new ID/Name DataPackage") ctx.consume_network_data_package(args['data']) elif cmd == 'ConnectionRefused': diff --git a/Main.py b/Main.py index 8dac8f7d20..e49d8e781d 100644 --- a/Main.py +++ b/Main.py @@ -114,7 +114,9 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No for _ in range(count): world.push_precollected(world.create_item(item_name, player)) - for item_name, count in world.start_inventory_from_pool.setdefault(player, StartInventoryPool({})).value.items(): + for item_name, count in getattr(world.worlds[player].options, + "start_inventory_from_pool", + StartInventoryPool({})).value.items(): for _ in range(count): world.push_precollected(world.create_item(item_name, player)) # remove from_pool items also from early items handling, as starting is plenty early. @@ -167,10 +169,14 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No # remove starting inventory from pool items. # Because some worlds don't actually create items during create_items this has to be as late as possible. - if any(world.start_inventory_from_pool[player].value for player in world.player_ids): + if any(getattr(world.worlds[player].options, "start_inventory_from_pool", None) for player in world.player_ids): new_items: List[Item] = [] depletion_pool: Dict[int, Dict[str, int]] = { - player: world.start_inventory_from_pool[player].value.copy() for player in world.player_ids} + player: getattr(world.worlds[player].options, + "start_inventory_from_pool", + StartInventoryPool({})).value.copy() + for player in world.player_ids + } for player, items in depletion_pool.items(): player_world: AutoWorld.World = world.worlds[player] for count in items.values(): diff --git a/ModuleUpdate.py b/ModuleUpdate.py index c33e894e8b..c3dc8c8a87 100644 --- a/ModuleUpdate.py +++ b/ModuleUpdate.py @@ -4,14 +4,29 @@ import subprocess import multiprocessing import warnings -local_dir = os.path.dirname(__file__) -requirements_files = {os.path.join(local_dir, 'requirements.txt')} if sys.version_info < (3, 8, 6): raise RuntimeError("Incompatible Python Version. 3.8.7+ is supported.") # don't run update if environment is frozen/compiled or if not the parent process (skip in subprocess) -update_ran = getattr(sys, "frozen", False) or multiprocessing.parent_process() +_skip_update = bool(getattr(sys, "frozen", False) or multiprocessing.parent_process()) +update_ran = _skip_update + + +class RequirementsSet(set): + def add(self, e): + global update_ran + update_ran &= _skip_update + super().add(e) + + def update(self, *s): + global update_ran + update_ran &= _skip_update + super().update(*s) + + +local_dir = os.path.dirname(__file__) +requirements_files = RequirementsSet((os.path.join(local_dir, 'requirements.txt'),)) if not update_ran: for entry in os.scandir(os.path.join(local_dir, "worlds")): diff --git a/MultiServer.py b/MultiServer.py index 9d2e9b564e..15ed22d715 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -2210,25 +2210,24 @@ def parse_args() -> argparse.Namespace: async def auto_shutdown(ctx, to_cancel=None): await asyncio.sleep(ctx.auto_shutdown) + + def inactivity_shutdown(): + ctx.server.ws_server.close() + ctx.exit_event.set() + if to_cancel: + for task in to_cancel: + task.cancel() + logging.info("Shutting down due to inactivity.") + while not ctx.exit_event.is_set(): if not ctx.client_activity_timers.values(): - ctx.server.ws_server.close() - ctx.exit_event.set() - if to_cancel: - for task in to_cancel: - task.cancel() - logging.info("Shutting down due to inactivity.") + inactivity_shutdown() else: newest_activity = max(ctx.client_activity_timers.values()) delta = datetime.datetime.now(datetime.timezone.utc) - newest_activity seconds = ctx.auto_shutdown - delta.total_seconds() if seconds < 0: - ctx.server.ws_server.close() - ctx.exit_event.set() - if to_cancel: - for task in to_cancel: - task.cancel() - logging.info("Shutting down due to inactivity.") + inactivity_shutdown() else: await asyncio.sleep(seconds) diff --git a/README.md b/README.md index a1e03293d5..ce2130ce8e 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Currently, the following games are supported: * Heretic * Landstalker: The Treasures of King Nole * Final Fantasy Mystic Quest +* TUNIC 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 diff --git a/WebHostLib/api/generate.py b/WebHostLib/api/generate.py index 61e9164e26..5a66d1e693 100644 --- a/WebHostLib/api/generate.py +++ b/WebHostLib/api/generate.py @@ -20,8 +20,8 @@ def generate_api(): race = False meta_options_source = {} if 'file' in request.files: - file = request.files['file'] - options = get_yaml_data(file) + files = request.files.getlist('file') + options = get_yaml_data(files) if isinstance(options, Markup): return {"text": options.striptags()}, 400 if isinstance(options, str): diff --git a/WebHostLib/templates/generate.html b/WebHostLib/templates/generate.html index 33f8dbc09e..53d98dfae6 100644 --- a/WebHostLib/templates/generate.html +++ b/WebHostLib/templates/generate.html @@ -69,8 +69,8 @@ @@ -185,12 +185,12 @@ Warning: playthrough can take a significant amount of time for larger multiworld + +
+
- -
-
diff --git a/WebHostLib/templates/islandFooter.html b/WebHostLib/templates/islandFooter.html index 7b89c4a9e0..08cf227990 100644 --- a/WebHostLib/templates/islandFooter.html +++ b/WebHostLib/templates/islandFooter.html @@ -1,6 +1,6 @@ {% block footer %}