No description
  • Rust 53%
  • Python 16.6%
  • Nix 12.7%
  • Kotlin 12.6%
  • Shell 4.9%
  • Other 0.2%
Find a file
Christian Lengert 46ab0ed7eb update
2026-07-12 18:04:51 +02:00
android update 2026-07-12 18:04:51 +02:00
feeder init server 2026-07-12 15:23:18 +02:00
hosts init server 2026-07-12 15:23:18 +02:00
lib init server 2026-07-12 15:23:18 +02:00
modules update 2026-07-12 18:04:51 +02:00
scripts init server 2026-07-12 15:23:18 +02:00
.gitignore Initial game platform infrastructure 2026-07-12 15:22:59 +02:00
debug-auth.sh init server 2026-07-12 15:23:18 +02:00
DEPLOYMENT.md Initial game platform infrastructure 2026-07-12 15:22:59 +02:00
flake.lock init server 2026-07-12 15:23:18 +02:00
flake.nix update 2026-07-12 18:04:51 +02:00
README.md Initial game platform infrastructure 2026-07-12 15:22:59 +02:00

Location-Based Game Platform

A multiplayer location-based game built with Rust + PostgreSQL + Android. Players move around a city, see each other on a map, and complete location-based challenges.

Architecture

Android Clients
    ↓ (sensor data + API key)
Ingestion Service (Rust/Rocket, port 8000)
    ↓ (SQL)
PostgreSQL + PostGIS
    ↑ (SQL queries)
Game Service (Rust/Rocket, port 8001)
    ↑↓ (JSON REST API + API key)
Android Clients (game state display)

Tech Stack

  • Backend: Rust + Rocket 0.5.1
  • Database: PostgreSQL + PostGIS (spatial indexing)
  • ORM/Database: SQLx (type-safe async queries)
  • Mobile: Kotlin (Android)
  • Deployment: NixOS + Systemd
  • Monitoring: Prometheus metrics built-in
  • Auth: Simple API key (Bearer token)

Quick Start

Prerequisites

  • Nix (for reproducible builds)
  • Git
  • For non-NixOS: PostgreSQL + Rust toolchain

Add to your /etc/nixos/configuration.nix:

{ config, pkgs, ... }:

{
  imports = [
    /path/to/game/modules/default-deployment.nix
  ];

  services.game = {
    enable = true;
    apiSecret = "your-secure-api-key-here";
    
    postgresql.enable = true;
    ingestionService.enable = true;
    ingestionService.port = 8000;
    
    gameService.enable = true;
    gameService.port = 8001;
    
    enableMetrics = true;
  };

  # Allow ports through firewall
  networking.firewall.allowedTCPPorts = [ 8000 8001 5432 ];
}

Then deploy:

sudo nixos-rebuild switch

Services will start automatically and be available at:

  • Ingestion: http://localhost:8000
  • Game: http://localhost:8001
  • PostgreSQL: localhost:5432

Option 2: Local Development (Any Linux)

Enter development shell:

nix develop

Start PostgreSQL (in terminal 1):

# Initialize data directory
export PGDATA=/tmp/postgres_data
mkdir -p $PGDATA
initdb $PGDATA

# Start server
postgres -D $PGDATA

In another terminal, initialize the database:

createdb game
psql game < modules/database/01-init_schema.sql

Start Ingestion Service (terminal 2):

export DATABASE_URL="postgres://localhost/game"
export API_SECRET="dev-secret"
export HOST="127.0.0.1"
export PORT="8000"
./result/bin/ingestion-service

Start Game Service (terminal 3):

export DATABASE_URL="postgres://localhost/game"
export API_SECRET="dev-secret"
export HOST="127.0.0.1"
export PORT="8001"
./result/bin/game-service

API Examples

All endpoints require: Authorization: Bearer {API_SECRET}

Submit Sensor Reading

curl -X POST http://localhost:8000/public/events \
  -H "Authorization: Bearer dev-secret" \
  -H "Content-Type: application/json" \
  -d '{
    "submissionId": "550e8400-e29b-41d4-a716-446655440000",
    "deviceId": "phone-001",
    "capturedAt": 1721046000000,
    "location": {
      "latitude": 37.7749,
      "longitude": -122.4194,
      "accuracy": 10,
      "bearing": 180,
      "speed": 5
    },
    "bluetooth": [],
    "wifi": [],
    "cellTowers": []
  }'

Create Game

curl -X POST http://localhost:8001/games \
  -H "Authorization: Bearer dev-secret" \
  -H "Content-Type: application/json" \
  -d '{"title": "Downtown Hunt"}'

# Response:
# {
#   "id": "550e8400-e29b-41d4-a716-446655440000",
#   "status": "pending",
#   "title": "Downtown Hunt",
#   "player_count": 0,
#   "created_at": "2026-07-12T10:00:00+00:00"
# }

Join Game

GAME_ID="550e8400-e29b-41d4-a716-446655440000"

curl -X POST http://localhost:8001/games/$GAME_ID/join \
  -H "Authorization: Bearer dev-secret" \
  -H "Content-Type: application/json" \
  -d '{"deviceId": "phone-001"}'

Get Game State

curl http://localhost:8001/games/$GAME_ID \
  -H "Authorization: Bearer dev-secret"

List Players

