Пример #1
0
    def get_raw_vspec(self):
        vspec_path = (self.versions_root / self.version_name /
                      "{}.json".format(self.version_name))
        if not self.version_manifest:
            if vspec_path.exists():
                logger.debug("Found custom vspec ({})".format(
                    self.version_name))
                with open(vspec_path) as fp:
                    return json.load(fp)
            else:
                die("Specified version ({}) not available".format(
                    self.version_name))
        url = self.version_manifest["url"]
        sha1 = self.version_manifest["sha1"]

        if vspec_path.exists() and file_sha1(vspec_path) == sha1:
            logger.debug(
                "Using cached vspec files, hash matches manifest ({})".format(
                    self.version_name))
            with open(vspec_path) as fp:
                return json.load(fp)

        try:
            logger.debug("Downloading vspec file")
            raw = requests.get(url).content
            vspec_path.parent.mkdir(parents=True, exist_ok=True)
            with open(vspec_path, "wb") as fp:
                fp.write(raw)
            j = json.loads(raw)
            return j
        except requests.ConnectionError:
            die("Failed to retrieve version json file. Check your internet connection."
                )
Пример #2
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))
Пример #3
0
 def get_raw_asset_index_nodl(self, id_):
     fpath = self.launcher.get_path(Directory.ASSET_INDEXES,
                                    "{}.json".format(id_))
     if fpath.exists():
         with open(fpath) as fp:
             return json.load(fp)
     else:
         die("Asset index specified in 'assets' not available.")
Пример #4
0
def rename(im, instance_name, new_name):
    """Rename an instance."""
    new_name = sanitize_name(new_name)
    if im.exists(instance_name):
        if im.exists(new_name):
            die("Instance with target name already exists.")
        im.rename(instance_name, new_name)
    else:
        die("No such instance exists.")
Пример #5
0
def install(pack_id, version, launcher, im, instance_name, use_beta):
    try:
        pack_manifest, version_manifest = resolve_pack_meta(pack_id, version, use_beta)
    except NotImplementedError as ex:
        die(ex)

    pack_name = pack_manifest["name"]
    pack_version = version_manifest["name"]

    if instance_name is None:
        instance_name = sanitize_name(f"{pack_name}-{pack_version}")

    if im.exists(instance_name):
        die("Instance {} already exists".format(instance_name))

    logger.info(f"Installing {pack_name} {pack_version} as {instance_name}")

    forge_version_name = None
    game_version = None
    for target in version_manifest["targets"]:
        if target["name"] == "forge":
            try:
                forge_version_name = forge.install(
                    versions_root=launcher.get_path(Directory.VERSIONS),
                    libraries_root=launcher.get_path(Directory.LIBRARIES),
                    forge_version=target["version"],
                )
            except forge.AlreadyInstalledError as ex:
                forge_version_name = ex.args[0]
        elif target["name"] == "minecraft":
            game_version = target["version"]
        else:
            logger.warn(f"Skipping unsupported target {target['name']}")

    inst_version = forge_version_name or game_version

    inst = im.create(instance_name, inst_version)
    inst.config["java.memory.max"] = str(version_manifest["specs"]["recommended"]) + "M"

    mcdir: Path = inst.get_minecraft_dir()
    dq = DownloadQueue()
    for f in version_manifest["files"]:
        filepath: Path = mcdir / PurePath(f["path"]) / f["name"]
        filepath.parent.mkdir(exist_ok=True, parents=True)
        dq.add(f["url"], filepath, f["size"])

    logger.info("Downloading modpack files")
    dq.download()

    logger.info(f"Installed successfully as {instance_name}")
