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,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
|
||||
Reference in new issue
Block a user