Skip to content

mamipour/shootball-server

Repository files navigation

Shootball Server

This is the backend for Shootball - a game born from my 9-year-old son Araz's idea to turn our penny football matches into something friends could play online. His game design instincts shaped every mode; this server makes sure nobody can cheat.

Powered by Nakama open-source game server.

The server handles matchmaking, runs all physics simulations, determines game outcomes, and streams frame-by-frame trajectories back to clients - ensuring both players always see identical results.

Features

  • Server-authoritative physics - Custom 2D circle physics engine (circle-circle collisions, circle-wall collisions, damping, bounce, friction)
  • 5 game modes - CoinBall, Football, Battle Arena, Volleyball, Curling
  • Per-mode world builders - Each mode constructs its own arena geometry, walls, goals, pits, and nets
  • Trajectory recording - Physics simulation records object positions at regular intervals for client-side replay
  • Matchmaking - Automatic player pairing with per-mode queues
  • Disconnect handling - Remaining player wins if opponent leaves

Prerequisites

Quick Start

# Clone the repository
git clone https://github.com/mamipour/shootball-server.git
cd shootball-server

# Install dependencies
npm install

# Copy environment variables
cp .env.example .env

# Build TypeScript
npm run build

# Start Nakama + PostgreSQL
docker compose up -d

The server exposes three ports:

Port Service
7349 Nakama Console (admin dashboard)
7350 Game API (client connections)
7351 gRPC API

Admin Console: http://localhost:7349 - default credentials are in .env

Development

Recompile TypeScript after changes:

npx tsc

Then restart the Nakama container to pick up the new build:

docker compose restart nakama

Project Structure

shootball-server/
├── src/
│   └── main.ts              # All server logic (physics, world builders, match handler)
├── dist/
│   └── main.js              # Compiled JS (loaded by Nakama runtime)
├── build/
│   └── index.d.ts           # Nakama runtime type definitions
├── docker-compose.yml        # Nakama 3.37.0 + PostgreSQL 16
├── tsconfig.json
├── .env.example              # Environment variable template
└── package.json

All server logic lives in a single src/main.ts file (Nakama requires a single JS entry point). It contains:

  1. Physics engine - Circle-circle and circle-wall collision resolution, velocity damping, impulse application, settling detection
  2. World builders - Per-mode arena construction (walls, goals, pits, nets, house rings)
  3. Simulation runners - runSingleShotSimulation (CoinBall), runMultiShotSimulation (Football, Battle Arena, Volleyball), runCurlingShotSimulation (Curling)
  4. Match handler - Nakama match lifecycle (matchInit, matchJoinAttempt, matchJoin, matchLeave, matchLoop, matchSignal, matchTerminate)

Protocol

Communication between client and server uses Nakama match state messages with the following op codes:

Code Name Direction Description
1 SHOT_SUBMIT Client -> Server Single shot input (pill index, direction, power)
2 ROUND_START Server -> Client New round begins, includes current positions
3 SHOTS_EXECUTE Server -> Client Both shots revealed (legacy, pre-authoritative)
4 GOAL_SCORED Server -> Client Goal event with updated scores
5 GAME_OVER Server -> Client Match ended, winner declared
6 PLAYER_READY Client -> Server Player ready for next round
7 OPPONENT_INFO Server -> Client Opponent display name and avatar
8 PLAYER_LEFT Server -> Client Opponent disconnected
9 TIMER_SYNC Server -> Client Remaining aim time
10 ROUND_RESULT Server -> Client Round outcome (legacy)
11 MULTI_SHOT_SUBMIT Client -> Server Multiple shots for multi-pill modes
12 MULTI_SHOTS_EXECUTE Server -> Client All multi-shots revealed (legacy)
13 ELIMINATION Server -> Client Pill eliminated (legacy)
14 CURLING_TURN Server -> Client Curling turn begins, includes positions
15 CURLING_END_SCORED Server -> Client Curling end scored (legacy)
16 SIM_RESULT Server -> Client Primary message - full trajectory + outcome data

The SIM_RESULT (op code 16) is the core of the server-authoritative model. It contains:

  • trajectory - Array of frames, each frame containing [x, y] positions for every object
  • outcome - Scorer, updated scores, eliminations, game over status, winner
  • positions - Final resting positions of all objects

Environment Variables

Variable Description Default
POSTGRES_PASSWORD PostgreSQL password localdb
NAKAMA_CONSOLE_USERNAME Admin console username admin
NAKAMA_CONSOLE_PASSWORD Admin console password password

For production, change all default credentials and consider adding TLS termination via a reverse proxy.

Deployment

A $5/month VPS (2 GB RAM) comfortably handles hundreds of concurrent matches. For production:

  1. Update credentials in .env
  2. Set up a reverse proxy (nginx/Caddy) with TLS for ports 7350 and 7349
  3. Run docker compose up -d

See the Nakama deployment docs for advanced configuration.

Related

License

This project is licensed under the CC BY-NC 4.0 (Creative Commons Attribution-NonCommercial 4.0 International) license. You are free to use, share, and adapt this work for non-commercial and educational purposes with appropriate credit. See the LICENSE file for details.

About

Authoritative game server for Shootball - custom 2D physics engine, matchmaking, and real-time match handling for 5 game modes. Runs on Nakama.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors