| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | from __future__ import annotations | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import asyncio | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | import copy | 
					
						
							|  |  |  | import ctypes | 
					
						
							|  |  |  | import logging | 
					
						
							|  |  |  | import multiprocessing | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  | import os.path | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | import re | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | import typing | 
					
						
							|  |  |  | import queue | 
					
						
							| 
									
										
										
										
											2023-01-24 03:54:23 +01:00
										 |  |  | import zipfile | 
					
						
							|  |  |  | import io | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | from pathlib import Path | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-01 15:24:05 +02:00
										 |  |  | # CommonClient import first to trigger ModuleUpdater | 
					
						
							|  |  |  | from CommonClient import CommonContext, server_loop, ClientCommandProcessor, gui_enabled, get_base_parser | 
					
						
							|  |  |  | from Utils import init_logging, is_windows | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     init_logging("SC2Client", exception_logger="Client") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | logger = logging.getLogger("Client") | 
					
						
							|  |  |  | sc2_logger = logging.getLogger("Starcraft2") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  | import nest_asyncio | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | import sc2 | 
					
						
							|  |  |  | from sc2.bot_ai import BotAI | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | from sc2.data import Race | 
					
						
							|  |  |  | from sc2.main import run_game | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | from sc2.player import Bot | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | from worlds.sc2wol import SC2WoLWorld | 
					
						
							| 
									
										
										
										
											2022-09-12 01:51:25 +02:00
										 |  |  | from worlds.sc2wol.Items import lookup_id_to_name, item_table, ItemData, type_flaggroups | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | from worlds.sc2wol.Locations import SC2WOL_LOC_ID_OFFSET | 
					
						
							|  |  |  | from worlds.sc2wol.MissionTables import lookup_id_to_mission | 
					
						
							|  |  |  | from worlds.sc2wol.Regions import MissionInfo | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | import colorama | 
					
						
							| 
									
										
										
										
											2022-10-01 15:24:05 +02:00
										 |  |  | from NetUtils import ClientStatus, NetworkItem, RawJSONtoTextParser | 
					
						
							|  |  |  | from MultiServer import mark_raw | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | nest_asyncio.apply() | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | max_bonus: int = 8 | 
					
						
							|  |  |  | victory_modulo: int = 100 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-20 00:55:12 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | class StarcraftClientProcessor(ClientCommandProcessor): | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |     ctx: SC2Context | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-08 15:20:51 -07:00
										 |  |  |     def _cmd_difficulty(self, difficulty: str = "") -> bool: | 
					
						
							|  |  |  |         """Overrides the current difficulty set for the seed.  Takes the argument casual, normal, hard, or brutal""" | 
					
						
							|  |  |  |         options = difficulty.split() | 
					
						
							|  |  |  |         num_options = len(options) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if num_options > 0: | 
					
						
							| 
									
										
										
										
											2023-03-07 08:14:49 -05:00
										 |  |  |             difficulty_choice = options[0].lower() | 
					
						
							| 
									
										
										
										
											2022-08-08 15:20:51 -07:00
										 |  |  |             if difficulty_choice == "casual": | 
					
						
							|  |  |  |                 self.ctx.difficulty_override = 0 | 
					
						
							|  |  |  |             elif difficulty_choice == "normal": | 
					
						
							|  |  |  |                 self.ctx.difficulty_override = 1 | 
					
						
							|  |  |  |             elif difficulty_choice == "hard": | 
					
						
							|  |  |  |                 self.ctx.difficulty_override = 2 | 
					
						
							|  |  |  |             elif difficulty_choice == "brutal": | 
					
						
							|  |  |  |                 self.ctx.difficulty_override = 3 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.output("Unable to parse difficulty '" + options[0] + "'") | 
					
						
							|  |  |  |                 return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             self.output("Difficulty set to " + options[0]) | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2023-03-07 08:14:49 -05:00
										 |  |  |             if self.ctx.difficulty == -1: | 
					
						
							|  |  |  |                 self.output("Please connect to a seed before checking difficulty.") | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.output("Current difficulty: " + ["Casual", "Normal", "Hard", "Brutal"][self.ctx.difficulty]) | 
					
						
							|  |  |  |             self.output("To change the difficulty, add the name of the difficulty after the command.") | 
					
						
							| 
									
										
										
										
											2022-08-08 15:20:51 -07:00
										 |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |     def _cmd_disable_mission_check(self) -> bool: | 
					
						
							|  |  |  |         """Disables the check to see if a mission is available to play.  Meant for co-op runs where one player can play
 | 
					
						
							|  |  |  |         the next mission in a chain the other player is doing."""
 | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  |         self.ctx.missions_unlocked = True | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |         sc2_logger.info("Mission check has been disabled") | 
					
						
							| 
									
										
										
										
											2022-08-08 15:20:51 -07:00
										 |  |  |         return True | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _cmd_play(self, mission_id: str = "") -> bool: | 
					
						
							|  |  |  |         """Start a Starcraft 2 mission""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         options = mission_id.split() | 
					
						
							|  |  |  |         num_options = len(options) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if num_options > 0: | 
					
						
							|  |  |  |             mission_number = int(options[0]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  |             self.ctx.play_mission(mission_number) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-05-20 00:55:12 +02:00
										 |  |  |             sc2_logger.info( | 
					
						
							|  |  |  |                 "Mission ID needs to be specified.  Use /unfinished or /available to view ids for available missions.") | 
					
						
							| 
									
										
										
										
											2022-08-08 15:20:51 -07:00
										 |  |  |             return False | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _cmd_available(self) -> bool: | 
					
						
							|  |  |  |         """Get what missions are currently available to play""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         request_available_missions(self.ctx) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _cmd_unfinished(self) -> bool: | 
					
						
							|  |  |  |         """Get what missions are currently available to play and have not had all locations checked""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         request_unfinished_missions(self.ctx) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 03:51:00 -04:00
										 |  |  |     @mark_raw | 
					
						
							|  |  |  |     def _cmd_set_path(self, path: str = '') -> bool: | 
					
						
							|  |  |  |         """Manually set the SC2 install directory (if the automatic detection fails).""" | 
					
						
							|  |  |  |         if path: | 
					
						
							|  |  |  |             os.environ["SC2PATH"] = path | 
					
						
							| 
									
										
										
										
											2022-10-08 22:19:17 -04:00
										 |  |  |             is_mod_installed_correctly() | 
					
						
							| 
									
										
										
										
											2022-07-14 03:51:00 -04:00
										 |  |  |             return True | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             sc2_logger.warning("When using set_path, you must type the path to your SC2 install directory.") | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-23 21:44:12 -05:00
										 |  |  |     def _cmd_download_data(self) -> bool: | 
					
						
							| 
									
										
										
										
											2022-10-08 22:19:17 -04:00
										 |  |  |         """Download the most recent release of the necessary files for playing SC2 with
 | 
					
						
							| 
									
										
										
										
											2023-01-23 21:44:12 -05:00
										 |  |  |         Archipelago. Will overwrite existing files."""
 | 
					
						
							| 
									
										
										
										
											2022-10-08 22:19:17 -04:00
										 |  |  |         if "SC2PATH" not in os.environ: | 
					
						
							|  |  |  |             check_game_install_path() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if os.path.exists(os.environ["SC2PATH"]+"ArchipelagoSC2Version.txt"): | 
					
						
							|  |  |  |             with open(os.environ["SC2PATH"]+"ArchipelagoSC2Version.txt", "r") as f: | 
					
						
							|  |  |  |                 current_ver = f.read() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             current_ver = None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-23 21:44:12 -05:00
										 |  |  |         tempzip, version = download_latest_release_zip('TheCondor07', 'Starcraft2ArchipelagoData', | 
					
						
							|  |  |  |                                                        current_version=current_ver, force_download=True) | 
					
						
							| 
									
										
										
										
											2022-10-08 22:19:17 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if tempzip != '': | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 zipfile.ZipFile(tempzip).extractall(path=os.environ["SC2PATH"]) | 
					
						
							|  |  |  |                 sc2_logger.info(f"Download complete. Version {version} installed.") | 
					
						
							|  |  |  |                 with open(os.environ["SC2PATH"]+"ArchipelagoSC2Version.txt", "w") as f: | 
					
						
							|  |  |  |                     f.write(version) | 
					
						
							|  |  |  |             finally: | 
					
						
							|  |  |  |                 os.remove(tempzip) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             sc2_logger.warning("Download aborted/failed. Read the log for more information.") | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  | class SC2Context(CommonContext): | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |     command_processor = StarcraftClientProcessor | 
					
						
							|  |  |  |     game = "Starcraft 2 Wings of Liberty" | 
					
						
							|  |  |  |     items_handling = 0b111 | 
					
						
							|  |  |  |     difficulty = -1 | 
					
						
							|  |  |  |     all_in_choice = 0 | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |     mission_order = 0 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     mission_req_table: typing.Dict[str, MissionInfo] = {} | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |     final_mission: int = 29 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     announcements = queue.Queue() | 
					
						
							| 
									
										
										
										
											2022-05-21 22:24:49 +02:00
										 |  |  |     sc2_run_task: typing.Optional[asyncio.Task] = None | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     missions_unlocked: bool = False  # allow launching missions ignoring requirements | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |     current_tooltip = None | 
					
						
							|  |  |  |     last_loc_list = None | 
					
						
							| 
									
										
										
										
											2022-08-08 15:20:51 -07:00
										 |  |  |     difficulty_override = -1 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     mission_id_to_location_ids: typing.Dict[int, typing.List[int]] = {} | 
					
						
							| 
									
										
										
										
											2022-09-12 02:08:33 +02:00
										 |  |  |     last_bot: typing.Optional[ArchipelagoBot] = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, *args, **kwargs): | 
					
						
							|  |  |  |         super(SC2Context, self).__init__(*args, **kwargs) | 
					
						
							|  |  |  |         self.raw_text_parser = RawJSONtoTextParser(self) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     async def server_auth(self, password_requested: bool = False): | 
					
						
							|  |  |  |         if password_requested and not self.password: | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |             await super(SC2Context, self).server_auth(password_requested) | 
					
						
							| 
									
										
										
										
											2022-06-27 03:10:41 -07:00
										 |  |  |         await self.get_username() | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |         await self.send_connect() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def on_package(self, cmd: str, args: dict): | 
					
						
							|  |  |  |         if cmd in {"Connected"}: | 
					
						
							|  |  |  |             self.difficulty = args["slot_data"]["game_difficulty"] | 
					
						
							|  |  |  |             self.all_in_choice = args["slot_data"]["all_in_map"] | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |             slot_req_table = args["slot_data"]["mission_req"] | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |             # Maintaining backwards compatibility with older slot data | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |             self.mission_req_table = { | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |                 mission: MissionInfo( | 
					
						
							|  |  |  |                     **{field: value for field, value in mission_info.items() if field in MissionInfo._fields} | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |                 for mission, mission_info in slot_req_table.items() | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |             self.mission_order = args["slot_data"].get("mission_order", 0) | 
					
						
							|  |  |  |             self.final_mission = args["slot_data"].get("final_mission", 29) | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             self.build_location_to_mission_mapping() | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 22:19:17 -04:00
										 |  |  |             # Looks for the required maps and mods for SC2. Runs check_game_install_path. | 
					
						
							| 
									
										
										
										
											2023-01-23 21:44:12 -05:00
										 |  |  |             maps_present = is_mod_installed_correctly() | 
					
						
							| 
									
										
										
										
											2022-10-08 22:19:17 -04:00
										 |  |  |             if os.path.exists(os.environ["SC2PATH"] + "ArchipelagoSC2Version.txt"): | 
					
						
							|  |  |  |                 with open(os.environ["SC2PATH"] + "ArchipelagoSC2Version.txt", "r") as f: | 
					
						
							|  |  |  |                     current_ver = f.read() | 
					
						
							|  |  |  |                 if is_mod_update_available("TheCondor07", "Starcraft2ArchipelagoData", current_ver): | 
					
						
							|  |  |  |                     sc2_logger.info("NOTICE: Update for required files found. Run /download_data to install.") | 
					
						
							| 
									
										
										
										
											2023-01-23 21:44:12 -05:00
										 |  |  |             elif maps_present: | 
					
						
							| 
									
										
										
										
											2023-01-24 03:45:43 +01:00
										 |  |  |                 sc2_logger.warning("NOTICE: Your map files may be outdated (version number not found). " | 
					
						
							|  |  |  |                                    "Run /download_data to update them.") | 
					
						
							| 
									
										
										
										
											2023-01-23 21:44:12 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 03:51:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     def on_print_json(self, args: dict): | 
					
						
							| 
									
										
										
										
											2022-09-09 21:28:24 +02:00
										 |  |  |         # goes to this world | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         if "receiving" in args and self.slot_concerns_self(args["receiving"]): | 
					
						
							|  |  |  |             relevant = True | 
					
						
							| 
									
										
										
										
											2022-09-09 21:28:24 +02:00
										 |  |  |         # found in this world | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         elif "item" in args and self.slot_concerns_self(args["item"].player): | 
					
						
							|  |  |  |             relevant = True | 
					
						
							| 
									
										
										
										
											2022-09-09 21:28:24 +02:00
										 |  |  |         # not related | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         else: | 
					
						
							|  |  |  |             relevant = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if relevant: | 
					
						
							|  |  |  |             self.announcements.put(self.raw_text_parser(copy.deepcopy(args["data"]))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         super(SC2Context, self).on_print_json(args) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def run_gui(self): | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         from kvui import GameManager, HoverBehavior, ServerToolTip | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |         from kivy.app import App | 
					
						
							| 
									
										
										
										
											2022-06-03 13:38:55 +02:00
										 |  |  |         from kivy.clock import Clock | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  |         from kivy.uix.tabbedpanel import TabbedPanelItem | 
					
						
							|  |  |  |         from kivy.uix.gridlayout import GridLayout | 
					
						
							|  |  |  |         from kivy.lang import Builder | 
					
						
							|  |  |  |         from kivy.uix.label import Label | 
					
						
							|  |  |  |         from kivy.uix.button import Button | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |         from kivy.uix.floatlayout import FloatLayout | 
					
						
							|  |  |  |         from kivy.properties import StringProperty | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         import Utils | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |         class HoverableButton(HoverBehavior, Button): | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |         class MissionButton(HoverableButton): | 
					
						
							|  |  |  |             tooltip_text = StringProperty("Test") | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |             ctx: SC2Context | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |             def __init__(self, *args, **kwargs): | 
					
						
							|  |  |  |                 super(HoverableButton, self).__init__(*args, **kwargs) | 
					
						
							|  |  |  |                 self.layout = FloatLayout() | 
					
						
							|  |  |  |                 self.popuplabel = ServerToolTip(text=self.text) | 
					
						
							|  |  |  |                 self.layout.add_widget(self.popuplabel) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             def on_enter(self): | 
					
						
							|  |  |  |                 self.popuplabel.text = self.tooltip_text | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if self.ctx.current_tooltip: | 
					
						
							|  |  |  |                     App.get_running_app().root.remove_widget(self.ctx.current_tooltip) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if self.tooltip_text == "": | 
					
						
							|  |  |  |                     self.ctx.current_tooltip = None | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     App.get_running_app().root.add_widget(self.layout) | 
					
						
							|  |  |  |                     self.ctx.current_tooltip = self.layout | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             def on_leave(self): | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |                 self.ctx.ui.clear_tooltip() | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |             @property | 
					
						
							|  |  |  |             def ctx(self) -> CommonContext: | 
					
						
							|  |  |  |                 return App.get_running_app().ctx | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  |         class MissionLayout(GridLayout): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class MissionCategory(GridLayout): | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         class SC2Manager(GameManager): | 
					
						
							|  |  |  |             logging_pairs = [ | 
					
						
							|  |  |  |                 ("Client", "Archipelago"), | 
					
						
							|  |  |  |                 ("Starcraft2", "Starcraft2"), | 
					
						
							|  |  |  |             ] | 
					
						
							|  |  |  |             base_title = "Archipelago Starcraft 2 Client" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  |             mission_panel = None | 
					
						
							|  |  |  |             last_checked_locations = {} | 
					
						
							|  |  |  |             mission_id_to_button = {} | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |             launching: typing.Union[bool, int] = False  # if int -> mission ID | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |             refresh_from_launching = True | 
					
						
							|  |  |  |             first_check = True | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |             ctx: SC2Context | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |             def __init__(self, ctx): | 
					
						
							|  |  |  |                 super().__init__(ctx) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |             def clear_tooltip(self): | 
					
						
							|  |  |  |                 if self.ctx.current_tooltip: | 
					
						
							|  |  |  |                     App.get_running_app().root.remove_widget(self.ctx.current_tooltip) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 self.ctx.current_tooltip = None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  |             def build(self): | 
					
						
							|  |  |  |                 container = super().build() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 panel = TabbedPanelItem(text="Starcraft 2 Launcher") | 
					
						
							|  |  |  |                 self.mission_panel = panel.content = MissionLayout() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 self.tabs.add_widget(panel) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 Clock.schedule_interval(self.build_mission_table, 0.5) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 return container | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             def build_mission_table(self, dt): | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |                 if (not self.launching and (not self.last_checked_locations == self.ctx.checked_locations or | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |                                             not self.refresh_from_launching)) or self.first_check: | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |                     self.refresh_from_launching = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     self.mission_panel.clear_widgets() | 
					
						
							|  |  |  |                     if self.ctx.mission_req_table: | 
					
						
							|  |  |  |                         self.last_checked_locations = self.ctx.checked_locations.copy() | 
					
						
							|  |  |  |                         self.first_check = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         self.mission_id_to_button = {} | 
					
						
							|  |  |  |                         categories = {} | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |                         available_missions, unfinished_missions = calc_unfinished_missions(self.ctx) | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |                         # separate missions into categories | 
					
						
							|  |  |  |                         for mission in self.ctx.mission_req_table: | 
					
						
							|  |  |  |                             if not self.ctx.mission_req_table[mission].category in categories: | 
					
						
							|  |  |  |                                 categories[self.ctx.mission_req_table[mission].category] = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                             categories[self.ctx.mission_req_table[mission].category].append(mission) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         for category in categories: | 
					
						
							|  |  |  |                             category_panel = MissionCategory() | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |                             if category.startswith('_'): | 
					
						
							|  |  |  |                                 category_display_name = '' | 
					
						
							|  |  |  |                             else: | 
					
						
							|  |  |  |                                 category_display_name = category | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |                             category_panel.add_widget( | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |                                 Label(text=category_display_name, size_hint_y=None, height=50, outline_width=1)) | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |                             for mission in categories[category]: | 
					
						
							| 
									
										
										
										
											2022-09-18 12:40:35 +02:00
										 |  |  |                                 text: str = mission | 
					
						
							|  |  |  |                                 tooltip: str = "" | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |                                 mission_id: int = self.ctx.mission_req_table[mission].id | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |                                 # Map has uncollected locations | 
					
						
							|  |  |  |                                 if mission in unfinished_missions: | 
					
						
							|  |  |  |                                     text = f"[color=6495ED]{text}[/color]" | 
					
						
							|  |  |  |                                 elif mission in available_missions: | 
					
						
							|  |  |  |                                     text = f"[color=FFFFFF]{text}[/color]" | 
					
						
							|  |  |  |                                 # Map requirements not met | 
					
						
							|  |  |  |                                 else: | 
					
						
							|  |  |  |                                     text = f"[color=a9a9a9]{text}[/color]" | 
					
						
							|  |  |  |                                     tooltip = f"Requires: " | 
					
						
							| 
									
										
										
										
											2022-09-18 12:40:35 +02:00
										 |  |  |                                     if self.ctx.mission_req_table[mission].required_world: | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |                                         tooltip += ", ".join(list(self.ctx.mission_req_table)[req_mission - 1] for | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |                                                              req_mission in | 
					
						
							|  |  |  |                                                              self.ctx.mission_req_table[mission].required_world) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-18 12:40:35 +02:00
										 |  |  |                                         if self.ctx.mission_req_table[mission].number: | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |                                             tooltip += " and " | 
					
						
							| 
									
										
										
										
											2022-09-18 12:40:35 +02:00
										 |  |  |                                     if self.ctx.mission_req_table[mission].number: | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |                                         tooltip += f"{self.ctx.mission_req_table[mission].number} missions completed" | 
					
						
							| 
									
										
										
										
											2022-09-18 12:40:35 +02:00
										 |  |  |                                 remaining_location_names: typing.List[str] = [ | 
					
						
							|  |  |  |                                     self.ctx.location_names[loc] for loc in self.ctx.locations_for_mission(mission) | 
					
						
							|  |  |  |                                     if loc in self.ctx.missing_locations] | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |                                 if mission_id == self.ctx.final_mission: | 
					
						
							|  |  |  |                                     if mission in available_missions: | 
					
						
							|  |  |  |                                         text = f"[color=FFBC95]{mission}[/color]" | 
					
						
							|  |  |  |                                     else: | 
					
						
							|  |  |  |                                         text = f"[color=D0C0BE]{mission}[/color]" | 
					
						
							|  |  |  |                                     if tooltip: | 
					
						
							|  |  |  |                                         tooltip += "\n" | 
					
						
							|  |  |  |                                     tooltip += "Final Mission" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-18 12:40:35 +02:00
										 |  |  |                                 if remaining_location_names: | 
					
						
							|  |  |  |                                     if tooltip: | 
					
						
							|  |  |  |                                         tooltip += "\n" | 
					
						
							|  |  |  |                                     tooltip += f"Uncollected locations:\n" | 
					
						
							|  |  |  |                                     tooltip += "\n".join(remaining_location_names) | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |                                 mission_button = MissionButton(text=text, size_hint_y=None, height=50) | 
					
						
							|  |  |  |                                 mission_button.tooltip_text = tooltip | 
					
						
							|  |  |  |                                 mission_button.bind(on_press=self.mission_callback) | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |                                 self.mission_id_to_button[mission_id] = mission_button | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |                                 category_panel.add_widget(mission_button) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                             category_panel.add_widget(Label(text="")) | 
					
						
							|  |  |  |                             self.mission_panel.add_widget(category_panel) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 elif self.launching: | 
					
						
							|  |  |  |                     self.refresh_from_launching = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     self.mission_panel.clear_widgets() | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |                     self.mission_panel.add_widget(Label(text="Launching Mission: " + | 
					
						
							|  |  |  |                                                              lookup_id_to_mission[self.launching])) | 
					
						
							|  |  |  |                     if self.ctx.ui: | 
					
						
							|  |  |  |                         self.ctx.ui.clear_tooltip() | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |             def mission_callback(self, button): | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |                 if not self.launching: | 
					
						
							| 
									
										
										
										
											2022-09-05 21:09:03 +02:00
										 |  |  |                     mission_id: int = next(k for k, v in self.mission_id_to_button.items() if v == button) | 
					
						
							|  |  |  |                     self.ctx.play_mission(mission_id) | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |                     self.launching = mission_id | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |                     Clock.schedule_once(self.finish_launching, 10) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             def finish_launching(self, dt): | 
					
						
							|  |  |  |                 self.launching = False | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |         self.ui = SC2Manager(self) | 
					
						
							|  |  |  |         self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") | 
					
						
							| 
									
										
										
										
											2022-10-01 15:24:05 +02:00
										 |  |  |         import pkgutil | 
					
						
							|  |  |  |         data = pkgutil.get_data(SC2WoLWorld.__module__, "Starcraft2.kv").decode() | 
					
						
							|  |  |  |         Builder.load_string(data) | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-21 22:24:49 +02:00
										 |  |  |     async def shutdown(self): | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |         await super(SC2Context, self).shutdown() | 
					
						
							| 
									
										
										
										
											2022-09-12 02:08:33 +02:00
										 |  |  |         if self.last_bot: | 
					
						
							|  |  |  |             self.last_bot.want_close = True | 
					
						
							| 
									
										
										
										
											2022-05-21 22:24:49 +02:00
										 |  |  |         if self.sc2_run_task: | 
					
						
							|  |  |  |             self.sc2_run_task.cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-05 21:09:03 +02:00
										 |  |  |     def play_mission(self, mission_id: int): | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  |         if self.missions_unlocked or \ | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |                 is_mission_available(self, mission_id): | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  |             if self.sc2_run_task: | 
					
						
							|  |  |  |                 if not self.sc2_run_task.done(): | 
					
						
							|  |  |  |                     sc2_logger.warning("Starcraft 2 Client is still running!") | 
					
						
							|  |  |  |                 self.sc2_run_task.cancel()  # doesn't actually close the game, just stops the python task | 
					
						
							|  |  |  |             if self.slot is None: | 
					
						
							|  |  |  |                 sc2_logger.warning("Launching Mission without Archipelago authentication, " | 
					
						
							|  |  |  |                                    "checks will not be registered to server.") | 
					
						
							|  |  |  |             self.sc2_run_task = asyncio.create_task(starcraft_launch(self, mission_id), | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |                                                     name="Starcraft 2 Launch") | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  |         else: | 
					
						
							|  |  |  |             sc2_logger.info( | 
					
						
							|  |  |  |                 f"{lookup_id_to_mission[mission_id]} is not currently unlocked.  " | 
					
						
							|  |  |  |                 f"Use /unfinished or /available to see what is available.") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     def build_location_to_mission_mapping(self): | 
					
						
							|  |  |  |         mission_id_to_location_ids: typing.Dict[int, typing.Set[int]] = { | 
					
						
							|  |  |  |             mission_info.id: set() for mission_info in self.mission_req_table.values() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for loc in self.server_locations: | 
					
						
							|  |  |  |             mission_id, objective = divmod(loc - SC2WOL_LOC_ID_OFFSET, victory_modulo) | 
					
						
							|  |  |  |             mission_id_to_location_ids[mission_id].add(objective) | 
					
						
							|  |  |  |         self.mission_id_to_location_ids = {mission_id: sorted(objectives) for mission_id, objectives in | 
					
						
							|  |  |  |                                            mission_id_to_location_ids.items()} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def locations_for_mission(self, mission: str): | 
					
						
							|  |  |  |         mission_id: int = self.mission_req_table[mission].id | 
					
						
							|  |  |  |         objectives = self.mission_id_to_location_ids[self.mission_req_table[mission].id] | 
					
						
							|  |  |  |         for objective in objectives: | 
					
						
							|  |  |  |             yield SC2WOL_LOC_ID_OFFSET + mission_id * 100 + objective | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | async def main(): | 
					
						
							|  |  |  |     multiprocessing.freeze_support() | 
					
						
							|  |  |  |     parser = get_base_parser() | 
					
						
							| 
									
										
										
										
											2022-05-24 00:20:02 +02:00
										 |  |  |     parser.add_argument('--name', default=None, help="Slot Name to connect as.") | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |     args = parser.parse_args() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |     ctx = SC2Context(args.connect, args.password) | 
					
						
							| 
									
										
										
										
											2022-05-24 00:20:02 +02:00
										 |  |  |     ctx.auth = args.name | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |     if ctx.server_task is None: | 
					
						
							|  |  |  |         ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if gui_enabled: | 
					
						
							|  |  |  |         ctx.run_gui() | 
					
						
							|  |  |  |     ctx.run_cli() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await ctx.exit_event.wait() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await ctx.shutdown() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-20 00:55:12 +02:00
										 |  |  | maps_table = [ | 
					
						
							|  |  |  |     "ap_traynor01", "ap_traynor02", "ap_traynor03", | 
					
						
							|  |  |  |     "ap_thanson01", "ap_thanson02", "ap_thanson03a", "ap_thanson03b", | 
					
						
							|  |  |  |     "ap_ttychus01", "ap_ttychus02", "ap_ttychus03", "ap_ttychus04", "ap_ttychus05", | 
					
						
							|  |  |  |     "ap_ttosh01", "ap_ttosh02", "ap_ttosh03a", "ap_ttosh03b", | 
					
						
							|  |  |  |     "ap_thorner01", "ap_thorner02", "ap_thorner03", "ap_thorner04", "ap_thorner05s", | 
					
						
							|  |  |  |     "ap_tzeratul01", "ap_tzeratul02", "ap_tzeratul03", "ap_tzeratul04", | 
					
						
							|  |  |  |     "ap_tvalerian01", "ap_tvalerian02a", "ap_tvalerian02b", "ap_tvalerian03" | 
					
						
							|  |  |  | ] | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  | wol_default_categories = [ | 
					
						
							|  |  |  |     "Mar Sara", "Mar Sara", "Mar Sara", "Colonist", "Colonist", "Colonist", "Colonist", | 
					
						
							|  |  |  |     "Artifact", "Artifact", "Artifact", "Artifact", "Artifact", "Covert", "Covert", "Covert", "Covert", | 
					
						
							|  |  |  |     "Rebellion", "Rebellion", "Rebellion", "Rebellion", "Rebellion", "Prophecy", "Prophecy", "Prophecy", "Prophecy", | 
					
						
							|  |  |  |     "Char", "Char", "Char", "Char" | 
					
						
							|  |  |  | ] | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  | wol_default_category_names = [ | 
					
						
							|  |  |  |     "Mar Sara", "Colonist", "Artifact", "Covert", "Rebellion", "Prophecy", "Char" | 
					
						
							|  |  |  | ] | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-01 15:24:05 +02:00
										 |  |  | def calculate_items(items: typing.List[NetworkItem]) -> typing.List[int]: | 
					
						
							|  |  |  |     network_item: NetworkItem | 
					
						
							| 
									
										
										
										
											2022-09-12 01:51:25 +02:00
										 |  |  |     accumulators: typing.List[int] = [0 for _ in type_flaggroups] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for network_item in items: | 
					
						
							|  |  |  |         name: str = lookup_id_to_name[network_item.item] | 
					
						
							|  |  |  |         item_data: ItemData = item_table[name] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # exists exactly once | 
					
						
							|  |  |  |         if item_data.quantity == 1: | 
					
						
							|  |  |  |             accumulators[type_flaggroups[item_data.type]] |= 1 << item_data.number | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # exists multiple times | 
					
						
							|  |  |  |         elif item_data.type == "Upgrade": | 
					
						
							|  |  |  |             accumulators[type_flaggroups[item_data.type]] += 1 << item_data.number | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # sum | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             accumulators[type_flaggroups[item_data.type]] += item_data.number | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return accumulators | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def calc_difficulty(difficulty): | 
					
						
							|  |  |  |     if difficulty == 0: | 
					
						
							|  |  |  |         return 'C' | 
					
						
							|  |  |  |     elif difficulty == 1: | 
					
						
							|  |  |  |         return 'N' | 
					
						
							|  |  |  |     elif difficulty == 2: | 
					
						
							|  |  |  |         return 'H' | 
					
						
							|  |  |  |     elif difficulty == 3: | 
					
						
							|  |  |  |         return 'B' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 'X' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | async def starcraft_launch(ctx: SC2Context, mission_id: int): | 
					
						
							| 
									
										
										
										
											2022-05-23 09:13:44 -04:00
										 |  |  |     sc2_logger.info(f"Launching {lookup_id_to_mission[mission_id]}. If game does not launch check log file for errors.") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 03:51:00 -04:00
										 |  |  |     with DllDirectory(None): | 
					
						
							|  |  |  |         run_game(sc2.maps.get(maps_table[mission_id - 1]), [Bot(Race.Terran, ArchipelagoBot(ctx, mission_id), | 
					
						
							|  |  |  |                                                                 name="Archipelago", fullscreen=True)], realtime=True) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ArchipelagoBot(sc2.bot_ai.BotAI): | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     game_running: bool = False | 
					
						
							|  |  |  |     mission_completed: bool = False | 
					
						
							|  |  |  |     boni: typing.List[bool] | 
					
						
							|  |  |  |     setup_done: bool | 
					
						
							|  |  |  |     ctx: SC2Context | 
					
						
							|  |  |  |     mission_id: int | 
					
						
							| 
									
										
										
										
											2022-09-12 02:08:33 +02:00
										 |  |  |     want_close: bool = False | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |     can_read_game = False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     last_received_update: int = 0 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |     def __init__(self, ctx: SC2Context, mission_id): | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         self.setup_done = False | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |         self.ctx = ctx | 
					
						
							| 
									
										
										
										
											2022-09-12 02:08:33 +02:00
										 |  |  |         self.ctx.last_bot = self | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |         self.mission_id = mission_id | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         self.boni = [False for _ in range(max_bonus)] | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         super(ArchipelagoBot, self).__init__() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async def on_step(self, iteration: int): | 
					
						
							| 
									
										
										
										
											2022-09-12 02:08:33 +02:00
										 |  |  |         if self.want_close: | 
					
						
							|  |  |  |             self.want_close = False | 
					
						
							|  |  |  |             await self._client.leave() | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |         game_state = 0 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         if not self.setup_done: | 
					
						
							|  |  |  |             self.setup_done = True | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |             start_items = calculate_items(self.ctx.items_received) | 
					
						
							| 
									
										
										
										
											2022-08-08 15:20:51 -07:00
										 |  |  |             if self.ctx.difficulty_override >= 0: | 
					
						
							|  |  |  |                 difficulty = calc_difficulty(self.ctx.difficulty_override) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 difficulty = calc_difficulty(self.ctx.difficulty) | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |             await self.chat_send("ArchipelagoLoad {} {} {} {} {} {} {} {} {} {} {} {} {}".format( | 
					
						
							| 
									
										
										
										
											2022-05-20 00:55:12 +02:00
										 |  |  |                 difficulty, | 
					
						
							|  |  |  |                 start_items[0], start_items[1], start_items[2], start_items[3], start_items[4], | 
					
						
							|  |  |  |                 start_items[5], start_items[6], start_items[7], start_items[8], start_items[9], | 
					
						
							| 
									
										
										
										
											2022-06-03 14:18:36 -04:00
										 |  |  |                 self.ctx.all_in_choice, start_items[10])) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |             self.last_received_update = len(self.ctx.items_received) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |             if not self.ctx.announcements.empty(): | 
					
						
							|  |  |  |                 message = self.ctx.announcements.get(timeout=1) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |                 await self.chat_send("SendMessage " + message) | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |                 self.ctx.announcements.task_done() | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # Archipelago reads the health | 
					
						
							|  |  |  |             for unit in self.all_own_units(): | 
					
						
							|  |  |  |                 if unit.health_max == 38281: | 
					
						
							|  |  |  |                     game_state = int(38281 - unit.health) | 
					
						
							| 
									
										
										
										
											2022-05-20 00:55:12 +02:00
										 |  |  |                     self.can_read_game = True | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |             if iteration == 160 and not game_state & 1: | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |                 await self.chat_send("SendMessage Warning: Archipelago unable to connect or has lost connection to " + | 
					
						
							|  |  |  |                                      "Starcraft 2 (This is likely a map issue)") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if self.last_received_update < len(self.ctx.items_received): | 
					
						
							|  |  |  |                 current_items = calculate_items(self.ctx.items_received) | 
					
						
							|  |  |  |                 await self.chat_send("UpdateTech {} {} {} {} {} {} {} {}".format( | 
					
						
							| 
									
										
										
										
											2022-05-20 00:55:12 +02:00
										 |  |  |                     current_items[0], current_items[1], current_items[2], current_items[3], current_items[4], | 
					
						
							|  |  |  |                     current_items[5], current_items[6], current_items[7])) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |                 self.last_received_update = len(self.ctx.items_received) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if game_state & 1: | 
					
						
							|  |  |  |                 if not self.game_running: | 
					
						
							|  |  |  |                     print("Archipelago Connected") | 
					
						
							|  |  |  |                     self.game_running = True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-20 00:55:12 +02:00
										 |  |  |                 if self.can_read_game: | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |                     if game_state & (1 << 1) and not self.mission_completed: | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |                         if self.mission_id != self.ctx.final_mission: | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |                             print("Mission Completed") | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |                             await self.ctx.send_msgs( | 
					
						
							|  |  |  |                                 [{"cmd": 'LocationChecks', | 
					
						
							|  |  |  |                                   "locations": [SC2WOL_LOC_ID_OFFSET + victory_modulo * self.mission_id]}]) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |                             self.mission_completed = True | 
					
						
							|  |  |  |                         else: | 
					
						
							|  |  |  |                             print("Game Complete") | 
					
						
							|  |  |  |                             await self.ctx.send_msgs([{"cmd": 'StatusUpdate', "status": ClientStatus.CLIENT_GOAL}]) | 
					
						
							|  |  |  |                             self.mission_completed = True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |                     for x, completed in enumerate(self.boni): | 
					
						
							|  |  |  |                         if not completed and game_state & (1 << (x + 2)): | 
					
						
							|  |  |  |                             await self.ctx.send_msgs( | 
					
						
							|  |  |  |                                 [{"cmd": 'LocationChecks', | 
					
						
							|  |  |  |                                   "locations": [SC2WOL_LOC_ID_OFFSET + victory_modulo * self.mission_id + x + 1]}]) | 
					
						
							|  |  |  |                             self.boni[x] = True | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     await self.chat_send("LostConnection - Lost connection to game.") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | def request_unfinished_missions(ctx: SC2Context): | 
					
						
							|  |  |  |     if ctx.mission_req_table: | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |         message = "Unfinished Missions: " | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         unlocks = initialize_blank_mission_dict(ctx.mission_req_table) | 
					
						
							|  |  |  |         unfinished_locations = initialize_blank_mission_dict(ctx.mission_req_table) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         _, unfinished_missions = calc_unfinished_missions(ctx, unlocks=unlocks) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-11 14:46:24 -05:00
										 |  |  |         # Removing All-In from location pool | 
					
						
							|  |  |  |         final_mission = lookup_id_to_mission[ctx.final_mission] | 
					
						
							|  |  |  |         if final_mission in unfinished_missions.keys(): | 
					
						
							|  |  |  |             message = f"Final Mission Available: {final_mission}[{ctx.final_mission}]\n" + message | 
					
						
							|  |  |  |             if unfinished_missions[final_mission] == -1: | 
					
						
							|  |  |  |                 unfinished_missions.pop(final_mission) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         message += ", ".join(f"{mark_up_mission_name(ctx, mission, unlocks)}[{ctx.mission_req_table[mission].id}] " + | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |                              mark_up_objectives( | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |                                  f"[{len(unfinished_missions[mission])}/" | 
					
						
							|  |  |  |                                  f"{sum(1 for _ in ctx.locations_for_mission(mission))}]", | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |                                  ctx, unfinished_locations, mission) | 
					
						
							|  |  |  |                              for mission in unfinished_missions) | 
					
						
							| 
									
										
										
										
											2022-05-20 20:47:16 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         if ctx.ui: | 
					
						
							|  |  |  |             ctx.ui.log_panels['All'].on_message_markup(message) | 
					
						
							|  |  |  |             ctx.ui.log_panels['Starcraft2'].on_message_markup(message) | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |         else: | 
					
						
							|  |  |  |             sc2_logger.info(message) | 
					
						
							| 
									
										
										
										
											2022-05-20 20:47:16 -04:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |         sc2_logger.warning("No mission table found, you are likely not connected to a server.") | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | def calc_unfinished_missions(ctx: SC2Context, unlocks=None): | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |     unfinished_missions = [] | 
					
						
							|  |  |  |     locations_completed = [] | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if not unlocks: | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         unlocks = initialize_blank_mission_dict(ctx.mission_req_table) | 
					
						
							| 
									
										
										
										
											2022-05-30 01:11:01 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     available_missions = calc_available_missions(ctx, unlocks) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for name in available_missions: | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         objectives = set(ctx.locations_for_mission(name)) | 
					
						
							|  |  |  |         if objectives: | 
					
						
							|  |  |  |             objectives_completed = ctx.checked_locations & objectives | 
					
						
							|  |  |  |             if len(objectives_completed) < len(objectives): | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |                 unfinished_missions.append(name) | 
					
						
							|  |  |  |                 locations_completed.append(objectives_completed) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         else:  # infer that this is the final mission as it has no objectives | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |             unfinished_missions.append(name) | 
					
						
							|  |  |  |             locations_completed.append(-1) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     return available_missions, dict(zip(unfinished_missions, locations_completed)) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | def is_mission_available(ctx: SC2Context, mission_id_to_check): | 
					
						
							|  |  |  |     unfinished_missions = calc_available_missions(ctx) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     return any(mission_id_to_check == ctx.mission_req_table[mission].id for mission in unfinished_missions) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | def mark_up_mission_name(ctx: SC2Context, mission, unlock_table): | 
					
						
							| 
									
										
										
										
											2022-05-20 20:47:16 -04:00
										 |  |  |     """Checks if the mission is required for game completion and adds '*' to the name to mark that.""" | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     if ctx.mission_req_table[mission].completion_critical: | 
					
						
							|  |  |  |         if ctx.ui: | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |             message = "[color=AF99EF]" + mission + "[/color]" | 
					
						
							| 
									
										
										
										
											2022-05-20 20:47:16 -04:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |             message = "*" + mission + "*" | 
					
						
							| 
									
										
										
										
											2022-05-20 20:47:16 -04:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |         message = mission | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     if ctx.ui: | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |         unlocks = unlock_table[mission] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if len(unlocks) > 0: | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |             pre_message = f"[ref={list(ctx.mission_req_table).index(mission)}|Unlocks: " | 
					
						
							|  |  |  |             pre_message += ", ".join(f"{unlock}({ctx.mission_req_table[unlock].id})" for unlock in unlocks) | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |             pre_message += f"]" | 
					
						
							|  |  |  |             message = pre_message + message + "[/ref]" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return message | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def mark_up_objectives(message, ctx, unfinished_locations, mission): | 
					
						
							|  |  |  |     formatted_message = message | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ctx.ui: | 
					
						
							|  |  |  |         locations = unfinished_locations[mission] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         pre_message = f"[ref={list(ctx.mission_req_table).index(mission) + 30}|" | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |         pre_message += "<br>".join(location for location in locations) | 
					
						
							|  |  |  |         pre_message += f"]" | 
					
						
							|  |  |  |         formatted_message = pre_message + message + "[/ref]" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return formatted_message | 
					
						
							| 
									
										
										
										
											2022-05-20 20:47:16 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | def request_available_missions(ctx: SC2Context): | 
					
						
							|  |  |  |     if ctx.mission_req_table: | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |         message = "Available Missions: " | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |         # Initialize mission unlock table | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         unlocks = initialize_blank_mission_dict(ctx.mission_req_table) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         missions = calc_available_missions(ctx, unlocks) | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |         message += \ | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |             ", ".join(f"{mark_up_mission_name(ctx, mission, unlocks)}" | 
					
						
							|  |  |  |                       f"[{ctx.mission_req_table[mission].id}]" | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |                       for mission in missions) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         if ctx.ui: | 
					
						
							|  |  |  |             ctx.ui.log_panels['All'].on_message_markup(message) | 
					
						
							|  |  |  |             ctx.ui.log_panels['Starcraft2'].on_message_markup(message) | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |         else: | 
					
						
							|  |  |  |             sc2_logger.info(message) | 
					
						
							| 
									
										
										
										
											2022-05-20 20:47:16 -04:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |         sc2_logger.warning("No mission table found, you are likely not connected to a server.") | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | def calc_available_missions(ctx: SC2Context, unlocks=None): | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |     available_missions = [] | 
					
						
							| 
									
										
										
										
											2022-05-18 23:32:52 -04:00
										 |  |  |     missions_complete = 0 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Get number of missions completed | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     for loc in ctx.checked_locations: | 
					
						
							|  |  |  |         if loc % victory_modulo == 0: | 
					
						
							| 
									
										
										
										
											2022-05-18 23:32:52 -04:00
										 |  |  |             missions_complete += 1 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     for name in ctx.mission_req_table: | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |         # Go through the required missions for each mission and fill up unlock table used later for hover-over tooltips | 
					
						
							|  |  |  |         if unlocks: | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |             for unlock in ctx.mission_req_table[name].required_world: | 
					
						
							|  |  |  |                 unlocks[list(ctx.mission_req_table)[unlock - 1]].append(name) | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         if mission_reqs_completed(ctx, name, missions_complete): | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |             available_missions.append(name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return available_missions | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  | def mission_reqs_completed(ctx: SC2Context, mission_name: str, missions_complete: int): | 
					
						
							| 
									
										
										
										
											2022-05-20 10:17:44 -04:00
										 |  |  |     """Returns a bool signifying if the mission has all requirements complete and can be done
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     Arguments: | 
					
						
							|  |  |  |     ctx -- instance of SC2Context | 
					
						
							| 
									
										
										
										
											2022-05-20 10:17:44 -04:00
										 |  |  |     locations_to_check -- the mission string name to check | 
					
						
							|  |  |  |     missions_complete -- an int of how many missions have been completed | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |     mission_path -- a list of missions that have already been checked | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  | """
 | 
					
						
							|  |  |  |     if len(ctx.mission_req_table[mission_name].required_world) >= 1: | 
					
						
							| 
									
										
										
										
											2022-05-20 10:17:44 -04:00
										 |  |  |         # A check for when the requirements are being or'd | 
					
						
							|  |  |  |         or_success = False | 
					
						
							| 
									
										
										
										
											2022-05-18 23:32:52 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-20 10:17:44 -04:00
										 |  |  |         # Loop through required missions | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         for req_mission in ctx.mission_req_table[mission_name].required_world: | 
					
						
							| 
									
										
										
										
											2022-05-20 10:17:44 -04:00
										 |  |  |             req_success = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Check if required mission has been completed | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |             if not (ctx.mission_req_table[list(ctx.mission_req_table)[req_mission - 1]].id * | 
					
						
							|  |  |  |                     victory_modulo + SC2WOL_LOC_ID_OFFSET) in ctx.checked_locations: | 
					
						
							|  |  |  |                 if not ctx.mission_req_table[mission_name].or_requirements: | 
					
						
							| 
									
										
										
										
											2022-05-20 10:17:44 -04:00
										 |  |  |                     return False | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     req_success = False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |             # Grid-specific logic (to avoid long path checks and infinite recursion) | 
					
						
							|  |  |  |             if ctx.mission_order in (3, 4): | 
					
						
							|  |  |  |                 if req_success: | 
					
						
							|  |  |  |                     return True | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     if req_mission is ctx.mission_req_table[mission_name].required_world[-1]: | 
					
						
							|  |  |  |                         return False | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-20 10:17:44 -04:00
										 |  |  |             # Recursively check required mission to see if it's requirements are met, in case !collect has been done | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |             # Skipping recursive check on Grid settings to speed up checks and avoid infinite recursion | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |             if not mission_reqs_completed(ctx, list(ctx.mission_req_table)[req_mission - 1], missions_complete): | 
					
						
							|  |  |  |                 if not ctx.mission_req_table[mission_name].or_requirements: | 
					
						
							| 
									
										
										
										
											2022-05-20 10:17:44 -04:00
										 |  |  |                     return False | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     req_success = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # If requirement check succeeded mark or as satisfied | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |             if ctx.mission_req_table[mission_name].or_requirements and req_success: | 
					
						
							| 
									
										
										
										
											2022-05-20 10:17:44 -04:00
										 |  |  |                 or_success = True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         if ctx.mission_req_table[mission_name].or_requirements: | 
					
						
							| 
									
										
										
										
											2022-05-20 10:17:44 -04:00
										 |  |  |             # Return false if or requirements not met | 
					
						
							|  |  |  |             if not or_success: | 
					
						
							| 
									
										
										
										
											2022-05-18 23:32:52 -04:00
										 |  |  |                 return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-20 10:17:44 -04:00
										 |  |  |         # Check number of missions | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |         if missions_complete >= ctx.mission_req_table[mission_name].number: | 
					
						
							| 
									
										
										
										
											2022-05-18 23:32:52 -04:00
										 |  |  |             return True | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  | def initialize_blank_mission_dict(location_table): | 
					
						
							|  |  |  |     unlocks = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for mission in list(location_table): | 
					
						
							|  |  |  |         unlocks[mission] = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return unlocks | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 03:51:00 -04:00
										 |  |  | def check_game_install_path() -> bool: | 
					
						
							|  |  |  |     # First thing: go to the default location for ExecuteInfo. | 
					
						
							|  |  |  |     # An exception for Windows is included because it's very difficult to find ~\Documents if the user moved it. | 
					
						
							|  |  |  |     if is_windows: | 
					
						
							|  |  |  |         # The next five lines of utterly inscrutable code are brought to you by copy-paste from Stack Overflow. | 
					
						
							|  |  |  |         # https://stackoverflow.com/questions/6227590/finding-the-users-my-documents-path/30924555# | 
					
						
							|  |  |  |         import ctypes.wintypes | 
					
						
							|  |  |  |         CSIDL_PERSONAL = 5  # My Documents | 
					
						
							|  |  |  |         SHGFP_TYPE_CURRENT = 0  # Get current, not default value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH) | 
					
						
							|  |  |  |         ctypes.windll.shell32.SHGetFolderPathW(None, CSIDL_PERSONAL, None, SHGFP_TYPE_CURRENT, buf) | 
					
						
							|  |  |  |         documentspath = buf.value | 
					
						
							|  |  |  |         einfo = str(documentspath / Path("StarCraft II\\ExecuteInfo.txt")) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         einfo = str(sc2.paths.get_home() / Path(sc2.paths.USERPATH[sc2.paths.PF])) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Check if the file exists. | 
					
						
							|  |  |  |     if os.path.isfile(einfo): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Open the file and read it, picking out the latest executable's path. | 
					
						
							|  |  |  |         with open(einfo) as f: | 
					
						
							|  |  |  |             content = f.read() | 
					
						
							|  |  |  |         if content: | 
					
						
							| 
									
										
										
										
											2022-09-18 10:48:36 -04:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 base = re.search(r" = (.*)Versions", content).group(1) | 
					
						
							|  |  |  |             except AttributeError: | 
					
						
							|  |  |  |                 sc2_logger.warning(f"Found {einfo}, but it was empty. Run SC2 through the Blizzard launcher, then " | 
					
						
							|  |  |  |                                    f"try again.") | 
					
						
							|  |  |  |                 return False | 
					
						
							| 
									
										
										
										
											2022-07-14 03:51:00 -04:00
										 |  |  |             if os.path.exists(base): | 
					
						
							|  |  |  |                 executable = sc2.paths.latest_executeble(Path(base).expanduser() / "Versions") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 # Finally, check the path for an actual executable. | 
					
						
							|  |  |  |                 # If we find one, great. Set up the SC2PATH. | 
					
						
							|  |  |  |                 if os.path.isfile(executable): | 
					
						
							|  |  |  |                     sc2_logger.info(f"Found an SC2 install at {base}!") | 
					
						
							|  |  |  |                     sc2_logger.debug(f"Latest executable at {executable}.") | 
					
						
							|  |  |  |                     os.environ["SC2PATH"] = base | 
					
						
							|  |  |  |                     sc2_logger.debug(f"SC2PATH set to {base}.") | 
					
						
							|  |  |  |                     return True | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     sc2_logger.warning(f"We may have found an SC2 install at {base}, but couldn't find {executable}.") | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 sc2_logger.warning(f"{einfo} pointed to {base}, but we could not find an SC2 install there.") | 
					
						
							|  |  |  |     else: | 
					
						
							| 
									
										
										
										
											2022-09-18 10:48:36 -04:00
										 |  |  |         sc2_logger.warning(f"Couldn't find {einfo}. Run SC2 through the Blizzard launcher, then try again. " | 
					
						
							|  |  |  |                            f"If that fails, please run /set_path with your SC2 install directory.") | 
					
						
							| 
									
										
										
										
											2022-07-14 03:51:00 -04:00
										 |  |  |     return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 22:19:17 -04:00
										 |  |  | def is_mod_installed_correctly() -> bool: | 
					
						
							|  |  |  |     """Searches for all required files.""" | 
					
						
							|  |  |  |     if "SC2PATH" not in os.environ: | 
					
						
							|  |  |  |         check_game_install_path() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     mapdir = os.environ['SC2PATH'] / Path('Maps/ArchipelagoCampaign') | 
					
						
							|  |  |  |     modfile = os.environ["SC2PATH"] / Path("Mods/Archipelago.SC2Mod") | 
					
						
							|  |  |  |     wol_required_maps = [ | 
					
						
							|  |  |  |         "ap_thanson01.SC2Map", "ap_thanson02.SC2Map", "ap_thanson03a.SC2Map", "ap_thanson03b.SC2Map", | 
					
						
							|  |  |  |         "ap_thorner01.SC2Map", "ap_thorner02.SC2Map", "ap_thorner03.SC2Map", "ap_thorner04.SC2Map", "ap_thorner05s.SC2Map", | 
					
						
							|  |  |  |         "ap_traynor01.SC2Map", "ap_traynor02.SC2Map", "ap_traynor03.SC2Map", | 
					
						
							|  |  |  |         "ap_ttosh01.SC2Map", "ap_ttosh02.SC2Map", "ap_ttosh03a.SC2Map", "ap_ttosh03b.SC2Map", | 
					
						
							|  |  |  |         "ap_ttychus01.SC2Map", "ap_ttychus02.SC2Map", "ap_ttychus03.SC2Map", "ap_ttychus04.SC2Map", "ap_ttychus05.SC2Map", | 
					
						
							|  |  |  |         "ap_tvalerian01.SC2Map", "ap_tvalerian02a.SC2Map", "ap_tvalerian02b.SC2Map", "ap_tvalerian03.SC2Map", | 
					
						
							|  |  |  |         "ap_tzeratul01.SC2Map", "ap_tzeratul02.SC2Map", "ap_tzeratul03.SC2Map", "ap_tzeratul04.SC2Map" | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  |     needs_files = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Check for maps. | 
					
						
							|  |  |  |     missing_maps = [] | 
					
						
							|  |  |  |     for mapfile in wol_required_maps: | 
					
						
							|  |  |  |         if not os.path.isfile(mapdir / mapfile): | 
					
						
							|  |  |  |             missing_maps.append(mapfile) | 
					
						
							|  |  |  |     if len(missing_maps) >= 19: | 
					
						
							|  |  |  |         sc2_logger.warning(f"All map files missing from {mapdir}.") | 
					
						
							|  |  |  |         needs_files = True | 
					
						
							|  |  |  |     elif len(missing_maps) > 0: | 
					
						
							|  |  |  |         for map in missing_maps: | 
					
						
							|  |  |  |             sc2_logger.debug(f"Missing {map} from {mapdir}.") | 
					
						
							|  |  |  |             sc2_logger.warning(f"Missing {len(missing_maps)} map files.") | 
					
						
							|  |  |  |         needs_files = True | 
					
						
							|  |  |  |     else:  # Must be no maps missing | 
					
						
							|  |  |  |         sc2_logger.info(f"All maps found in {mapdir}.") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Check for mods. | 
					
						
							|  |  |  |     if os.path.isfile(modfile): | 
					
						
							|  |  |  |         sc2_logger.info(f"Archipelago mod found at {modfile}.") | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         sc2_logger.warning(f"Archipelago mod could not be found at {modfile}.") | 
					
						
							|  |  |  |         needs_files = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Final verdict. | 
					
						
							|  |  |  |     if needs_files: | 
					
						
							|  |  |  |         sc2_logger.warning(f"Required files are missing. Run /download_data to acquire them.") | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return True | 
					
						
							| 
									
										
										
										
											2022-07-14 03:51:00 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DllDirectory: | 
					
						
							|  |  |  |     # Credit to Black Sliver for this code. | 
					
						
							|  |  |  |     # More info: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setdlldirectoryw | 
					
						
							|  |  |  |     _old: typing.Optional[str] = None | 
					
						
							|  |  |  |     _new: typing.Optional[str] = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, new: typing.Optional[str]): | 
					
						
							|  |  |  |         self._new = new | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         old = self.get() | 
					
						
							|  |  |  |         if self.set(self._new): | 
					
						
							|  |  |  |             self._old = old | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __exit__(self, *args): | 
					
						
							|  |  |  |         if self._old is not None: | 
					
						
							|  |  |  |             self.set(self._old) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     def get() -> typing.Optional[str]: | 
					
						
							| 
									
										
										
										
											2022-07-14 03:51:00 -04:00
										 |  |  |         if sys.platform == "win32": | 
					
						
							|  |  |  |             n = ctypes.windll.kernel32.GetDllDirectoryW(0, None) | 
					
						
							|  |  |  |             buf = ctypes.create_unicode_buffer(n) | 
					
						
							|  |  |  |             ctypes.windll.kernel32.GetDllDirectoryW(n, buf) | 
					
						
							|  |  |  |             return buf.value | 
					
						
							|  |  |  |         # NOTE: other OS may support os.environ["LD_LIBRARY_PATH"], but this fix is windows-specific | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def set(s: typing.Optional[str]) -> bool: | 
					
						
							|  |  |  |         if sys.platform == "win32": | 
					
						
							|  |  |  |             return ctypes.windll.kernel32.SetDllDirectoryW(s) != 0 | 
					
						
							|  |  |  |         # NOTE: other OS may support os.environ["LD_LIBRARY_PATH"], but this fix is windows-specific | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 22:19:17 -04:00
										 |  |  | def download_latest_release_zip(owner: str, repo: str, current_version: str = None, force_download=False) -> (str, str): | 
					
						
							|  |  |  |     """Downloads the latest release of a GitHub repo to the current directory as a .zip file.""" | 
					
						
							|  |  |  |     import requests | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     headers = {"Accept": 'application/vnd.github.v3+json'} | 
					
						
							|  |  |  |     url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     r1 = requests.get(url, headers=headers) | 
					
						
							|  |  |  |     if r1.status_code == 200: | 
					
						
							|  |  |  |         latest_version = r1.json()["tag_name"] | 
					
						
							|  |  |  |         sc2_logger.info(f"Latest version: {latest_version}.") | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         sc2_logger.warning(f"Status code: {r1.status_code}") | 
					
						
							|  |  |  |         sc2_logger.warning(f"Failed to reach GitHub. Could not find download link.") | 
					
						
							|  |  |  |         sc2_logger.warning(f"text: {r1.text}") | 
					
						
							|  |  |  |         return "", current_version | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (force_download is False) and (current_version == latest_version): | 
					
						
							|  |  |  |         sc2_logger.info("Latest version already installed.") | 
					
						
							|  |  |  |         return "", current_version | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sc2_logger.info(f"Attempting to download version {latest_version} of {repo}.") | 
					
						
							|  |  |  |     download_url = r1.json()["assets"][0]["browser_download_url"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     r2 = requests.get(download_url, headers=headers) | 
					
						
							| 
									
										
										
										
											2023-01-24 03:54:23 +01:00
										 |  |  |     if r2.status_code == 200 and zipfile.is_zipfile(io.BytesIO(r2.content)): | 
					
						
							| 
									
										
										
										
											2022-10-08 22:19:17 -04:00
										 |  |  |         with open(f"{repo}.zip", "wb") as fh: | 
					
						
							|  |  |  |             fh.write(r2.content) | 
					
						
							|  |  |  |         sc2_logger.info(f"Successfully downloaded {repo}.zip.") | 
					
						
							|  |  |  |         return f"{repo}.zip", latest_version | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         sc2_logger.warning(f"Status code: {r2.status_code}") | 
					
						
							|  |  |  |         sc2_logger.warning("Download failed.") | 
					
						
							|  |  |  |         sc2_logger.warning(f"text: {r2.text}") | 
					
						
							|  |  |  |         return "", current_version | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def is_mod_update_available(owner: str, repo: str, current_version: str) -> bool: | 
					
						
							|  |  |  |     import requests | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     headers = {"Accept": 'application/vnd.github.v3+json'} | 
					
						
							|  |  |  |     url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     r1 = requests.get(url, headers=headers) | 
					
						
							|  |  |  |     if r1.status_code == 200: | 
					
						
							|  |  |  |         latest_version = r1.json()["tag_name"] | 
					
						
							|  |  |  |         if current_version != latest_version: | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         sc2_logger.warning(f"Failed to reach GitHub while checking for updates.") | 
					
						
							|  |  |  |         sc2_logger.warning(f"Status code: {r1.status_code}") | 
					
						
							|  |  |  |         sc2_logger.warning(f"text: {r1.text}") | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     colorama.init() | 
					
						
							| 
									
										
										
										
											2022-05-19 06:02:36 +02:00
										 |  |  |     asyncio.run(main()) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |     colorama.deinit() |