togethere.cloud/public_html/api/game-validator.php

267 lines
8.2 KiB
PHP

<?php
/**
* Server-side Game Validation API
* Copyright (c) 2026 Wspólnie - wspolpraca@togethere.cloud
* Weryfikuje wyniki gier po stronie serwera
*/
// Ochrona przed bezpośrednim dostępem
if (!defined('VALID_REQUEST')) {
die('Direct access not permitted');
}
class GameValidator {
private $db;
private $maxScore = 10;
private $minGameDuration = 30; // sekundy
private $maxGameDuration = 600; // 10 minut
public function __construct($db) {
$this->db = $db;
}
/**
* Waliduje wynik gry
* @param array $gameData - Dane z gry
* @return array - Rezultat walidacji
*/
public function validateGameResult($gameData) {
$errors = [];
// 1. Sprawdź czy wszystkie wymagane pola są obecne
$requiredFields = ['playerScore', 'botScore', 'gameDuration', 'difficulty', 'sessionToken'];
foreach ($requiredFields as $field) {
if (!isset($gameData[$field])) {
$errors[] = "Missing required field: $field";
}
}
if (!empty($errors)) {
return ['valid' => false, 'errors' => $errors];
}
// 2. Sprawdź token sesji
if (!$this->validateSessionToken($gameData['sessionToken'])) {
$errors[] = "Invalid session token";
}
// 3. Sprawdź wyniki
if ($gameData['playerScore'] > $this->maxScore || $gameData['botScore'] > $this->maxScore) {
$errors[] = "Score exceeds maximum allowed";
}
if ($gameData['playerScore'] < 0 || $gameData['botScore'] < 0) {
$errors[] = "Negative scores not allowed";
}
// Jeden z graczy musi mieć 10 punktów
if ($gameData['playerScore'] != $this->maxScore && $gameData['botScore'] != $this->maxScore) {
$errors[] = "Invalid game end condition";
}
// 4. Sprawdź czas gry
if ($gameData['gameDuration'] < $this->minGameDuration) {
$errors[] = "Game duration too short (possible speed hack)";
}
if ($gameData['gameDuration'] > $this->maxGameDuration) {
$errors[] = "Game duration too long";
}
// 5. Sprawdź statystyki gracza (wykryj cheating)
if (!$this->checkPlayerStats($gameData)) {
$errors[] = "Suspicious player statistics detected";
}
// 6. Rate limiting - max 10 gier na godzinę
if (!$this->checkRateLimit($gameData['userId'])) {
$errors[] = "Too many games in short time";
}
if (!empty($errors)) {
$this->logSuspiciousActivity($gameData, $errors);
return ['valid' => false, 'errors' => $errors];
}
return ['valid' => true, 'message' => 'Game result validated successfully'];
}
/**
* Waliduje token sesji
*/
private function validateSessionToken($token) {
// TODO: Zaimplementuj weryfikację tokenu z bazy danych
// Token powinien być generowany przy starcie gry i weryfikowany tutaj
if (empty($token) || strlen($token) < 32) {
return false;
}
// Przykładowa weryfikacja (zaimplementuj według swojej logiki)
/*
$stmt = $this->db->prepare("SELECT * FROM game_sessions WHERE token = ? AND expires_at > NOW()");
$stmt->execute([$token]);
return $stmt->rowCount() > 0;
*/
return true; // Tymczasowo
}
/**
* Sprawdza statystyki gracza pod kątem cheating
*/
private function checkPlayerStats($gameData) {
// Przykładowe sprawdzenia:
// 1. Niemożliwy czas reakcji
if (isset($gameData['averageReactionTime']) && $gameData['averageReactionTime'] < 50) {
return false; // Ludzki czas reakcji to ~150-250ms
}
// 2. Idealna celność (100%) jest podejrzana
if (isset($gameData['accuracy']) && $gameData['accuracy'] >= 99) {
return false;
}
// 3. Sprawdź historię gracza
$userId = $gameData['userId'] ?? null;
if ($userId) {
$winRate = $this->getPlayerWinRate($userId);
if ($winRate > 95) { // 95%+ win rate jest podejrzane
return false;
}
}
return true;
}
/**
* Sprawdza rate limiting
*/
private function checkRateLimit($userId) {
if (!$userId) return true;
// TODO: Zaimplementuj sprawdzanie w bazie
/*
$stmt = $this->db->prepare("
SELECT COUNT(*) as game_count
FROM game_results
WHERE user_id = ? AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)
");
$stmt->execute([$userId]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result['game_count'] < 10;
*/
return true; // Tymczasowo
}
/**
* Pobiera współczynnik wygranych gracza
*/
private function getPlayerWinRate($userId) {
// TODO: Zaimplementuj
/*
$stmt = $this->db->prepare("
SELECT
(SUM(CASE WHEN player_score = 10 THEN 1 ELSE 0 END) * 100.0 / COUNT(*)) as win_rate
FROM game_results
WHERE user_id = ?
");
$stmt->execute([$userId]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result['win_rate'] ?? 0;
*/
return 50; // Tymczasowo
}
/**
* Loguje podejrzaną aktywność
*/
private function logSuspiciousActivity($gameData, $errors) {
// TODO: Zapisz do bazy danych lub pliku log
$logEntry = [
'timestamp' => date('Y-m-d H:i:s'),
'user_id' => $gameData['userId'] ?? 'unknown',
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
'errors' => $errors,
'data' => $gameData
];
// Zapisz do pliku log
$logFile = __DIR__ . '/../../logs/suspicious_activity.log';
file_put_contents($logFile, json_encode($logEntry) . PHP_EOL, FILE_APPEND);
// Opcjonalnie: wyślij alert do adminów
// mail('admin@example.com', 'Suspicious game activity', json_encode($logEntry));
}
/**
* Zapisuje zweryfikowany wynik gry
*/
public function saveGameResult($gameData) {
// TODO: Zapisz do bazy danych
/*
$stmt = $this->db->prepare("
INSERT INTO game_results
(user_id, player_score, bot_score, difficulty, game_duration, created_at)
VALUES (?, ?, ?, ?, ?, NOW())
");
return $stmt->execute([
$gameData['userId'],
$gameData['playerScore'],
$gameData['botScore'],
$gameData['difficulty'],
$gameData['gameDuration']
]);
*/
return true;
}
}
/**
* Endpoint API do walidacji wyników
*/
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
define('VALID_REQUEST', true);
header('Content-Type: application/json');
// Pobierz dane z requestu
$input = file_get_contents('php://input');
$gameData = json_decode($input, true);
if (!$gameData) {
http_response_code(400);
echo json_encode(['error' => 'Invalid JSON data']);
exit;
}
// TODO: Połącz z bazą danych
// $db = new PDO(...);
$db = null;
$validator = new GameValidator($db);
$result = $validator->validateGameResult($gameData);
if ($result['valid']) {
$validator->saveGameResult($gameData);
http_response_code(200);
echo json_encode([
'success' => true,
'message' => 'Game result validated and saved'
]);
} else {
http_response_code(400);
echo json_encode([
'success' => false,
'errors' => $result['errors']
]);
}
}
?>