Пример #6
0
 def get_raw_asset_index(self, asset_index_spec):
     iid = asset_index_spec["id"]
     url = asset_index_spec["url"]
     sha1 = asset_index_spec["sha1"]
     fpath = self.launcher.get_path(Directory.ASSET_INDEXES,
                                    "{}.json".format(iid))
     if fpath.exists() and file_sha1(fpath) == sha1:
         logger.debug("Using cached asset index, hash matches vspec")
         with open(fpath) as fp:
             return json.load(fp)
     try:
         logger.debug("Downloading new asset index")
         raw = requests.get(url).content
         with open(fpath, "wb") as fp:
             fp.write(raw)
         return json.loads(raw)
     except requests.ConnectionError:
         die("Failed to retrieve asset index.")
Пример #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")
Пример #8
0
def install_from_path(path, launcher, instance_manager, instance_name=None):
    if path.isascii() and path.isdecimal():
        path = resolve_project_id(path)
    elif os.path.exists(path):
        if path.endswith(".ccip"):
            path = resolve_ccip(path)
        elif path.endswith(".zip"):
            with open(path, "rb") as fd:
                return install_from_zip(fd, launcher, instance_manager,
                                        instance_name)
        else:
            die("File must be .ccip or .zip")

    zipurl = resolve_packurl(path)
    with requests.get(zipurl, stream=True) as r:
        r.raise_for_status()
        with TemporaryFile() as tempfile:
            for chunk in r.iter_content(chunk_size=8192):
                tempfile.write(chunk)
            install_from_zip(tempfile, launcher, instance_manager,
                             instance_name)
Пример #9
0
    def get_jarfile_dl(self, verify_hashes=False, force=False):
        """Checks existence and hash of cached jar. Returns None if ok, otherwise
        returns download (url, size)"""
        logger.debug("Attempting to use jarfile: {}".format(self.jarfile))
        dlspec = self.vspec.downloads.get("client", None)
        if dlspec is None:
            logger.debug("jarfile dlspec not availble, skipping hash check.")
            if not self.jarfile.exists():
                die("jarfile does not exist and can not be downloaded.")
            return

        logger.debug("Checking jarfile.")
        if (force or not self.jarfile.exists()
                # The fabric-installer places an empty jarfile here, due to some
                # quirk of an old (git blame 2 years) version of the vanilla launcher.
                # https://github.com/FabricMC/fabric-installer/blob/master/src/main/java/net/fabricmc/installer/client/ClientInstaller.java#L49
                or os.path.getsize(self.jarfile) == 0 or
            (verify_hashes and file_sha1(self.jarfile) != dlspec["sha1"])):
            logger.info(
                "Jar file ({}) will be downloaded with libraries.".format(
                    self.jarname))
            return dlspec["url"], dlspec.get("size", None)
