def download_assets(self, verify_hashes=False, force=False): """Downloads missing assets.""" hashes = dict() for obj in self.raw_asset_index["objects"].values(): hashes[obj["hash"]] = obj["size"] logger.info("Checking {} assets.".format(len(hashes))) is_virtual = self.raw_asset_index.get("virtual", False) fileset = set(recur_files(self.assets_root)) q = DownloadQueue() objpath = self.launcher.get_path(Directory.ASSET_OBJECTS) for sha in hashes: abspath = objpath / sha[0:2] / sha ok = abspath in fileset # file exists if verify_hashes: ok = ok and file_sha1(abspath) == sha if force or not ok: url = urllib.parse.urljoin(self.ASSETS_URL, posixpath.join(sha[0:2], sha)) q.add(url, abspath, size=hashes[sha]) if len(q) > 0: logger.info("Downloading {} assets.".format(len(q))) if not q.download(): logger.warning("Some assets failed to download.") if is_virtual: logger.info("Copying virtual assets") where = self.get_virtual_asset_path() logger.debug("Virtual asset path: {}".format(where)) self._populate_virtual_assets(self.raw_asset_index, where)
def make_base_vspec(ctx: ForgeInstallContext): vi = ctx.version_info vspec = {} for key in [ "arguments", "minecraftArguments", "inheritsFrom", "type", "releaseTime", "time", "mainClass", ]: if key in vi: vspec[key] = vi[key] vspec["id"] = ctx.version_name if "inheritsFrom" in vi: vspec["jar"] = vi["inheritsFrom"] # Prevent vanilla jar duplication else: # This is the case for som really old forge versions, before the # launcher supported inheritsFrom. Libraries should also be filtered # in this case, as they contain everything from the vanilla vspec as well. # TODO logger.warning( "Support for this version of Forge is not epic yet. Problems may arise." ) vspec["jar"] = ctx.game_version vspec["inheritsFrom"] = ctx.game_version vspec["libraries"] = vi["libraries"] return vspec
def match_rule(rule, java_info): # This launcher currently does not support any of the extended # features, which currently include at least: # - is_demo_user # - has_custom_resolution # It is not clear whether an `os` and `features` matcher may # be present simultaneously - assuming not. if "features" in rule: return False if "os" in rule: os_version = osinfo.get_os_version(java_info) osmatch = True if "name" in rule["os"]: osmatch = osmatch and rule["os"]["name"] == osinfo.platform if "arch" in rule["os"]: osmatch = osmatch and re.match(rule["os"]["arch"], osinfo.arch) if "version" in rule["os"]: osmatch = osmatch and re.match(rule["os"]["version"], os_version) return osmatch if len(rule) > 1: logger.warning("Not matching unknown rule {}".format(rule.keys())) return False return True
def cancel(self, tq, tpe): tq.close() logger.warning("Stopping downloader threads.") self.stop_event.set() tpe.shutdown() for fut in self.fut_to_url: fut.cancel()
def _ms_oauth(self): data = {"client_id": CLIENT_ID, "scope": SCOPE} resp = requests.post(URL_DEVICE_AUTH, data) resp.raise_for_status() j = resp.json() device_code = j["device_code"] msg = j["message"] user_code = j["user_code"] link = j["verification_uri"] msg = msg.replace( user_code, colorama.Fore.RED + user_code + colorama.Fore.RESET).replace( link, colorama.Style.BRIGHT + link + colorama.Style.NORMAL) logger.info(msg) data = { "code": device_code, "grant_type": GRANT_TYPE, "client_id": CLIENT_ID } first = True while True: if first: input("Press enter to continue... ") else: input("Press enter to try again... ") first = False resp = requests.post(URL_TOKEN, data) if resp.status_code == 400: j = resp.json() logger.debug(j) if j["error"] == "authorization_pending": logger.warning(j["error_description"]) logger.info(msg) continue else: raise AuthenticationError(j["error_description"]) resp.raise_for_status() j = resp.json() break access_token = j["access_token"] refresh_token = j["refresh_token"] logger.debug("OAuth device code flow successful") return access_token, refresh_token
def _populate(self): js = self.json_lib self.descriptor = js["name"] self.is_native = "natives" in js self.is_classpath = not (self.is_native or js.get("presenceOnly", False)) self.base_url = js.get("url", Library.MOJANG_BASE_URL) self.available = True self.native_classifier = None if self.is_native: try: classifier_tmpl = self.json_lib["natives"][osinfo.platform] arch = architecture()[0][:2] self.native_classifier = Template(classifier_tmpl).substitute( arch=arch) self.descriptor = self.descriptor + ":" + self.native_classifier except KeyError: logger.warning(f"Native {self.descriptor} is not available " f"for current platform {osinfo.platform}.") self.available = False return self.virt_artifact = Artifact.make(self.descriptor) self.virt_artifact.url = urllib.parse.urljoin( self.base_url, self.virt_artifact.path.as_posix()) self.artifact = self.resolve_artifact() # Just use filename and path derived from the name. self.filename = self.virt_artifact.filename self.path = self.virt_artifact.path if self.artifact: final_art = self.artifact # Sanity check if self.artifact.path is not None: assert self.virt_artifact.path == self.artifact.path else: final_art = self.virt_artifact self.url = final_art.url self.sha1 = final_art.sha1 self.size = final_art.size
def jar(version, which, output): """Download the file and save.""" dlspec = version.vspec.downloads.get(which, None) if not dlspec: die("No such dlspec exists for version {}".format( version.version_name)) url = dlspec["url"] sha1 = dlspec["sha1"] ext = posixpath.basename(urllib.parse.urlsplit(url).path).split(".")[-1] if output is None: output = "{}_{}.{}".format(version.version_name, which, ext) if os.path.exists(output): die("Refusing to overwrite {}".format(output)) logger.info("Hash (sha1) should be {}".format(sha1)) logger.info("Downloading the {} file and saving to {}".format( which, output)) urllib.request.urlretrieve(dlspec["url"], output) if file_sha1(output) != sha1: logger.warning("Hash of downloaded file does not match")
def get_appdata(): # If the used Python installation comes from the Microsoft Store, it is # subject to path redirection for the AppData folder. The paths then passed # to java are invalid. Instead figure out the real location on disk and use # that. Another option would be to use a completely different location # for all files, not sure which solution is better. # https://docs.microsoft.com/en-us/windows/msix/desktop/desktop-to-uwp-behind-the-scenes # HACK: This check is relatively fragile if "WindowsApps\\PythonSoftwareFoundation" in sys.base_exec_prefix: logger.warning( "Detected Microsoft Store Python distribution. " "It is recommended to install Python using the official installer " "or a package manager like Chocolatey.") appdata = get_appdata_uwp() logger.warning( "Using redirected AppData directory: {}".format(appdata)) return Path(appdata) else: return Path(os.getenv("APPDATA"))
def assert_java(java, wanted): try: jinfo = get_java_info(java) bitness = jinfo.get("sun.arch.data.model", None) if bitness and bitness != "64": logger.warning( "You are not using 64-bit java. Things will probably not work." ) logger.info("Using java version: {} ({})".format( jinfo["java.version"], jinfo["java.vm.name"])) if not check_version_against(jinfo["java.version"], wanted): logger.warning("The version of Minecraft you are launching " "uses java {} by default.".format( wanted_to_str(wanted))) logger.warning( "You may experience issues, especially with older versions of Minecraft." ) major = get_major_version(jinfo["java.version"]) if int(major) < wanted["majorVersion"]: logger.error( "Note that at least java {} is required to launch at all.". format(wanted_to_str(wanted))) return jinfo except FileNotFoundError: die("Could not execute java at: {}. Have you installed it? Is it in yout PATH?" .format(java))
def assert_java(java): try: jinfo = get_java_info(java) badjv = not jinfo["java.version"].startswith("1.8.0") bitness = jinfo.get("sun.arch.data.model", None) if bitness and bitness != "64": logger.warning( "You are not using 64-bit java. Things will probably not work." ) logger.info("Using java version: {} ({})".format( jinfo["java.version"], jinfo["java.vm.name"])) if badjv: logger.warning( "Minecraft uses java 1.8.0 by default." " You may experience issues, especially with older versions of Minecraft." ) return jinfo except FileNotFoundError: die("Could not execute java at: {}. Have you installed it? Is it in yout PATH?" .format(java))
def get_manifest(self): manifest_filepath = self.launcher.get_path(Directory.VERSIONS, "manifest.json") try: m = requests.get(self.MANIFEST_URL).json() with open(manifest_filepath, "w") as mfile: json.dump(m, mfile, indent=4, sort_keys=True) return m except requests.ConnectionError: logger.warning("Failed to retrieve version_manifest. " "Check your internet connection.") try: with open(manifest_filepath) as mfile: logger.warning("Using cached version_manifest.") return json.load(mfile) except FileNotFoundError: logger.warning("Cached version manifest not available.") raise RuntimeError("Failed to retrieve version manifest.")
def _exec_mc(self, account, v, java, java_info, gamedir, libraries, natives, verify_hashes): libs = [lib.get_abspath(self.libraries_root) for lib in libraries] libs.append(v.jarfile) classpath = join_classpath(*libs) version_type, user_type = (("picomc", "mojang") if account.online else ("picomc/offline", "offline")) mc = v.vspec.mainClass if hasattr(v.vspec, "minecraftArguments"): mcargs = shlex.split(v.vspec.minecraftArguments) sjvmargs = [ "-Djava.library.path={}".format(natives), "-cp", classpath ] elif hasattr(v.vspec, "arguments"): mcargs, jvmargs = process_arguments(v.vspec.arguments, java_info) sjvmargs = [] for a in jvmargs: tmpl = Template(a) res = tmpl.substitute( natives_directory=natives, launcher_name="picomc", launcher_version=picomc.__version__, classpath=classpath, ) sjvmargs.append(res) try: account.refresh() except requests.exceptions.ConnectionError: logger.warning( "Failed to refresh account due to a connectivity error. Continuing." ) smcargs = [] for a in mcargs: tmpl = Template(a) res = tmpl.substitute( auth_player_name=account.gname, auth_uuid=account.uuid, auth_access_token=account.access_token, # Only used in old versions. auth_session="token:{}:{}".format(account.access_token, account.uuid), user_type=user_type, user_properties={}, version_type=version_type, version_name=v.version_name, game_directory=gamedir, assets_root=self.assets_root, assets_index_name=v.vspec.assets, game_assets=v.get_virtual_asset_path(), ) smcargs.append(res) my_jvm_args = [ "-Xms{}".format(self.config["java.memory.min"]), "-Xmx{}".format(self.config["java.memory.max"]), ] if verify_hashes: my_jvm_args.append("-Dpicomc.verify=true") my_jvm_args += shlex.split(self.config["java.jvmargs"]) fargs = [java] + sjvmargs + my_jvm_args + [mc] + smcargs if logging.debug: logger.debug("Launching: " + shlex.join(fargs)) else: logger.info("Launching the game") subprocess.run(fargs, cwd=gamedir)