Simple server jar updater (bash)

  • After careful consideration and due to limited usage, we’ve made the decision to discontinue the PaperMC forums. Moving forward, we recommend using Hangar for plugin uploads, and for all other community discussions and support, please join us on Discord.

Tau

New member
Jan 12, 2022
28
8
3
I just updated it to v3, thought I'd share this rather than keep it to myself.
Don't expect support for this.

Code:
#!/usr/bin/env bash

#
# A simple jar updater script you can run on every startup.
# By default, it only attempts an update if the server jar is missing or 
# more than half a day has passed since the last update.
#
# Requirements: curl, jq, bash, sha256sum, stat
#
# Usage: -h
#
# Author: Tau ([email protected], [email protected])
#

# Check we have all dependencies
for cmd in curl jq sha256sum stat; do
  command -v $cmd >/dev/null 2>&1 || { echo "$cmd is required but not installed."; exit 1; }
done

PROJECT="paper"

# Defaults
MINECRAFT_VERSION_DEF="1.21.8"
STABLE_ONLY_DEF="true"
SERVER_JAR_PATH_DEF="server.jar"
JAR_AGE_DEF=3600 # One hour

# Do not change
MINECRAFT_VERSION="$MINECRAFT_VERSION_DEF"
STABLE_ONLY="$STABLE_ONLY_DEF"
SERVER_JAR_PATH="$SERVER_JAR_PATH_DEF"
JAR_AGE="$JAR_AGE_DEF"

USER_AGENT="User-Agent: taucu-jar-updater/1.0.0"

while getopts "v:s:j:a:h" opt; do
  case $opt in
    v) MINECRAFT_VERSION="$OPTARG" ;;
    s) STABLE_ONLY="$OPTARG" ;;
    j) SERVER_JAR_PATH="$OPTARG" ;;
    a) JAR_AGE="$OPTARG" ;;
    h)
      echo "Usage: $0 [-v version] [-s true|false] [-j path] [-a seconds]"
      echo "  -v <version>     Target Minecraft version             def: $MINECRAFT_VERSION_DEF"
      echo "  -s <true|false>  Only update to stable releases       def: $STABLE_ONLY_DEF"
      echo "  -j <path>        Path of the jar to update            def: $SERVER_JAR_PATH_DEF"
      echo "  -a <seconds|-1>  Only update if jar is older than -a  def: $JAR_AGE_DEF"
      exit 0
      ;;
    \?)
      echo "Invalid option: -$OPTARG"
      exit 1
      ;;
  esac
done

# Parse/check variables
if ! [[ "$JAR_AGE" =~ ^-?[0-9]+$ ]]; then
  echo "Error: -a must be a number. Got: $JAR_AGE"
  exit 1
fi
STABLE_ONLY="${STABLE_ONLY,,}"
if [[ "$STABLE_ONLY" != "true" && "$STABLE_ONLY" != "false" ]]; then
  echo "Error: -s must be a true or false. Got: $STABLE_ONLY"
  exit 1
fi

# Only update if the jar doesn't exist or is older than JAR_AGE
if (( JAR_AGE > -1 )); then
    CURTIME=$(date +%s)
    if [ -f "$SERVER_JAR_PATH" ]; then
        FILETIME=$(stat -c %Y "$SERVER_JAR_PATH")
        TIMEDIFF=$((CURTIME - FILETIME))
        if (( TIMEDIFF < JAR_AGE )); then
            echo "Server jar updated recently: $TIMEDIFF seconds ago; Skipping update."
            exit 0
        fi
    fi
fi

echo "Checking for updates..."

# Get available builds
AVAILABLE_BUILDS=$(curl -sSf --retry 3 --retry-delay 5 -H "${USER_AGENT}" "https://fill.papermc.io/v3/projects/${PROJECT}/versions/${MINECRAFT_VERSION}/builds") || {
    echo "Error: Failed to fetch builds from PaperMC API"
    exit 1
}

