#!/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" < "/etc/init.d/$SVC" chmod +x "/etc/init.d/$SVC" if [ -d /run/systemd/system ]; then cat > "/etc/systemd/system/$SVC.service" </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 "") 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"