156 lines
5.5 KiB
Bash
Executable File
156 lines
5.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Reaper's Sky - dedicated server deploy script (Debian 13 / trixie)
|
|
#
|
|
# Installs everything needed to run the Reaper's Sky NeoForge 21.1.248 server:
|
|
# - OpenJDK 21 (JRE, headless) + curl/unzip
|
|
# - NeoForge 21.1.248 server via its official installer
|
|
# - the server-safe mods (pinned in mods.json, verified by sha1)
|
|
# - eula.txt + server.properties (IP reaperofveriod.nl)
|
|
# - run.sh (8 GB, G1GC)
|
|
#
|
|
# Usage: sudo ./deploy.sh (installs packages, needs root/sudo)
|
|
# ./deploy.sh (idempotent; safe to re-run)
|
|
#
|
|
# Defaults:
|
|
# DIR = "$HOME/reaper-sky-server"
|
|
# MEMORY = 8G
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
NEOFORGE_VERSION="21.1.248"
|
|
MC_VERSION="1.21.1"
|
|
INSTALLER_URL="https://maven.neoforged.net/releases/net/neoforged/neoforge/${NEOFORGE_VERSION}/neoforge-${NEOFORGE_VERSION}-installer.jar"
|
|
|
|
# --- config (override via env if you want) ------------------------------------
|
|
SERVER_DIR="${DIR:-$HOME/reaper-sky-server}"
|
|
MEMORY="${MEMORY:-8G}"
|
|
SERVER_IP="${SERVER_IP:-}" # empty = bind all interfaces; reach server via edge port-forward
|
|
DEFAULT_MOTD="Reaper's Sky"
|
|
MOTD="${MOTD:-$DEFAULT_MOTD}"
|
|
MAX_PLAYERS="${MAX_PLAYERS:-10}"
|
|
|
|
log() { printf '\033[1;34m[deploy]\033[0m %s\n' "$*"; }
|
|
die() { printf '\033[1;31m[deploy] ERROR:\033[0m %s\n' "$*" >&2; exit 1; }
|
|
|
|
# --- 1. system packages (Debian 13 ships OpenJDK 21) --------------------------
|
|
install_packages() {
|
|
if [ "$(id -u)" -ne 0 ]; then
|
|
if command -v sudo >/dev/null 2>&1; then SUDO="sudo"; else SUDO=""; fi
|
|
else
|
|
SUDO=""
|
|
fi
|
|
if command -v java >/dev/null 2>&1; then
|
|
log "Java already present: $(java -version 2>&1 | head -1)"
|
|
else
|
|
log "Installing openjdk-21-jre-headless, curl, unzip (apt)..."
|
|
${SUDO:+sudo} apt-get update -qq
|
|
${SUDO:+sudo} apt-get install -y -qq openjdk-21-jre-headless curl unzip
|
|
fi
|
|
java -version >/dev/null 2>&1 || die "Java 21 not available after install"
|
|
}
|
|
|
|
# --- 2. NeoForge server --------------------------------------------------------
|
|
install_neoforge() {
|
|
mkdir -p "$SERVER_DIR"
|
|
cd "$SERVER_DIR"
|
|
if [ -f "run.sh" ] && [ -d "libraries/net/neoforged/neoforge/$NEOFORGE_VERSION" ]; then
|
|
log "NeoForge $NEOFORGE_VERSION already installed."
|
|
return
|
|
fi
|
|
log "Downloading NeoForge $NEOFORGE_VERSION installer..."
|
|
curl -fL --retry 3 -o "neoforge-${NEOFORGE_VERSION}-installer.jar" "$INSTALLER_URL"
|
|
log "Running installer --installServer ..."
|
|
java -jar "neoforge-${NEOFORGE_VERSION}-installer.jar" --installServer
|
|
rm -f "neoforge-${NEOFORGE_VERSION}-installer.jar"
|
|
chmod +x run.sh
|
|
}
|
|
|
|
# --- 3. server mods (verified by sha1) -----------------------------------------
|
|
install_mods() {
|
|
[ -f "$SCRIPT_DIR/mods.json" ] || die "mods.json not found next to deploy.sh"
|
|
mkdir -p "$SERVER_DIR/mods"
|
|
cd "$SERVER_DIR/mods"
|
|
python3 - "$SCRIPT_DIR/mods.json" <<'PY'
|
|
import json, sys, hashlib, subprocess, os
|
|
manifest = json.load(open(sys.argv[1]))
|
|
keep = set()
|
|
for m in manifest["mods"]:
|
|
fname, url, want = m["file"], m["url"], m["sha1"]
|
|
keep.add(fname)
|
|
tmp = fname + ".part"
|
|
if os.path.exists(fname):
|
|
h = hashlib.sha1()
|
|
with open(fname, "rb") as fh:
|
|
for b in iter(lambda: fh.read(65536), b""):
|
|
h.update(b)
|
|
if h.hexdigest() == want:
|
|
continue # already present & correct
|
|
if not url:
|
|
raise SystemExit(f"no download URL for {fname}")
|
|
subprocess.run(["curl", "-fsSL", "--retry", "3", "-o", tmp, url], check=True)
|
|
h = hashlib.sha1()
|
|
with open(tmp, "rb") as fh:
|
|
for b in iter(lambda: fh.read(65536), b""):
|
|
h.update(b)
|
|
if h.hexdigest() != want:
|
|
os.unlink(tmp)
|
|
raise SystemExit(f"sha1 mismatch for {fname}")
|
|
os.rename(tmp, fname)
|
|
print(f" + {fname}")
|
|
# remove jars that are no longer part of the pack
|
|
for f in sorted(os.listdir(".")):
|
|
if f.endswith(".jar") and f not in keep:
|
|
os.unlink(f)
|
|
print(f" - {f} (stale)")
|
|
PY
|
|
log "Mods ready: $(ls -1 "$SERVER_DIR/mods"/*.jar | wc -l) jars installed."
|
|
}
|
|
|
|
# --- 4. eula + server.properties -----------------------------------------------
|
|
write_config() {
|
|
cd "$SERVER_DIR"
|
|
if [ ! -f eula.txt ]; then
|
|
printf 'eula=true\n' > eula.txt
|
|
log "Accepted Minecraft EULA (eula.txt)."
|
|
fi
|
|
cat > server.properties <<EOF
|
|
# Generated by Reaper's Sky deploy.sh
|
|
server-ip=$SERVER_IP
|
|
server-port=25565
|
|
motd=$MOTD
|
|
max-players=$MAX_PLAYERS
|
|
online-mode=true
|
|
difficulty=hard
|
|
level-type=default
|
|
allow-nether=true
|
|
spawn-protection=0
|
|
view-distance=10
|
|
simulation-distance=10
|
|
EOF
|
|
log "Wrote server.properties (IP=$SERVER_IP, max=$MAX_PLAYERS)."
|
|
}
|
|
|
|
# --- 5. run script ---------------------------------------------------------------
|
|
write_run() {
|
|
cat > "$SERVER_DIR/run.sh" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
cd "$(dirname "$(readlink -f "$0")")"
|
|
exec java -XmsMEMORY -XmxMEMORY -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 \
|
|
@libraries/net/neoforged/neoforge/NEOFORGE_VERSION/unix_args.txt nogui
|
|
EOF
|
|
sed -i "s/MEMORY/$MEMORY/g; s/NEOFORGE_VERSION/$NEOFORGE_VERSION/g" "$SERVER_DIR/run.sh"
|
|
chmod +x "$SERVER_DIR/run.sh"
|
|
log "Wrote run.sh ($MEMORY RAM). Start with: cd $SERVER_DIR && ./run.sh"
|
|
}
|
|
|
|
install_packages
|
|
install_neoforge
|
|
install_mods
|
|
write_config
|
|
write_run
|
|
|
|
log "Done. Reaper's Sky server is at: $SERVER_DIR"
|
|
log "First boot generates the world. Run it with: cd $SERVER_DIR && ./run.sh"
|
|
log "Console: run inside tmux/screen so it keeps running when you disconnect."
|
|
log "To stop later: use the server console command stop" |