{"id":50885930,"url":"https://github.com/kbennett2000/lan-games","last_synced_at":"2026-06-15T17:01:34.601Z","repository":{"id":357498356,"uuid":"1237074963","full_name":"kbennett2000/lan-games","owner":"kbennett2000","description":"A lightweight, vanilla JavaScript framework for building real-time multiplayer LAN games (Monopoly, Connect Four, Scrabble, Uno, etc.). Includes auth, persistence, hot-reload config, and pure-function game logic.","archived":false,"fork":false,"pushed_at":"2026-05-25T13:54:41.000Z","size":480,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-25T15:25:15.745Z","etag":null,"topics":["board-games","game-framework","javascript","lan-party","multiplayer-game","socketio","sqlite","vanilla-js"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kbennett2000.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2026-05-12T21:13:06.000Z","updated_at":"2026-05-25T13:54:45.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kbennett2000/lan-games","commit_stats":null,"previous_names":["kbennett2000/monopoly-client-server","kbennett2000/lan-games"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kbennett2000/lan-games","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbennett2000%2Flan-games","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbennett2000%2Flan-games/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbennett2000%2Flan-games/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbennett2000%2Flan-games/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kbennett2000","download_url":"https://codeload.github.com/kbennett2000/lan-games/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbennett2000%2Flan-games/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34372130,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["board-games","game-framework","javascript","lan-party","multiplayer-game","socketio","sqlite","vanilla-js"],"created_at":"2026-06-15T17:01:33.754Z","updated_at":"2026-06-15T17:01:34.593Z","avatar_url":"https://github.com/kbennett2000.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LAN Games\n\n\u003e A self-hosted multiplayer game platform for LAN parties. Eight classic games with real-time play, hidden information, and spectator support — all running on a server you control.\n\n![LAN Games — The Game of Life mid-game](docs/screenshots/hero.png)\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\n8 games · 660+ tests · single-server setup · Node.js 18+\n\n---\n\n## Why This Exists\n\nLAN parties want games everyone in the room can play together. Most multiplayer games today require internet, accounts, and matchmaking — LAN Games is the opposite. One Node.js server on your network hosts eight turn-based board games in the browser. No accounts beyond a username, no internet after install, no configuration required. The platform is game-agnostic: each game is a server module behind a [small interface](server/src/game-logic-interface.js), so adding a ninth game doesn't touch the framework. Hidden-information games like Battleship and Risk coexist with open-board games through a per-game state-filtering contract, and spectators can watch any game with full visibility.\n\n---\n\n## Quickstart\n\n1. **Clone and install:**\n   ```bash\n   git clone https://github.com/kbennett2000/lan-games.git\n   cd lan-games/server\n   npm install\n   ```\n\n2. **Start the server:**\n   ```bash\n   # Generate a random secret (once) and start:\n   export JWT_SECRET=$(node -e \"process.stdout.write(require('crypto').randomBytes(48).toString('hex'))\")\n   npm start\n   ```\n\n3. **Play.** Open `http://localhost:3000` in your browser. Create a game, share the URL with friends on your network, and play.\n\nNo database setup, no external services, no configuration files. Docker is also supported — see the detailed [Quick Start](#quick-start) below.\n\n\u003e **Windows note:** The root `package.json` scripts use bash syntax (`. ./.env`). Run from Git Bash, WSL, or use the `cd server \u0026\u0026 npm start` path directly with `JWT_SECRET` set as an environment variable. A `.env.example` is included for reference.\n\n---\n\n## The Games\n\n| | | |\n|:---:|:---:|:---:|\n| **Monopoly** \u003cbr\u003e 2–8 players · ~90 min \u003cbr\u003e ![Monopoly](docs/screenshots/monopoly.png) | **Risk** \u003cbr\u003e 2–6 players · ~120 min \u003cbr\u003e ![Risk](docs/screenshots/risk.png) | **The Game of Life** \u003cbr\u003e 2–6 players · ~45 min \u003cbr\u003e ![Life](docs/screenshots/life.png) |\n| **Battleship** \u003cbr\u003e 2 players · ~15 min \u003cbr\u003e ![Battleship](docs/screenshots/battleship.png) | **Yahtzee** \u003cbr\u003e 1–8 players · ~20 min \u003cbr\u003e ![Yahtzee](docs/screenshots/yahtzee.png) | **Checkers** \u003cbr\u003e 2 players · ~15 min \u003cbr\u003e ![Checkers](docs/screenshots/checkers.png) |\n| **Connect Four** \u003cbr\u003e 2 players · ~5 min \u003cbr\u003e ![Connect Four](docs/screenshots/connect-four.png) | **Tic-Tac-Toe** \u003cbr\u003e 2 players · ~2 min \u003cbr\u003e ![Tic-Tac-Toe](docs/screenshots/tic-tac-toe.png) | |\n\nEvery game supports save/resume, in-game chat, and spectator mode. Games with hidden information (Battleship, Risk, Life) enforce privacy server-side — each player sees only what they're allowed to.\n\n---\n\n## Table of Contents\n\n[Why This Exists](#why-this-exists) · [Quickstart](#quickstart) · [The Games](#the-games) · [Features](#features) · [Quick Start](#quick-start)\n\n**In this README:** [Project Structure](#project-structure) · [Architecture](#architecture) · [Security Notes](#security-notes) · [Development](#development) · [Documentation](#documentation) · [Roadmap](#roadmap)\n\n**Standalone docs:** [How to Play](docs/how-to-play.md) · [Configuration](docs/configuration.md) · [Adding a New Game](docs/adding-a-game.md) · [API Reference](docs/api.md) · [Socket.io Events](docs/socket-events.md)\n\n---\n\n## Features\n\n### Framework\n- **Multi-game** — add any turn-based game by implementing one interface file; no framework changes required\n- **Real-time multiplayer** — all clients sync instantly via Socket.io WebSockets\n- **Player accounts** — register/login with username + password; JWT persisted in `localStorage`\n- **Lobby** — create, browse, and join open games; game-type badge shown on every card\n- **Save \u0026 Resume** — pause any in-progress game and continue it later from the lobby\n- **Auto-reconnect** — disconnected players are marked AFK; their turn is auto-skipped after 30 s\n- **In-game chat** — room-scoped, real-time, 300-character cap\n- **Spectator mode** — anyone logged in can watch any game in progress (👁 Spectate button on every in-progress lobby card). Spectators see the full unfiltered state (including hidden information for games like Battleship, Risk, and Life), chat with players, and watch the action log; they cannot take actions. Players are notified when spectators join. See the \"Spectator mode\" section below.\n- **Configurable** — every rule, price, and board value lives in JSON; hot-reload without a restart\n\n### Monopoly\n- Full rules: dice, doubles (3× → jail), property buying, auctions, rent, color-group monopoly detection, even-building rule, mortgage/unmortgage, jail (fine / doubles / card), Chance \u0026 Community Chest (full 16-card decks), trades (money + properties + jail cards), Income Tax (flat or 10% of net worth), bankruptcy with asset transfer\n- Visual CSS Grid board with color bands, player tokens, house/hotel indicators, and ownership dots\n\n### Connect Four\n- Standard 7 × 6 board; drop pieces by clicking column buttons\n- Win detection: horizontal, vertical, and both diagonals\n- Draw detection when the board is full\n\n### Risk\n- Classic 42-territory world map across 6 continents; 2–6 players\n- Three-phase turns: reinforce → attack → fortify\n- Auto-distributed initial setup; armies and territories dealt evenly to all players\n- Dice combat: attacker rolls up to 3, defender auto-rolls up to 2; ties go to defender\n- Continent bonuses (NA 5, SA 2, EU 5, AF 3, AS 7, AU 2) applied at the start of every reinforce phase\n- 44-card deck (42 territory + 2 wild); valid sets (3 of a kind, 3 different, or any 2 + wild) traded for escalating bonus armies (4, 6, 8, 10, 12, 15, then +5 each)\n- Player elimination transfers all cards to the conqueror; last player standing wins by world domination\n- **First game with hidden information** — each player's hand is private, enforced server-side by the game's `getStateForPlayer` filter\n\n### Tic-Tac-Toe\n- Classic 3 × 3 board; 2 players take turns marking cells with `✕` and `◯`\n- Win detection: rows, columns, both diagonals\n- Draw detection when the board fills\n\n### Yahtzee\n- Standard 5-dice, 13-category, three-rolls-per-turn rules; 1–8 players (solo play supported)\n- Up to 3 rolls per turn with arbitrary holds between rolls\n- All 13 categories (six upper + three-/four-of-a-kind, full house, two straights, Yahtzee, chance)\n- Upper-section bonus (+35 when subtotal ≥ 63); ties produce a shared-winner array\n- Two-click commit on category selection prevents accidental score-locking\n- **First non-board game** — UI is a shared score sheet (players as columns, categories as rows) plus a dice tray, all inside the renderer-owned board area\n\n### Battleship\n- Classic 2-player, 10×10 grid; five ships per side (Carrier 5, Battleship 4, Cruiser 3, Submarine 3, Destroyer 2)\n- Drag-and-drop ship placement with **R-key rotation** during drag\n- Two-grid firing layout — your fleet on the left, opponent's waters on the right; fixed positions across turns with active/inactive treatment that flips\n- Both fleets revealed at game over (winner and loser see the layout that beat them)\n- **First game with a simultaneous private setup phase** — both players place ships in parallel; the phase transitions to firing on a barrier (both Ready) rather than via a turn. Modelled as `status='playing'` + `turnState.phase='setup'` with `currentPlayerIndex=null`\n- **First game with fully hidden state per player** — opponents' ship positions are stripped by `getStateForPlayer` on every emit, not masked\n\n### Checkers\n- Classic American Checkers on an 8×8 board; 12 pieces per side, 2 players\n- Diagonal movement with mandatory captures and multi-jump chain captures\n- King coronation on reaching the back row (kings move forward and backward, one square at a time — not \"flying\" kings)\n- Stalemate-as-loss: player with no legal move loses\n- Click-driven move selection consuming server-supplied action descriptors — the renderer highlights only legal pieces and destinations\n- Animated piece movement, capture fading, chain-capture sequencing, and king coronation visual\n\n### The Game of Life\n- 64-square branching board with a start fork (Career vs College), main track, and two-path retirement choice (Countryside Acres vs Millionaire Estates)\n- 1–10 spinner (no dice) — CSS-animated wheel that decelerates and settles on the result\n- College path costs $40,000 in loans but unlocks degree-required careers; career path skips the loan but is locked out of degree careers\n- Marriage adds a spouse peg and collects $5,000 from each other player as wedding gifts\n- Children mechanic — single births (+$5k/player) and twins (+$10k/player); each child counts toward final scoring at $50k each\n- Insurance — auto and life, optional out-of-band purchases that nullify the matching accident squares\n- Stocks — pick a number 1–10; collect $10,000 whenever **any** player's spinner matches it (cross-turn payouts)\n- House purchase — pick from 2–3 offered house cards (cost vs scoring value); contributes to final score\n- Retirement is the strategic crux: Countryside Acres draws life tiles from a shrinking deck; Millionaire Estates is a cash gamble that resolves at game over\n- **First game with a branching, non-grid board** — squares are graph nodes with explicit `next[]` adjacency; the renderer places squares at hand-tuned grid coordinates with arrows showing direction\n- **First game with deferred-resolution game over** — ME retirees' win/loss is undetermined until the last player retires; documented in [docs/renderer-contract.md](docs/renderer-contract.md)\n- **First game with a \"retired-but-still-in-game\" pattern** — retired players stay in `state.players` and turn rotation skips past them; the game ends when every player is retired\n- Life tiles are hidden information per player (count visible to opponents, values masked until game over); revealed with a per-tile flip animation during the final score reveal\n\n---\n\n## Quick Start\n\n### Prerequisites\n\n- **Node.js 18+** (LTS recommended) — *or* **Docker** (see below)\n- **C++ build toolchain** — `better-sqlite3` and `bcrypt` are native modules compiled during `npm install`. If the build fails, install the toolchain for your OS:\n  - **Windows:** `npm install -g windows-build-tools` *or* install the \"Desktop development with C++\" workload from [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)\n  - **macOS:** `xcode-select --install`\n  - **Linux (Debian/Ubuntu):** `sudo apt install build-essential python3`\n  - **Docker sidesteps this entirely** — the `node:20` (Debian) build stage ships the toolchain\n- No external database — SQLite is embedded via `better-sqlite3`\n\n### Install \u0026 run\n\n```bash\ncd server\nnpm install\nnpm start          # production\nnpm run dev        # development (nodemon auto-restart)\n```\n\nThe server binds to `0.0.0.0:3000` by default — reachable on your entire LAN.\n\n### Docker\n\n**1. Create a `.env` file** at the repo root containing your JWT secret (the server refuses to start without one):\n\n```bash\n# Generates a random secret and writes it to .env in one step\nnode -e \"console.log('JWT_SECRET=' + require('crypto').randomBytes(48).toString('hex'))\" \u003e .env\n```\n\n**2. Build and start:**\n\n```bash\ndocker compose up --build\n```\n\nThe Dockerfile runs both the unit and integration test suites during the build — the image is only produced if all tests pass.\n\nOpen `http://localhost:3000`. The SQLite database is stored in a Docker named volume (`db-data`) and survives container restarts and image rebuilds.\n\n**To reset the database inside the container:**\n\n```bash\ndocker compose run --rm server node scripts/reset-db.js\n```\n\n### Connect\n\n| Who | URL |\n|-----|-----|\n| Host machine | `http://localhost:3000` |\n| LAN players | `http://\u003chost-ip\u003e:3000` |\n\nFind `\u003chost-ip\u003e` with `ip addr` (Linux/macOS) or `ipconfig` (Windows).\n\n### Play\n\n1. Every player registers a username and password on their own browser.\n2. One player creates a game, picks a game type, and optionally customises rules.\n3. Other players join from the lobby.\n4. The host clicks **Start Game**.\n\n### Spectator mode\n\nAnyone logged in can watch any in-progress game without playing.\n\n- In the lobby, every game in `playing` status has a **👁 Spectate** button next to the Join/Rejoin button.\n- A spectator sees the full unfiltered state — *including* hidden information like Battleship ship positions, Risk card hands, and Life tiles. (The point is to enjoy watching strategy unfold; spectators are watching, not playing.)\n- Spectators can chat in the same room as players; their messages are prefixed with 👁 so players know which lines come from the gallery.\n- Spectators see the action log, the player roster, and any in-progress modals.\n- The action panel is hidden; the \"You're spectating\" banner persists at the top of the play area.\n- Players are notified when a spectator joins (log entry). Leaves are silent.\n- A spectator can leave any time via the **✕ Leave** button (replacing the host's Save/Quit). Leaving has no effect on the game.\n\nSpectators cannot take actions. The server enforces this (any `game:action` from a spectator is rejected with a `game:error`); the client gates click handlers as a UX layer so spectator clicks don't appear to do anything.\n\nA user who is already a player in a game cannot also spectate it — the Spectate button is suppressed in that case, and the server rejects an explicit attempt with \"You are already a player in this game\".\n\n---\n\n## Project Structure\n\nThe repository has three top-level directories: `server/` (Node.js backend with game-logic modules under `server/games/` and framework under `server/src/`), `client/` (single-page application with per-game renderers under `client/js/games/`), and `docs/` (design notes and reference documentation).\n\nSee [docs/project-structure.md](docs/project-structure.md) for the full annotated directory tree.\n\n---\n\n## Architecture\n\nThe platform is a single Node.js server (Express + Socket.io) backed by embedded SQLite. Game logic lives in pure-function modules behind a small interface; the framework handles persistence, real-time sync, authentication, and the lobby. Each client renders its game type through a registry-dispatched renderer.\n\nSee [docs/architecture.md](docs/architecture.md) for the component diagram, data-flow walkthrough, database schema, and common GameState shape.\n\n---\n\n## Security Notes\n\nLAN Games is designed for trusted local networks. Authentication uses JWT tokens (the server refuses to start without a `JWT_SECRET`) and bcrypt-hashed passwords. Every game action is validated server-side, and every state-bearing emission routes through `getStateForPlayer` to protect hidden information. A full [state-emission audit](docs/state-emission-audit.md) documents the boundary.\n\nThe platform is **offline by design** — once installed, the server and client need zero internet connectivity. All assets are served locally, there are no CDN dependencies, no telemetry, and no outbound HTTP calls.\n\nSee [docs/security.md](docs/security.md) for the complete security notes including JWT configuration, LAN binding warnings, and the offline-by-design inventory.\n\n---\n\n## Development\n\n### Scripts\n\n```bash\nnpm start              # run the server\nnpm run dev            # run with nodemon (auto-restart)\nnpm test               # unit tests (625 across 11 suites)\nnpm run test:integration  # integration tests (40 across 6 suites)\nnpm run lint           # ESLint\nnpm run format:check   # Prettier (verify)\nnpm run format:write   # Prettier (auto-fix)\nnpm run reset-db       # drop all tables and recreate schema\nnpm run reset-db:hard  # delete the .db file entirely\nnpm run screenshots    # regenerate README screenshots (requires Playwright)\n```\n\nSee [docs/development.md](docs/development.md) for environment variables, port configuration, test details, hot-reloading, and the new-game checklist.\n\n---\n\n## Documentation\n\n**Guides**\n\n- **[How to Play](docs/how-to-play.md)** — complete rules and controls for all eight games\n- **[Adding a New Game](docs/adding-a-game.md)** — interface contract, state versioning, renderer registration\n- **[Development](docs/development.md)** — environment variables, scripts, testing, hot-reload, new-game checklist\n\n**Reference**\n\n- **[Configuration](docs/configuration.md)** — every tunable setting, board layout, and card deck\n- **[API Reference](docs/api.md)** — REST endpoints for auth and game management\n- **[Socket.io Events](docs/socket-events.md)** — real-time event protocol for gameplay\n- **[Architecture](docs/architecture.md)** — component diagram, data flow, database schema, GameState shape\n- **[Project Structure](docs/project-structure.md)** — annotated repository layout\n- **[Security Notes](docs/security.md)** — security model, JWT, LAN binding, offline-by-design\n\n**Design Documents**\n\n- **[Action Descriptors](docs/action-descriptors.md)** — optional rich-action interface (5 of 8 games)\n- **[Renderer Contract](docs/renderer-contract.md)** — client-side renderer interface design memo\n- **[State-Emission Audit](docs/state-emission-audit.md)** — security audit of every state-bearing emit\n- **[Screenshot Generation](docs/screenshots.md)** — how the README gallery images are captured\n\n---\n\n## Roadmap\n\n- **More games** — Chess, Scrabble, Catan, Coup, Liar's Dice, …\n- **Action descriptor contract** — implemented across five of the eight games (Battleship, Checkers, Risk, Yahtzee, and The Game of Life); the renderer-side rule mirrors are gone in all five. Connect Four and Tic-Tac-Toe deliberately skip the contract — their action surfaces (`dropPiece`, `markCell`) are trivial enough that `getValidActions` covers them. See [`docs/action-descriptors.md`](docs/action-descriptors.md) for the contract and the patterns the five migrations established.\n- **Turn timer UI** — server emits absolute-deadline warnings via `game:turn_warning`; the client-side countdown bar is implemented in [`client/js/turn-warning.js`](client/js/turn-warning.js).\n- ~~**Spectator mode**~~ — shipped; see the Spectator mode section above\n- **AI players** — pluggable bot interface implementing the same `applyAction` contract\n- **Custom board themes** — CSS variable overrides per game type\n- **Mobile optimisation** — touch-friendly controls for handheld players\n- **HTTPS / mDNS** — easier LAN discovery and secure transport without manual IP lookup\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkbennett2000%2Flan-games","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkbennett2000%2Flan-games","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkbennett2000%2Flan-games/lists"}