Skip to content

Commit

Permalink
game: Propagate join events.
Browse files Browse the repository at this point in the history
When a user hosts a game and others join, everyone's
screen is updated to reflect the new joined player.
  • Loading branch information
kuv2707 committed Jun 15, 2024
1 parent a1244c0 commit 3cf4acd
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 10 deletions.
22 changes: 14 additions & 8 deletions backend/src/controllers/gameControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Response } from 'express';
import { enqueueForSend } from '../eventRecipients';
import { AuthRequest } from '../middlewares/authMiddleware';
import { createGame, retrieveGame } from '../gameStore';
import { GameEngine } from '../uno-game-engine/engine';

export async function handleGameEvent(req: AuthRequest, res: Response) {
const event = req.body;
Expand All @@ -24,14 +25,7 @@ export async function handleGameEvent(req: AuthRequest, res: Response) {
res.status(400).send({ message: result.message });
return;
} else {
// the game state after a successful event is propagated to all clients
// we can choose to relay the event received, so that the clients apply the event
// to their local game state, but that would be an extra implementation burden.
// Instead, we can just send the new game state to the clients.
// todo: send updated game state rather than event
for (const player of game.players) {
enqueueForSend(player.id, event);
}
propagateChanges(game);
res.status(200).send({ message: 'Event propagated to clients.' });
}
}
Expand All @@ -53,6 +47,7 @@ export async function handleGameJoin(req: AuthRequest, res: Response) {
//note: when retrieving game from database, it is not an instance of GameEngine
// we'd need to add these functions to the mongodb game schema
game.dispatchEvent({ type: 'JOIN_GAME', playerId: req.user.id });
propagateChanges(game);
req.user.activeGameId = gameCode;
await req.user.save();
res.status(200).send({
Expand All @@ -78,3 +73,14 @@ export async function handleGameCreate(req: AuthRequest, res: Response) {
gameState: game,
});
}

// temporarily here
function propagateChanges(game: GameEngine) {
// the game state after a successful event is propagated to all clients
// we can choose to relay the event received, so that the clients apply the event
// to their local game state, but that would be an extra implementation burden.
// Instead, we can just send the new game state to the clients.
for (const player of game.players) {
enqueueForSend(player.id, game);
}
}
2 changes: 2 additions & 0 deletions backend/src/controllers/userController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ const loginUser: ControllerFunction = catchError(
const verifyUser: ControllerFunction = catchError(
async (req: AuthRequest, res: Response): Promise<void> => {
const user = req.user as IUser;
req.user.activeGameId = null;
await req.user.save();
res.status(200).json({ user });
}
);
Expand Down
2 changes: 1 addition & 1 deletion backend/src/middlewares/authMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const verifyToken = async (
next: NextFunction
) => {
try {
const accessToken: string = req.body.token;
const accessToken: string = req.body.token || req.headers.authorization;
if (!accessToken) {
return res.status(401).json({ error: 'Access token is required' });
}
Expand Down
5 changes: 4 additions & 1 deletion backend/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// We declare those types which are used throughout the application here.
// For types that are used only in one file, we can declare them in that file itself.

import { GameEngine } from './uno-game-engine/engine';

export type CardType = 'number' | 'special' | 'wild';

export type CardColor = 'red' | 'blue' | 'green' | 'yellow' | 'wild';
Expand Down Expand Up @@ -73,5 +75,6 @@ export type GameEvent =
};

// Represent all the events that can be sent to the client
export type AppEvent = GameEvent;
// a workaround for now to make things work - this will be refactored later
export type AppEvent = GameEvent | GameEngine;
//todo: Add more events
20 changes: 20 additions & 0 deletions frontend/src/contexts/GameContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,26 @@ export const GameProvider = () => {
setupGame();
}, [location.search]);

// polling
useEffect(() => {
async function poll() {
const res = await fetch(`${backendUrl}/game/events`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: auth.jwt,
},
});
if (!res.ok) {
throw new Error((await res.json()).error);
}
const data = await res.json();
// to be changed later to have a more sensible structure
setGameState(data.events[0]);
}
poll();
}, [gameState]);

return (
<GameContext.Provider value={{ gameState, setGameState }}>
{gameState ? (
Expand Down

0 comments on commit 3cf4acd

Please sign in to comment.