e37e8a08e0
Matches the 9-12p sizing tier from HOSTING.md. Verified clean boot (Done in 5.3s, no OOM) and checked actual headroom on the box (~2.3GB container overhead, 14GB total) before applying.
139 lines
5.6 KiB
Bash
Executable File
139 lines
5.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# One-time setup for the LAN party's dedicated box: installs a Forge 1.20.1
|
|
# Minecraft server and wraps it in lazymc (auto-sleep when empty / auto-wake
|
|
# on connect), as a systemd service so it runs unattended for the 3 days.
|
|
#
|
|
# File distribution (install.sh/install.ps1/modpack.zip) is NOT handled by
|
|
# this script — see HOSTING.md, it's served from Gitea instead.
|
|
#
|
|
# Target: a Linux machine (Debian/Ubuntu/etc with systemd) at the venue.
|
|
# Run as a normal user with sudo rights: ./setup-server.sh
|
|
set -euo pipefail
|
|
|
|
# ---- EDIT ME ----
|
|
FORGE_MC_VERSION="1.20.1"
|
|
FORGE_BUILD="47.4.28" # current recommended build for 1.20.1 — check files.minecraftforge.net if this is stale
|
|
MC_PUBLIC_PORT=25565 # what players connect to (lazymc listens here)
|
|
MC_INTERNAL_PORT=25566 # real server listens here, lazymc proxies to it
|
|
SLEEP_AFTER=300 # idle seconds before lazymc stops the real server (this lazymc build wants a plain integer, not "5m")
|
|
MIN_RAM="8G"
|
|
MAX_RAM="8G" # 9-12 players; see ../HOSTING.md sizing table. Xms=Xmx avoids GC resize pauses. Check `free -h` / `docker stats` before going higher — this box runs other services too.
|
|
# ------------------
|
|
|
|
BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
SERVER_DIR="$BASE_DIR/mcserver"
|
|
FORGE_VERSION="${FORGE_MC_VERSION}-${FORGE_BUILD}"
|
|
|
|
command -v java >/dev/null || { echo "Java not found. Install a Java 17+ JDK (e.g. 'sudo apt install -y openjdk-17-jre-headless') then re-run."; exit 1; }
|
|
mkdir -p "$SERVER_DIR"
|
|
|
|
# ---- 1. Install the Forge server ----
|
|
if [[ ! -f "$SERVER_DIR/run.sh" ]]; then
|
|
echo "==> Downloading Forge $FORGE_VERSION installer"
|
|
installer_url="https://maven.minecraftforge.net/net/minecraftforge/forge/${FORGE_VERSION}/forge-${FORGE_VERSION}-installer.jar"
|
|
curl -fsSL -o "$SERVER_DIR/forge-installer.jar" "$installer_url"
|
|
|
|
read -rp "Do you accept the Minecraft EULA (https://aka.ms/MinecraftEULA)? [y/N] " eula_ans
|
|
[[ "$eula_ans" =~ ^[Yy]$ ]] || { echo "EULA not accepted, aborting."; exit 1; }
|
|
|
|
echo "==> Running Forge server installer"
|
|
(cd "$SERVER_DIR" && java -jar forge-installer.jar --installServer)
|
|
echo "eula=true" > "$SERVER_DIR/eula.txt"
|
|
else
|
|
echo "==> Forge server already installed, skipping"
|
|
fi
|
|
|
|
# JVM heap flags go in user_jvm_args.txt, NOT on lazymc's command line — run.sh
|
|
# forwards any args after "nogui" straight to Minecraft's own arg parser (not
|
|
# the JVM), so passing -Xmx/-Xms there crashes with "X is not a recognized option".
|
|
cat > "$SERVER_DIR/user_jvm_args.txt" <<EOF
|
|
# Managed by setup-server.sh — edit MIN_RAM/MAX_RAM at the top of that script instead.
|
|
-Xms${MIN_RAM}
|
|
-Xmx${MAX_RAM}
|
|
EOF
|
|
|
|
# server.properties: lazymc will rewrite host/port/rcon itself, we just set gameplay-relevant bits
|
|
touch "$SERVER_DIR/server.properties"
|
|
for kv in "online-mode=false" "motd=LAN Party - Create & Delight" "difficulty=normal" "max-players=20"; do
|
|
key="${kv%%=*}"
|
|
if grep -q "^${key}=" "$SERVER_DIR/server.properties" 2>/dev/null; then
|
|
sed -i "s/^${key}=.*/${kv}/" "$SERVER_DIR/server.properties"
|
|
else
|
|
echo "$kv" >> "$SERVER_DIR/server.properties"
|
|
fi
|
|
done
|
|
|
|
mkdir -p "$SERVER_DIR/mods"
|
|
echo "==> NOTE: copy the same mod jars from your client modpack's mods/ folder into $SERVER_DIR/mods/ (Create, Flywheel, Farmer's Delight + addons, JEI, etc.) before first boot."
|
|
|
|
cp "$BASE_DIR/rcon.py" "$SERVER_DIR/rcon.py"
|
|
echo "==> Copied rcon.py into $SERVER_DIR — use it to run server commands (op, gamemode, etc), see ../HOSTING.md."
|
|
|
|
# ---- 2. Install lazymc ----
|
|
# Pinned to v0.2.10, NOT "latest" — v0.2.11 has a confirmed, still-open upstream
|
|
# regression where it never detects a Forge server as ready (timvisee/lazymc#65,
|
|
# #86). 0.2.10 was the last version that correctly detects Forge startup.
|
|
LAZYMC_VERSION="v0.2.10"
|
|
if [[ ! -x "$BASE_DIR/lazymc" ]]; then
|
|
echo "==> Downloading lazymc $LAZYMC_VERSION"
|
|
curl -fsSL -o "$BASE_DIR/lazymc" "https://github.com/timvisee/lazymc/releases/download/${LAZYMC_VERSION}/lazymc-${LAZYMC_VERSION}-linux-x64"
|
|
chmod +x "$BASE_DIR/lazymc"
|
|
else
|
|
echo "==> lazymc already present, skipping"
|
|
fi
|
|
|
|
cat > "$BASE_DIR/lazymc.toml" <<EOF
|
|
[server]
|
|
directory = "$SERVER_DIR"
|
|
command = "bash run.sh nogui"
|
|
forge = true
|
|
address = "127.0.0.1:${MC_INTERNAL_PORT}"
|
|
freeze_process = false
|
|
wake_whitelist = false
|
|
|
|
[public]
|
|
address = "0.0.0.0:${MC_PUBLIC_PORT}"
|
|
|
|
[time]
|
|
sleep_after = ${SLEEP_AFTER}
|
|
minimum_online_time = 60
|
|
|
|
[motd]
|
|
sleeping = "☀ Server is asleep, join to wake it up"
|
|
starting = "⏳ Server is starting, hang tight..."
|
|
|
|
[rcon]
|
|
enabled = true
|
|
port = 25575
|
|
EOF
|
|
echo "==> Wrote lazymc.toml (sleeps after ${SLEEP_AFTER} empty, public port ${MC_PUBLIC_PORT} -> internal ${MC_INTERNAL_PORT})"
|
|
|
|
# ---- 3. systemd services ----
|
|
sudo tee /etc/systemd/system/mc-lazymc.service >/dev/null <<EOF
|
|
[Unit]
|
|
Description=LAN party Minecraft server (via lazymc)
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
WorkingDirectory=${BASE_DIR}
|
|
ExecStart=${BASE_DIR}/lazymc
|
|
Restart=on-failure
|
|
User=${USER}
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now mc-lazymc.service
|
|
|
|
echo "==> Done."
|
|
echo " - Minecraft server: public port ${MC_PUBLIC_PORT}, sleeps after ${SLEEP_AFTER} idle, wakes on connect."
|
|
echo " - Install scripts + modpack.zip are served from Gitea, not this box — see ../HOSTING.md."
|
|
echo " - Copy your client mod jars into ${SERVER_DIR}/mods/ if you haven't yet, then: sudo systemctl restart mc-lazymc"
|
|
if command -v ufw >/dev/null && sudo ufw status | grep -q "Status: active"; then
|
|
echo "==> ufw is active — opening ${MC_PUBLIC_PORT}/tcp"
|
|
sudo ufw allow "${MC_PUBLIC_PORT}/tcp"
|
|
fi
|