Пример #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))
Пример #11
0
def install_from_zip(zipfileobj,
                     launcher,
                     instance_manager,
                     instance_name=None):
    with ZipFile(zipfileobj) as pack_zf:
        for fileinfo in pack_zf.infolist():
            fpath = PurePath(fileinfo.filename)
            if fpath.parts[-1] == "manifest.json" and len(fpath.parts) <= 2:
                manifest_zipinfo = fileinfo
                archive_prefix = fpath.parent
                break
        else:
            raise ValueError("Zip file does not contain manifest")

        with pack_zf.open(manifest_zipinfo) as fd:
            manifest = json.load(fd)

        assert manifest["manifestType"] == "minecraftModpack"
        assert manifest["manifestVersion"] == 1

        assert len(manifest["minecraft"]["modLoaders"]) == 1
        forge_ver = manifest["minecraft"]["modLoaders"][0]["id"]

        assert forge_ver.startswith(FORGE_PREFIX)
        forge_ver = forge_ver[len(FORGE_PREFIX):]
        packname = manifest["name"]
        packver = manifest["version"]
        if instance_name is None:
            instance_name = "{}-{}".format(sanitize_name(packname),
                                           sanitize_name(packver))
            logger.info(f"Installing {packname} version {packver}")
        else:
            logger.info(
                f"Installing {packname} version {packver} as instance {instance_name}"
            )

        if instance_manager.exists(instance_name):
            die("Instace {} already exists".format(instance_name))

        try:
            forge.install(
                versions_root=launcher.get_path(Directory.VERSIONS),
                libraries_root=launcher.get_path(Directory.LIBRARIES),
                forge_version=forge_ver,
            )
        except forge.AlreadyInstalledError:
            pass

        # Trusting the game version from the manifest may be a bad idea
        inst = instance_manager.create(
            instance_name,
            "{}-forge-{}".format(manifest["minecraft"]["version"], forge_ver),
        )
        # This is a random guess, but better than the vanilla 1G
        inst.config["java.memory.max"] = "4G"

        project_files = {
            mod["projectID"]: mod["fileID"]
            for mod in manifest["files"]
        }
        headers = {"User-Agent": "curl"}
        dq = DownloadQueue()

        logger.info("Retrieving mod metadata from curse")
        modcount = len(project_files)
        mcdir: Path = inst.get_minecraft_dir()
        moddir = mcdir / "mods"
        with tqdm(total=modcount) as tq:
            # Try to get as many file_infos as we can in one request
            # This endpoint only provides a few "latest" files for each project,
            # so it's not guaranteed that the response will contain the fileID
            # we are looking for. It's a gamble, but usually worth it in terms
            # of request count. The time benefit is not that great, as the endpoint
            # is slow.
            resp = requests.post(ADDON_URL,
                                 json=list(project_files.keys()),
                                 headers=headers)
            resp.raise_for_status()
            projects_meta = resp.json()
            for proj in projects_meta:
                proj_id = proj["id"]
                want_file = project_files[proj_id]
                for file_info in proj["latestFiles"]:
                    if want_file == file_info["id"]:
                        dq.add(
                            file_info["downloadUrl"],
                            moddir / file_info["fileName"],
                            size=file_info["fileLength"],
                        )
                        del project_files[proj_id]

            batch_recvd = modcount - len(project_files)
            logger.debug("Got {} batched".format(batch_recvd))
            tq.update(batch_recvd)

            with ThreadPoolExecutor(max_workers=16) as tpe:

                def dl(pid, fid):
                    resp = requests.get(GETINFO_URL.format(pid, fid),
                                        headers=headers)
                    resp.raise_for_status()
                    file_info = resp.json()
                    assert file_info["id"] == fid
                    dq.add(
                        file_info["downloadUrl"],
                        moddir / file_info["fileName"],
                        size=file_info["fileLength"],
                    )

                # Get remaining individually
                futmap = {}
                for pid, fid in project_files.items():
                    fut = tpe.submit(dl, pid, fid)
                    futmap[fut] = (pid, fid)

                for fut in concurrent.futures.as_completed(futmap.keys()):
                    try:
                        fut.result()
                    except Exception as ex:
                        pid, fid = futmap[fut]
                        logger.error(
                            "Could not get metadata for {}/{}: {}".format(
                                pid, fid, ex))
                    else:
                        tq.update(1)

        logger.info("Downloading mod jars")
        dq.download()

        logger.info("Copying overrides")
        overrides = archive_prefix / manifest["overrides"]
        for fileinfo in pack_zf.infolist():
            if fileinfo.is_dir():
                continue
            fname = fileinfo.filename
            try:
                outpath = mcdir / PurePath(fname).relative_to(overrides)
            except ValueError:
                continue
            if not outpath.parent.exists():
                outpath.parent.mkdir(parents=True, exist_ok=True)
            with pack_zf.open(fileinfo) as infile, open(outpath,
                                                        "wb") as outfile:
                shutil.copyfileobj(infile, outfile)

        logger.info("Done installing {}".format(instance_name))
Пример #12
0
def extract_natives(im, instance_name):
    """Extract natives and leave them on disk."""
    if not im.exists(instance_name):
        die("No such instance exists.")
    inst = im.get(instance_name)
    inst.extract_natives()
Пример #13
0
def config_cli(ctx, im, instance_name):
    """Configure an instance."""
    if im.exists(instance_name):
        ctx.obj = im.get(instance_name).config
    else:
        die("No such instance exists.")