展示HN:和朋友一起免费玩LongTurn FreeCiv
Show HN: Playing LongTurn FreeCiv with Friends

原始链接: https://github.com/ndroo/freeciv.andrewmcgrath.info

## Longturn Freeciv 服务器摘要 该项目在 Fly.io 上部署了一个自托管的 Freeciv 3.2.3 多人服务器,针对“长回合”游戏进行了优化——一种具有 23 小时回合的慢速游戏风格。目前正在运行一个活跃的 16 人游戏! 该服务器使用 Dockerfile 进行构建,并使用 Fly.io 的基础设施进行托管,包括用于游戏存档的持久存储。主要功能包括回合开始和提醒的电子邮件通知、显示排名、历史记录、外交关系的实时状态页面,以及由 OpenAI 提供支持的 AI 生成的“战时报纸”。 该系统通过 FIFO 管道进行服务器命令操作,并依赖脚本执行自动保存、回合管理和数据生成等任务。SQLite 数据库处理玩家身份验证。配置通过诸如 `longturn.serv`(用于游戏设置)和 `.env`(用于敏感凭据,使用 Fly.io 密钥)之类的文件进行管理。 玩家通过标准的 Freeciv 客户端连接。该项目提供了玩家管理、存档文件编辑和服务器维护工具,并提供详细的设置和操作文档。

## LongTurn FreeCiv:一款社交策略游戏 一位开发者在Hacker News上分享了一个项目——使用极长的回合时间(约23小时)设置来与朋友一起玩FreeCiv。目标是创建一个休闲、长期进行的游戏,不会占据玩家过多时间,并在游戏过程中培养社区。 该项目利用LLM生成“文明编年史”——一份总结每个回合的、游戏内的新闻报纸,以增强参与感。开发者强调了为实现这一目的,FreeCiv的存档文件易于处理。玩家指出FreeCiv客户端本身有些笨拙,但长回合制和随之而来的社交互动(通过WhatsApp)正变得越来越受欢迎。 23小时的回合时长是故意的,旨在避免固定截止日期对不同时区玩家造成不利影响。这种方式呼应了较早期的“邮件游戏”形式,例如邮寄象棋,并旨在将社交联系与战略游戏玩法并重。演示地址:[https://freeciv.andrewmcgrath.info](https://freeciv.andrewmcgrath.info)。
相关文章

原文

A self-hosted Freeciv 3.2.3 multiplayer server designed for longturn games (23-hour turns), running on Fly.io with email notifications, a live status page, and an AI-generated newspaper.

An active 16-player game is running on this codebase right now. Check out the status page to see live rankings, turn countdowns, history charts, diplomacy tracking, and the AI-generated wartime newspaper.


Longturn is a style of Freeciv multiplayer where each turn lasts ~23 hours instead of minutes. Players log in once a day, make their moves, click "Turn Done", and go about their lives. When all players have ended their turn (or the timer runs out), the next turn begins.

┌─────────────────────────────────────────────────┐
│  Fly.io Container                               │
│                                                  │
│  entrypoint.sh                                   │
│    ├── busybox crond (status page refresh)       │
│    └── start.sh                                  │
│         ├── freeciv-server (port 5556)           │
│         ├── busybox httpd (port 8080 → 80/443)  │
│         ├── FIFO command writer                  │
│         ├── Turn change watcher                  │
│         ├── Auto-saver (every 5 min)             │
│         └── Turn reminder checker                │
│                                                  │
│  /data/saves (persistent volume)                 │
│    ├── lt-game-*.sav.gz    (save files)          │
│    ├── freeciv.sqlite       (player auth DB)     │
│    ├── status.json          (live game state)    │
│    ├── history.json         (per-turn stats)     │
│    ├── attendance.json      (missed turns)       │
│    ├── diplomacy.json       (relationships)      │
│    └── gazette.json         (AI newspaper)       │
└─────────────────────────────────────────────────┘

The server communicates via a FIFO pipe (/tmp/server-input) — scripts send commands to the running Freeciv server by writing to this pipe.

