Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version 4.4.0 #224

Merged
merged 15 commits into from
Jul 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ by the LWJGL-provided natives).
#### Miscellaneous
With `--dry`, the start command does not start the game, but simply installs it.

With `--demo` you can enable the [demo mode](https://minecraft.gamepedia.com/Demo_mode)
With `--demo` you can enable the [demo mode](https://minecraft.wiki/w/Demo_mode)
of the game.

With `--resolution <width>x<height>` you can change the resolution of the game window.
Expand Down
144 changes: 72 additions & 72 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion portablemc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"""

LAUNCHER_NAME = "portablemc"
LAUNCHER_VERSION = "4.3.0"
LAUNCHER_VERSION = "4.4.0"
LAUNCHER_AUTHORS = ["Théo Rozier <contact@theorozier.fr>", "Contributors"]
LAUNCHER_COPYRIGHT = "PortableMC Copyright (C) 2021-2024 Théo Rozier"
LAUNCHER_URL = "https://github.com/mindstorm38/portablemc"
33 changes: 20 additions & 13 deletions portablemc/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

from portablemc.fabric import FabricVersion, FabricResolveEvent
from portablemc.forge import ForgeVersion, ForgeResolveEvent, ForgePostProcessingEvent, \
ForgePostProcessedEvent, ForgeInstallError, _FORGE_REPO, _NEO_FORGE_REPO
ForgePostProcessedEvent, ForgeInstallError, _NeoForgeVersion

from typing import cast, Optional, List, Union, Dict, Callable, Any, Tuple

Expand Down Expand Up @@ -281,9 +281,9 @@ def cmd_search_handler(ns: SearchNs, kind: str, table: OutputTable):
if search is None or search in alias:
table.add(alias, version)

elif kind in ("fabric", "quilt", "legacyfabric"):
elif kind in ("fabric", "quilt", "legacyfabric", "babric"):

from ..fabric import FABRIC_API, QUILT_API, LEGACYFABRIC_API
from ..fabric import FABRIC_API, QUILT_API, LEGACYFABRIC_API, BABRIC_API

table.add(_("search.loader_version"), _("search.flags"))
table.separator()
Expand All @@ -292,8 +292,10 @@ def cmd_search_handler(ns: SearchNs, kind: str, table: OutputTable):
api = FABRIC_API
elif kind == "quilt":
api = QUILT_API
else:
elif kind == "legacyfabric":
api = LEGACYFABRIC_API
elif kind == "babric":
api = BABRIC_API

for loader in api._request_loaders():
if search is None or search in loader.version:
Expand Down Expand Up @@ -458,25 +460,31 @@ def cmd_start_handler(ns: StartNs, kind: str, parts: List[str]) -> Optional[Vers
return None
return Version(version, context=ns.context)

elif kind in ("fabric", "quilt", "legacyfabric"):
elif kind in ("fabric", "quilt", "legacyfabric", "babric"):

if len(parts) > 2:
return None

# Legacy fabric has a special case because it will never be supported for
# versions past 1.13.2, it is not made for latest release version.
if kind == "legacyfabric" and version == "release":
version = "1.13.2"
if version == "release":
if kind == "legacyfabric":
version = "1.13.2"
elif kind == "babric":
version = "b1.7.3"

if kind == "fabric":
constructor = FabricVersion.with_fabric
prefix = ns.fabric_prefix
elif kind == "quilt":
constructor = FabricVersion.with_quilt
prefix = ns.quilt_prefix
else:
elif kind == "legacyfabric":
constructor = FabricVersion._with_legacyfabric
prefix = ns.legacyfabric_prefix
elif kind == "babric":
constructor = FabricVersion._with_babric
prefix = ns.babric_prefix

if len(parts) != 2:
ns.socket_error_tips.append(f"{kind}_loader_version")
Expand All @@ -486,9 +494,9 @@ def cmd_start_handler(ns: StartNs, kind: str, parts: List[str]) -> Optional[Vers
elif kind in ("forge", "neoforge"):
if len(parts) != 1:
return None
repo = _FORGE_REPO if kind == "forge" else _NEO_FORGE_REPO
constructor = ForgeVersion if kind == "forge" else _NeoForgeVersion
prefix = ns.forge_prefix if kind == "forge" else ns.neoforge_prefix
return ForgeVersion(version, context=ns.context, prefix=prefix, _forge_repo=repo)
return constructor(version, context=ns.context, prefix=prefix)

else:
return None
Expand Down Expand Up @@ -822,11 +830,10 @@ def fabric_resolve(e: FabricResolveEvent) -> None:
ns.out.finish()

def forge_resolve(e: ForgeResolveEvent) -> None:
api = "forge" if e._forge_repo == _FORGE_REPO else "neoforge"
if e.alias:
ns.out.task("..", "start.forge.resolving", api=api, version=e.forge_version)
ns.out.task("..", "start.forge.resolving", api=e._api, version=e.forge_version)
else:
ns.out.task("OK", "start.forge.resolved", api=api, version=e.forge_version)
ns.out.task("OK", "start.forge.resolved", api=e._api, version=e.forge_version)
ns.out.finish()

super().__init__({
Expand Down
9 changes: 7 additions & 2 deletions portablemc/cli/lang.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ def get(key: str, **kwargs) -> str:
"args._":
" A fast, reliable and cross-platform command-line Minecraft launcher and API\n"
" for developers. Including fast and easy installation of common mod loaders such\n"
" as Fabric, LegacyFabric, Forge, NeoForge and Quilt. This launcher is compatible\n"
" with the standard Minecraft directories.\n\n",
" as Fabric, LegacyFabric, Forge, NeoForge, Quilt and Babric. This launcher is\n"
" compatible with the standard Minecraft directories.\n\n",
"args.main_dir": "Set the main directory where libraries, assets and versions.",
"args.work_dir": "Set the working directory where the game run and place for examples "
"saves, screenshots (and resources for legacy versions), it also store "
Expand Down Expand Up @@ -74,6 +74,7 @@ def get(key: str, **kwargs) -> str:
"args.search.kind.comp.fabric": "Search for Fabric versions.",
"args.search.kind.comp.legacyfabric": "Search for LegacyFabric versions.",
"args.search.kind.comp.quilt": "Search for Quilt versions.",
"args.search.kind.comp.babric": "Search for Babric versions.",
"args.search.input": "Search input.",
"args.search.input.comp.release": "Resolve version of the latest release.",
"args.search.input.comp.snapshot": "Resolve version of the latest snapshot.",
Expand All @@ -84,13 +85,15 @@ def get(key: str, **kwargs) -> str:
"args.start.version.fabric": "fabric:[<vanilla-version>[:<loader-version>]]",
"args.start.version.legacyfabric": "legacyfabric:[<vanilla-version>[:<loader-version>]]",
"args.start.version.quilt": "quilt:[<vanilla-version>[:<loader-version>]]",
"args.start.version.babric": "babric::[<loader-version>]",
"args.start.version.forge": "forge:[<forge-version>] (forge-version >= 1.5.2)",
"args.start.version.neoforge": "neoforge:[<neoforge-version>] (neoforge-version >= 1.20.1)",
"args.start.version.comp.release": "Start the latest release (default).",
"args.start.version.comp.snapshot": "Start the latest snapshot.",
"args.start.version.comp.fabric": "Start Fabric mod loader with latest release.",
"args.start.version.comp.legacyfabric": "Start LegacyFabric mod loader with latest release.",
"args.start.version.comp.quilt": "Start Quilt mod loader with latest release.",
"args.start.version.comp.babric": "Start Babric mod loader with beta 1.7.3.",
"args.start.version.comp.forge": "Start Forge mod loader with latest release.",
"args.start.version.comp.neoforge": "Start NeoForge mod loader with latest release.",
"args.start.dry": "Simulate game starting.",
Expand All @@ -106,6 +109,7 @@ def get(key: str, **kwargs) -> str:
"enabled by default.",
"args.start.fabric_prefix": "Change the prefix of the version ID when starting with Fabric (<prefix>-<vanilla-version>-<loader-version>).",
"args.start.legacyfabric_prefix": "Change the prefix of the version ID when starting with LegacyFabric (<prefix>-<vanilla-version>-<loader-version>).",
"args.start.babric_prefix": "Change the prefix of the version ID when starting with Babric (<prefix>-<vanilla-version>-<loader-version>).",
"args.start.quilt_prefix": "Change the prefix of the version ID when starting with Quilt (<prefix>-<vanilla-version>-<loader-version>).",
"args.start.forge_prefix": "Change the prefix of the version ID when starting with Forge (<prefix>-<forge-version>).",
"args.start.neoforge_prefix": "Change the prefix of the version ID when starting with NeoForge (<prefix>-<neoforge-version>).",
Expand Down Expand Up @@ -174,6 +178,7 @@ def get(key: str, **kwargs) -> str:
"error.socket.tip.fabric_loader_version": "Fabric loader version must be specified if network is not operational.",
"error.socket.tip.legacyfabric_loader_version": "Fabric loader version must be specified if network is not operational.",
"error.socket.tip.quilt_loader_version": "Quilt loader version must be specified if network is not operational.",
"error.socket.tip.babric_loader_version": "Babric loader version must be specified if network is not operational.",
"error.cert": "Certificate verification failed, you can try installing 'certifi' package:",
# Command search
"search.type": "Type",
Expand Down
8 changes: 5 additions & 3 deletions portablemc/cli/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class StartNs(AuthBaseNs):
jvm: Optional[Path]
jvm_args: Optional[str]
no_fix: bool
babric_prefix: str
fabric_prefix: str
legacyfabric_prefix: str
quilt_prefix: str
Expand Down Expand Up @@ -147,6 +148,7 @@ def register_start_arguments(parser: ArgumentParser) -> None:
parser.add_argument("--fabric-prefix", help=_("args.start.fabric_prefix"), default="fabric", metavar="PREFIX")
parser.add_argument("--quilt-prefix", help=_("args.start.quilt_prefix"), default="quilt", metavar="PREFIX")
parser.add_argument("--legacyfabric-prefix", help=_("args.start.legacyfabric_prefix"), default="legacyfabric", metavar="PREFIX")
parser.add_argument("--babric-prefix", help=_("args.start.babric_prefix"), default="babric", metavar="PREFIX")
parser.add_argument("--forge-prefix", help=_("args.start.forge_prefix"), default="forge", metavar="PREFIX")
parser.add_argument("--neoforge-prefix", help=_("args.start.neoforge_prefix"), default="neoforge", metavar="PREFIX")
parser.add_argument("--lwjgl", help=_("args.start.lwjgl"))
Expand All @@ -161,10 +163,10 @@ def register_start_arguments(parser: ArgumentParser) -> None:
parser.add_argument("-s", "--server", help=_("args.start.server"), type=type_host)
parser.add_argument("-p", "--server-port", help=_("args.start.server_port"), metavar="PORT")

version_arg = parser.add_argument("version", nargs="?", default="release", help=_("args.start.version", formats=", ".join(map(lambda s: _(f"args.start.version.{s}"), ("standard", "fabric", "quilt", "legacyfabric", "forge", "neoforge")))))
version_arg = parser.add_argument("version", nargs="?", default="release", help=_("args.start.version", formats=", ".join(map(lambda s: _(f"args.start.version.{s}"), ("standard", "fabric", "quilt", "legacyfabric", "babric", "forge", "neoforge")))))
for standard in ("release", "snapshot"):
add_completion(version_arg, standard, _(f"args.start.version.comp.{standard}"))
for loader in ("fabric", "quilt", "legacyfabric", "forge", "neoforge"):
for loader in ("fabric", "quilt", "legacyfabric", "babric", "forge", "neoforge"):
add_completion(version_arg, f"{loader}:", _(f"args.start.version.comp.{loader}"))


Expand Down Expand Up @@ -216,7 +218,7 @@ def get_outputs() -> List[str]:
return ["human-color", "human", "machine"]

def get_search_kinds() -> List[str]:
return ["mojang", "local", "forge", "fabric", "quilt", "legacyfabric"]
return ["mojang", "local", "forge", "fabric", "quilt", "legacyfabric", "babric"]

def get_auth_services() -> List[str]:
return ["microsoft", "yggdrasil"]
Expand Down
35 changes: 26 additions & 9 deletions portablemc/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def __init__(self,
self.executable = executable

def __repr__(self) -> str:
return f"<DownloadEntry {self.name}>"
return f"<DownloadEntry {self.name} @ {self.url}>"

def __hash__(self) -> int:
# Making size and sha1 in the hash is useful to make them,
Expand All @@ -54,16 +54,18 @@ class _DownloadEntry:
unsupported URL schemes.
"""

__slots__ = "https", "host", "port", "entry"
__slots__ = "https", "host", "port", "path", "entry", "redirect"

def __init__(self, https: bool, host: str, port: Optional[int], entry: DownloadEntry) -> None:
def __init__(self, https: bool, host: str, port: Optional[int], path: str, entry: DownloadEntry, *, redirect: int = 0) -> None:
self.https = https
self.host = host
self.port = port
self.path = path
self.entry = entry
self.redirect = redirect

@classmethod
def from_entry(cls, entry: DownloadEntry) -> "_DownloadEntry":
def from_entry(cls, entry: DownloadEntry, *, redirect: int = 0) -> "_DownloadEntry":

# We only support HTTP/HTTPS
url_parsed = urllib.parse.urlparse(entry.url)
Expand All @@ -74,7 +76,9 @@ def from_entry(cls, entry: DownloadEntry) -> "_DownloadEntry":
url_parsed.scheme == "https",
url_parsed.netloc,
url_parsed.port,
entry)
url_parsed.path,
entry,
redirect=redirect)


