206 lines
7.8 KiB
PHP
206 lines
7.8 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
@ini_set('display_errors', '1');
|
||
@error_reporting(E_ALL);
|
||
@date_default_timezone_set('UTC');
|
||
|
||
session_start();
|
||
require_once __DIR__ . '/includes/rooms.php';
|
||
|
||
ensure_rooms_schema();
|
||
|
||
$roomId = (int) ($_GET['id'] ?? 0);
|
||
if ($roomId <= 0) {
|
||
http_response_code(404);
|
||
echo 'Room not found';
|
||
exit;
|
||
}
|
||
|
||
if (isset($_GET['leave'])) {
|
||
$sessionPlayer = get_session_player($roomId);
|
||
if ($sessionPlayer) {
|
||
$room = get_room($roomId);
|
||
if ($room) {
|
||
$newRoom = remove_player_from_room($room, $sessionPlayer['token']);
|
||
if ($newRoom === null) {
|
||
delete_room($roomId);
|
||
} else {
|
||
save_room_state($roomId, $newRoom['state'], $newRoom['status']);
|
||
}
|
||
}
|
||
}
|
||
unset($_SESSION['room_players'][$roomId]);
|
||
header('Location: /');
|
||
exit;
|
||
}
|
||
|
||
$room = get_room($roomId);
|
||
if (!$room) {
|
||
http_response_code(404);
|
||
echo 'Room not found';
|
||
exit;
|
||
}
|
||
|
||
$errors = [];
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'join') {
|
||
$playerName = trim($_POST['player_name'] ?? '');
|
||
if ($playerName === '') {
|
||
$errors[] = 'Введите ник.';
|
||
} else {
|
||
try {
|
||
$token = random_token();
|
||
$state = add_player_to_room($room, $playerName, $token);
|
||
save_room_state($roomId, $state, $room['status']);
|
||
set_session_player($roomId, $token, $playerName);
|
||
$room['state'] = $state;
|
||
} catch (RuntimeException $e) {
|
||
$errors[] = $e->getMessage();
|
||
}
|
||
}
|
||
}
|
||
|
||
$sessionPlayer = get_session_player($roomId);
|
||
$state = $room['state'];
|
||
$players = $state['players'] ?? [];
|
||
$isMember = false;
|
||
$isHost = false;
|
||
foreach ($players as $player) {
|
||
if ($sessionPlayer && $player['token'] === $sessionPlayer['token']) {
|
||
$isMember = true;
|
||
$isHost = !empty($player['is_host']);
|
||
break;
|
||
}
|
||
}
|
||
|
||
$phpVersion = PHP_VERSION;
|
||
?>
|
||
<!doctype html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Комната <?= htmlspecialchars($room['name']) ?> — Bomber Rooms</title>
|
||
<?php
|
||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||
?>
|
||
<?php if ($projectDescription): ?>
|
||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||
<?php endif; ?>
|
||
<?php if ($projectImageUrl): ?>
|
||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||
<?php endif; ?>
|
||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||
<link rel="stylesheet" href="assets/css/custom.css?v=<?= time(); ?>" />
|
||
</head>
|
||
<body>
|
||
<nav class="navbar navbar-light bg-white border-bottom small shadow-sm sticky-top">
|
||
<div class="container">
|
||
<a class="navbar-brand fw-semibold text-dark" href="/">Bomber Rooms</a>
|
||
<div class="d-flex gap-2">
|
||
<a class="btn btn-outline-dark btn-sm" href="/">Лобби</a>
|
||
<a class="btn btn-dark btn-sm" href="room.php?id=<?= (int) $roomId ?>&leave=1">Выйти</a>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<main class="py-5">
|
||
<div class="container">
|
||
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-4">
|
||
<div>
|
||
<p class="text-uppercase text-muted small mb-2">Комната #<?= (int) $roomId ?></p>
|
||
<h1 class="h3 fw-semibold mb-2"><?= htmlspecialchars($room['name']) ?></h1>
|
||
<p class="text-muted mb-0">Статус: <span class="badge text-bg-light border"><?= htmlspecialchars($room['status']) ?></span></p>
|
||
</div>
|
||
<div class="panel px-3 py-2 small text-muted">
|
||
Игроков: <strong><?= count($players) ?> / <?= (int) $room['max_players'] ?></strong>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if ($errors): ?>
|
||
<div class="alert alert-danger"><?= htmlspecialchars(implode(' ', $errors)) ?></div>
|
||
<?php endif; ?>
|
||
|
||
<?php if (!$isMember): ?>
|
||
<div class="panel p-4 mb-4">
|
||
<h2 class="h5 fw-semibold mb-3">Войти в комнату</h2>
|
||
<form method="post" class="row g-3">
|
||
<input type="hidden" name="action" value="join">
|
||
<div class="col-md-6">
|
||
<label class="form-label small text-muted">Ваш ник</label>
|
||
<input type="text" name="player_name" class="form-control" required>
|
||
</div>
|
||
<div class="col-12">
|
||
<button class="btn btn-dark">Войти и ожидать</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="row g-4">
|
||
<div class="col-lg-4">
|
||
<div class="panel p-4 h-100">
|
||
<h2 class="h6 text-uppercase text-muted">Игроки в комнате</h2>
|
||
<ul id="player-list" class="list-unstyled small mb-4"></ul>
|
||
<div class="d-grid gap-2">
|
||
<?php if ($isHost && $room['status'] === 'waiting'): ?>
|
||
<button id="start-match" class="btn btn-dark">Запустить матч</button>
|
||
<?php endif; ?>
|
||
<?php if ($room['status'] === 'playing'): ?>
|
||
<a class="btn btn-dark" href="match.php?id=<?= (int) $roomId ?>">Перейти в матч</a>
|
||
<?php endif; ?>
|
||
<?php if ($room['status'] === 'finished'): ?>
|
||
<a class="btn btn-outline-dark" href="match.php?id=<?= (int) $roomId ?>">Смотреть итог</a>
|
||
<?php endif; ?>
|
||
<a class="btn btn-outline-dark" href="/">Вернуться в лобби</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-lg-8">
|
||
<div class="panel p-4 h-100">
|
||
<h2 class="h6 text-uppercase text-muted">Ожидание старта</h2>
|
||
<p class="text-muted">Матч запустится после команды хоста. Статус обновляется автоматически.</p>
|
||
<div class="room-status-grid">
|
||
<div>
|
||
<div class="label">Подключено</div>
|
||
<div id="room-count" class="value">—</div>
|
||
</div>
|
||
<div>
|
||
<div class="label">Состояние</div>
|
||
<div id="room-state" class="value">—</div>
|
||
</div>
|
||
<div>
|
||
<div class="label">Победитель</div>
|
||
<div id="room-winner" class="value">—</div>
|
||
</div>
|
||
</div>
|
||
<div class="mt-4 small text-muted">
|
||
<strong>Подсказка:</strong> комната обновляется в realtime через push‑stream, fallback — обычный polling.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
||
<div id="room-toast" class="toast align-items-center text-bg-dark border-0" role="alert" aria-live="assertive" aria-atomic="true">
|
||
<div class="d-flex">
|
||
<div class="toast-body" id="room-toast-body">Обновление комнаты.</div>
|
||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
window.ROOM_ID = <?= (int) $roomId ?>;
|
||
window.PLAYER_TOKEN = <?= $sessionPlayer ? json_encode($sessionPlayer['token']) : 'null' ?>;
|
||
</script>
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||
<script src="assets/js/main.js?v=<?= time(); ?>"></script>
|
||
</body>
|
||
</html>
|