39366-vm/api/fps_matches.php
2026-03-30 18:54:34 +00:00

108 lines
4.3 KiB
PHP

<?php
declare(strict_types=1);
header('Content-Type: application/json; charset=utf-8');
require_once __DIR__ . '/../game_bootstrap.php';
function jsonResponse(array $payload, int $status = 200): void
{
http_response_code($status);
echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
exit;
}
function safeLength(string $value): int
{
return function_exists('mb_strlen') ? mb_strlen($value) : strlen($value);
}
try {
ensureFpsMatchesTable();
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
if (isset($_GET['id'])) {
$id = filter_var($_GET['id'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
if (!$id) {
jsonResponse(['success' => false, 'error' => 'Invalid match id.'], 422);
}
$match = fetchMatchById($id);
if (!$match) {
jsonResponse(['success' => false, 'error' => 'Match not found.'], 404);
}
jsonResponse(['success' => true, 'match' => $match]);
}
$limit = filter_var($_GET['limit'] ?? 8, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 20]]) ?: 8;
jsonResponse(['success' => true, 'matches' => fetchRecentMatches($limit)]);
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
jsonResponse(['success' => false, 'error' => 'Method not allowed.'], 405);
}
$raw = file_get_contents('php://input') ?: '';
$payload = json_decode($raw, true);
if (!is_array($payload)) {
jsonResponse(['success' => false, 'error' => 'Invalid request payload.'], 422);
}
$playerName = trim((string)($payload['player_name'] ?? 'Operator'));
$weaponKey = trim((string)($payload['weapon_key'] ?? 'carbine'));
$weaponName = trim((string)($payload['weapon_name'] ?? 'Carbine'));
$outcome = trim((string)($payload['outcome'] ?? 'defeat'));
if ($playerName === '' || safeLength($playerName) > 80) {
jsonResponse(['success' => false, 'error' => 'Player name must be between 1 and 80 characters.'], 422);
}
if ($weaponKey === '' || safeLength($weaponKey) > 40 || $weaponName === '' || safeLength($weaponName) > 80) {
jsonResponse(['success' => false, 'error' => 'Weapon metadata is invalid.'], 422);
}
if (!in_array($outcome, ['victory', 'defeat', 'timeout'], true)) {
$outcome = 'defeat';
}
$kills = max(0, min(999, (int)($payload['kills'] ?? 0)));
$shotsFired = max(0, min(9999, (int)($payload['shots_fired'] ?? 0)));
$shotsHit = max(0, min($shotsFired, (int)($payload['shots_hit'] ?? 0)));
$damageTaken = max(0, min(999, (int)($payload['damage_taken'] ?? 0)));
$durationSeconds = max(0, min(3600, (int)($payload['duration_seconds'] ?? 0)));
$score = max(0, min(999999, (int)($payload['score'] ?? 0)));
$accuracy = $shotsFired > 0 ? round(($shotsHit / $shotsFired) * 100, 2) : 0.0;
$stmt = db()->prepare(
'INSERT INTO fps_matches (
player_name, weapon_key, weapon_name, kills, shots_fired, shots_hit, accuracy,
damage_taken, duration_seconds, score, outcome
) VALUES (
:player_name, :weapon_key, :weapon_name, :kills, :shots_fired, :shots_hit, :accuracy,
:damage_taken, :duration_seconds, :score, :outcome
)'
);
$stmt->bindValue(':player_name', $playerName, PDO::PARAM_STR);
$stmt->bindValue(':weapon_key', $weaponKey, PDO::PARAM_STR);
$stmt->bindValue(':weapon_name', $weaponName, PDO::PARAM_STR);
$stmt->bindValue(':kills', $kills, PDO::PARAM_INT);
$stmt->bindValue(':shots_fired', $shotsFired, PDO::PARAM_INT);
$stmt->bindValue(':shots_hit', $shotsHit, PDO::PARAM_INT);
$stmt->bindValue(':accuracy', $accuracy);
$stmt->bindValue(':damage_taken', $damageTaken, PDO::PARAM_INT);
$stmt->bindValue(':duration_seconds', $durationSeconds, PDO::PARAM_INT);
$stmt->bindValue(':score', $score, PDO::PARAM_INT);
$stmt->bindValue(':outcome', $outcome, PDO::PARAM_STR);
$stmt->execute();
$id = (int)db()->lastInsertId();
$match = fetchMatchById($id);
jsonResponse(['success' => true, 'message' => 'Match saved.', 'match' => $match], 201);
} catch (Throwable $e) {
error_log('fps_matches API error: ' . $e->getMessage());
jsonResponse(['success' => false, 'error' => 'Unable to process match request right now.'], 500);
}