Skip to content

Commit

Permalink
Merge branch 'mod'
Browse files Browse the repository at this point in the history
  • Loading branch information
sammko committed Jul 15, 2020
2 parents d73d88d + 91c4b36 commit ebdd8bd
Show file tree
Hide file tree
Showing 9 changed files with 665 additions and 68 deletions.
2 changes: 2 additions & 0 deletions picomc/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
from .config import register_config_cli
from .instance import register_instance_cli
from .main import picomc_cli
from .mod import register_mod_cli
from .version import register_version_cli

register_account_cli(picomc_cli)
register_version_cli(picomc_cli)
register_instance_cli(picomc_cli)
register_config_cli(picomc_cli)
register_mod_cli(picomc_cli)
52 changes: 52 additions & 0 deletions picomc/cli/mod.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from pathlib import Path

import click

from picomc import mod
from picomc.env import get_filepath


@click.group()
def mod_cli():
"""Helpers to install modded Minecraft."""
pass


def list_loaders(ctx, param, value):
if not value or ctx.resilient_parsing:
return
for loader in mod.LOADERS:
print(loader._loader_name)
ctx.exit()


@mod_cli.group("loader")
@click.option(
"--list",
"-l",
is_eager=True,
is_flag=True,
expose_value=False,
callback=list_loaders,
help="List available mod loaders",
)
@click.pass_context
def loader_cli(ctx):
"""Manage mod loaders.
Loaders are customized Minecraft
versions which can load other mods, e.g. Forge or Fabric.
Installing a loader creates a new version which can be used by instances."""
ctx.ensure_object(dict)

ctx.obj["VERSIONS_ROOT"] = Path(get_filepath("versions"))
ctx.obj["LIBRARIES_ROOT"] = Path(get_filepath("libraries"))
pass


for loader in mod.LOADERS:
loader.register_cli(loader_cli)


def register_mod_cli(picomc_cli):
picomc_cli.add_command(mod_cli, name="mod")
14 changes: 8 additions & 6 deletions picomc/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,13 @@ def launch(self, account, version=None, verify_hashes=False):
self, filter(attrgetter("is_native"), libraries)
) as natives_dir:
self._exec_mc(
account, vobj, java, java_info, gamedir, libraries, natives_dir
account,
vobj,
java,
java_info,
gamedir,
filter(attrgetter("is_classpath"), libraries),
natives_dir,
)

def extract_natives(self):
Expand All @@ -131,11 +137,7 @@ def extract_natives(self):
logger.info("Extracted natives to {}".format(ne.get_natives_path()))

def _exec_mc(self, account, v, java, java_info, gamedir, libraries, natives):
libs = [
lib.get_abspath(get_filepath("libraries"))
for lib in libraries
if not lib.is_native
]
libs = [lib.get_abspath(get_filepath("libraries")) for lib in libraries]
libs.append(v.jarfile)
classpath = join_classpath(*libs)

Expand Down
91 changes: 52 additions & 39 deletions picomc/library.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
import urllib.parse
from dataclasses import dataclass
from pathlib import Path, PurePosixPath
from platform import architecture
from string import Template

Expand All @@ -9,23 +9,48 @@


@dataclass
class LibraryArtifact:
class Artifact:
url: str
path: str
path: PurePosixPath
sha1: str
size: int
filename: str

@classmethod
def from_json(cls, obj):
path = None
if "path" in obj:
path = PurePosixPath(obj["path"])
filename = None
if path:
filename = path.name
return cls(
url=obj["url"],
path=obj["path"],
url=obj.get("url", None),
path=path,
sha1=obj["sha1"],
size=obj["size"],
filename=None,
filename=filename,
)

@classmethod
def make(cls, descriptor):
descriptor, *ext = descriptor.split("@")
ext = ext[0] if ext else "jar"
group, art_id, version, *class_ = descriptor.split(":")
classifier = None
if class_:
classifier = class_[0]
group = group.replace(".", "/")
v2 = "-".join([version] + ([classifier] if classifier else []))

filename = f"{art_id}-{v2}.{ext}"
path = PurePosixPath(group) / art_id / version / filename

return cls(url=None, path=path, sha1=None, size=None, filename=filename)

def get_localpath(self, base):
return Path(base) / self.path


class Library:
MOJANG_BASE_URL = "https://libraries.minecraft.net/"
Expand All @@ -36,80 +61,68 @@ def __init__(self, json_lib):

def _populate(self):
js = self.json_lib
self.libname = js["name"]
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_suffix = ""
self.native_classifier = None
if self.is_native:
try:
classifier_tmpl = self.json_lib["natives"][Env.platform]
arch = architecture()[0][:2]
self.native_classifier = Template(classifier_tmpl).substitute(arch=arch)
self.native_suffix = "-" + self.native_classifier
self.descriptor = self.descriptor + ":" + self.native_classifier
except KeyError:
logger.warning(
f"Native {self.libname} is not available for current platform {Env.platform}."
f"Native {self.descriptor} is not available for current platform {Env.platform}."
)
self.native_classifier = None
self.available = False
return

self.virt_artifact = self.make_virtual_artifact()
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.url
self.filename = self.virt_artifact.filename
self.path = self.virt_artifact.path

# Actual fs path
self.relpath = os.path.join(*self.path.split("/"))

if self.artifact:
final_art = self.artifact

# Sanity check
assert self.virt_artifact.path == self.artifact.path
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 make_virtual_artifact(self):
# I don't know where the *va part comes from, it was already implemented
# before a refactor, unfortunately the reason was not documented.
# Currently I don't have a version in my versions directory which
# utilizes that.
# I am leaving it implemented, as there probably was motivation to do it.
group, art_id, version, *va = self.libname.split(":")
group = group.replace(".", "/")
v2 = "-".join([version] + va)

filename = f"{art_id}-{v2}{self.native_suffix}.jar"
path = f"{group}/{art_id}/{version}/{filename}"
url = urllib.parse.urljoin(self.base_url, path)

return LibraryArtifact(
url=url, path=path, sha1=None, size=None, filename=filename
)

def resolve_artifact(self):
if self.is_native:
if self.native_classifier is None:
# Native not available for current platform
return None
else:
art = self.json_lib["downloads"]["classifiers"][self.native_classifier]
return LibraryArtifact.from_json(art)
try:
art = self.json_lib["downloads"]["classifiers"][
self.native_classifier
]
return Artifact.from_json(art)
except KeyError:
return None
else:
try:
return LibraryArtifact.from_json(self.json_lib["downloads"]["artifact"])
return Artifact.from_json(self.json_lib["downloads"]["artifact"])
except KeyError:
return None

def get_abspath(self, library_root):
return os.path.join(library_root, self.relpath)
return self.virt_artifact.get_localpath(library_root)
3 changes: 3 additions & 0 deletions picomc/mod/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import fabric, forge

LOADERS = [fabric, forge]
Loading

0 comments on commit ebdd8bd

Please sign in to comment.