Files
mc-lan-party/server/setup-server.sh
T
Mykhailo Ivanchenko 18ef303935 Add rcon.py for running server commands (op, gamemode, etc)
No interactive console exists since lazymc/systemd run the server in
the background, and mcrcon isn't packaged for Debian 13. Plain
Python3 RCON client instead, reads the password straight from
server.properties. setup-server.sh now copies it into mcserver/.
2026-10-23 15:55:43 +02:00

139 lines
5.5 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="2G"
MAX_RAM="6G" # 5-8 players; see ../HOSTING.md sizing table — bump to 8G (9-12p) or 10-12G (13-15p)
# ------------------
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