feature/bestof-series-interface #99
@@ -91,7 +91,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, onUnmounted, watch, inject } from 'vue'
|
import { ref, computed, onMounted, onUnmounted, watch, inject } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router'
|
||||||
import { useSocketStore } from '@/stores/socket'
|
import { useSocketStore } from '@/stores/socket'
|
||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
import { toast } from 'vue-sonner'
|
import { toast } from 'vue-sonner'
|
||||||
@@ -284,6 +284,32 @@ const handlePlayCard = (card) => {
|
|||||||
wsStore.playCard(gameId.value, card.id)
|
wsStore.playCard(gameId.value, card.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleBeforeUnload = (event) => {
|
||||||
|
if (shouldShowBoard.value && !gameOver.value) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.returnValue = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onBeforeRouteLeave((to, from, next) => {
|
||||||
|
if (gameOver.value || !shouldShowBoard.value) {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const answer = window.confirm(
|
||||||
|
"If you leave, you will be register has 'losing' or 'surrendered', Are you sure you want to leave?"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (answer) {
|
||||||
|
wsStore.surrender(gameId.value);
|
||||||
|
wsStore.disconnect();
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
next(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// --- FIX: SURRENDER LOGIC ---
|
// --- FIX: SURRENDER LOGIC ---
|
||||||
const handleSurrender = () => {
|
const handleSurrender = () => {
|
||||||
if(confirm('Are you sure you want to surrender? You will lose.')) {
|
if(confirm('Are you sure you want to surrender? You will lose.')) {
|
||||||
@@ -317,6 +343,8 @@ onMounted(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('beforeunload', handleBeforeUnload)
|
||||||
|
|
||||||
if (timerInterval.value) clearInterval(timerInterval.value)
|
if (timerInterval.value) clearInterval(timerInterval.value)
|
||||||
wsStore.disconnect()
|
wsStore.disconnect()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -248,7 +248,8 @@ export default (io, socket) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
socket.join(`game_${gameId}`);
|
socket.join(`game_${gameId}`);
|
||||||
|
socket.activeGameId = gameId;
|
||||||
|
|
||||||
if (game.players[myId]) {
|
if (game.players[myId]) {
|
||||||
game.players[myId].token = socket.handshake.auth.token;
|
game.players[myId].token = socket.handshake.auth.token;
|
||||||
|
|
||||||
|
|||||||
+57
-2
@@ -10,6 +10,9 @@ export const server = {
|
|||||||
|
|
||||||
const API_URL = "http://localhost:8000/api/v1";
|
const API_URL = "http://localhost:8000/api/v1";
|
||||||
|
|
||||||
|
const disconnectTimers = new Map();
|
||||||
|
const DISCONNECT_TIMEOUT = 1 * 60 * 1000; // 1 minutes
|
||||||
|
|
||||||
export const serverStart = (port) => {
|
export const serverStart = (port) => {
|
||||||
server.io = new Server(port, {
|
server.io = new Server(port, {
|
||||||
cors: {
|
cors: {
|
||||||
@@ -45,6 +48,12 @@ export const serverStart = (port) => {
|
|||||||
socket.user = user;
|
socket.user = user;
|
||||||
socket.handshake.auth.token = token;
|
socket.handshake.auth.token = token;
|
||||||
|
|
||||||
|
if (disconnectTimers.has(user.id)) {
|
||||||
|
console.log(`[Connection] User ${user.id} reconnected! Cancelling surrender timer.`);
|
||||||
|
clearTimeout(disconnectTimers.get(user.id));
|
||||||
|
disconnectTimers.delete(user.id);
|
||||||
|
}
|
||||||
|
|
||||||
addUser(socket.id, user);
|
addUser(socket.id, user);
|
||||||
|
|
||||||
next();
|
next();
|
||||||
@@ -64,11 +73,57 @@ export const serverStart = (port) => {
|
|||||||
|
|
||||||
server.io.on("connection", (socket) => {
|
server.io.on("connection", (socket) => {
|
||||||
gameEvents(server.io, socket);
|
gameEvents(server.io, socket);
|
||||||
|
console.log(`[Connection] User ${userId} connected. Socket ID: ${socket.id}`);
|
||||||
|
|
||||||
handleConnectionEvents(server.io, socket);
|
handleConnectionEvents(socket);
|
||||||
|
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
|
// Remove from active socket list
|
||||||
removeUser(socket.id);
|
removeUser(socket.id);
|
||||||
|
|
||||||
|
if (userId) {
|
||||||
|
console.log(`[Disconnect] User ${userId} disconnected. Waiting ${RECONNECT_GRACE_PERIOD/1000}s...`);
|
||||||
|
|
||||||
|
// -----------------------------------------------------------
|
||||||
|
// 2. START SURRENDER TIMER
|
||||||
|
// -----------------------------------------------------------
|
||||||
|
const timer = setTimeout(async () => {
|
||||||
|
console.log(`[Timeout] User ${userId} did not return. Forcing surrender.`);
|
||||||
|
|
||||||
|
// A. Find the Game ID this user is playing (You need a way to look this up)
|
||||||
|
// Ideally, your 'socket' object or a global map knows which gameID the user is in.
|
||||||
|
// For this example, let's assume you stored `socket.activeGameId` when they joined.
|
||||||
|
const gameId = socket.activeGameId;
|
||||||
|
|
||||||
|
if (gameId) {
|
||||||
|
try {
|
||||||
|
// B. Call Laravel API to resign on their behalf
|
||||||
|
// We use the token we saved during the handshake
|
||||||
|
await axios.post(`${API_URL}/games/${gameId}/resign`, {}, {
|
||||||
|
headers: {
|
||||||
|
Authorization: socket.handshake.auth.token,
|
||||||
|
Accept: "application/json"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// C. Notify the room (The API likely triggers a Pusher/Socket event,
|
||||||
|
// but we can also emit locally if needed)
|
||||||
|
server.io.to(gameId).emit("game_over", {
|
||||||
|
winner: "opponent",
|
||||||
|
reason: "disconnect"
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`[Timeout] Successfully resigned game ${gameId} for user ${userId}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[Timeout] Failed to resign game for user ${userId}:`, err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectTimers.delete(userId);
|
||||||
|
}, RECONNECT_GRACE_PERIOD);
|
||||||
|
|
||||||
|
disconnectTimers.set(userId, timer);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user