267 lines
8.2 KiB
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']
|
|
]);
|
|
}
|
|
}
|
|
?>
|