4ea2243e4d
On a Linux without ALSA's library (most minimal server images), the game exits at once with the loader's "error while loading shared libraries: libasound.so.2". The supervisor took that for a crash and restarted it five times over two and a half minutes before giving up with "see logs/latest.log", which holds nothing about it. Now: - `run` starts the game with --help first (it loads no game files) and, if the system can't load it, stops at once with the loader's line and the packages to install on Debian/Ubuntu, Fedora, Arch and NixOS; - `init` prints the same as a warning after "Game:"; - install.sh lists the libraries `ldd` can't find, with the same advice; - docs/server.md says so in the install section. Also, a service installed without the installer's launcher on the PATH ran `<dir>/releases/<tag>/horizon-server`: Linux reports the running program with links resolved, so the unit pinned that release and an update never reached it. The fallback now goes through `<dir>/current` while it points at that release. Found by the end-to-end install check on the ThinkPad (NixOS, no nix-ld libraries) with v2026.10.18.15. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
111 lines
5.4 KiB
Bash
Executable File
111 lines
5.4 KiB
Bash
Executable File
#!/bin/sh
|
|
# Installs horizon-server (docs/server.md) on Linux or macOS: the latest Horizon release for this computer,
|
|
# downloaded from GitHub Releases and checked against the SHA-256 GitHub publishes for it.
|
|
#
|
|
# curl -fsSL https://raw.githubusercontent.com/ludovic111/horizon/main/packaging/server/install.sh | sh
|
|
# curl -fsSL .../install.sh | sh -s -- --version v0.5.0 --dir /opt/horizon --bin /usr/local/bin
|
|
#
|
|
# It needs curl, tar or unzip, and sha256sum or shasum; it never asks for sudo (pick folders you can write).
|
|
# Releases go to <dir>/releases/<version>, <dir>/current points at the one in use, and <bin>/horizon-server
|
|
# runs it. The game files you prepare from your own Minecraft stay in <dir>/game-files across updates.
|
|
# Nothing of Mojang's is downloaded.
|
|
set -eu
|
|
|
|
REPO=ludovic111/horizon
|
|
VERSION=latest
|
|
DIR="${XDG_DATA_HOME:-$HOME/.local/share}/horizon"
|
|
BIN="$HOME/.local/bin"
|
|
|
|
die() { echo "horizon install: $*" >&2; exit 1; }
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--version) [ $# -ge 2 ] || die "--version needs a tag"; VERSION=$2; shift 2 ;;
|
|
--dir) [ $# -ge 2 ] || die "--dir needs a folder"; DIR=$2; shift 2 ;;
|
|
--bin) [ $# -ge 2 ] || die "--bin needs a folder"; BIN=$2; shift 2 ;;
|
|
-h|--help) sed -n '2,12p' "$0" 2>/dev/null || true; exit 0 ;;
|
|
*) die "unknown option $1" ;;
|
|
esac
|
|
done
|
|
|
|
case "$(uname -s)-$(uname -m)" in
|
|
Linux-x86_64) ASSET=horizon-linux-x86_64.tar.gz ;;
|
|
Darwin-arm64) ASSET=horizon-macos-arm64.zip ;;
|
|
*) die "no Horizon release for $(uname -s) $(uname -m) yet: build it from source (docs/server.md)" ;;
|
|
esac
|
|
|
|
command -v curl >/dev/null || die "curl is needed"
|
|
if command -v sha256sum >/dev/null; then SHA="sha256sum"
|
|
elif command -v shasum >/dev/null; then SHA="shasum -a 256"
|
|
else die "sha256sum or shasum is needed"; fi
|
|
|
|
if [ "$VERSION" = latest ]; then API="https://api.github.com/repos/$REPO/releases/latest"
|
|
else API="https://api.github.com/repos/$REPO/releases/tags/$VERSION"; fi
|
|
JSON=$(curl -fsSL -H "Accept: application/vnd.github+json" "$API") || die "no release $VERSION on GitHub"
|
|
|
|
TAG=$(printf '%s\n' "$JSON" | sed -n 's/^ "tag_name": "\([^"]*\)".*/\1/p' | head -n 1)
|
|
[ -n "$TAG" ] || die "could not read the release's tag"
|
|
# The asset's download address and digest, from its block in the release's JSON.
|
|
FIELDS=$(printf '%s\n' "$JSON" | awk -v asset="$ASSET" '
|
|
/"name": / { line = $0; sub(/.*"name": "/, "", line); sub(/".*/, "", line); current = line }
|
|
current == asset && /"digest": / { line = $0; sub(/.*"digest": "sha256:/, "", line); sub(/".*/, "", line); digest = line }
|
|
current == asset && /"browser_download_url": / { line = $0; sub(/.*"browser_download_url": "/, "", line); sub(/".*/, "", line); url = line }
|
|
END { print url; print digest }')
|
|
URL=$(printf '%s\n' "$FIELDS" | sed -n 1p)
|
|
DIGEST=$(printf '%s\n' "$FIELDS" | sed -n 2p)
|
|
[ -n "$URL" ] || die "release $TAG has no $ASSET"
|
|
[ -n "$DIGEST" ] || die "release $TAG publishes no SHA-256 for $ASSET"
|
|
|
|
TMP=$(mktemp -d)
|
|
trap 'rm -rf "$TMP"' EXIT
|
|
echo "Downloading Horizon $TAG ($ASSET)..."
|
|
curl -fL --progress-bar -o "$TMP/$ASSET" "$URL"
|
|
GOT=$($SHA "$TMP/$ASSET" | cut -d' ' -f1)
|
|
[ "$GOT" = "$DIGEST" ] || die "$ASSET doesn't match its SHA-256 (got $GOT, expected $DIGEST): not installed"
|
|
|
|
mkdir -p "$TMP/x"
|
|
case "$ASSET" in
|
|
*.tar.gz) tar -xzf "$TMP/$ASSET" -C "$TMP/x" ;;
|
|
*.zip) command -v unzip >/dev/null || die "unzip is needed"; unzip -q "$TMP/$ASSET" -d "$TMP/x" ;;
|
|
esac
|
|
SOURCE=$(dirname "$(find "$TMP/x" -name horizon-server -type f | head -n 1)")
|
|
[ -x "$SOURCE/horizon-server" ] || die "$ASSET holds no horizon-server (a release from before it existed?)"
|
|
|
|
RELEASE="$DIR/releases/$TAG"
|
|
mkdir -p "$DIR/releases" "$DIR/game-files/horizon-client" "$BIN"
|
|
rm -rf "$RELEASE"
|
|
mv "$SOURCE" "$RELEASE"
|
|
# Prepared game files live outside the release, so updates and rollbacks keep them.
|
|
mkdir -p "$RELEASE/.local"
|
|
ln -sfn "$DIR/game-files/horizon-client" "$RELEASE/.local/horizon-client"
|
|
ln -sfn "$RELEASE" "$DIR/current"
|
|
# Small launchers rather than links: the programs find their release folder from where they run.
|
|
for tool in horizon-server horizon-prepare; do
|
|
printf '#!/bin/sh\nexec "%s/current/%s" "$@"\n' "$DIR" "$tool" > "$BIN/$tool"
|
|
chmod +x "$BIN/$tool"
|
|
done
|
|
|
|
echo "Installed Horizon $TAG in $RELEASE; horizon-server and horizon-prepare are in $BIN."
|
|
# The game links ALSA and udev, even as a server; minimal Linux images often lack them.
|
|
if [ "$(uname -s)" = Linux ] && command -v ldd >/dev/null; then
|
|
MISSING=$(ldd "$RELEASE/horizon-client" 2>/dev/null | awk '/not found/ { printf "%s ", $1 }')
|
|
if [ -n "$MISSING" ]; then
|
|
echo "The game needs libraries this system doesn't have: $MISSING"
|
|
echo "Install them before starting a server:"
|
|
echo " Debian, Ubuntu: sudo apt install libasound2 libudev1 (Ubuntu 24.04 and later: libasound2t64)"
|
|
echo " Fedora, RHEL: sudo dnf install alsa-lib systemd-libs"
|
|
echo " Arch: sudo pacman -S alsa-lib systemd-libs"
|
|
echo " NixOS: enable programs.nix-ld with alsa-lib and udev in its libraries"
|
|
fi
|
|
fi
|
|
case ":$PATH:" in
|
|
*":$BIN:"*) ;;
|
|
*) echo "Add $BIN to your PATH, for example: echo 'export PATH=\"$BIN:\$PATH\"' >> ~/.profile" ;;
|
|
esac
|
|
if [ -z "$(ls -A "$DIR/game-files/horizon-client" 2>/dev/null)" ]; then
|
|
echo "Next, prepare the game files from your own Minecraft 26.3 (it needs Java 25 and the game installed once"
|
|
echo "by the official launcher; see docs/server.md):"
|
|
echo " horizon-prepare --prepare --server-jar /path/to/server.jar"
|
|
fi
|
|
echo "Then set up a server: mkdir my-server && cd my-server && horizon-server init"
|