158 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			158 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								import os
							 | 
						||
| 
								 | 
							
								import platform
							 | 
						||
| 
								 | 
							
								import re
							 | 
						||
| 
								 | 
							
								import sys
							 | 
						||
| 
								 | 
							
								from contextlib import suppress
							 | 
						||
| 
								 | 
							
								from pathlib import Path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from worlds._sc2common.bot import logger
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from . import wsl
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								BASEDIR = {
							 | 
						||
| 
								 | 
							
								    "Windows": "C:/Program Files (x86)/StarCraft II",
							 | 
						||
| 
								 | 
							
								    "WSL1": "/mnt/c/Program Files (x86)/StarCraft II",
							 | 
						||
| 
								 | 
							
								    "WSL2": "/mnt/c/Program Files (x86)/StarCraft II",
							 | 
						||
| 
								 | 
							
								    "Darwin": "/Applications/StarCraft II",
							 | 
						||
| 
								 | 
							
								    "Linux": "~/StarCraftII",
							 | 
						||
| 
								 | 
							
								    "WineLinux": "~/.wine/drive_c/Program Files (x86)/StarCraft II",
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								USERPATH = {
							 | 
						||
| 
								 | 
							
								    "Windows": "Documents\\StarCraft II\\ExecuteInfo.txt",
							 | 
						||
| 
								 | 
							
								    "WSL1": "Documents/StarCraft II/ExecuteInfo.txt",
							 | 
						||
| 
								 | 
							
								    "WSL2": "Documents/StarCraft II/ExecuteInfo.txt",
							 | 
						||
| 
								 | 
							
								    "Darwin": "Library/Application Support/Blizzard/StarCraft II/ExecuteInfo.txt",
							 | 
						||
| 
								 | 
							
								    "Linux": None,
							 | 
						||
| 
								 | 
							
								    "WineLinux": None,
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								BINPATH = {
							 | 
						||
| 
								 | 
							
								    "Windows": "SC2_x64.exe",
							 | 
						||
| 
								 | 
							
								    "WSL1": "SC2_x64.exe",
							 | 
						||
| 
								 | 
							
								    "WSL2": "SC2_x64.exe",
							 | 
						||
| 
								 | 
							
								    "Darwin": "SC2.app/Contents/MacOS/SC2",
							 | 
						||
| 
								 | 
							
								    "Linux": "SC2_x64",
							 | 
						||
| 
								 | 
							
								    "WineLinux": "SC2_x64.exe",
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								CWD = {
							 | 
						||
| 
								 | 
							
								    "Windows": "Support64",
							 | 
						||
| 
								 | 
							
								    "WSL1": "Support64",
							 | 
						||
| 
								 | 
							
								    "WSL2": "Support64",
							 | 
						||
| 
								 | 
							
								    "Darwin": None,
							 | 
						||
| 
								 | 
							
								    "Linux": None,
							 | 
						||
| 
								 | 
							
								    "WineLinux": "Support64",
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def platform_detect():
							 | 
						||
| 
								 | 
							
								    pf = os.environ.get("SC2PF", platform.system())
							 | 
						||
| 
								 | 
							
								    if pf == "Linux":
							 | 
						||
| 
								 | 
							
								        return wsl.detect() or pf
							 | 
						||
| 
								 | 
							
								    return pf
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								PF = platform_detect()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def get_home():
							 | 
						||
| 
								 | 
							
								    """Get home directory of user, using Windows home directory for WSL."""
							 | 
						||
| 
								 | 
							
								    if PF in {"WSL1", "WSL2"}:
							 | 
						||
| 
								 | 
							
								        return wsl.get_wsl_home() or Path.home().expanduser()
							 | 
						||
| 
								 | 
							
								    return Path.home().expanduser()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def get_user_sc2_install():
							 | 
						||
| 
								 | 
							
								    """Attempts to find a user's SC2 install if their OS has ExecuteInfo.txt"""
							 | 
						||
| 
								 | 
							
								    if USERPATH[PF]:
							 | 
						||
| 
								 | 
							
								        einfo = str(get_home() / Path(USERPATH[PF]))
							 | 
						||
| 
								 | 
							
								        if os.path.isfile(einfo):
							 | 
						||
| 
								 | 
							
								            with open(einfo) as f:
							 | 
						||
| 
								 | 
							
								                content = f.read()
							 | 
						||
| 
								 | 
							
								            if content:
							 | 
						||
| 
								 | 
							
								                base = re.search(r" = (.*)Versions", content).group(1)
							 | 
						||
| 
								 | 
							
								                if PF in {"WSL1", "WSL2"}:
							 | 
						||
| 
								 | 
							
								                    base = str(wsl.win_path_to_wsl_path(base))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if os.path.exists(base):
							 | 
						||
| 
								 | 
							
								                    return base
							 | 
						||
| 
								 | 
							
								    return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def get_env():
							 | 
						||
| 
								 | 
							
								    # TODO: Linux env conf from: https://github.com/deepmind/pysc2/blob/master/pysc2/run_configs/platforms.py
							 | 
						||
| 
								 | 
							
								    return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def get_runner_args(cwd):
							 | 
						||
| 
								 | 
							
								    if "WINE" in os.environ:
							 | 
						||
| 
								 | 
							
								        runner_file = Path(os.environ.get("WINE"))
							 | 
						||
| 
								 | 
							
								        runner_file = runner_file if runner_file.is_file() else runner_file / "wine"
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        TODO Is converting linux path really necessary?
							 | 
						||
| 
								 | 
							
								        That would convert
							 | 
						||
| 
								 | 
							
								        '/home/burny/Games/battlenet/drive_c/Program Files (x86)/StarCraft II/Support64'
							 | 
						||
| 
								 | 
							
								        to
							 | 
						||
| 
								 | 
							
								        'Z:\\home\\burny\\Games\\battlenet\\drive_c\\Program Files (x86)\\StarCraft II\\Support64'
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return [runner_file, "start", "/d", cwd, "/unix"]
							 | 
						||
| 
								 | 
							
								    return []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def latest_executeble(versions_dir, base_build=None):
							 | 
						||
| 
								 | 
							
								    latest = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if base_build is not None:
							 | 
						||
| 
								 | 
							
								        with suppress(ValueError):
							 | 
						||
| 
								 | 
							
								            latest = (
							 | 
						||
| 
								 | 
							
								                int(base_build[4:]),
							 | 
						||
| 
								 | 
							
								                max(p for p in versions_dir.iterdir() if p.is_dir() and p.name.startswith(str(base_build))),
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if base_build is None or latest is None:
							 | 
						||
| 
								 | 
							
								        latest = max((int(p.name[4:]), p) for p in versions_dir.iterdir() if p.is_dir() and p.name.startswith("Base"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    version, path = latest
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if version < 55958:
							 | 
						||
| 
								 | 
							
								        logger.critical("Your SC2 binary is too old. Upgrade to 3.16.1 or newer.")
							 | 
						||
| 
								 | 
							
								        sys.exit(1)
							 | 
						||
| 
								 | 
							
								    return path / BINPATH[PF]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _MetaPaths(type):
							 | 
						||
| 
								 | 
							
								    """"Lazily loads paths to allow importing the library even if SC2 isn't installed."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # pylint: disable=C0203
							 | 
						||
| 
								 | 
							
								    def __setup(self):
							 | 
						||
| 
								 | 
							
								        if PF not in BASEDIR:
							 | 
						||
| 
								 | 
							
								            logger.critical(f"Unsupported platform '{PF}'")
							 | 
						||
| 
								 | 
							
								            sys.exit(1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            base = os.environ.get("SC2PATH") or get_user_sc2_install() or BASEDIR[PF]
							 | 
						||
| 
								 | 
							
								            self.BASE = Path(base).expanduser()
							 | 
						||
| 
								 | 
							
								            self.EXECUTABLE = latest_executeble(self.BASE / "Versions")
							 | 
						||
| 
								 | 
							
								            self.CWD = self.BASE / CWD[PF] if CWD[PF] else None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            self.REPLAYS = self.BASE / "Replays"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (self.BASE / "maps").exists():
							 | 
						||
| 
								 | 
							
								                self.MAPS = self.BASE / "maps"
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                self.MAPS = self.BASE / "Maps"
							 | 
						||
| 
								 | 
							
								        except FileNotFoundError as e:
							 | 
						||
| 
								 | 
							
								            logger.critical(f"SC2 installation not found: File '{e.filename}' does not exist.")
							 | 
						||
| 
								 | 
							
								            sys.exit(1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # pylint: disable=C0203
							 | 
						||
| 
								 | 
							
								    def __getattr__(self, attr):
							 | 
						||
| 
								 | 
							
								        # pylint: disable=E1120
							 | 
						||
| 
								 | 
							
								        self.__setup()
							 | 
						||
| 
								 | 
							
								        return getattr(self, attr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class Paths(metaclass=_MetaPaths):
							 | 
						||
| 
								 | 
							
								    """Paths for SC2 folders, lazily loaded using the above metaclass."""
							 |