PixelLauncher 2.1.0 — стабильная сборка

Офлайн-лаунчер Minecraft: аккаунты без Microsoft, установка версий и лоадеров
(Fabric, Quilt, Forge, NeoForge), каталог модпаков Modrinth, поиск и обновление
модов, разбор вылетов, системный трей, автозагрузка, плагины, прокси и зеркала.
This commit is contained in:
kosta2014p committed 2026-10-18 22:44:19 +07:00
commit ec6f13ddcc
33 files changed
+10445

No files matched your search

+153
View File
@@ -0,0 +1,153 @@
"""Пути и настройки лаунчера (settings.json рядом с main.py)."""
import json
import os
APP_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SETTINGS_PATH = os.path.join(APP_DIR, "settings.json")
DEFAULT_GAME_DIR = os.path.join(APP_DIR, ".minecraft")
DEFAULTS = {
"username": "Player",
"version": "latest-release",
"ram": 2048,
"auto_ram": False,
"java_path": "",
"jvm_args": "",
"jvm_preset": "",
"game_dir": DEFAULT_GAME_DIR,
# аккаунты (оффлайн-ники) и активный
"accounts": ["Player"],
"active_account": "Player",
# интерфейс
"lang": "ru",
"theme": "dark", # "dark" | "light"
"accent": "#6fae4a",
"ui_scale": 1.0,
"scene": "day",
# запуск игры
"win_w": 0, # 0 = значение игры по умолчанию
"win_h": 0,
"fullscreen": False,
"auto_backup": False, # резервная копия мира перед запуском
"autostart": False, # ярлык в автозагрузке Windows
"minimize_to_tray_on_launch": False, # прятать окно при запуске игры
# прокси: {"type": "http"|"socks5", "host", "port", "user", "pass"}
"proxy": {"enabled": False, "type": "http", "host": "", "port": 1080,
"user": "", "pass": ""},
# зеркала скачивания (пустые списки = официальные серверы Mojang)
"asset_mirrors": [],
"lib_mirrors": [],
# третья волна
"fav_versions": [], # закреплённые версии (id)
"profiles": [], # профили запуска (см. ui._open_profiles)
"active_profile": "", # имя применённого профиля ("" — без профиля)
"world_backups": 5, # сколько последних копий мира хранить
# мастер первоначальной настройки (False — показать при первом запуске;
# апгрейд со старой версии считается настроенным, см. load_settings)
"onboarding_done": False,
}
def load_settings():
s = dict(DEFAULTS)
try:
with open(SETTINGS_PATH, encoding="utf-8") as f:
data = json.load(f)
except Exception:
data = None
if isinstance(data, dict):
s.update(data)
if "onboarding_done" not in data:
# апгрейд со старой версии: мастер первого запуска не показываем
s["onboarding_done"] = True
_migrate(s)
return s
def _migrate(s):
"""Приводит старые настройки к новой структуре."""
nick = (s.get("username") or "Player").strip() or "Player"
accounts = s.get("accounts")
if not isinstance(accounts, list) or not accounts:
accounts = [nick]
accounts = [str(a).strip() for a in accounts if str(a).strip()]
if nick not in accounts:
accounts.insert(0, nick)
s["accounts"] = accounts
active = s.get("active_account")
if not isinstance(active, str) or active not in accounts:
s["active_account"] = nick if nick in accounts else accounts[0]
s["username"] = s["active_account"]
for key in ("asset_mirrors", "lib_mirrors"):
v = s.get(key)
if not isinstance(v, list):
s[key] = []
if not isinstance(s.get("accent"), str) or not s["accent"].startswith("#"):
s["accent"] = DEFAULTS["accent"]
if s.get("theme") not in ("dark", "light"):
s["theme"] = "dark"
proxy = s.get("proxy")
if not isinstance(proxy, dict):
proxy = dict(DEFAULTS["proxy"])
for k, dv in DEFAULTS["proxy"].items():
if k not in proxy:
proxy[k] = dv
s["proxy"] = proxy
if s.get("scene") not in ("day", "night", "nether", "cave", "random"):
s["scene"] = "day"
if s.get("ui_scale") not in (1.0, 1.25, 1.5):
try:
sc = float(s.get("ui_scale"))
s["ui_scale"] = sc if sc in (1.0, 1.25, 1.5) else 1.0
except (TypeError, ValueError):
s["ui_scale"] = 1.0
for k in ("win_w", "win_h"):
try:
s[k] = max(0, int(s.get(k, 0)))
except (TypeError, ValueError):
s[k] = 0
s["fullscreen"] = bool(s.get("fullscreen"))
s["auto_backup"] = bool(s.get("auto_backup"))
s["autostart"] = bool(s.get("autostart"))
s["minimize_to_tray_on_launch"] = bool(
s.get("minimize_to_tray_on_launch"))
fav = s.get("fav_versions")
if isinstance(fav, list):
seen_f, out_f = set(), []
for v in fav[:200]:
v = str(v).strip()
if v and v not in seen_f:
seen_f.add(v)
out_f.append(v)
s["fav_versions"] = out_f
else:
s["fav_versions"] = []
profs, names = [], set()
if isinstance(s.get("profiles"), list):
for p in s["profiles"][:100]:
name = str((p or {}).get("name", "")).strip() if isinstance(
p, dict) else ""
if name and name not in names:
names.add(name)
profs.append(p)
s["profiles"] = profs
ap = s.get("active_profile")
s["active_profile"] = ap if isinstance(ap, str) and ap in names else ""
try:
s["world_backups"] = min(50, max(1, int(s.get("world_backups", 5))))
except (TypeError, ValueError):
s["world_backups"] = 5
def save_settings(s):
try:
with open(SETTINGS_PATH, "w", encoding="utf-8") as f:
json.dump(s, f, indent=2, ensure_ascii=False)
except Exception:
pass