prepare("INSERT INTO matches (game,p1,turn,state,status,created_at,updated_at) VALUES (?,?,1,?, 'open',?,?)") ->execute([$game, $uid, $state, $now, $now]); return (int)db()->lastInsertId(); } function mp_join(string $game, int $mid, int $uid): bool { $s = db()->prepare("SELECT * FROM matches WHERE id=? AND game=?"); $s->execute([$mid, $game]); $m = $s->fetch(); if (!$m || $m['status'] !== 'open' || (int)$m['p1'] === $uid) return false; db()->prepare("UPDATE matches SET p2=?, status='playing', updated_at=? WHERE id=?") ->execute([$uid, time(), $mid]); return true; } function mp_get(string $game, int $mid): ?array { $s = db()->prepare("SELECT m.*, a.username AS n1, b.username AS n2 FROM matches m LEFT JOIN users a ON a.id=m.p1 LEFT JOIN users b ON b.id=m.p2 WHERE m.id=? AND m.game=?"); $s->execute([$mid, $game]); return $s->fetch() ?: null; } function mp_save(int $mid, string $state, int $turn, string $status = 'playing', ?int $winner = null): void { db()->prepare("UPDATE matches SET state=?, turn=?, status=?, winner=?, updated_at=? WHERE id=?") ->execute([$state, $turn, $status, $winner, time(), $mid]); } // Which seat is this user in? 1, 2, or 0 for spectator. function mp_seat(array $m, int $uid): int { if ((int)$m['p1'] === $uid) return 1; if ((int)$m['p2'] === $uid) return 2; return 0; } // Render the lobby: open games to join, your active games, and a create button. function mp_lobby(string $game, string $self, int $uid, string $newState): void { $d = db(); $s = $d->prepare("SELECT m.*, a.username AS n1 FROM matches m LEFT JOIN users a ON a.id=m.p1 WHERE m.game=? AND m.status='open' AND m.p1<>? ORDER BY m.id DESC LIMIT 10"); $s->execute([$game, $uid]); $open = $s->fetchAll(); $s = $d->prepare("SELECT m.*, a.username AS n1, b.username AS n2 FROM matches m LEFT JOIN users a ON a.id=m.p1 LEFT JOIN users b ON b.id=m.p2 WHERE m.game=? AND (m.p1=? OR m.p2=?) AND m.status IN ('open','playing') ORDER BY m.updated_at DESC LIMIT 10"); $s->execute([$game, $uid, $uid]); $mine = $s->fetchAll(); if ($mine) { p_para('Your games:'); foreach ($mine as $m) { $opp = ((int)$m['p1'] === $uid) ? ($m['n2'] ?? 'waiting...') : ($m['n1'] ?? '?'); $yourTurn = ($m['status'] === 'playing' && mp_seat($m, $uid) === (int)$m['turn']); p_link($self, '#' . $m['id'] . ' vs ' . $opp . ($yourTurn ? ' - YOUR TURN' : ''), ['g' => $m['id']]); } } if ($open) { p_para('Open games to join:'); foreach ($open as $m) { p_link($self, 'Join #' . $m['id'] . ' by ' . ($m['n1'] ?? '?'), ['g' => $m['id'], 'join' => 1]); } } if (!$mine && !$open) p_para('No games yet - create one and wait for an opponent.'); p_form($self, [ ['name' => 'act', 'type' => 'hidden', 'value' => 'create'], ], 'Create new game'); }