initial commit
This commit is contained in:
commit
abcee32d38
6 changed files with 176 additions and 0 deletions
6
.dockerignore
Normal file
6
.dockerignore
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
/.git
|
||||||
|
/node_modules
|
||||||
|
.dockerignore
|
||||||
|
.env
|
||||||
|
Dockerfile
|
||||||
|
fly.toml
|
||||||
39
Dockerfile
Normal file
39
Dockerfile
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# syntax = docker/dockerfile:1
|
||||||
|
|
||||||
|
# Adjust NODE_VERSION as desired
|
||||||
|
ARG NODE_VERSION=22.13.1
|
||||||
|
FROM node:${NODE_VERSION}-slim AS base
|
||||||
|
|
||||||
|
LABEL fly_launch_runtime="Node.js"
|
||||||
|
|
||||||
|
# Node.js app lives here
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Set production environment
|
||||||
|
ENV NODE_ENV="production"
|
||||||
|
|
||||||
|
|
||||||
|
# Throw-away build stage to reduce size of final image
|
||||||
|
FROM base AS build
|
||||||
|
|
||||||
|
# Install packages needed to build node modules
|
||||||
|
RUN apt-get update -qq && \
|
||||||
|
apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3
|
||||||
|
|
||||||
|
# Install node modules
|
||||||
|
COPY package-lock.json package.json ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Copy application code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
|
||||||
|
# Final stage for app image
|
||||||
|
FROM base
|
||||||
|
|
||||||
|
# Copy built application
|
||||||
|
COPY --from=build /app /app
|
||||||
|
|
||||||
|
# Start the server by default, this can be overwritten at runtime
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD [ "npm", "run", "start" ]
|
||||||
3
README.md
Normal file
3
README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
## Custom game server
|
||||||
|
Serve games to the appropriate gameboard service from postgres db
|
||||||
|
Very basic, threw this together in a day
|
||||||
22
fly.toml
Normal file
22
fly.toml
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# fly.toml app configuration file generated for snake-server on 2025-08-18T22:02:23-07:00
|
||||||
|
#
|
||||||
|
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
|
||||||
|
#
|
||||||
|
|
||||||
|
app = 'snake-server'
|
||||||
|
primary_region = 'sea'
|
||||||
|
|
||||||
|
[build]
|
||||||
|
|
||||||
|
[http_service]
|
||||||
|
internal_port = 3000
|
||||||
|
force_https = true
|
||||||
|
auto_stop_machines = 'suspend'
|
||||||
|
auto_start_machines = true
|
||||||
|
min_machines_running = 0
|
||||||
|
processes = ['app']
|
||||||
|
|
||||||
|
[[vm]]
|
||||||
|
memory = '512mb'
|
||||||
|
cpu_kind = 'shared'
|
||||||
|
cpus = 1
|
||||||
85
index.js
Normal file
85
index.js
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
import express from "express";
|
||||||
|
import http from "http";
|
||||||
|
import { WebSocketServer } from "ws";
|
||||||
|
import cors from "cors";
|
||||||
|
import pkg from "pg";
|
||||||
|
|
||||||
|
const { Pool } = pkg;
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const PORT = 3000;
|
||||||
|
|
||||||
|
// Setup Postgres connection pool
|
||||||
|
const pool = new Pool({
|
||||||
|
connectionString: process.env.DATABASE_URL || "neverleavetheconnectionstringhereforproduction",
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
|
||||||
|
// HTTP GET /games/:id → return game info from DB
|
||||||
|
app.get("/games/:id", async (req, res) => {
|
||||||
|
const gameId = req.params.id;
|
||||||
|
try {
|
||||||
|
const result = await pool.query("SELECT info FROM items WHERE id = $1", [gameId]);
|
||||||
|
if (result.rows.length === 0) {
|
||||||
|
return res.redirect(302, "https://engine.battlesnake.com");
|
||||||
|
// return res.status(404).json({ error: "Game not found" });
|
||||||
|
|
||||||
|
}
|
||||||
|
res.json(result.rows[0].info); // send back the JSONB field
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).json({ error: "Database error" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create HTTP server and attach WebSocket server
|
||||||
|
const server = http.createServer(app);
|
||||||
|
const wss = new WebSocketServer({ server });
|
||||||
|
|
||||||
|
// Helper to match the path pattern
|
||||||
|
function matchGameEventsPath(url) {
|
||||||
|
if (!url) return null;
|
||||||
|
const match = url.match(/^\/games\/([^\/]+)\/events$/);
|
||||||
|
return match ? match[1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
wss.on("connection", async (ws, req) => {
|
||||||
|
const gameId = matchGameEventsPath(req.url);
|
||||||
|
if (!gameId) {
|
||||||
|
ws.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`WS client connected for game: ${gameId}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Grab frames array from DB
|
||||||
|
const result = await pool.query("SELECT frames FROM items WHERE id = $1", [gameId]);
|
||||||
|
if (result.rows.length === 0) {
|
||||||
|
ws.send(JSON.stringify({ error: "Game not found" }));
|
||||||
|
ws.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const frames = result.rows[0].frames || [];
|
||||||
|
|
||||||
|
// Stream frames one by one with delay
|
||||||
|
frames.forEach((frame, i) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (ws.readyState === ws.OPEN) {
|
||||||
|
ws.send(JSON.stringify(frame));
|
||||||
|
}
|
||||||
|
}, i * 5); // staggered delay
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
ws.send(JSON.stringify({ error: "Database error" }));
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(PORT, "0.0.0.0", () => {
|
||||||
|
console.log(`Server running at http://localhost:${PORT} (HTTP + WebSocket)`);
|
||||||
|
});
|
||||||
21
package.json
Normal file
21
package.json
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "game-server",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node index.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^5.1.0",
|
||||||
|
"pg": "^8.16.3",
|
||||||
|
"ws": "^8.18.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@flydotio/dockerfile": "^0.7.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue