diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 04b9fda..3b37432 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -74,6 +74,16 @@ Given that you have already forked the repository and set it up locally: - Addressing reviews on existing PRs is more important than creating new PRs. Please be responsive to the feedback and make the necessary updates. - Create a new branch for each issue you are working on. This will help you keep your changes isolated and make it easier to manage multiple PRs. The branch should be created from upstream/master, and only after fetching the latest changes from the main branch from upstream first. +## How to make a good Pull Request +- Make sure your PR is solving only one issue. If you are solving multiple issues, create separate PRs for each issue. +- Make sure your PR is up-to-date with the main branch. If there are any conflicts, resolve them before opening the PR. +- See [code review](#code-review) section for more details on how to address review comments. +- Write a coherent pull request description linking to the issue you are solving and the approach and notable decisions you made while solving the issue. +- The Pull Request should pass all the checks before it can be merged. The checks include: + - ESLint checks (There should be no eslint errors at least in the files you have modified.) + - Prettier checks + - Unit tests + ## Common Git Operations you may need to perform During contribution, you will often need to rewrite commit history or sync your branch with the main branch. @@ -157,7 +167,7 @@ This will open VSCode whenever you run a command that requires a text editor, li All contributions go through a code review process to ensure the quality and maintainability of the codebase. During the review, maintainers may provide feedback or request changes to your code. Please be responsive to the feedback and make the necessary updates. There may be multiple rounds of review before your changes are approved. -When you open a pull request, you can request a review from the maintainers. You can also request a review after making changes in resopnse to feedback. The requested reviewer may then review the PR themselves or delegate to another maintainer. +When you open a pull request, you can request a review from the maintainers. You can also request a review after making changes in response to feedback. The requested reviewer may then review the PR themselves or delegate it to another maintainer. When requesting a review, make sure that your PR doesn't have merge conflicts. If it does, resolve the conflicts before requesting a review. Your PR will be merged only after the maintainers approve it. Different areas of codebase are handled by different maintainers. diff --git a/backend/.prettierrc b/backend/.prettierrc index 25b37d2..fe1af8e 100644 --- a/backend/.prettierrc +++ b/backend/.prettierrc @@ -2,5 +2,6 @@ "singleQuote": true, "trailingComma": "es5", "tabWidth": 4, - "semi": true + "semi": true, + "endOfLine": "lf" } diff --git a/backend/eventRecipients.ts b/backend/eventRecipients.ts new file mode 100644 index 0000000..3861c85 --- /dev/null +++ b/backend/eventRecipients.ts @@ -0,0 +1,18 @@ +// this module is responsible for handling the clients currently connected to the server. +// It stores the clients in a Map object, where the key is the client's user_id and the value is the client's http response object. + +import { Response } from 'express'; + +const clients = new Map(); + +export function addClient(userId: string, res: Response) { + clients.set(userId, res); +} + +export function removeClient(userId: string) { + clients.delete(userId); +} + +export function getClient(userId: string) { + return clients.get(userId); +} diff --git a/backend/routes/gameRoutes.js b/backend/routes/gameRoutes.js index e69de29..fa43827 100644 --- a/backend/routes/gameRoutes.js +++ b/backend/routes/gameRoutes.js @@ -0,0 +1,10 @@ +import express from 'express'; +import { addClient } from '../eventRecipients'; +const router = express.Router(); + +router.get('/events', (req, res) => { + addClient('user_id', res); +}); + +// the post handler should retrieve the game the user is currently in, and update the game state. +// The request body contains the event data, as described in ARCHITECTURE.md diff --git a/backend/src/types.d.ts b/backend/src/types.d.ts index 832d640..a606980 100644 --- a/backend/src/types.d.ts +++ b/backend/src/types.d.ts @@ -1,3 +1,6 @@ +// 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. + type CardType = 'number' | 'special' | 'wild'; type CardColor = 'red' | 'blue' | 'green' | 'yellow'; @@ -18,3 +21,31 @@ type Player = { id: string; cards: UNOCard[]; }; + +type EventResult = { + type: 'SUCCESS' | 'ERROR'; + message: string; +}; + +declare global { + type GameEngine = import('./engine').GameEngine; +} + +type GameEventType = 'DRAW_CARD' | 'THROW_CARD'; + +type GameEvent = + | { + type: 'DRAW_CARD'; + playerId: string; + data: { + card: UNOCard; + }; + } + | { + type: 'THROW_CARD'; + playerId: string; + data: { + card: UNOCard; + }; + }; +//todo: Add more events diff --git a/backend/uno-game-engine/engine.ts b/backend/uno-game-engine/engine.ts index 2af035d..17bf2fe 100644 --- a/backend/uno-game-engine/engine.ts +++ b/backend/uno-game-engine/engine.ts @@ -1,4 +1,5 @@ import { getShuffledCardDeck } from './deck'; +import { handleEvent } from './gameEvents'; const NUM_CARDS_PER_PLAYER = 7; @@ -52,4 +53,8 @@ export class GameEngine { .find((p: Player) => p.id === player.id) .cards.push(this.cardDeck.pop()); } + dispatchEvent(event: GameEvent) { + // handle different types of events based on event.type + handleEvent(this, event); + } } diff --git a/backend/uno-game-engine/gameEvents.ts b/backend/uno-game-engine/gameEvents.ts new file mode 100644 index 0000000..e8126b8 --- /dev/null +++ b/backend/uno-game-engine/gameEvents.ts @@ -0,0 +1,22 @@ +// this module houses the handlers for various game events. + +import { type GameEngine } from './engine'; + +type GameEventHandler = (game: GameEngine, event: GameEvent) => EventResult; + +const map = new Map(); + +export function registerEventHandler( + eventType: GameEventType, + handler: GameEventHandler +) { + map.set(eventType, handler); +} + +export function handleEvent(game: GameEngine, event: GameEvent): EventResult { + const handler = map.get(event.type); + if (!handler) { + return { type: 'ERROR', message: 'Invalid event type' }; + } + return handler(game, event); +}