158 lines
5.9 KiB
Python
158 lines
5.9 KiB
Python
"""Пути и настройки лаунчера (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": [],
|
||
# ключ CurseForge API (необязателен: есть зеркало без ключа)
|
||
"cf_api_key": "",
|
||
# третья волна
|
||
"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 not isinstance(s.get("cf_api_key"), str):
|
||
s["cf_api_key"] = ""
|
||
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
|