diff --git a/Starcraft2Client.py b/Starcraft2Client.py index c1eed74b..de0a9041 100644 --- a/Starcraft2Client.py +++ b/Starcraft2Client.py @@ -114,12 +114,40 @@ class StarcraftClientProcessor(ClientCommandProcessor): """Manually set the SC2 install directory (if the automatic detection fails).""" if path: os.environ["SC2PATH"] = path - check_mod_install() + is_mod_installed_correctly() return True else: sc2_logger.warning("When using set_path, you must type the path to your SC2 install directory.") return False + def _cmd_download_data(self, force: bool = False) -> bool: + """Download the most recent release of the necessary files for playing SC2 with + Archipelago. force should be True or False. force=True will overwrite your files.""" + 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 + + tempzip, version = download_latest_release_zip('TheCondor07', 'Starcraft2ArchipelagoData', current_version=current_ver, force_download=force) + + if tempzip != '': + try: + import zipfile + 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 + class SC2Context(CommonContext): command_processor = StarcraftClientProcessor @@ -158,10 +186,13 @@ class SC2Context(CommonContext): self.build_location_to_mission_mapping() - # Look for and set SC2PATH. - # check_game_install_path() returns True if and only if it finds + sets SC2PATH. - if "SC2PATH" not in os.environ and check_game_install_path(): - check_mod_install() + # Looks for the required maps and mods for SC2. Runs check_game_install_path. + is_mod_installed_correctly() + 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.") def on_print_json(self, args: dict): # goes to this world @@ -820,18 +851,53 @@ def check_game_install_path() -> bool: return False -def check_mod_install() -> bool: - # Pull up the SC2PATH if set. If not, encourage the user to manually run /set_path. - try: - # Check inside the Mods folder for Archipelago.SC2Mod. If found, tell user. If not, tell user. - if os.path.isfile(modfile := (os.environ["SC2PATH"] / Path("Mods") / Path("Archipelago.SC2Mod"))): - sc2_logger.info(f"Archipelago mod found at {modfile}.") - return True - else: - sc2_logger.warning(f"Archipelago mod could not be found at {modfile}. Please install the mod file there.") - except KeyError: - sc2_logger.warning(f"SC2PATH isn't set. Please run /set_path with the path to your SC2 install.") - return False +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 class DllDirectory: @@ -870,6 +936,64 @@ class DllDirectory: return False +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) + if r2.status_code == 200: + 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 + + if __name__ == '__main__': colorama.init() asyncio.run(main()) diff --git a/worlds/sc2wol/docs/en_Starcraft 2 Wings of Liberty.md b/worlds/sc2wol/docs/en_Starcraft 2 Wings of Liberty.md index 8fa20c86..f7c8519a 100644 --- a/worlds/sc2wol/docs/en_Starcraft 2 Wings of Liberty.md +++ b/worlds/sc2wol/docs/en_Starcraft 2 Wings of Liberty.md @@ -15,7 +15,7 @@ missions. When you receive items, they will immediately become available, even d notified via a text box in the top-right corner of the game screen. (The text client for StarCraft 2 also records all items in all worlds.) -Missions are launched only through the text client. The Hyperion is never visited. Aditionally, credits are not used. +Missions are launched only through the text client. The Hyperion is never visited. Additionally, credits are not used. ## What is the goal of this game when randomized? diff --git a/worlds/sc2wol/docs/setup_en.md b/worlds/sc2wol/docs/setup_en.md index 1539a212..267c8430 100644 --- a/worlds/sc2wol/docs/setup_en.md +++ b/worlds/sc2wol/docs/setup_en.md @@ -13,9 +13,10 @@ to obtain a config file for StarCraft 2. 1. Install StarCraft 2 and Archipelago using the first two links above. (The StarCraft 2 client for Archipelago is included by default.) -2. Click the third link above and follow the instructions there. -3. Linux users should also follow the instructions found at the bottom of this page - (["Running in Linux"](#running-in-linux)). + - Linux users should also follow the instructions found at the bottom of this page + (["Running in Linux"](#running-in-linux)). +2. Run ArchipelagoStarcraft2Client.exe. +3. Type the command `/download_data`. This will automatically install the Maps and Data files from the third link above. ## Where do I get a config file (aka "YAML") for this game? @@ -40,16 +41,9 @@ Check out [Creating a YAML](https://archipelago.gg/tutorial/Archipelago/setup/en ## The game isn't launching when I try to start a mission. -First, check the log file for issues (stored at `[Archipelago Directory]/logs/SC2Client.txt`). If the below fix doesn't -work for you, and you can't figure out the log file, visit our [Discord's](https://discord.com/invite/8Z65BR2) -tech-support channel for help. Please include a specific description of what's going wrong and attach your log file to -your message. - -### Check your installation - -Make sure you've followed the installation instructions completely. Specifically, make sure that you've placed the Maps -and Mods folders directly inside the StarCraft II installation folder. They should be in the same location as the -SC2Data, Support, Support64, and Versions folders. +First, check the log file for issues (stored at `[Archipelago Directory]/logs/SC2Client.txt`). If you can't figure out +the log file, visit our [Discord's](https://discord.com/invite/8Z65BR2) tech-support channel for help. Please include a +specific description of what's going wrong and attach your log file to your message. ## Running in Linux