Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
 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()
Exemplo n.º 5
0
    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
Exemplo n.º 6
0
    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
Exemplo n.º 7
0
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")
Exemplo n.º 8
0
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"))
Exemplo n.º 9
0
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))
Exemplo n.º 10
0
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))
Exemplo n.º 11
0
 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.")
Exemplo n.º 12
0
    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)