The server itself: repo now mirrors the machine it runs on, plus install.sh

The repository carried two different servers side by side - the early
`justrp` prototype with its Python auth API, and a snapshot of the real
one under `server-code/`. Only the second one is a server anyone should
start from, so the prototype is gone and the real one moved to the root.

Taken from the live box, so the UI is the finished version (the tokens,
the county-records sheets and the variable fonts landed after the last
snapshot was pushed):

  resources/[rp]/   rp_db, rp_core, rp_session, rp_loading, rp_ui,
                    rp_selftest, rp_dbtest
  bin/              supervise.sh (keep-alive + console FIFO), rcon, init script
  etc/schema.sql    accounts, characters, transactions, inventory, vehicles
  assets/fonts/     the bundled subsets
  docs/SERVER.md    how it is put together, resource by resource

install.sh turns a clean Ubuntu/Debian box into this server in one
command: recommended FXServer build, MariaDB with the schema, resources,
server.cfg + a generated database password, boot entry (systemd or
init.d), then it waits for the Cfx registration. --name/--port/--db-name
let a second server live on the same machine. Tested end to end on a
spare install root: build 25770 fetched, schema applied, boot entry
written, FXServer started and stopped exactly where a wrong licence key
should stop it.

No secrets travel with it: the licence key and the database password live
in data/secrets.cfg on the machine, and .gitignore now names it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Claude Opus 5
2026-08-12 23:23:33 +00:00
co-authored by Claude Opus 5
parent 80c1d75d2f
commit 71856b15e9
66 changed files with 2030 additions and 3814 deletions
Executable
+60
View File
@@ -0,0 +1,60 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: fivem
# Required-Start: $network mariadb
# Default-Start: 2 3 4 5
# Short-Description: Los Santos RP FXServer
### END INIT INFO
NAME=fivem
DATA=/opt/fivem/data
SERVER=/opt/fivem/server
LOG=/opt/fivem/logs/server.log
SUPPID=/run/$NAME.sup.pid
CHILDPID=/run/$NAME.child.pid
status() {
if [ -f $CHILDPID ] && kill -0 "$(cat $CHILDPID)" 2>/dev/null; then
echo "$NAME running (pid $(cat $CHILDPID))"
return 0
fi
echo "$NAME not running"
return 3
}
start() {
if status >/dev/null 2>&1; then echo "$NAME already running"; return 0; fi
# the database has to answer before resources start querying it
service mariadb start >/dev/null 2>&1
i=0
while ! mysqladmin ping >/dev/null 2>&1; do
i=$((i+1))
[ $i -gt 30 ] && echo "warning: mariadb did not come up" && break
sleep 1
done
cd $DATA || exit 1
setsid /opt/fivem/bin/supervise.sh "$NAME" "$LOG" \
$SERVER/run.sh +set citizen_dir $SERVER/alpine/opt/cfx-server/citizen/ \
+exec $DATA/server.cfg >/dev/null 2>&1 < /dev/null &
sleep 2
echo "$NAME started"
}
stop() {
[ -f $SUPPID ] && kill -TERM "$(cat $SUPPID)" 2>/dev/null
[ -f $CHILDPID ] && kill -TERM "$(cat $CHILDPID)" 2>/dev/null
sleep 2
[ -f $CHILDPID ] && kill -9 "$(cat $CHILDPID)" 2>/dev/null
rm -f $SUPPID $CHILDPID
echo "$NAME stopped"
}
case "$1" in
start) start ;;
stop) stop ;;
restart) stop; start ;;
status) status ;;
*) echo "usage: $0 {start|stop|restart|status}"; exit 1 ;;
esac
Executable
+7
View File
@@ -0,0 +1,7 @@
#!/bin/sh
# Send a command to the running FXServer console, e.g. rcon refresh
if [ ! -p /run/fivem.stdin ]; then
echo "server is not running (no console pipe)" >&2
exit 1
fi
printf '%s\n' "$*" > /run/fivem.stdin
+66
View File
@@ -0,0 +1,66 @@
#!/bin/sh
# ---------------------------------------------------------------------------
# supervise.sh <name> <logfile> <command...>
#
# Keeps one process alive. There is no systemd in this container, so this is
# what restarts FXServer if it crashes.
#
# Two details that matter for FXServer specifically:
#
# * It reads its console from stdin and quits the moment stdin reaches EOF,
# so it is given a FIFO whose write end this script holds open forever.
# That doubles as a command channel - see /opt/fivem/bin/rcon.
#
# * A server that dies immediately is usually failing for a reason that will
# not fix itself (bad licence key, database down, Cfx rate limiting). The
# restart delay backs off so a broken server does not hammer an upstream
# service, and resets once it has stayed up for a while.
# ---------------------------------------------------------------------------
NAME="$1"; LOG="$2"; shift 2
FIFO="${FIFO:-/run/$NAME.stdin}"
MIN_DELAY=5
MAX_DELAY=300
HEALTHY_AFTER=120 # seconds of uptime that count as "it actually started"
mkdir -p "$(dirname "$LOG")"
echo $$ > "/run/$NAME.sup.pid"
[ -p "$FIFO" ] || { rm -f "$FIFO"; mkfifo "$FIFO"; }
# read-write open: holds the pipe open without blocking and without a helper process
exec 9<> "$FIFO"
cleanup() {
kill "$CHILD" 2>/dev/null
rm -f "/run/$NAME.sup.pid" "/run/$NAME.child.pid"
exit 0
}
trap cleanup TERM INT
delay=$MIN_DELAY
while true; do
if [ -f "$LOG" ] && [ "$(stat -c %s "$LOG")" -gt 209715200 ]; then
mv "$LOG" "$LOG.1"
fi
echo "=== $(date -Is) starting $NAME ===" >> "$LOG"
started=$(date +%s)
"$@" >> "$LOG" 2>&1 <&9 &
CHILD=$!
echo $CHILD > "/run/$NAME.child.pid"
wait $CHILD
RC=$?
ran=$(( $(date +%s) - started ))
if [ "$ran" -ge "$HEALTHY_AFTER" ]; then
delay=$MIN_DELAY
else
delay=$(( delay * 2 ))
[ "$delay" -gt "$MAX_DELAY" ] && delay=$MAX_DELAY
fi
echo "=== $(date -Is) $NAME exited (rc=$RC) after ${ran}s, restarting in ${delay}s ===" >> "$LOG"
sleep "$delay"
done