Skip to content

Repository files navigation

NodeByte Game Recipes

This directory contains game server configuration recipes think of them as templates or profiles for each supported game. Each recipe defines how to provision, configure, and run a specific game server!

Structure

Each game gets its own subdirectory with:

games/
├── minecraft/
│ ├── index.ts # Game definition & metadata
│ ├── install.ts # Docker image & installation setup
│ └── settings.ts # User-configurable settings schema
├── rust/
│ ├── index.ts
│ ├── install.ts
│ └── settings.ts
└── ...

Root Files

  • index.ts — Exports the array of all supported games and helper functions (getGame, getDefaults, etc.)
  • settings.ts — Type definitions and utilities for game settings (shared schema logic)
  • compose.ts — Docker Compose builder utilities (UFW rules, port mapping, container names)
  • steam.ts — SteamCMD integration for games that require Steam

Local Quality Commands

The games repo can be validated standalone (no dependency on the main GSM repo tooling).

npm run fmt:check
npm run lint
npm run validate:recipes
npm run typecheck

Or run all checks in one command:

npm run ci

Adding a New Game

1. Create the game directory

mkdir games/yourserver
cd games/yourserver

2. Create index.ts

This is the game definition. It exports a constant object with metadata, ports, resource requirements, and settings:

importtype{ComposeConfig}from"../compose";import{resolveSettings}from"../settings";import{buildYourServerCompose,dockerImage}from"./install";import{yourServerSettings}from"./settings";constbuildCompose=(config: ComposeConfig,raw: unknown): string=>buildYourServerCompose(config,resolveSettings(yourServerSettings,raw));exportconstyourserver={// Required metadataid: "yourserver",// Unique slug used in URLs/APIsname: "Your Server",// Display namedescription: "Your server desc",// Marketing textenabled: true,// Toggle visibility in UI// Game identificationgamedigId: "yourserver",// GameDig protocol ID for status checksimage: "/games/yourserver.png",// UI icon (200x200px PNG)// Docker & compose
buildCompose,// Function to generate docker-compose.yml
dockerImage,// Base container image reference// Network ports exposed to playersports: [{from: 25_565,// Container internal portto: 25_565,// External listening portprotocol: "tcp",// "tcp" or "udp"},// Add more ports as needed],// Minimum host requirementsrequirements: {cpu: 2,// vCPU countmemory: 8,// GBdisk: 10,// GB},// Settings UI schemasettings: yourServerSettings,// Auth behaviorusesJoinPassword: false,// Does the game require a join password?}asconst;

3. Create settings.ts

Define user-configurable settings via Zod schema. Settings appear in the UI and are passed to the server container:

import{defineSettings}from"../settings";exportconstyourServerSettings=defineSettings({server_name: {type: "string",label: "Server Name",help: "Displayed in server browser",default: "Your Server",maxLength: 32,},difficulty: {type: "select",label: "Difficulty",default: "normal",options: [{value: "easy",label: "Easy"},{value: "normal",label: "Normal"},{value: "hard",label: "Hard"},],},max_players: {type: "number",label: "Max Players",default: 32,min: 1,max: 128,},enable_pvp: {type: "boolean",label: "Enable PvP",default: true,},});

Field types:

  • string — Text input with optional maxLength
  • number — Numeric input with optional min/max/step
  • boolean — Toggle checkbox
  • select — Dropdown with fixed options

Every field must have a default value.

4. Create install.ts

Define Docker image and build the docker-compose.yml for that game:

importtype{ComposeConfig}from"../compose";importtype{SettingsValues}from"../settings";import{yourServerSettings}from"./settings";exportconstdockerImage="steamcmd/proton:latest";exportconstbuildYourServerCompose=(config: ComposeConfig,settings: SettingsValues<typeofyourServerSettings>): string=>{// Destructure settings passed from userconst{ server_name, difficulty, max_players }=settings;// Build environment variablesconstenv=[`SERVER_NAME=${server_name}`,`DIFFICULTY=${difficulty}`,`MAX_PLAYERS=${max_players}`,];// Return docker-compose YAML as a stringreturn`version: '3'services: yourserver: image: ${dockerImage} container_name: ${config.containerName} environment:${env.map((e)=>` - ${e}`).join("\n")} ports:${config.ports.map((p)=>` - "${p.hostPort}:${p.containerPort}/${p.protocol}"`).join("\n")} volumes: - ${config.serverDir}:/data restart: unless-stopped `.trim();};

Key Integration Points

1. Adding to the Games Export

Once your game is ready, add it to games/index.ts:

import{yourserver}from"./yourserver";exportconstgames=[minecraft,rust,yourserver,// ← Add here// ...];

2. Docker Image Sources

Common sources for game server images:

  • Official: steamcmd/proton:latest, ubuntu:22.04 + manual install
  • Community: DockerHub (cm2network/csgo, itzg/minecraft-server)
  • GameServers.com: Pre-built images for managed games

3. SteamCMD Integration

For Steam-based games, use the steam.ts helpers:

import{buildSteamCmdInstall}from"../steam";exportconstbuildYourServerCompose=(config: ComposeConfig,settings: SettingsValues<typeofyourServerSettings>)=>{conststeamCmd=buildSteamCmdInstall({appId: 232330,// Steam App IDvalidateFiles: true,});return`version: '3'services: yourserver: image: steamcmd/proton:latest container_name: ${config.containerName} environment: - STEAM_APP_ID=232330 command: ${steamCmd} # ... `.trim();};

4. Port Mapping

The ports array defines what the player connects to:

ports: [{from: 7777,to: 7777,protocol: "udp"},// Game port{from: 27015,to: 27015,protocol: "udp"},// Query port],

Game ports must match what the container expects.

5. Docker Image Assets

Place game icons at:

public/games/yourserver.png (200×200 PNG, max 50KB)

These are referenced in index.ts via image: "/games/yourserver.png".

Testing Your Recipe

1. Type checking

bun run types

2. Validating settings

Test your settings schema:

import{resolveSettings}from"@/games/settings";import{yourServerSettings}from"@/games/yourserver/settings";constsettings=resolveSettings(yourServerSettings,{server_name: "Test",difficulty: "hard",max_players: 64,});

3. Build locally

Generate and validate the docker-compose output:

# In your app code, call buildCompose with mock ComposeConfig
const compose = buildYourServerCompose(
{
containerName: "test-yourserver",
ports: [{ containerPort: 7777, hostPort: 7777, protocol: "udp" }],
serverDir: "/data",
},
{ server_name: "Test", difficulty: "normal", max_players: 32 }
);
console.log(compose);

Resources

Docker Image Inspiration

GameDig IDs

GameDig is used for server status queries. Find supported game protocols:

Docker Compose Reference

Example: Minimal Recipe

Here's a minimal but complete recipe template:

games/example/index.ts:

importtype{ComposeConfig}from"../compose";import{resolveSettings}from"../settings";import{buildExampleCompose,dockerImage}from"./install";import{exampleSettings}from"./settings";constbuildCompose=(config: ComposeConfig,raw: unknown): string=>buildExampleCompose(config,resolveSettings(exampleSettings,raw));exportconstexample={
buildCompose,description: "Example server",
dockerImage,enabled: true,gamedigId: "example",id: "example",image: "/games/example.png",name: "Example",ports: [{from: 7777,protocol: "udp",to: 7777}],requirements: {cpu: 1,disk: 5,memory: 2},settings: exampleSettings,usesJoinPassword: false,}asconst;

games/example/settings.ts:

import{defineSettings}from"../settings";exportconstexampleSettings=defineSettings({name: {type: "string",label: "Server Name",default: "My Server",},});

games/example/install.ts:

importtype{ComposeConfig}from"../compose";importtype{SettingsValues}from"../settings";import{exampleSettings}from"./settings";exportconstdockerImage="example/server:latest";exportconstbuildExampleCompose=(config: ComposeConfig,settings: SettingsValues<typeofexampleSettings>): string=>{return`version: '3'services: example: image: ${dockerImage} container_name: ${config.containerName} environment: - SERVER_NAME=${settings.name} ports: - "${config.ports[0]?.hostPort}:7777/udp" volumes: - ${config.serverDir}:/data restart: unless-stopped `.trim();};

Then add to games/index.ts:

import{example}from"./example";exportconstgames=[// ...example,];

Questions?

For implementation help or to discuss recipes:

About

Standalone CI and validation helpers for NodeByte game recipes.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages