#!/usr/bin/env bash # fetch-authlib.sh — download the latest authlib-injector.jar into runtime/. # # The Minecraft container mounts ./runtime read-only at /extras and loads the # server JVM agent from /extras/authlib-injector.jar (points the server at Drasl # for session validation). The jar is gitignored, so a fresh checkout needs this. # # Usage: # tooling/fetch-authlib.sh # download if missing (or invalid) # tooling/fetch-authlib.sh --force # re-download even if present # # Cautious by design: resolves the release asset by name, halts on any error, # and verifies the result is a real zip/jar before trusting it (a half-written # or HTML error body would otherwise surface later as the cryptic # "Error opening zip file or JAR manifest missing"). set -euo pipefail cd "$(dirname "$0")/.." # repo root log() { printf '\n\033[1;32m==>\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m!!\033[0m %s\n' "$*" >&2; } die() { printf '\033[1;31mxx\033[0m %s\n' "$*" >&2; exit 1; } command -v curl >/dev/null || die "curl required" command -v jq >/dev/null || die "jq required" DEST="runtime/authlib-injector.jar" API="https://api.github.com/repos/yushijinhun/authlib-injector/releases/latest" # A valid jar is a zip — first two bytes are "PK". Reject anything else. is_jar() { [ -s "$1" ] && [ "$(head -c2 "$1" 2>/dev/null)" = "PK" ]; } if [ "${1:-}" != "--force" ] && is_jar "$DEST"; then log "$DEST already present and valid — skipping (use --force to re-download)" exit 0 fi log "resolving latest authlib-injector release asset" url="$(curl -fsSL "$API" \ | jq -r '.assets[] | select(.name|test("authlib-injector-[0-9].*\\.jar$")) | .browser_download_url' \ | head -1)" [ -n "$url" ] && [ "$url" != "null" ] || die "could not resolve authlib-injector release asset" mkdir -p runtime tmp="${DEST}.part" log "downloading $(basename "$url")" curl -fSL --retry 3 -o "$tmp" "$url" is_jar "$tmp" || { rm -f "$tmp"; die "downloaded file is not a valid jar (zip) — aborting"; } mv -f "$tmp" "$DEST" log "done → $DEST ($(wc -c <"$DEST") bytes)"