Files
kosta2014p ec6f13ddcc PixelLauncher 2.1.0 — стабильная сборка
Офлайн-лаунчер Minecraft: аккаунты без Microsoft, установка версий и лоадеров
(Fabric, Quilt, Forge, NeoForge), каталог модпаков Modrinth, поиск и обновление
модов, разбор вылетов, системный трей, автозагрузка, плагины, прокси и зеркала.
2026-10-18 22:44:19 +07:00

176 lines
5.3 KiB
Python

"""Поиск и выбор Java."""
import json
import os
import re
import shutil
import struct
import subprocess
import sys
CREATE_NO_WINDOW = 0x08000000 if os.name == "nt" else 0
def is64():
return struct.calcsize("P") * 8 == 64
def _exe(name="java"):
return name + (".exe" if os.name == "nt" else "")
def java_major(path):
"""Версия Java в виде числа (8, 11, 17, 21...), 0 если не определилась."""
try:
p = subprocess.run(
[path, "-version"],
capture_output=True, text=True, timeout=20,
creationflags=CREATE_NO_WINDOW,
)
text = (p.stderr or "") + (p.stdout or "")
except Exception:
return 0
m = re.search(r'version\s+"(\d+)(?:\.(\d+))?(?:_(\d+))?', text)
if not m:
return 0
major = int(m.group(1))
if major == 1: # старый формат 1.8.0_382
return int(m.group(2) or 0)
return major
def _add(cands, home):
if not home:
return
exe = os.path.join(home, "bin", _exe())
if os.path.isfile(exe) and exe not in cands:
cands.append(exe)
def find_javas():
cands = []
# Переменные окружения
_add(cands, os.environ.get("JAVA_HOME"))
for k, v in os.environ.items():
if k.upper().startswith("JAVA_HOME") and v:
_add(cands, v)
# PATH
w = shutil.which(_exe())
if w:
cands.append(w)
if os.name == "nt":
pf = [
r"C:\Program Files\Java",
r"C:\Program Files (x86)\Java",
r"C:\Program Files\Eclipse Adoptium",
r"C:\Program Files\AdoptOpenJDK",
r"C:\Program Files\Microsoft",
r"C:\Program Files\Amazon Corretto",
r"C:\Program Files\Zulu",
r"C:\Program Files\BellSoft",
r"C:\Program Files\GraalVM",
r"C:\Program Files\JetBrains",
]
home_jdks = os.path.join(os.path.expanduser("~"), ".jdks")
pf.append(home_jdks)
for root in pf:
if not os.path.isdir(root):
continue
try:
for name in os.listdir(root):
_add(cands, os.path.join(root, name))
except OSError:
pass
elif sys.platform == "darwin":
for root in ("/Library/Java/JavaVirtualMachines",
os.path.expanduser("~/Library/Java/JavaVirtualMachines")):
if not os.path.isdir(root):
continue
try:
for name in os.listdir(root):
_add(cands, os.path.join(root, name, "Contents", "Home"))
except OSError:
pass
else:
jvm_dir = "/usr/lib/jvm"
if os.path.isdir(jvm_dir):
try:
for name in os.listdir(jvm_dir):
_add(cands, os.path.join(jvm_dir, name))
except OSError:
pass
return cands
def pick_java(required=8, preferred="", cache_path=""):
"""Выбирает Java: (путь, major, подходит_ли).
Из подходящих (major >= required) берётся самая старая — максимальная
совместимость со старыми версиями Minecraft.
cache_path — файл кэша с результатами определения версии. Пока файл
java.exe не менялся, `java -version` повторно не запускается.
"""
if preferred and os.path.isfile(preferred):
major = java_major(preferred)
return preferred, major, (major == 0 or major >= required)
cache = {}
if cache_path:
try:
with open(cache_path, encoding="utf-8") as f:
cache = json.load(f)
if not isinstance(cache, dict):
cache = {}
except Exception: # noqa: BLE001
cache = {}
changed = False
cands = find_javas()
scored = []
for exe in cands:
try:
mtime = int(os.path.getmtime(exe))
except OSError:
continue
ent = cache.get(exe)
if isinstance(ent, list) and len(ent) == 2 and ent[1] == mtime:
major = int(ent[0]) # кэш валиден: файл не менялся
else:
major = java_major(exe)
cache[exe] = [major, mtime]
changed = True
if major > 0:
scored.append((exe, major))
# выбывшие установки — убрать из кэша
alive = set(cands)
for exe in [e for e in cache if e not in alive]:
del cache[exe]
changed = True
if cache_path and changed:
try:
os.makedirs(os.path.dirname(cache_path), exist_ok=True)
with open(cache_path, "w", encoding="utf-8") as f:
json.dump(cache, f)
except OSError:
pass
if not scored:
return "", 0, False
suitable = [s for s in scored if s[1] >= required]
if suitable:
suitable.sort(key=lambda s: s[1])
exe, major = suitable[0]
return exe, major, True
# Ничего подходящего — берём самую новую и предупредим
scored.sort(key=lambda s: s[1])
exe, major = scored[-1]
return exe, major, False