# -*- coding: utf-8 -*- """История запусков и статистика игры. Всё хранится в .launcher/stats.json рядом с кэшем Java: plays — {версия: {count, seconds, last, crashes}} launches — последние запуски (кап 300): {ts, ver, ok, code, seconds} """ import json import os import threading import time _FILE = None # путь к stats.json (задаётся set_path) _LOCK = threading.Lock() _MAX_LAUNCHES = 300 def set_path(launcher_dir): """Каталог .launcher — тут лежат stats.json и java_cache.json.""" global _FILE _FILE = os.path.join(str(launcher_dir), "stats.json") def _load(): try: with open(_FILE, "r", encoding="utf-8") as f: data = json.load(f) if not isinstance(data, dict): raise ValueError return data except (OSError, ValueError): return {"plays": {}, "launches": []} def _save(data): tmp = _FILE + ".tmp" try: with open(tmp, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False) os.replace(tmp, _FILE) except OSError: pass def record_start(version): """Вызывается при запуске игры: +1 к счётчику запусков версии.""" if not _FILE: return with _LOCK: data = _load() plays = data.setdefault("plays", {}) rec = plays.setdefault(version, {"count": 0, "seconds": 0, "last": 0.0, "crashes": 0}) rec["count"] += 1 rec["last"] = time.time() _save(data) def record_finish(version, seconds, ok, code): """Вызывается при завершении игры: длительность, код, вылет.""" if not _FILE: return with _LOCK: data = _load() plays = data.setdefault("plays", {}) rec = plays.setdefault(version, {"count": 0, "seconds": 0, "last": 0.0, "crashes": 0}) rec["seconds"] = max(0, int(seconds)) if not ok: rec["crashes"] = rec.get("crashes", 0) + 1 launches = data.setdefault("launches", []) launches.append({"ts": time.time(), "ver": version, "ok": bool(ok), "code": code, "seconds": max(0, int(seconds))}) del launches[:-_MAX_LAUNCHES] _save(data) def totals(): """Агрегаты для окна статистики.""" if not _FILE: return {"total_seconds": 0, "total_launches": 0, "per_version": [], "recent": []} with _LOCK: data = _load() plays = data.get("plays") or {} per = [] total_s = 0 for ver, rec in plays.items(): sec = int(rec.get("seconds") or 0) total_s += sec per.append({"ver": ver, "count": int(rec.get("count") or 0), "seconds": sec, "last": float(rec.get("last") or 0), "crashes": int(rec.get("crashes") or 0)}) per.sort(key=lambda r: r["last"], reverse=True) recent = list(data.get("launches") or [])[-20:] recent.reverse() return {"total_seconds": total_s, "total_launches": sum(r["count"] for r in per), "per_version": per, "recent": recent} def fmt_duration(seconds): """1800 → «30 мин»; 30 → «30 с»; 3700 → «1 ч 1 мин».""" seconds = int(seconds or 0) if seconds < 60: return f"{seconds} с" if seconds < 3600: return f"{seconds // 60} мин" h, m = seconds // 3600, (seconds % 3600) // 60 return f"{h} ч {m} мин" if m else f"{h} ч" def fmt_time(ts): """Unix-время → «дд.мм.гггг чч:мм».""" if not ts: return "—" try: return time.strftime("%d.%m.%Y %H:%M", time.localtime(ts)) except (ValueError, OSError): return "—" def clear(): """Полная очистка истории и статистики.""" if not _FILE: return with _LOCK: try: os.remove(_FILE) except OSError: pass