curl http://localhost:8001/games/$GAME_ID/players \
  -H "Authorization: Bearer dev-secret"

Database Schema

Core Tables

  • sensor_readings - Raw sensor data from mobile devices

    • Indexed by: device_id, location (GIST), timestamp
    • Includes: GPS, Bluetooth, WiFi, cellular data
  • games - Game sessions

    • Status: pending, active, completed
    • Time tracking: created_at, started_at, ended_at
  • game_players - Players in a game

    • Tracks: join time, status, score
    • One row per player per game
  • challenges - Location-based tasks

    • Geometry: point (PostGIS)
    • References: game_id
  • challenge_completions - Challenge progress

    • Tracks: which player completed which challenge

Query Examples

-- Latest position for each device
SELECT device_id, location, latitude, longitude, submitted_at
FROM latest_positions
WHERE submitted_at > NOW() - INTERVAL '5 minutes';

-- Games with player counts
SELECT g.id, g.title, g.status, COUNT(gp.device_id) as players
FROM games g
LEFT JOIN game_players gp ON g.id = gp.game_id
GROUP BY g.id;

-- Players in a specific game
SELECT device_id, score, status
FROM game_players
WHERE game_id = '550e8400-e29b-41d4-a716-446655440000';

-- Sensors submitted in last hour
SELECT COUNT(*) FROM sensor_readings
WHERE submitted_at > NOW() - INTERVAL '1 hour';

Monitoring

Metrics Endpoints

Both services export Prometheus metrics:

curl http://localhost:8000/metrics | grep ingestion
curl http://localhost:8001/metrics | grep game

Key metrics:

  • ingestion_events_submitted_total - submissions
  • game_games_created_total - games created
  • http_requests_total - request count
  • http_request_duration_seconds - request latency

Logs

View logs (NixOS):

journalctl -u game-ingestion-service -f
journalctl -u game-game-service -f
journalctl -u postgresql -f

View logs (local dev):

  • Check terminal output where services are running

Building & Deployment

Build Services with Nix

# Build ingestion service
nix build .#ingestion-service

# Build game service
nix build .#game-service

# Build both
nix build .#ingestion-service .#game-service

Binaries appear in result/bin/.

Update Flake Lock

nix flake update

Cleanup

# Remove build artifacts (but keep .gitignore working)
rm -rf modules/services/*/target/
rm -rf modules/clients/*/target/
rm -rf result result-*

# Prune Nix store
nix-collect-garbage

Project Structure

.
├── flake.nix                          # Nix build & deployment config
├── DEPLOYMENT.md                      # Detailed deployment guide
├── README.md                          # This file
├── .gitignore                         # Git ignore rules
│
├── modules/
│   ├── default.nix                    # Module imports
│   ├── default-deployment.nix         # Complete deployment config
│   ├── postgresql/                    # PostgreSQL NixOS module
│   │   └── default.nix
│   ├── database/                      # SQL migrations
│   │   └── 01-init_schema.sql
│   └── services/
│       ├── ingestion-service/         # Sensor data ingestion
│       │   ├── src/
│       │   ├── Cargo.toml
│       │   ├── Cargo.lock
│       │   ├── default.nix            # NixOS module
│       │   └── migrations/
│       └── game-service/              # Game logic & state
│           ├── src/
│           ├── Cargo.toml
│           ├── Cargo.lock
│           └── default.nix            # NixOS module
│
├── hosts/
│   ├── example-deployment.nix         # Example NixOS config
│   └── derelict/
│
└── android/                           # Android app (Kotlin)
    ├── app/
    └── build.gradle.kts

Development

Add New Endpoint

  1. Create route in modules/services/game-service/src/web/game_routes.rs
  2. Add database query using SQLx
  3. Update main.rs to mount the route
  4. Rebuild: nix build .#game-service

Modify Database Schema

  1. Create new SQL migration in appropriate directory
  2. Ensure it's idempotent (safe to run multiple times)
  3. Rebuild PostgreSQL module: sudo nixos-rebuild switch (or re-init locally)

Update Dependencies

Edit Cargo.toml files, regenerate locks:

nix develop --command bash -c "cd modules/services/game-service && cargo update"
git add modules/services/game-service/Cargo.lock

Troubleshooting

Database connection refused

# Check PostgreSQL is running
pg_isready -h localhost -p 5432

# Check DATABASE_URL
echo $DATABASE_URL

# Verify database exists
psql -l | grep game

Port already in use

# Find process using port 8000
lsof -i :8000

# Kill if needed
kill -9 <PID>

401 Unauthorized on API calls

  • Check Authorization: Bearer {secret} header is present
  • Verify API_SECRET env var matches on both client and server
  • Example: curl -H "Authorization: Bearer dev-secret" http://localhost:8000/healthz

Build failures

# Update Nix
nix flake update

# Clean rebuild
nix build --no-cache .#ingestion-service

# Check Rust version
rustc --version

Next Steps

  • Build Android client in Kotlin
  • Add WebSocket support for realtime updates
  • Implement challenge types (waypoints, timers, etc.)
  • Add player visibility rules
  • Setup TLS/HTTPS with reverse proxy (nginx)
  • Add authentication (OAuth, JWT)
  • Setup CI/CD pipeline
  • Add game history and replays

License

[Add your license here]

Contact

[Add contact info]