fix:websocket errors & polict change

This commit is contained in:
2026-01-02 14:26:54 +00:00
parent ef277a3cfd
commit 3a7b1b9794
6 changed files with 64 additions and 42 deletions
+17 -9
View File
@@ -20,7 +20,7 @@ class GamePolicy
*/ */
public function view(User $user, Game $game): bool public function view(User $user, Game $game): bool
{ {
if ($user->type === 'A') { if ($user->type === "A") {
return true; return true;
} }
@@ -31,6 +31,10 @@ class GamePolicy
return true; return true;
} }
if ($game->status === "Pending") {
return true;
}
return false; return false;
} }
@@ -39,7 +43,7 @@ class GamePolicy
*/ */
public function create(User $user): bool public function create(User $user): bool
{ {
if ($user->type === 'A') { if ($user->type === "A") {
return false; return false;
} }
@@ -48,28 +52,32 @@ class GamePolicy
public function join(User $user, Game $game): bool public function join(User $user, Game $game): bool
{ {
if ($user->type === 'A') { if ($user->type === "A") {
return false; return false;
} }
if ( if (
$game->status !== 'waiting' || $game->status !== "Pending" ||
$game->player1_user_id === $user->id $game->player1_user_id === $user->id
) { ) {
return false; return false;
} }
if ($game->player2_user_id !== null && $game->player2_user_id !== 1) {
return false;
}
return true; return true;
} }
public function resign(User $user, Game $game): bool public function resign(User $user, Game $game): bool
{ {
if ($user->type === 'A') { if ($user->type === "A") {
return false; return false;
} }
if ( if (
$game->status !== 'ongoing' || $game->status !== "Playing" ||
($game->player1_user_id !== $user->id && ($game->player1_user_id !== $user->id &&
$game->player2_user_id !== $user->id) $game->player2_user_id !== $user->id)
) { ) {
@@ -84,12 +92,12 @@ class GamePolicy
*/ */
public function update(User $user, Game $game): bool public function update(User $user, Game $game): bool
{ {
if ($user->type === 'A') { if ($user->type === "A") {
return false; return false;
} }
if ( if (
$game->status !== 'ongoing' || $game->status !== "Playing" ||
($game->player1_user_id !== $user->id && ($game->player1_user_id !== $user->id &&
$game->player2_user_id !== $user->id) $game->player2_user_id !== $user->id)
) { ) {
@@ -105,7 +113,7 @@ class GamePolicy
public function delete(User $user, Game $game): bool public function delete(User $user, Game $game): bool
{ {
// Only admins can delete games // Only admins can delete games
return $user->type === 'A'; return $user->type === "A";
} }
/** /**
+15
View File
@@ -0,0 +1,15 @@
meta {
name: Create Game
type: http
seq: 3
}
post {
url: {{api_url}}/games
body: none
auth: inherit
}
settings {
encodeUrl: true
}
+15 -13
View File
@@ -17,7 +17,7 @@ class BiscaGame {
}; };
this.currentTurn = null; this.currentTurn = null;
this.status = "pending"; this.status = 'pending';
this.startTime = null; this.startTime = null;
this.players = { this.players = {
@@ -50,7 +50,7 @@ class BiscaGame {
this.currentTurn = playerIds.find((id) => id != 1) || playerIds[0]; this.currentTurn = playerIds.find((id) => id != 1) || playerIds[0];
this.startTime = Date.now(); this.startTime = Date.now();
this.status = "playing"; this.status = 'playing';
} }
startTurnTimer(callback) { startTurnTimer(callback) {
@@ -74,7 +74,9 @@ class BiscaGame {
} }
handleTimeout() { handleTimeout() {
console.log(`[Game ${this.id}] Timeout! Auto-playing for ${this.currentTurn}`); console.log(
`[Game ${this.id}] Timeout! Auto-playing for ${this.currentTurn}`,
);
const lowestCard = this.getLowestCard(this.currentTurn); const lowestCard = this.getLowestCard(this.currentTurn);
@@ -92,7 +94,7 @@ class BiscaGame {
} }
createDeck() { createDeck() {
const suits = ["c", "e", "o", "p"]; const suits = ['c', 'e', 'o', 'p'];
const ranks = [1, 2, 3, 4, 5, 6, 7, 11, 12, 13]; const ranks = [1, 2, 3, 4, 5, 6, 7, 11, 12, 13];
let deck = []; let deck = [];
@@ -128,7 +130,7 @@ class BiscaGame {
} }
playCard(userId, cardId) { playCard(userId, cardId) {
if (this.currentTurn != userId) return { error: "Not your turn!" }; if (this.currentTurn != userId) return { error: 'Not your turn!' };
const player = this.players[userId]; const player = this.players[userId];
const cardIndex = player.hand.findIndex((c) => c.id === cardId); const cardIndex = player.hand.findIndex((c) => c.id === cardId);
@@ -142,7 +144,7 @@ class BiscaGame {
const suitToFollow = this.table.playerCard.suit; const suitToFollow = this.table.playerCard.suit;
const hasSuit = player.hand.some((c) => c.suit === suitToFollow); const hasSuit = player.hand.some((c) => c.suit === suitToFollow);
if (hasSuit && card.suit !== suitToFollow) { if (hasSuit && card.suit !== suitToFollow) {
return { error: "You must follow the suit!" }; return { error: 'You must follow the suit!' };
} }
} }
@@ -154,7 +156,7 @@ class BiscaGame {
this.table.playerCard = card; this.table.playerCard = card;
this.table.firstPlayerId = userId; this.table.firstPlayerId = userId;
this.currentTurn = this.getOpponentId(userId); this.currentTurn = this.getOpponentId(userId);
return { action: "next_turn" }; return { action: 'next_turn' };
} else { } else {
this.table.opponentCard = card; this.table.opponentCard = card;
return this.resolveTrick(); return this.resolveTrick();
@@ -194,7 +196,7 @@ class BiscaGame {
this.table = { playerCard: null, opponentCard: null, firstPlayerId: null }; this.table = { playerCard: null, opponentCard: null, firstPlayerId: null };
if (this.players[winnerId].hand.length === 0) { if (this.players[winnerId].hand.length === 0) {
this.status = "finished"; this.status = 'finished';
this.stopTimer(); this.stopTimer();
const pIds = Object.keys(this.players); const pIds = Object.keys(this.players);
@@ -214,11 +216,11 @@ class BiscaGame {
const finalLoser = isDraw const finalLoser = isDraw
? null ? null
: gameWinnerId == p1Obj.id : gameWinnerId == p1Obj.id
? p2Obj.id ? p2Obj.id
: p1Obj.id; : p1Obj.id;
return { return {
action: "game_ended", action: 'game_ended',
trickResult, trickResult,
winnerId: finalWinner, winnerId: finalWinner,
loserId: finalLoser, loserId: finalLoser,
@@ -231,7 +233,7 @@ class BiscaGame {
}; };
} }
return { action: "trick_resolved", trickResult }; return { action: 'trick_resolved', trickResult };
} }
getStateForPlayer(userId) { getStateForPlayer(userId) {
@@ -277,4 +279,4 @@ class BiscaGame {
} }
} }
module.exports = BiscaGame; export default BiscaGame;
+8 -8
View File
@@ -1,4 +1,4 @@
import { addUser, removeUser } from "../state/connection"; import { addUser, removeUser } from '../state/connection.js';
const HEARTBEAT_INTERVAL = 30000; // 30 seconds const HEARTBEAT_INTERVAL = 30000; // 30 seconds
const HEARTBEAT_TIMEOUT = 10000; // 10 seconds const HEARTBEAT_TIMEOUT = 10000; // 10 seconds
@@ -8,36 +8,36 @@ export const handleConnectionEvents = (io, socket) => {
let heartbeatTimeout; let heartbeatTimeout;
const startHeartbeat = () => { const startHeartbeat = () => {
heartbeatInterval = setInterval(() => { heartbeatInterval = setInterval(() => {
socket.emit("heartbeat"); socket.emit('heartbeat');
heartbeatTimeout = setTimeout(() => { heartbeatTimeout = setTimeout(() => {
console.log( console.log(
`[Heartbeat] No response from ${socket.id}, disconnecting...` `[Heartbeat] No response from ${socket.id}, disconnecting...`,
); );
socket.disconnect(true); socket.disconnect(true);
}, HEARTBEAT_TIMEOUT); }, HEARTBEAT_TIMEOUT);
}, HEARTBEAT_INTERVAL); }, HEARTBEAT_INTERVAL);
}; };
socket.on("heartbeat_ack", () => { socket.on('heartbeat_ack', () => {
clearTimeout(heartbeatTimeout); clearTimeout(heartbeatTimeout);
console.log(`[Heartbeat] Received pong from ${socket.id}`); console.log(`[Heartbeat] Received pong from ${socket.id}`);
}); });
socket.on("join", (user) => { socket.on('join', (user) => {
addUser(socket, user); addUser(socket, user);
console.log(`[Connection] User ${user.name} has joined the server`); console.log(`[Connection] User ${user.name} has joined the server`);
}); });
socket.on("leave", () => { socket.on('leave', () => {
const user = removeUser(socket.id); const user = removeUser(socket.id);
if (user) { if (user) {
console.log(`[Connection] User ${user.name} has left the server`); console.log(`[Connection] User ${user.name} has left the server`);
} }
}); });
socket.on("disconnect", () => { socket.on('disconnect', () => {
console.log("Connection Lost:", socket.id); console.log('Connection Lost:', socket.id);
removeUser(socket.id); removeUser(socket.id);
}); });
}; };
+1
View File
@@ -2,6 +2,7 @@
"name": "websockets", "name": "websockets",
"version": "1.0.0", "version": "1.0.0",
"main": "index.js", "main": "index.js",
"type": "module",
"scripts": { "scripts": {
"dev": "nodemon index.js", "dev": "nodemon index.js",
"start": "bun index.js " "start": "bun index.js "
+6 -10
View File
@@ -1,4 +1,4 @@
const BiscaGame = require("../classes/BiscaGame"); import BiscaGame from '../classes/BiscaGame.js';
const activeGames = new Map(); const activeGames = new Map();
@@ -18,16 +18,12 @@ const getGame = (id) => {
}; };
const removeGame = (id) => { const removeGame = (id) => {
const game = activeGames.get(id); const game = activeGames.get(id);
if (game) { if (game) {
game.stopTimer(); game.stopTimer();
activeGames.delete(id); activeGames.delete(id);
console.log(`[Game State] Game removed: ${id}`); console.log(`[Game State] Game removed: ${id}`);
} }
}; };
module.exports = { export { createGame, getGame, removeGame };
createGame,
getGame,
removeGame,
};