예제 #1
0
def _dir(launcher, im, instance_name):
    """Print root directory of instance."""
    if not instance_name:
        # TODO
        print(launcher.get_path(Directory.INSTANCES))
    else:
        instance_name = sanitize_name(instance_name)
        print(im.get_root(instance_name))
예제 #2
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.")
예제 #3
0
    def __init__(self, launcher, root, name):
        self.instance_manager = launcher.instance_manager
        self.launcher = launcher

        self.name = sanitize_name(name)
        self.libraries_root = self.launcher.get_path(Directory.LIBRARIES)
        self.assets_root = self.launcher.get_path(Directory.ASSETS)
        self.directory = root
        self.config = self.launcher.config_manager.get_instance_config(
            Path("instances", Path(self.name), "config.json"))
예제 #4
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}")
예제 #5
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))
예제 #6
0
 def inner(*args, instance_name, **kwargs):
     return fn(*args, instance_name=sanitize_name(instance_name), **kwargs)