PixelLauncher 2.1.0 — стабильная сборка
Офлайн-лаунчер Minecraft: аккаунты без Microsoft, установка версий и лоадеров (Fabric, Quilt, Forge, NeoForge), каталог модпаков Modrinth, поиск и обновление модов, разбор вылетов, системный трей, автозагрузка, плагины, прокси и зеркала.
This commit is contained in:
commit
ec6f13ddcc
33 files changed
+10445
No files matched your search
@@ -0,0 +1,198 @@
|
||||
"""Скачивание и установка Java с Eclipse Adoptium, если она не найдена.
|
||||
|
||||
Ставит JRE в `.launcher/runtime/java-<major>` и возвращает путь к java.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tarfile
|
||||
import zipfile
|
||||
|
||||
from . import http
|
||||
from .i18n import T
|
||||
|
||||
API = ("https://api.adoptium.net/v3/binary/latest/{feature}/ga/"
|
||||
"{platform}/{arch}/jre/hotspot/normal/eclipse")
|
||||
LTS = (8, 11, 17, 21, 25)
|
||||
|
||||
|
||||
def _platform():
|
||||
if sys.platform.startswith("win"):
|
||||
return "windows"
|
||||
if sys.platform == "darwin":
|
||||
return "mac"
|
||||
return "linux"
|
||||
|
||||
|
||||
def _arch():
|
||||
try:
|
||||
import platform
|
||||
m = platform.machine().lower()
|
||||
except Exception: # noqa: BLE001
|
||||
return "x64"
|
||||
if m in ("arm64", "aarch64"):
|
||||
return "aarch64"
|
||||
if m in ("x86_64", "amd64"):
|
||||
return "x64"
|
||||
return "x86"
|
||||
|
||||
|
||||
def _exe():
|
||||
return "java.exe" if os.name == "nt" else "java"
|
||||
|
||||
|
||||
def pick_feature(required):
|
||||
"""Ближайшая LTS-версия не ниже требуемой."""
|
||||
for f in LTS:
|
||||
if f >= required:
|
||||
return f
|
||||
return LTS[-1]
|
||||
|
||||
|
||||
def runtime_dir(runtime_root, feature):
|
||||
return os.path.join(runtime_root, "java-" + str(feature))
|
||||
|
||||
|
||||
def find_runtime_java(runtime_root, feature):
|
||||
"""Путь к bin/java установленной рантайм-Java, "" — не найдено."""
|
||||
base = runtime_dir(runtime_root, feature)
|
||||
exe = _exe()
|
||||
direct = os.path.join(base, "bin", exe)
|
||||
if os.path.isfile(direct):
|
||||
return direct
|
||||
# архив Adoptium распаковывается во вложенную папку (jdk-21.0.x+y)
|
||||
try:
|
||||
for root, dirs, files in os.walk(base):
|
||||
if root.count(os.sep) - base.count(os.sep) >= 3:
|
||||
dirs[:] = []
|
||||
continue
|
||||
if exe in files and os.path.basename(root) == "bin":
|
||||
return os.path.join(root, exe)
|
||||
except OSError:
|
||||
pass
|
||||
return ""
|
||||
|
||||
|
||||
def find_installed(runtime_root, required):
|
||||
"""Лучшая уже скачанная Java не ниже required (путь, "" — нет).
|
||||
|
||||
Название каталога (java-21) хранит major-версию — не запускаем
|
||||
`java -version` повторно.
|
||||
"""
|
||||
best, best_major = "", 999
|
||||
try:
|
||||
names = os.listdir(runtime_root)
|
||||
except OSError:
|
||||
return ""
|
||||
for name in names:
|
||||
if not name.startswith("java-"):
|
||||
continue
|
||||
try:
|
||||
major = int(name.split("-", 1)[1])
|
||||
except ValueError:
|
||||
continue
|
||||
if not (required <= major < best_major):
|
||||
continue
|
||||
exe = find_runtime_java(runtime_root, major)
|
||||
if exe:
|
||||
best, best_major = exe, major
|
||||
return best
|
||||
|
||||
|
||||
def ensure_java(required, runtime_root, progress=None, log=None, cancel=None):
|
||||
"""Возвращает путь к Java не ниже required; при необходимости скачивает.
|
||||
|
||||
progress(done_bytes, total_bytes|None) — прогресс скачивания архива.
|
||||
"""
|
||||
feature = pick_feature(required)
|
||||
exe = find_runtime_java(runtime_root, feature)
|
||||
if exe:
|
||||
return exe
|
||||
|
||||
url = API.format(feature=feature, platform=_platform(), arch=_arch())
|
||||
dest = runtime_dir(runtime_root, feature)
|
||||
os.makedirs(dest, exist_ok=True)
|
||||
archive = os.path.join(runtime_root, "java-%s.download" % feature)
|
||||
if os.path.exists(archive):
|
||||
try:
|
||||
os.remove(archive)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
if log:
|
||||
log(T("Скачивание Java {v} (Adoptium)...").format(v=feature))
|
||||
total = http.head_size(url)
|
||||
try:
|
||||
http.download_file(url, archive, size=total, progress=progress,
|
||||
cancel=cancel)
|
||||
if log:
|
||||
log(T("Распаковка Java..."))
|
||||
_extract(archive, dest)
|
||||
finally:
|
||||
try:
|
||||
os.remove(archive)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
exe = find_runtime_java(runtime_root, feature)
|
||||
if not exe:
|
||||
raise RuntimeError(T("Не удалось распаковать Java"))
|
||||
if log:
|
||||
log(T("Java {v} установлена").format(v=feature), "ok")
|
||||
return exe
|
||||
|
||||
|
||||
def _safe_join(base, name):
|
||||
"""Путь внутри base; None — попытка выйти за пределы (path traversal)."""
|
||||
parts = [p for p in name.replace("\\", "/").split("/")
|
||||
if p not in ("", ".", "..")]
|
||||
if not parts:
|
||||
return None
|
||||
return os.path.join(base, *parts)
|
||||
|
||||
|
||||
def _extract(archive, dest):
|
||||
# формат определяем по сигнатуре: имя файла служебное (java-XX.download)
|
||||
with open(archive, "rb") as f:
|
||||
magic = f.read(4)
|
||||
if magic[:2] == b"PK": # ZIP
|
||||
with zipfile.ZipFile(archive) as z:
|
||||
for info in z.infolist():
|
||||
target = _safe_join(dest, info.filename)
|
||||
if target is None:
|
||||
continue
|
||||
if info.is_dir():
|
||||
os.makedirs(target, exist_ok=True)
|
||||
continue
|
||||
os.makedirs(os.path.dirname(target), exist_ok=True)
|
||||
with z.open(info) as src, open(target, "wb") as dst:
|
||||
shutil.copyfileobj(src, dst)
|
||||
else:
|
||||
with tarfile.open(archive, "r:*") as t:
|
||||
for m in t.getmembers():
|
||||
if not (m.isfile() or m.isdir()):
|
||||
continue
|
||||
target = _safe_join(dest, m.name)
|
||||
if target is None:
|
||||
continue
|
||||
if m.isdir():
|
||||
os.makedirs(target, exist_ok=True)
|
||||
continue
|
||||
os.makedirs(os.path.dirname(target), exist_ok=True)
|
||||
src = t.extractfile(m)
|
||||
if src is None:
|
||||
continue
|
||||
with src, open(target, "wb") as dst:
|
||||
shutil.copyfileobj(src, dst)
|
||||
|
||||
# права на исполнение для содержимого bin/ (linux/mac)
|
||||
if os.name != "nt":
|
||||
for root, dirs, files in os.walk(dest):
|
||||
if os.path.basename(root) != "bin":
|
||||
continue
|
||||
for f in files:
|
||||
try:
|
||||
os.chmod(os.path.join(root, f), 0o755)
|
||||
except OSError:
|
||||
pass
|
||||
Reference in new issue
Block a user