Script Purpose
entrypoint.sh Container entrypoint. Starts crond, then drops privileges and runs start.sh.
start.sh Main orchestrator. Starts the Freeciv server, FIFO pipe, auto-save, turn watcher, reminder loop, HTTP server, and handles resume logic (preserving turn timer across restarts).
longturn.serv Game settings: 23-hour turns, 10-hour unitwaittime, allied victory only, player list.
Script Purpose
generate_status_json.sh Extracts game state from save files into JSON. Runs every 5 minutes via cron and on each turn change. Produces status.json, history.json, attendance.json, and diplomacy.json.
www/index.html Client-side status page. Fetches JSON and renders rankings, charts (Chart.js), diplomacy, countdown timer, and gazette articles.
www/cgi-bin/health Healthcheck endpoint. Returns 503 if status.json is stale (>7 min), used by uptime monitors.
Script Purpose
turn_notify.sh Sends HTML email to all players when a new turn starts. Includes rankings table, gazette, and deadline.
turn_reminder.sh Runs every 60 seconds. If within 2 hours of the deadline, sends a nudge email to players who haven't clicked "Turn Done".
turn_notify.lua Freeciv signal handler that triggers turn_notify.sh on turn change.
Script Purpose
manage_players.sh Create player accounts in the SQLite auth DB, send welcome emails, list players.
fcdb.conf / database.lua SQLite auth database configuration and initialization.
Script Purpose
fix_turn_timer.sh Override the turn deadline to a specific clock time (e.g., ./fix_turn_timer.sh 4 for 4 AM). Restores normal 23hr timeout on the next turn.
change_gold.sh Adjust a player's gold via Lua command (e.g., ./change_gold.sh andrew 50).
generate_gazette.sh Calls OpenAI to generate "The Civ Chronicle" — an era-appropriate, unreliable wartime newspaper article for each turn.
generate_nations.sh Generates a static HTML page listing all available nations.
local_preview.sh Preview the status page locally using save file data.
File Purpose
email_enabled.settings Set to true or false to toggle all email notifications.
crontab Cron schedule — runs generate_status_json.sh every 5 minutes.
fly.toml Fly.io deployment config (region, VM size, ports, volume).
Dockerfile Multi-stage build: compiles Freeciv 3.2.3 from source, then creates a lean runtime image.
  • Fly.io CLI (flyctl)
  • Docker (for local builds/testing)
  • An AWS account with SES configured (for email notifications)
  • An OpenAI API key (optional, for the AI gazette feature)
git clone <repo-url>
cd freeciv-server
cp .env.sample .env

Edit .env with your credentials:

SES_SMTP_USER=your-ses-smtp-username
SES_SMTP_PASS=your-ses-smtp-password
SES_SMTP_HOST=email-smtp.us-east-1.amazonaws.com
OPENAI_API_KEY=your-openai-key  # optional, for gazette

2. Customize Game Settings

Edit longturn.serv to configure your game:

  • timeout 82800 — Turn length in seconds (82800 = 23 hours)
  • unitwaittime 36000 — Prevents double-moves (36000 = 10 hours)
  • victories ALLIED — Victory conditions
  • Player list (create commands at the bottom)

Update email settings in the notification scripts:

  • FROM_EMAIL — The sender address (must be verified in SES)
  • SERVER_HOST — Your server's hostname
  • CC_EMAIL — Optional CC address for all emails

Copy the sample players file and add your players:

cp players.conf.sample players.conf

Edit players.conf with one line per player:

PLAYERS=(
  "player1:pass123:[email protected]:Australian"
  "player2:pass456:[email protected]:Canadian"
  # ... add one line per player
)

Format: "username:password:email:nation". This file is gitignored — credentials stay local.

You'll also need to add matching create commands in longturn.serv and aitoggle entries in start.sh for each player. See HOWTO-PROVISION-PLAYERS.md for the full walkthrough.

# Create the app
fly launch --name your-app-name

# Create a persistent volume for saves
fly volumes create freeciv_saves --size 1 --region your-region

# Set secrets (instead of hardcoding in scripts)
fly secrets set \
  SES_SMTP_USER=your-ses-smtp-username \
  SES_SMTP_PASS=your-ses-smtp-password \
  OPENAI_API_KEY=your-openai-key

# Deploy
fly deploy

5. Create Player Accounts

