Add cookies and game deletions

This commit is contained in:
jheaps
2025-03-27 16:15:48 -06:00
parent 79a37c911d
commit 9f6327f5d9
4 changed files with 144 additions and 37 deletions
+30 -6
View File
@@ -67,6 +67,8 @@ public class ChessController : ControllerBase
isWhite = false;
}
ScheduleRemoveGame(gameState.GameId, TimeSpan.FromDays(1));
return Ok(new
{
Id = playerId,
@@ -146,12 +148,12 @@ public class ChessController : ControllerBase
if (result.IsCheckmate || result.IsStalemate)
{
// queue game removal
_gameRemovalTasks.TryAdd(moveDto.GameId, Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromMinutes(1));
_games.Remove(moveDto.GameId, out _);
_gameRemovalTasks.Remove(moveDto.GameId, out _);
}));
ScheduleRemoveGame(gameState.GameId, TimeSpan.FromMinutes(1));
}
else
{
// increase timeout if play continues.
ScheduleRemoveGame(gameState.GameId, TimeSpan.FromDays(1));
}
return Ok(result);
@@ -189,4 +191,26 @@ public class ChessController : ControllerBase
return Ok(allMoves);
}
private static void ScheduleRemoveGame(Guid id, TimeSpan delay)
{
if (_gameRemovalTasks.ContainsKey(id))
{
_gameRemovalTasks[id] = Task.Run(async () =>
{
await Task.Delay(delay);
_games.Remove(id, out _);
_gameRemovalTasks.Remove(id, out _);
});
return;
}
_gameRemovalTasks.TryAdd(id, Task.Run(async () =>
{
await Task.Delay(delay);
_games.Remove(id, out _);
_gameRemovalTasks.Remove(id, out _);
}));
}
}
+8 -2
View File
@@ -11,8 +11,14 @@ public class ChessHub : Hub
await Groups.AddToGroupAsync(Context.ConnectionId, gameId);
}
public async Task MoveMade(string gameId, MoveResultDto moveResult)
public async Task MoveMade(string gameId, MoveDto moveDto, MoveResultDto moveResult)
{
await Clients.OthersInGroup(gameId).SendAsync("ReceiveMoveUpdate", gameId, moveResult);
await Clients.OthersInGroup(gameId).SendAsync("ReceiveMoveUpdate", gameId, moveDto, moveResult);
}
public async Task LeaveWebsocketGroup(string gameId)
{
Console.WriteLine($"❌ Leaving group {gameId}");
await Groups.RemoveFromGroupAsync(Context.ConnectionId, gameId);
}
}
+10
View File
@@ -41,4 +41,14 @@
#chessBoard.flipped {
transform: rotate(180deg);
}
.chessSquare.previous-start {
box-sizing: border-box;
border: 4px solid #ffd700; /* gold or whatever you like */
}
.chessSquare.previous-end {
box-sizing: border-box;
border: 4px solid #ff8c00; /* orange */
}
@@ -4,6 +4,8 @@ let currentPlayerIsWhite = null;
let signalRConnection = null;
let selectedPiece = null;
let legalMoves = [];
let previousMoveStart = null;
let previousMoveEnd = null;
async function startNewGame() {
// Stop previous SignalR connection if needed
@@ -19,35 +21,15 @@ async function startNewGame() {
currentGameId = gameData.gameId;
currentPlayerId = gameData.id;
currentPlayerIsWhite = gameData.isWhite;
previousMoveStart = null;
previousMoveEnd = null;
document.cookie = `chessGameId=${currentGameId}; path=/; max-age=86400`; // expires in 1 day
document.cookie = `chessPlayerId=${currentPlayerId}; path=/; max-age=86400`;
document.cookie = `chessPlayerIsWhite=${currentPlayerIsWhite}; path=/; max-age=86400`
console.log("🆕 Game started:", currentGameId);
// Build and start SignalR connection
signalRConnection = new signalR.HubConnectionBuilder()
.withUrl("/chessHub")
.configureLogging(signalR.LogLevel.Information)
.build();
signalRConnection.onclose(err => {
console.error("❌ SignalR connection closed:", err?.message);
});
signalRConnection.on("ReceiveMoveUpdate", async (gameId, moveResultDto) => {
if (gameId !== currentGameId) return;
const res = await fetch(`/api/chess/${gameId}`);
const data = await res.json();
renderPieces(data.pieces);
alertGameStatusChange(moveResultDto);
});
try {
await signalRConnection.start();
console.log("✅ SignalR connected");
await signalRConnection.invoke("JoinWebsocketGroup", currentGameId);
} catch (err) {
console.error("❌ SignalR failed to start or join:", err);
}
await setupSignalRConnection();
// Render initial state
const gameState = await fetch(`/api/chess/${currentGameId}`);
@@ -87,11 +69,23 @@ function renderPieces(pieces) {
for (let i = 0; i < 64; i++) {
const square = document.getElementById(`square-${i}`);
square.classList.remove("previous-start", "previous-end");
if (!square.classList.contains("legal")) {
square.onclick = () => clearHighlights();
}
}
if (previousMoveStart && previousMoveEnd) {
const [startRow, startCol] = flipCoordinates(...previousMoveStart);
const [endRow, endCol] = flipCoordinates(...previousMoveEnd);
const startIndex = startRow * 8 + startCol;
const endIndex = endRow * 8 + endCol;
document.getElementById(`square-${startIndex}`)?.classList.add("previous-start");
document.getElementById(`square-${endIndex}`)?.classList.add("previous-end");
}
}
function getPieceImageUrl(piece) {
@@ -164,6 +158,9 @@ async function handleMove(targetRow, targetCol) {
PromotionChoice: null // optional
};
previousMoveStart = [selectedPiece.row, selectedPiece.col];
previousMoveEnd = [targetRow, targetCol];
const res = await fetch("/api/chess/move", {
method: "POST",
headers: { "Content-Type": "application/json" },
@@ -184,7 +181,7 @@ async function handleMove(targetRow, targetCol) {
selectedPiece = null;
legalMoves = [];
clearHighlights();
await signalRConnection.invoke("MoveMade", currentGameId, moveResultDto);
await signalRConnection.invoke("MoveMade", currentGameId, moveDto, moveResultDto);
alertGameStatusChange(moveResultDto);
}
@@ -208,7 +205,8 @@ function clearHighlights() {
}
function alertGameStatusChange(moveResultDto) {
setTimeout(() => {
setTimeout(async () => {
let gameOver = false;
// 🟡 Optional: show check
if (moveResultDto.isCheck) {
console.log("🛑 Check!");
@@ -222,6 +220,12 @@ function alertGameStatusChange(moveResultDto) {
alert("🤝 Stalemate!");
gameOver = true;
}
if (gameOver) {
await signalRConnection.invoke("LeaveWebsocketGroup", currentGameId);
await signalRConnection.stop();
signalRConnection = null;
}
}, 500);
}
@@ -232,5 +236,68 @@ function flipCoordinates(row, col) {
return [row, col];
}
function getCookie(name) {
const value = document.cookie.split('; ')
.find(row => row.startsWith(name + '='));
return value ? value.split('=')[1] : null;
}
async function setupSignalRConnection() {
signalRConnection = new signalR.HubConnectionBuilder()
.withUrl("/chessHub")
.configureLogging(signalR.LogLevel.Information)
.build();
signalRConnection.onclose(err => {
console.error("❌ SignalR connection closed:", err?.message);
});
signalRConnection.on("ReceiveMoveUpdate", async (gameId, moveDto, moveResultDto) => {
if (gameId !== currentGameId) return;
const res = await fetch(`/api/chess/${gameId}`);
const data = await res.json();
previousMoveStart = [moveDto.sourceRow, moveDto.sourceCol];
previousMoveEnd = [moveDto.targetRow, moveDto.targetCol];
renderPieces(data.pieces);
alertGameStatusChange(moveResultDto);
});
try {
await signalRConnection.start();
console.log("✅ SignalR connected");
await signalRConnection.invoke("JoinWebsocketGroup", currentGameId);
} catch (err) {
console.error("❌ SignalR failed to start or join:", err);
}
}
console.log("chessLogic.js loaded");
window.startNewGame = startNewGame;
window.startNewGame = startNewGame;
window.addEventListener('load', async () => {
const savedGameId = getCookie("chessGameId");
const savedPlayerId = getCookie("chessPlayerId");
const savedPlayerIsWhite = getCookie("chessPlayerIsWhite");
if (savedGameId && savedPlayerId) {
try {
const response = await fetch(`/api/chess/${savedGameId}`);
if (response.ok) {
console.log("🧠 Rejoining saved game...");
currentGameId = savedGameId;
currentPlayerId = savedPlayerId;
currentPlayerIsWhite = (savedPlayerIsWhite === "true");
await setupSignalRConnection(); // use your existing SignalR connect logic
const gameData = await response.json();
renderPieces(gameData.pieces);
} else {
console.warn("Saved game not found or expired.");
}
} catch (err) {
console.error("Failed to rejoin saved game:", err);
}
}
});