# Parse/check response
if ! echo "$AVAILABLE_BUILDS" | jq empty 2>/dev/null; then
    echo "Error: Received invalid JSON"
    echo "Got: $AVAILABLE_BUILDS"
    exit 1
fi

# Get latest build
if [ "$STABLE_ONLY" = "true" ]; then
    LATEST_BUILD=$(echo "$AVAILABLE_BUILDS" | jq 'first(.[] | map(select(.channel == "STABLE")))')
else
    LATEST_BUILD=$(echo "$AVAILABLE_BUILDS" | jq 'first(.[])')
fi

# Parse/check response
if [ "$LATEST_BUILD" = "null" ] || [ -z "$LATEST_BUILD" ]; then
  echo "Couldn't find a build for: [$MINECRAFT_VERSION, stable: $STABLE_ONLY]"
  exit 1
fi

# Resolve build number, hash and download url
LATEST_BUILD_NUM=$(echo "$LATEST_BUILD" | jq -r '.id')
EXPECTED_HASH=$(echo "$LATEST_BUILD" | jq -r '.downloads."server:default".checksums.sha256')
LATEST_BUILD_DL_URL=$(echo "$LATEST_BUILD" | jq -r '.downloads."server:default".url')

if [ -f "$SERVER_JAR_PATH" ]; then
    LAST_HASH=$(sha256sum "$SERVER_JAR_PATH" | awk '{ print $1 }')
    if [ "$EXPECTED_HASH" = "$LAST_HASH" ]; then
        echo "Hash is identical; Skipping update."
		touch "$SERVER_JAR_PATH"
        exit 0
    fi
fi

# Download jar
echo "Downloading: $LATEST_BUILD_DL_URL"
curl -Sf --retry 3 --retry-delay 5 -H "${USER_AGENT}" -o "$SERVER_JAR_PATH.tmp" "$LATEST_BUILD_DL_URL"

# Compute the hash
FILE_HASH=$(sha256sum "$SERVER_JAR_PATH.tmp" | awk '{print $1}')

# Verify the hash and move the jar
if [ "$EXPECTED_HASH" = "$FILE_HASH" ]; then
    if [ -f "$SERVER_JAR_PATH" ]; then
        mv -f "$SERVER_JAR_PATH" "$SERVER_JAR_PATH.old"
    fi
    mv -f "$SERVER_JAR_PATH.tmp" "$SERVER_JAR_PATH"
    echo "Server jar updated to: ${MINECRAFT_VERSION}-$LATEST_BUILD_NUM"
else
    echo "Hash mismatch! Update failed."
    echo "Expected: $EXPECTED_HASH"
    echo "Actual:   $FILE_HASH"
    exit 1
fi

exit 0
 
Last edited:

Roseccarty

New member
Dec 28, 2024
2
0
1
I just updated it to v3, thought I'd share this rather than keep it to myself.
Don't expect support for this.
# Do not change
MINECRAFT_VERSION="$MINECRAFT_VERSION_DEF"
STABLE_ONLY="$STABLE_ONLY_DEF"
SERVER_JAR_PATH="$SERVER_JAR_PATH_DEF"
Check we have all dependencies
for cmd in curl jq sha256sum stat; Doodle Jump 2
command -v $cmd >/dev/null 2>&1 || { echo "$cmd is required but not installed."; exit 1; }
done
Have you considered adding a log output for when updates occur? It could help track if the script is running as expected.
 

Tau

New member
Jan 12, 2022
28
8
3
Update:
1. Script will no longer update if the server jar's hash = the latest build hash in order to save bandwidth for papermc's api.
2. Lowered jar age check default to 1 hour as it isn't bandwidth intensive anymore.
 

Tau

New member
Jan 12, 2022
28
8
3
Have you considered adding a log output for when updates occur? It could help track if the script is running as expected.

Nah, I don't think it's needed. If you notice your server not updating then you already know it isn't working.