{"id":31265461,"url":"https://github.com/anmoltutejagithub/64","last_synced_at":"2026-04-10T13:31:56.190Z","repository":{"id":315878192,"uuid":"1049758027","full_name":"AnmolTutejaGitHub/64","owner":"AnmolTutejaGitHub","description":"64 is multiplayer chess gaming platform build for players to compete in chess games.","archived":false,"fork":false,"pushed_at":"2025-10-23T13:56:37.000Z","size":65857,"stargazers_count":0,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-23T15:37:01.658Z","etag":null,"topics":["mongodb","nodejs","reactjs","redis","websocket"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AnmolTutejaGitHub.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-03T13:02:47.000Z","updated_at":"2025-10-23T13:56:41.000Z","dependencies_parsed_at":"2025-09-21T11:48:34.059Z","dependency_job_id":null,"html_url":"https://github.com/AnmolTutejaGitHub/64","commit_stats":null,"previous_names":["anmoltutejagithub/64"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/AnmolTutejaGitHub/64","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnmolTutejaGitHub%2F64","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnmolTutejaGitHub%2F64/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnmolTutejaGitHub%2F64/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnmolTutejaGitHub%2F64/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AnmolTutejaGitHub","download_url":"https://codeload.github.com/AnmolTutejaGitHub/64/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnmolTutejaGitHub%2F64/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31645281,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-10T07:40:12.752Z","status":"ssl_error","status_checked_at":"2026-04-10T07:40:11.664Z","response_time":98,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mongodb","nodejs","reactjs","redis","websocket"],"created_at":"2025-09-23T14:36:30.349Z","updated_at":"2026-04-10T13:31:56.166Z","avatar_url":"https://github.com/AnmolTutejaGitHub.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## NOTE : \nI have some bills to pay, so ask me to start the AWS servers.   \nPlease note that the AWS public DNS may change as there is no static IP assigned.   \nCurrent URL (may change after restart):   \n`http://ec2-52-91-4-210.compute-1.amazonaws.com:5173`\n\n# 64\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://socialify.git.ci/AnmolTutejaGithub/64/image?font=Raleway\u0026forks=1\u0026issues=1\u0026language=1\u0026name=1\u0026owner=1\u0026pattern=Floating+Cogs\u0026pulls=1\u0026stargazers=1\u0026theme=Dark\" alt=\"64\" /\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://hits.sh/github.com/AnmolTutejaGitHub/64/\"\u003e\n    \u003cimg src=\"https://hits.sh/github.com/AnmolTutejaGitHub/64.svg?style=plastic\u0026color=0077bf\" alt=\"Hits\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n### Introduction \n**64** is a real-time multiplayer chess platform where players can compete in various modes (Bullet, Blitz, and Rapid). \nThe app allows user to play chess game with other players and with world's best chess engine (stockfish)\n\n### Setup and Installation \n#### Pre-Requisities     \n- Node.js\n- Redis\n- MongoDB (local or cloud-based,like MongoDB Atlas)\n- npm or yarn for package management\n\n#### Installation\n1. Clone the repository:   \n```bash\ngit clone https://github.com/AnmolTutejaGitHub/64\ncd 64\n```\n2. Install dependencies for both the client and server:\n```bash\n# Navigate to server and install\ncd server\nnpm install\n\n# Navigate to client and install\ncd ../client\nnpm install\n```\n\n3. Set up Environment Variables: See `env.example` for required variables and setup.    \n\n4. Run The Application:   \n```bash\n# In the client folder\nnpm run dev\n\n# In the server folder\nnpm run start:main\nnpm run start:ws\nnpm run start:stockfish\nnpm run start:microservice:dbupdates\n```\n\n### Environment Variables\nThe project relies on several environment variables. Create a .env file in both the server and client directories with the following variables:         \n##### For Server: \n```bash\nNODEMAIL_APP_PASSWORD=YOUR_NODEMAIL_APP_PASSWORD\nNODEMAILER_MAIL=YOUR_NODEMAIL_EMAIL\nMONGODB_URL=\"mongodb://localhost:27017\"\nJWT_TOKEN_SECRET=YOUR_JWT_SECRET_1\nJWT_TOKEN_SIGNUP_MAIL_SECRET=YOUR_JWT_SECRET_2\nJWT_RESET_PASSWORD_SECRET=YOUR_JWT_SECRET_3\nFRONTEND_URL=\"http://localhost:5173\"\nREDIS_URL=\"redis://localhost:6379\"\nSTOCKFISH_EMAIL=STOCKFISH_ACCOUNT_EMAIL_ID # stockfish is treated as a user \nSTOCKFISH_PASSWORD=STOCKFISH_ACCOUNT_PASSWORD\n```\n\n##### For Client:\n```bash\nVITE_API_URL=\"http://localhost:8080\"\nVITE_GAME_SERVER_API_URL=\"http://localhost:9090\"\nVITE_STOCKFISH_SERVER_API_URL=\"http://localhost:8081\"\n```\n\n### Tech Stack\n\n#### Frontend\n- **React**: For building a responsive and dynamic user interface.\n- **React Router**: For handling routing in the single-page application.\n- **Zustand**: Lightweight state management to handle user and session data.\n- **Tailwind CSS**: For styling the application with a focus on customization and a responsive layout.\n- **React Hot Toast**: For displaying user-friendly notifications.\n- **React Chessboard**: For displaying chessboard.\n\n#### Backend\n- **Node.js**: JavaScript runtime used to build the server-side of the application.\n- **Express.js**: Web framework for building RESTful APIs and handling middleware.\n- **MongoDB**: NoSQL database for storing user and transaction data.\n- **Mongoose**: ODM for MongoDB, providing schema and data validation.\n- **JWT (JSON Web Tokens)**: For secure user authentication and session management.\n- **Nodemailer**: For sending email notifications and handling email verification.\n- **Redis** : For cache and storing game state temporarily.\n- **Axios**: For handling HTTP requests in API calls.\n- **chess.js** : For chess move validation\n- **Stockfish** : To get best move in a chess position.\n\n#### Security and Authorization\n- **bcrypt**: For hashing user passwords to enhance security.\n- **JWT Authentication**: For secure, token-based user authentication.\n- **Environment Variables**: Sensitive information is stored in environment variables using .env files for security.\n\n#### Authentication and Authorization\n- **Signup \u0026 Login**: Users must sign up and log in to access most features.\n- **JWT**: JSON Web Token (JWT) is used for user sessions.\n- **Protected Routes**: API endpoints require a valid token in the Authorization header.\n- **Email Verification**: After signup, users must verify their email before accessing their dashboard.\n\n#### Backend Architecture\nThe backend is split into multiple servers, all communicating through **Redis** as a shared backbone.\n\n| Server | Port | Responsibility |\n|---|---|---|\n| **Main Server** | 8080 | Auth, matchmaking, REST APIs, socket registry |\n| **WS (Game) Server** | 9090 | Real-time gameplay via WebSocket |\n| **Stockfish Server** | 8081 | gameplay with stockfish |\n| **DB Microservice** | — | Batch-writes ended games to MongoDB |\n\n---\n\n#### Matchmaking \u0026 Game Flow\n\n```\nPhase 1 — Get a Request ID\n──────────────────────────\nClient  →  POST /api/game/get-requestid  { mode }\n        ←  { requestId, redirect: \"/find/blitz/\u003crequestId\u003e\" }\nRedis:     HSET requestIdMap   requestId → { userid, mode }\n\nPhase 2 — Register Socket on Main Server\n─────────────────────────────────────────\nClient opens socket to Main Server (on the /find/\u003cmode\u003e/\u003crequestId\u003e page)\nRedis:     HSET socketMap   userid → socket.id\n           (used later to push MATCH_FOUND to the right client)\n\nPhase 3 — Enter the Matchmaking Queue\n──────────────────────────────────────\nClient  →  POST /api/game/find-match  { mode, requestId }\n\nAtomic Lua script runs in Redis:\n  ┌──────────────────────────────────────────────────┐\n  │  opponent = LPOP queue:\u003cmode\u003e                    │\n  │  if opponent found:                              │\n  │      HDEL queueMap:\u003cmode\u003e  opponent              │\n  │      return { userid: opponent, requestId }      │\n  │  else (no one waiting yet):                      │\n  │      RPUSH queue:\u003cmode\u003e  myUserId                │\n  │      HSET queueMap:\u003cmode\u003e  myUserId → requestId  │\n  │      return nil                                  │\n  └──────────────────────────────────────────────────┘\n\n  → No opponent:   return { status: \"WAITING\" }\n                   Client sits on /find/\u003cmode\u003e/\u003crequestId\u003e page and waits.\n\n  → Opponent found (second player to hit find-match):\n      gameid = new UUID\n      PUBLISH \"game:new\" → { gameid, white_id, black_id, mode }\n      Lookup socket IDs of both players from socketMap\n      Emit MATCH_FOUND to both sockets:\n          { gameid, white, black, websocket_url, redirect }\n      HSET requestIdResolved  requestId → gameDetails (both players)\n      return { status: \"MATCH_FOUND\", gameid }\n\n  ⚡ Client does NOT poll — the server pushes MATCH_FOUND\n     via socket the moment an opponent is found.\n\nPhase 4 — WS Server Creates the Game (Redis Pub/Sub)\n──────────────────────────────────────────────────────\nWS Server is subscribed to \"game:new\" channel.\nOn message:\n    gameRegistry.createGame(gameid, white_id, black_id, mode)\n    → Game object created in WS server memory\n    → Game state saved to Redis as  game:\u003cgameid\u003e  (TTL 1hr)\n\nPhase 5 — Players Connect to WS Server \u0026 Play\n───────────────────────────────────────────────\nBoth clients receive MATCH_FOUND and redirect to /game/blitz/\u003cgameid\u003e\nThey connect their socket to WS Server (port 9090):\n    handshake: { token (JWT), gameid }\n\n    → gameRegistry.getGame(gameid)  (memory → Redis fallback)\n    → socket.join(gameid)           both players in same room\n\nEvents handled by WS Server:\n    NEW_MOVE  →  game.makeMove()   →  broadcast to room\n    RESIGN    →  game.resign()     →  publishEndedGame()\n                                         → Redis publish \"gameEnded\"\n                                         → game deleted from registry\n\nPhase 6 — Game End\n────────────────────\npublishEndedGame():\n    PUBLISH \"gameEnded\"  →  DB Microservice picks it up\n                             and batch-writes final state to MongoDB\n    onEnd(gameid)        →  Game object removed from WS server memory\n```\n\n---\n\n#### Redis Keys Reference\n\n| Key | Type | Purpose |\n|---|---|---|\n| `requestIdMap` | Hash | `requestId → { userid, mode }` — pending matchmaking requests |\n| `requestIdResolved` | Hash | `requestId → gameDetails` — marks a request as matched/cancelled |\n| `queue:\u003cmode\u003e` | List | FIFO queue of userids waiting for a match |\n| `queueMap:\u003cmode\u003e` | Hash | `userid → requestId` — maps queued users to their request |\n| `socketMap` | Hash | `userid → socketId` — used to emit events to specific clients |\n| `game:\u003cgameid\u003e` | String | Full serialized game state (TTL: 1 hour) |\n| `gameInvite:\u003cuuid\u003e` | Hash | `{ mode, player1, player2 }` — private invite state (TTL: 10 min) |\n| `queueheartbeatmap` | Hash | `userid → timestamp` — heartbeat for detecting stale queue entries |\n\n---\n\n#### TL;DR — Matchmaking\n\n1. **Client clicks \"Start Game\" (Rapid/Blitz/Bullet)**\n   → hits `POST /api/game/get-requestid`\n   → server generates a `requestId` (UUID), stores `requestId → { userid, mode }` in Redis (`requestIdMap`)\n   → redirects client to `/find/\u003cmode\u003e/\u003crequestId\u003e`\n\n2. **Client lands on `/find/\u003cmode\u003e/\u003crequestId\u003e`**\n   → opens a socket connection to the **Main Server** (sends `userid` in handshake)\n   → Main Server stores `userid → socketId` in Redis (`socketMap`) and fires back `socket:registered`\n\n3. **On `socket:registered`, client hits `POST /api/game/find-match` once (not a poll)**\n   → server first checks:\n   - **Already in queue with same requestId?** → return `WAITING` (no-op)\n   - **requestId already resolved?** → match already found, return `RESOLVED`\n   → then runs an atomic Lua script in Redis:\n   - **No one in queue?** → `RPUSH queue:\u003cmode\u003e`, stores `userid → requestId` in `queueMap:\u003cmode\u003e`, return `WAITING`\n   - **Opponent found?** → `LPOP` opponent, create `gameid`, then:\n     - mark both `requestId`s as resolved in `requestIdResolved` (so stale retries are handled)\n     - publish `game:new` to Redis pub/sub → WS Server creates the Game object\n     - look up both socket IDs from `socketMap`\n     - **push `match_found` socket event to both clients**\n\n4. **Client receives `match_found` → navigates to `/game/\u003cgameid\u003e`**\n   → connects to **WS Server** with `{ token (JWT), gameid }` in handshake\n   → WS Server decodes JWT to get `userid`, loads the game, joins the socket room\n   → both players are now in the same room — moves, resign etc. flow in real-time\n\n\n### Changelog\nRefer to [CHANGELOG](CHANGELOG.md) for version history and updates.\n\n### Version 2 Updates : \nRef to [CHALLENGES](Challenges.md) to see what's coming up in V2.\n\n### Contributing\nWe appreciate your interest in contributing to 64! Your contributions help us improve and grow. Please feel free to submit pull requests, report issues, or suggest new features. Your feedback and participation are highly valued as we continue to develop and enhance the platform.\n\n### License\n64 is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanmoltutejagithub%2F64","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanmoltutejagithub%2F64","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanmoltutejagithub%2F64/lists"}