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:
co-authored by
Claude Opus 5
parent
80c1d75d2f
commit
71856b15e9
Executable
+236
@@ -0,0 +1,236 @@
|
||||
#!/bin/sh
|
||||
# ---------------------------------------------------------------------------
|
||||
# Los Santos RP - installer
|
||||
#
|
||||
# Puts a working server on a clean Ubuntu/Debian box: FXServer artefacts,
|
||||
# MariaDB with the schema, the resources from this repository, the keep-alive
|
||||
# supervisor and a boot entry.
|
||||
#
|
||||
# sudo sh install.sh --licence cfxk_xxxxxxxx
|
||||
#
|
||||
# Options
|
||||
# --licence KEY Cfx.re server key from https://keymaster.fivem.net
|
||||
# --dir PATH install root (default /opt/fivem)
|
||||
# --hostname NAME server name shown in the list (default from server.cfg.example)
|
||||
# --port N game port (default 30120)
|
||||
# --name NAME service name, one per server (default fivem)
|
||||
# --db-name NAME database (default rp)
|
||||
# --db-user USER database user (default rp)
|
||||
# --db-pass PASS password for that user (default: generated)
|
||||
# --build ID FXServer build to use (default: latest recommended)
|
||||
# --no-start install everything, do not start the server
|
||||
# ---------------------------------------------------------------------------
|
||||
set -e
|
||||
|
||||
DIR=/opt/fivem
|
||||
LICENCE=""
|
||||
HOSTNAME_=""
|
||||
BUILD=""
|
||||
DB_PASS=""
|
||||
DB_NAME=rp
|
||||
DB_USER=rp
|
||||
PORT=30120
|
||||
SVC=fivem
|
||||
START=1
|
||||
ARTIFACTS="https://runtime.fivem.net/artifacts/fivem/build_proot_linux/master/"
|
||||
SRC=$(cd "$(dirname "$0")" && pwd)
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--licence|--license) LICENCE="$2"; shift 2 ;;
|
||||
--dir) DIR="$2"; shift 2 ;;
|
||||
--hostname) HOSTNAME_="$2"; shift 2 ;;
|
||||
--build) BUILD="$2"; shift 2 ;;
|
||||
--db-pass) DB_PASS="$2"; shift 2 ;;
|
||||
--db-name) DB_NAME="$2"; shift 2 ;;
|
||||
--db-user) DB_USER="$2"; shift 2 ;;
|
||||
--port) PORT="$2"; shift 2 ;;
|
||||
--name) SVC="$2"; shift 2 ;;
|
||||
--no-start) START=0; shift ;;
|
||||
-h|--help) sed -n '2,20p' "$0"; exit 0 ;;
|
||||
*) echo "unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
say() { printf '\n\033[1;33m==\033[0m %s\n' "$*"; }
|
||||
ok() { printf ' \033[32mok\033[0m %s\n' "$*"; }
|
||||
die() { printf '\n\033[31mfailed:\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
[ "$(id -u)" = 0 ] || die "run as root (sudo sh install.sh ...)"
|
||||
[ -f "$SRC/etc/schema.sql" ] || die "run this from a clone of the repository"
|
||||
command -v apt-get >/dev/null || die "this installer is for Debian/Ubuntu"
|
||||
|
||||
# --- 1. packages -----------------------------------------------------------
|
||||
say "Installing packages"
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq curl ca-certificates xz-utils mariadb-server >/dev/null
|
||||
ok "curl, xz-utils, mariadb-server"
|
||||
|
||||
# --- 2. FXServer artefacts -------------------------------------------------
|
||||
say "Fetching FXServer"
|
||||
if [ -n "$BUILD" ]; then
|
||||
REL=$(curl -fsSL "$ARTIFACTS" | tr '>' '\n' | grep -oE "\./$BUILD-[0-9a-f]+/fx\.tar\.xz" | head -1)
|
||||
[ -n "$REL" ] || die "build $BUILD not found on $ARTIFACTS"
|
||||
else
|
||||
REL=$(curl -fsSL "$ARTIFACTS" | tr '>' '\n' | grep -B4 'LATEST RECOMMENDED' \
|
||||
| grep -oE '\./[0-9]+-[0-9a-f]+/fx\.tar\.xz' | head -1)
|
||||
[ -n "$REL" ] || die "could not find the recommended build on $ARTIFACTS"
|
||||
fi
|
||||
URL="$ARTIFACTS${REL#./}"
|
||||
BUILD_ID=$(echo "$REL" | grep -oE '[0-9]+' | head -1)
|
||||
|
||||
mkdir -p "$DIR/server" "$DIR/data/resources" "$DIR/bin" "$DIR/logs" "$DIR/etc"
|
||||
if [ -x "$DIR/server/run.sh" ] && [ -f "$DIR/server/.build" ] && [ "$(cat "$DIR/server/.build")" = "$BUILD_ID" ]; then
|
||||
ok "build $BUILD_ID already installed"
|
||||
else
|
||||
TMP=$(mktemp -d)
|
||||
curl -fL# "$URL" -o "$TMP/fx.tar.xz" || die "download failed: $URL"
|
||||
rm -rf "$DIR/server"; mkdir -p "$DIR/server"
|
||||
tar xJf "$TMP/fx.tar.xz" -C "$DIR/server"
|
||||
rm -rf "$TMP"
|
||||
chmod +x "$DIR/server/run.sh"
|
||||
echo "$BUILD_ID" > "$DIR/server/.build"
|
||||
ok "build $BUILD_ID unpacked into $DIR/server"
|
||||
fi
|
||||
|
||||
# --- 3. database -----------------------------------------------------------
|
||||
say "Preparing the database"
|
||||
if [ ! -f /etc/init.d/mariadb ] && [ ! -d /run/systemd/system ]; then
|
||||
die "no init system found for mariadb"
|
||||
fi
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl enable --now mariadb >/dev/null 2>&1 || true
|
||||
else
|
||||
service mariadb start >/dev/null 2>&1 || true
|
||||
fi
|
||||
i=0
|
||||
while ! mysqladmin ping >/dev/null 2>&1; do
|
||||
i=$((i+1)); [ $i -gt 60 ] && die "mariadb did not start"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [ -z "$DB_PASS" ]; then
|
||||
if [ -f "$DIR/data/secrets.cfg" ]; then
|
||||
DB_PASS=$(sed -n 's/^set rp_db_pass "\(.*\)"$/\1/p' "$DIR/data/secrets.cfg" | head -1)
|
||||
fi
|
||||
[ -n "$DB_PASS" ] || DB_PASS=$(head -c 24 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' | cut -c1-24)
|
||||
fi
|
||||
|
||||
mysql -e "CREATE DATABASE IF NOT EXISTS \`$DB_NAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
||||
mysql -e "CREATE USER IF NOT EXISTS '$DB_USER'@'127.0.0.1' IDENTIFIED BY '$DB_PASS';"
|
||||
mysql -e "ALTER USER '$DB_USER'@'127.0.0.1' IDENTIFIED BY '$DB_PASS';"
|
||||
mysql -e "GRANT SELECT, INSERT, UPDATE, DELETE ON \`$DB_NAME\`.* TO '$DB_USER'@'127.0.0.1'; FLUSH PRIVILEGES;"
|
||||
mysql "$DB_NAME" < "$SRC/etc/schema.sql"
|
||||
ok "database $DB_NAME, user $DB_USER@127.0.0.1, schema applied"
|
||||
|
||||
# --- 4. resources and configuration ---------------------------------------
|
||||
say "Installing the server files"
|
||||
rm -rf "$DIR/data/resources/[rp]"
|
||||
mkdir -p "$DIR/data/resources"
|
||||
cp -r "$SRC/resources/[rp]" "$DIR/data/resources/"
|
||||
cp "$SRC/bin/supervise.sh" "$DIR/bin/"
|
||||
sed "s|/run/fivem.stdin|/run/$SVC.stdin|" "$SRC/bin/rcon" > "$DIR/bin/rcon"
|
||||
chmod +x "$DIR/bin/supervise.sh" "$DIR/bin/rcon"
|
||||
cp "$SRC/etc/schema.sql" "$DIR/etc/schema.sql"
|
||||
ln -sf "$DIR/bin/rcon" "/usr/local/bin/$([ "$SVC" = fivem ] && echo rcon || echo "rcon-$SVC")"
|
||||
|
||||
if [ -f "$DIR/data/server.cfg" ]; then
|
||||
ok "server.cfg kept (already present)"
|
||||
else
|
||||
sed -e "s|exec /opt/fivem/data/secrets.cfg|exec $DIR/data/secrets.cfg|" \
|
||||
-e "s|0.0.0.0:30120|0.0.0.0:$PORT|g" \
|
||||
"$SRC/server.cfg.example" > "$DIR/data/server.cfg"
|
||||
[ -n "$HOSTNAME_" ] && sed -i "s|^sv_hostname .*|sv_hostname \"$HOSTNAME_\"|;s|^sets sv_projectName .*|sets sv_projectName \"$HOSTNAME_\"|" "$DIR/data/server.cfg"
|
||||
ok "server.cfg written"
|
||||
fi
|
||||
|
||||
if [ -z "$LICENCE" ] && [ -f "$DIR/data/secrets.cfg" ]; then
|
||||
LICENCE=$(sed -n 's/^sv_licenseKey "\(.*\)"$/\1/p' "$DIR/data/secrets.cfg" | head -1)
|
||||
fi
|
||||
cat > "$DIR/data/secrets.cfg" <<EOF
|
||||
# Licence key and database credentials. Never commit this file.
|
||||
sv_licenseKey "${LICENCE:-cfxk_your_key_here}"
|
||||
|
||||
set rp_db_host "127.0.0.1"
|
||||
set rp_db_port "3306"
|
||||
set rp_db_user "$DB_USER"
|
||||
set rp_db_pass "$DB_PASS"
|
||||
set rp_db_name "$DB_NAME"
|
||||
EOF
|
||||
chmod 600 "$DIR/data/secrets.cfg"
|
||||
ok "secrets.cfg written (mode 600)"
|
||||
|
||||
# --- 5. boot entry ---------------------------------------------------------
|
||||
say "Setting up start on boot"
|
||||
sed -e "s|^DATA=.*|DATA=$DIR/data|" -e "s|^SERVER=.*|SERVER=$DIR/server|" \
|
||||
-e "s|^LOG=.*|LOG=$DIR/logs/server.log|" -e "s|^NAME=.*|NAME=$SVC|" \
|
||||
-e "s|/opt/fivem/bin/supervise.sh|$DIR/bin/supervise.sh|" \
|
||||
"$SRC/bin/fivem.init" > "/etc/init.d/$SVC"
|
||||
chmod +x "/etc/init.d/$SVC"
|
||||
|
||||
if [ -d /run/systemd/system ]; then
|
||||
cat > "/etc/systemd/system/$SVC.service" <<EOF
|
||||
[Unit]
|
||||
Description=Los Santos RP (FXServer)
|
||||
After=network-online.target mariadb.service
|
||||
Wants=mariadb.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=$DIR/data
|
||||
ExecStart=$DIR/bin/supervise.sh $SVC $DIR/logs/server.log $DIR/server/run.sh +set citizen_dir $DIR/server/alpine/opt/cfx-server/citizen/ +exec $DIR/data/server.cfg
|
||||
KillMode=mixed
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
systemctl enable "$SVC" >/dev/null 2>&1
|
||||
ok "systemd unit $SVC.service"
|
||||
elif [ -d /etc/redl/enabled ]; then
|
||||
# REDL VDS: no systemd, the platform starts whatever is marked here
|
||||
touch "/etc/redl/enabled/$SVC" /etc/redl/enabled/mariadb
|
||||
ok "marked in /etc/redl/enabled (REDL VDS autostart)"
|
||||
elif [ -f /etc/redl/enabled ]; then
|
||||
grep -qx "$SVC" /etc/redl/enabled || echo "$SVC" >> /etc/redl/enabled
|
||||
grep -qx mariadb /etc/redl/enabled || echo mariadb >> /etc/redl/enabled
|
||||
ok "marked in /etc/redl/enabled (REDL VDS autostart)"
|
||||
else
|
||||
command -v update-rc.d >/dev/null && update-rc.d "$SVC" defaults >/dev/null 2>&1 || true
|
||||
ok "/etc/init.d/$SVC installed"
|
||||
fi
|
||||
|
||||
# --- 6. start --------------------------------------------------------------
|
||||
if [ "$START" = 0 ]; then
|
||||
say "Done (not started, --no-start)"
|
||||
exit 0
|
||||
fi
|
||||
if [ -z "$LICENCE" ]; then
|
||||
say "Done"
|
||||
echo " No licence key given. Get a free one at https://keymaster.fivem.net,"
|
||||
echo " put it into $DIR/data/secrets.cfg and run: service $SVC start"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
say "Starting the server"
|
||||
if [ -d /run/systemd/system ]; then systemctl restart "$SVC"; else service "$SVC" restart >/dev/null; fi
|
||||
|
||||
i=0
|
||||
while [ $i -lt 90 ]; do
|
||||
if grep -q "Authenticated with cfx.re Nucleus" "$DIR/logs/server.log" 2>/dev/null; then
|
||||
ok "server is up and registered with Cfx"
|
||||
grep -q "\[selftest\] data path OK" "$DIR/logs/server.log" && ok "self-test passed: the data path works"
|
||||
IP=$(curl -fsS --max-time 5 https://api.ipify.org 2>/dev/null || echo "<server ip>")
|
||||
printf '\n connect %s:%s\n logs: tail -f %s\n console: rcon status\n\n' "$IP" "$PORT" "$DIR/logs/server.log"
|
||||
exit 0
|
||||
fi
|
||||
if grep -qE "Could not authenticate server license key|invalid license key|Failed to verify" "$DIR/logs/server.log" 2>/dev/null; then
|
||||
if [ -d /run/systemd/system ]; then systemctl stop "$SVC"; else service "$SVC" stop >/dev/null 2>&1; fi
|
||||
die "the licence key was refused - get one at https://keymaster.fivem.net, put it into $DIR/data/secrets.cfg and run: service $SVC start"
|
||||
fi
|
||||
i=$((i+1)); sleep 2
|
||||
done
|
||||
die "the server did not come up in 3 minutes - see $DIR/logs/server.log"
|
||||
Reference in New Issue
Block a user