class DownloadResult:
Expand Down Expand Up @@ -103,6 +107,7 @@ class DownloadResultError(DownloadResult):
"""

CONNECTION = "connection"
TOO_MANY_REDIRECT = "too_many_redirect"
NOT_FOUND = "not_found"
INVALID_SIZE = "invalid_size"
INVALID_SHA1 = "invalid_sha1"
Expand All @@ -120,17 +125,19 @@ class DownloadList:
with multithreading.
"""

__slots__ = "entries", "count", "size"
__slots__ = "entries", "count", "size", "_dst_entries"

def __init__(self):
self.entries: List[_DownloadEntry] = []
self._dst_entries = {}
self.count = 0
self.size = 0

def clear(self) -> None:
"""Clear the download entry, removing all entries and computed count/size.
"""
self.entries.clear()
self._dst_entries.clear()
self.count = 0
self.size = 0

Expand All @@ -142,10 +149,14 @@ def add(self, entry: DownloadEntry, *, verify: bool = False) -> None:
size has the given entry, in such case the entry is not added.
"""

if entry.dst in self._dst_entries:
raise ValueError("duplicate entry destination", entry, self._dst_entries[entry.dst])

if verify and entry.dst.is_file() and (entry.size is None or entry.size == entry.dst.stat().st_size):
return

self.entries.append(_DownloadEntry.from_entry(entry))
self._dst_entries[entry.dst] = entry
self.count += 1
if entry.size is not None:
self.size += entry.size
Expand Down Expand Up @@ -271,6 +282,7 @@ def _download_thread(

# Maximum tries count or a single entry.
max_try_count = 3
max_redirect = 10

# For speed calculation.
speed_update_interval = 0.25
Expand Down Expand Up @@ -319,7 +331,7 @@ def _download_thread(
# This try-except block is around all potential
try:

conn.request("GET", entry.url)
conn.request("GET", raw_entry.path)
res = conn.getresponse()

if res.status != 200:
Expand All @@ -328,8 +340,13 @@ def _download_thread(
# and allow further request.
while res.readinto(buffer):
pass

if res.status == 301 or res.status == 302:

if raw_entry.redirect >= max_redirect:
last_error = DownloadResultError.TOO_MANY_REDIRECT
continue

# If location header is absent, consider it not found.
redirect_url = res.headers.get("location")
if redirect_url is not None:
Expand All @@ -340,7 +357,7 @@ def _download_thread(
sha1=entry.sha1,
name=entry.name)

entries_queue.put(_DownloadEntry.from_entry(redirect_entry))
entries_queue.put(_DownloadEntry.from_entry(redirect_entry, redirect=raw_entry.redirect + 1))
break # Abort on redirect

# Any other non-200 code is considered not found and we retry...
Expand Down
9 changes: 9 additions & 0 deletions portablemc/fabric.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ def request_fabric_loader_version(self, vanilla_version: str) -> Optional[str]:
FABRIC_API = FabricApi("fabric", "https://meta.fabricmc.net/v2/")
QUILT_API = FabricApi("quilt", "https://meta.quiltmc.org/v3/")
LEGACYFABRIC_API = FabricApi("legacyfabric", "https://meta.legacyfabric.net/v2/")
BABRIC_API = FabricApi("babric", "https://meta.babric.glass-launcher.net/v2/")


class FabricVersion(Version):
Expand Down Expand Up @@ -116,6 +117,14 @@ def _with_legacyfabric(cls, vanilla_version: str = "release", loader_version: Op
"""Construct a root for resolving a LegacyFabric version"""
return cls(LEGACYFABRIC_API, vanilla_version, loader_version, prefix, context=context)

@classmethod
def _with_babric(cls, vanilla_version: str = "release", loader_version: Optional[str] = None, *,
context: Optional[Context] = None,
prefix="babric"
) -> "FabricVersion":
"""Construct a root for resolving a LegacyFabric version"""
return cls(BABRIC_API, vanilla_version, loader_version, prefix, context=context)

def _resolve_version(self, watcher: Watcher) -> None:

# Vanilla version may be "release" or "snapshot"
Expand Down
Loading
Loading