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,111 @@
|
||||
"""Работа с манифестом версий Mojang (piston-meta)."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
|
||||
from . import http
|
||||
|
||||
MANIFEST_URL = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json"
|
||||
MANIFEST_MAX_AGE = 3600 # свежий кэш не перекачиваем (час)
|
||||
|
||||
_memory_cache = None
|
||||
_skip_ttl_once = False
|
||||
|
||||
|
||||
def _cache_dir(game_dir):
|
||||
d = os.path.join(game_dir, ".launcher")
|
||||
os.makedirs(d, exist_ok=True)
|
||||
return d
|
||||
|
||||
|
||||
def invalidate():
|
||||
"""Сбросить кэш: следующий get_manifest скачает манифест заново."""
|
||||
global _memory_cache, _skip_ttl_once
|
||||
_memory_cache = None
|
||||
_skip_ttl_once = True
|
||||
|
||||
|
||||
def get_manifest(game_dir, net=True, max_age=MANIFEST_MAX_AGE):
|
||||
"""Возвращает манифест версий.
|
||||
|
||||
Свежий кэш на диске используется сразу, без обращения к сети.
|
||||
Сеть -> кэш на диске -> ошибка.
|
||||
"""
|
||||
global _memory_cache, _skip_ttl_once
|
||||
if _memory_cache is not None:
|
||||
return _memory_cache
|
||||
|
||||
cache = os.path.join(_cache_dir(game_dir), "version_manifest.json")
|
||||
force = _skip_ttl_once
|
||||
_skip_ttl_once = False
|
||||
|
||||
fresh = False
|
||||
if os.path.isfile(cache):
|
||||
try:
|
||||
fresh = (time.time() - os.path.getmtime(cache)) < max_age
|
||||
except OSError:
|
||||
fresh = False
|
||||
|
||||
if net and (force or not fresh):
|
||||
try:
|
||||
data = json.loads(http.get_bytes(MANIFEST_URL, timeout=20))
|
||||
with open(cache, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f)
|
||||
_memory_cache = data
|
||||
return data
|
||||
except Exception:
|
||||
pass
|
||||
if os.path.isfile(cache):
|
||||
with open(cache, encoding="utf-8") as f:
|
||||
_memory_cache = json.load(f)
|
||||
return _memory_cache
|
||||
raise RuntimeError("Нет доступа к сети и нет кэша манифеста версий")
|
||||
|
||||
|
||||
def version_url(manifest, vid):
|
||||
for v in manifest.get("versions", []):
|
||||
if v.get("id") == vid:
|
||||
return v.get("url")
|
||||
return None
|
||||
|
||||
|
||||
def local_versions(game_dir):
|
||||
"""Идентификаторы версий, установленных локально (Fabric, Quilt и т.п.)."""
|
||||
root = os.path.join(game_dir, "versions")
|
||||
out = []
|
||||
try:
|
||||
for name in sorted(os.listdir(root)):
|
||||
if os.path.isfile(os.path.join(root, name, name + ".json")):
|
||||
out.append(name)
|
||||
except OSError:
|
||||
pass
|
||||
return out
|
||||
|
||||
|
||||
def fetch_version_json(game_dir, vid, net=True, max_age=21600):
|
||||
"""JSON конкретной версии: свежий кэш -> сеть -> versions/{id}/{id}.json."""
|
||||
path = os.path.join(game_dir, "versions", vid, vid + ".json")
|
||||
fresh = False
|
||||
if os.path.isfile(path):
|
||||
try:
|
||||
fresh = (time.time() - os.path.getmtime(path)) < max_age
|
||||
except OSError:
|
||||
fresh = False
|
||||
if net and not fresh:
|
||||
try:
|
||||
manifest = get_manifest(game_dir, net=True)
|
||||
url = version_url(manifest, vid)
|
||||
if not url:
|
||||
raise RuntimeError(f"Версия {vid} не найдена в манифесте")
|
||||
raw = http.get_bytes(url, timeout=20)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, "wb") as f:
|
||||
f.write(raw)
|
||||
return json.loads(raw)
|
||||
except Exception:
|
||||
pass
|
||||
if os.path.isfile(path):
|
||||
with open(path, encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
raise RuntimeError(f"Не удалось получить JSON версии {vid}")
|
||||
Reference in new issue
Block a user