Files
Alex Cube 6006ad0690 Add PHP server version: leaderboard, identity, menu screens, lore
- php/: MySQL-backed (via config.php, gitignored) top-5 solutions per
  level, cookie identity with rename, server-side progress for "Продолжить"
- Menu screens: Играть / Продолжить / Предыстория / Настройки, plus
  placeholder start/end screens for a future intro/outro
- Lore screen: EXO:GRID backstory text (6 epochs, no gameplay changes yet)
- levels.js/solver.js copied in as-is
2026-08-10 15:18:53 +03:00

52 lines
2.5 KiB
PHP

<?php
// db.php — MySQL, БД уже создана на сервере (панель Тенетты), не SQLite.
// Схема создаётся сама при первом обращении, руками ничего накатывать не
// нужно — залил файлы и работает.
function get_db(): PDO {
$cfg = require __DIR__ . '/config.php';
$dsn = "mysql:host={$cfg['host']};dbname={$cfg['db']};charset=utf8mb4";
$pdo = new PDO($dsn, $cfg['user'], $cfg['pass']);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec('CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
token VARCHAR(64) UNIQUE NOT NULL,
name VARCHAR(40) NOT NULL,
last_level INT NOT NULL DEFAULT 0,
created_at INT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
// move_key хранится как md5-хеш последовательности ходов (фикс. длина
// 32 символа) — а не сырой склеенной строкой, у которой длина растёт
// с числом ходов и может упереться в лимит длины ключа InnoDB-индекса.
$pdo->exec('CREATE TABLE IF NOT EXISTS solutions (
id INT AUTO_INCREMENT PRIMARY KEY,
level_name VARCHAR(64) NOT NULL,
user_id INT NOT NULL,
moves MEDIUMTEXT NOT NULL,
move_count INT NOT NULL,
move_key CHAR(32) NOT NULL,
source VARCHAR(16) NOT NULL DEFAULT "player",
created_at INT NOT NULL,
UNIQUE KEY uniq_level_movekey (level_name, move_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
// MySQL/MariaDB не везде понимают "CREATE INDEX IF NOT EXISTS" — просто
// глотаем "уже есть" при повторных запусках.
try { $pdo->exec('CREATE INDEX idx_level_count ON solutions(level_name, move_count)'); }
catch (PDOException $e) {}
return $pdo;
}
// Идентичность — долгий cookie (год) с токеном, не PHP-сессия: имя должно
// помниться неделями, а не до закрытия вкладки.
function current_user(PDO $pdo): ?array {
if (empty($_COOKIE['sokoban_token'])) return null;
$stmt = $pdo->prepare('SELECT * FROM users WHERE token = ?');
$stmt->execute([$_COOKIE['sokoban_token']]);
$u = $stmt->fetch(PDO::FETCH_ASSOC);
return $u ?: null;
}