2026-06-01 08:40:23 +02:00

239 lines
7.0 KiB
PHP

<?php
declare(strict_types=1);
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
$dataDir = __DIR__ . DIRECTORY_SEPARATOR . 'data';
$dataFile = $dataDir . DIRECTORY_SEPARATOR . 'lobbies.json';
if (!is_dir($dataDir)) {
mkdir($dataDir, 0777, true);
}
if (!file_exists($dataFile)) {
file_put_contents($dataFile, "{}\n");
}
function respond(array $payload, int $status = 200): void
{
http_response_code($status);
echo json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
exit;
}
function read_body(): array
{
$raw = file_get_contents('php://input');
if ($raw === false || trim($raw) === '') {
return [];
}
$decoded = json_decode($raw, true);
return is_array($decoded) ? $decoded : [];
}
function clean_name(mixed $value, int $maxLength = 40): string
{
$name = trim((string) $value);
$name = preg_replace('/\s+/', ' ', $name) ?? '';
return substr($name, 0, $maxLength);
}
function clean_scores(mixed $value): array
{
if (!is_array($value)) {
return [];
}
return array_slice(array_map(static function ($score): int {
return max(0, min(100, (int) $score));
}, $value), 0, 3);
}
function clean_countries(mixed $value): array
{
if (!is_array($value)) {
return [];
}
return array_slice(array_map(static function ($country): string {
return clean_name($country, 80);
}, $value), 0, 3);
}
function with_lobbies(callable $callback, bool $write = false): mixed
{
global $dataFile;
$handle = fopen($dataFile, 'c+');
if ($handle === false) {
respond(['ok' => false, 'error' => 'Could not open data file.'], 500);
}
flock($handle, $write ? LOCK_EX : LOCK_SH);
rewind($handle);
$raw = stream_get_contents($handle);
$lobbies = json_decode($raw ?: '{}', true);
if (!is_array($lobbies)) {
$lobbies = [];
}
$result = $callback($lobbies);
if ($write) {
rewind($handle);
ftruncate($handle, 0);
fwrite($handle, json_encode($lobbies, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n");
fflush($handle);
}
flock($handle, LOCK_UN);
fclose($handle);
return $result;
}
function ensure_lobby(array &$lobbies, string $lobbyName): array
{
if (!isset($lobbies[$lobbyName]) || !is_array($lobbies[$lobbyName])) {
$lobbies[$lobbyName] = [
'name' => $lobbyName,
'players' => [],
'leaderboard' => [],
];
}
$lobbies[$lobbyName]['players'] = array_values($lobbies[$lobbyName]['players'] ?? []);
$lobbies[$lobbyName]['leaderboard'] = array_values($lobbies[$lobbyName]['leaderboard'] ?? []);
return $lobbies[$lobbyName];
}
$action = $_GET['action'] ?? '';
$body = read_body();
if ($action === 'createLobby') {
$lobbyName = clean_name($body['lobbyName'] ?? '');
if ($lobbyName === '') {
respond(['ok' => false, 'error' => 'Lobby name is required.'], 400);
}
$lobby = with_lobbies(static function (array &$lobbies) use ($lobbyName): array {
return ensure_lobby($lobbies, $lobbyName);
}, true);
respond(['ok' => true, 'lobby' => ['name' => $lobby['name']]]);
}
if ($action === 'joinLobby') {
$lobbyName = clean_name($body['lobbyName'] ?? '');
$playerName = clean_name($body['playerName'] ?? '', 24);
if ($lobbyName === '' || $playerName === '') {
respond(['ok' => false, 'error' => 'Lobby name and player name are required.'], 400);
}
$lobby = with_lobbies(static function (array &$lobbies) use ($lobbyName, $playerName): array {
ensure_lobby($lobbies, $lobbyName);
if (!in_array($playerName, $lobbies[$lobbyName]['players'], true)) {
$lobbies[$lobbyName]['players'][] = $playerName;
}
return $lobbies[$lobbyName];
}, true);
respond(['ok' => true, 'lobby' => ['name' => $lobby['name'], 'players' => $lobby['players']]]);
}
if ($action === 'leaveLobby') {
$lobbyName = clean_name($body['lobbyName'] ?? '');
$playerName = clean_name($body['playerName'] ?? '', 24);
with_lobbies(static function (array &$lobbies) use ($lobbyName, $playerName): void {
if ($lobbyName === '' || $playerName === '' || !isset($lobbies[$lobbyName])) {
return;
}
$lobbies[$lobbyName]['players'] = array_values(array_filter(
$lobbies[$lobbyName]['players'] ?? [],
static fn ($name): bool => $name !== $playerName,
));
}, true);
respond(['ok' => true]);
}
if ($action === 'submitScore') {
$lobbyName = clean_name($body['lobbyName'] ?? '');
$playerName = clean_name($body['playerName'] ?? '', 24);
if ($lobbyName === '' || $playerName === '') {
respond(['ok' => false, 'error' => 'Lobby name and player name are required.'], 400);
}
$entry = [
'playerName' => $playerName,
'totalScore' => max(0, min(300, (int) ($body['totalScore'] ?? 0))),
'scores' => clean_scores($body['scores'] ?? []),
'countries' => clean_countries($body['countries'] ?? []),
'date' => gmdate('c'),
];
with_lobbies(static function (array &$lobbies) use ($lobbyName, $playerName, $entry): void {
ensure_lobby($lobbies, $lobbyName);
if (!in_array($playerName, $lobbies[$lobbyName]['players'], true)) {
$lobbies[$lobbyName]['players'][] = $playerName;
}
$lobbies[$lobbyName]['leaderboard'][] = $entry;
usort(
$lobbies[$lobbyName]['leaderboard'],
static fn (array $a, array $b): int => ($b['totalScore'] ?? 0) <=> ($a['totalScore'] ?? 0),
);
$lobbies[$lobbyName]['leaderboard'] = array_slice($lobbies[$lobbyName]['leaderboard'], 0, 20);
}, true);
respond(['ok' => true, 'entry' => $entry]);
}
if ($action === 'getLeaderboard') {
$lobbyName = clean_name($_GET['lobbyName'] ?? '');
if ($lobbyName === '') {
respond(['ok' => false, 'error' => 'Lobby name is required.'], 400);
}
$leaderboard = with_lobbies(static function (array &$lobbies) use ($lobbyName): array {
return $lobbies[$lobbyName]['leaderboard'] ?? [];
});
respond(['ok' => true, 'leaderboard' => $leaderboard]);
}
if ($action === 'getLobby') {
$lobbyName = clean_name($_GET['lobbyName'] ?? '');
if ($lobbyName === '') {
respond(['ok' => false, 'error' => 'Lobby name is required.'], 400);
}
$lobby = with_lobbies(static function (array &$lobbies) use ($lobbyName): array {
if (!isset($lobbies[$lobbyName])) {
return ['name' => $lobbyName, 'players' => []];
}
return [
'name' => $lobbies[$lobbyName]['name'] ?? $lobbyName,
'players' => array_values($lobbies[$lobbyName]['players'] ?? []),
];
});
respond(['ok' => true, 'lobby' => $lobby]);
}
respond(['ok' => false, 'error' => 'Unknown action.'], 404);