fix:websocket errors & polict change #92
@@ -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";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
meta {
|
||||||
|
name: Create Game
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{api_url}}/games
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
settings {
|
||||||
|
encodeUrl: true
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -218,7 +220,7 @@ class BiscaGame {
|
|||||||
: 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;
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 "
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const BiscaGame = require("../classes/BiscaGame");
|
import BiscaGame from '../classes/BiscaGame.js';
|
||||||
|
|
||||||
const activeGames = new Map();
|
const activeGames = new Map();
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ 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);
|
||||||
@@ -26,8 +26,4 @@ const game = activeGames.get(id);
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
export { createGame, getGame, removeGame };
|
||||||
createGame,
|
|
||||||
getGame,
|
|
||||||
removeGame,
|
|
||||||
};
|
|
||||||
|
|||||||
Reference in New Issue
Block a user