Once deployed, provision all player accounts from your players.conf:

# Create all accounts and send welcome emails
./manage_players.sh create-all

# Or add a single player
./manage_players.sh create username password [email protected]

This creates entries in the SQLite auth database and sends each player a welcome email with connection instructions.

6. Share Connection Details

Players connect using the Freeciv 3.2.3 client:

  • Host: your-app-name.fly.dev
  • Port: 5556
  • Username/Password: as created above

The status page is available at https://your-app-name.fly.dev.

# Deploy changes
fly deploy

# SSH into the container
fly ssh console --app your-app-name

# Force a save
fly ssh console --app your-app-name -C "sh -c 'echo save > /tmp/server-input'"

# Regenerate the status page
fly ssh console --app your-app-name -C "/opt/freeciv/generate_status_json.sh"

# Check server logs
fly ssh console --app your-app-name -C "tail -50 /data/saves/server.log"

# Override turn deadline to 4 AM
./fix_turn_timer.sh 4

# Change a player's gold
./change_gold.sh playername 100

# Toggle emails off
# Edit email_enabled.settings to "false" and redeploy

# Restart the server (preserves turn timer)
fly apps restart your-app-name

The most reliable way to change game state mid-game is editing the save file directly. FIFO commands get garbled beyond ~200 characters, and many server commands are blocked mid-game.

# 1. Force a save
fly ssh console --app your-app-name -C "sh -c 'echo save > /tmp/server-input; sleep 3'"

# 2. Download it
fly ssh console --app your-app-name -C "cat /data/saves/save-latest.sav.gz" > /tmp/save.sav.gz
gzip -dc /tmp/save.sav.gz > /tmp/save.txt

# 3. Edit /tmp/save.txt (it's plaintext INI-style)

# 4. Upload and restart
gzip -c /tmp/save.txt > /tmp/save-edited.sav.gz
cat /tmp/save-edited.sav.gz | base64 | fly ssh console --app your-app-name \
  -C "sh -c 'base64 -d > /data/saves/save-latest.sav.gz'"
fly apps restart your-app-name

The server preserves the turn timer across restarts and redeploys. On resume, start.sh:

  1. Reads phase_seconds (time elapsed in the current turn) from the save file
  2. Calculates remaining time: timeout - phase_seconds
  3. Restores the correct deadline so players don't lose time
Variable Required Default Description
SES_SMTP_USER For emails AWS SES SMTP username
SES_SMTP_PASS For emails AWS SES SMTP password
SES_SMTP_HOST No email-smtp.us-east-1.amazonaws.com SES SMTP endpoint
OPENAI_API_KEY For gazette OpenAI API key for AI newspaper
SERVER_HOST No freeciv.andrewmcgrath.info Server hostname (for emails/status page)
FROM_EMAIL No [email protected] Sender email address

Set these as Fly.io secrets for production:

fly secrets set SES_SMTP_USER=... SES_SMTP_PASS=... OPENAI_API_KEY=...
├── Dockerfile                  # Multi-stage build (compile Freeciv + runtime)
├── fly.toml                    # Fly.io config
├── entrypoint.sh               # Container entrypoint
├── start.sh                    # Server startup orchestrator
├── longturn.serv               # Game settings
├── fcdb.conf                   # Auth DB config
├── database.lua                # DB initialization
├── crontab                     # Scheduled tasks
├── email_enabled.settings      # Email toggle
├── turn_notify.lua             # Turn change signal handler
├── generate_status_json.sh     # Status page data pipeline
├── generate_gazette.sh         # AI newspaper generator
├── generate_nations.sh         # Nations list page
├── turn_notify.sh              # Turn email notifications
├── turn_reminder.sh            # Deadline reminder emails
├── manage_players.sh           # Player account management
├── fix_turn_timer.sh           # Manual deadline override
├── change_gold.sh              # Gold adjustment utility
├── local_preview.sh            # Local testing helper
├── .env.sample                 # Environment variables template
├── www/
│   ├── index.html              # Status page (JS-rendered)
│   ├── changelog.html          # Game changelog
│   └── cgi-bin/
│       └── health              # Healthcheck endpoint
└── CLAUDE.md                   # Operations reference
联系我们 contact @ memedata.com