Files
egg-hytale/entry.sh
NATroutter ffb410f146 Renamed variables in the egg to make them more clear
Improved the default Jvm args
Improved messages in entry
Removed the zip restriction from Asset packs (Do not use directories for assets yet)
Added script for local dev building
2026-01-17 14:52:18 +02:00

544 lines
19 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
################################################################################
# egg-hytale Entry Script
#
# This script handles:
# - Hytale server download and updates
# - Authentication with Hytale services
# - Session token generation
# - Launching the main server startup script
#
# DO NOT EDIT THIS FILE - it is managed by the Docker image.
# To customize server settings, use the egg configuration variables in your
# Pelican or Pterodactyl panel instead.
################################################################################
DOWNLOAD_URL="https://downloader.hytale.com/hytale-downloader.zip"
DOWNLOAD_FILE="hytale-downloader.zip"
DOWNLOAD_CRED_FILE=".hytale-downloader-credentials.json"
AUTH_CACHE_FILE=".hytale-auth-tokens.json"
VERSION_FILE="version.txt"
MODS_FOLDER="mods"
# Detect architecture and set appropriate downloader binary
ARCH=$(uname -m)
echo "Platform: $ARCH"
case "$ARCH" in
x86_64)
DOWNLOADER="./hytale-downloader-linux-amd64"
;;
aarch64|arm64)
DOWNLOADER="./hytale-downloader-linux-arm64"
;;
*)
echo "Error: Unsupported architecture: $ARCH"
echo "Supported architectures: x86_64 (amd64), aarch64/arm64"
exit 1
;;
esac
# Function to extract downloaded server files
extract_server_files() {
echo "Extracting server files..."
SERVER_ZIP="server.zip"
if [ -f "$SERVER_ZIP" ]; then
echo "✓ Found server archive: $SERVER_ZIP"
# Extract to current directory
unzip -o "$SERVER_ZIP"
if [ $? -ne 0 ]; then
echo "Error: Failed to extract $SERVER_ZIP"
exit 1
fi
echo "✓ Extraction completed successfully."
# Move contents from Server folder to current directory
if [ -d "Server" ]; then
echo "Moving server files from Server directory..."
mv -f Server/* .
rm -rf ./Server
echo "✓ Server files moved to root directory."
fi
# Clean up the zip file
echo "Cleaning up archive file..."
rm "$SERVER_ZIP"
echo "✓ Archive removed."
else
echo "Error: Server archive not found at $SERVER_ZIP"
exit 1
fi
}
# Function to check if cached tokens exist
check_cached_tokens() {
if [ -f "$AUTH_CACHE_FILE" ]; then
# Check if jq is available
if ! command -v jq &> /dev/null; then
echo "Warning: jq not found, cannot use cached tokens"
return 1
fi
# Validate JSON format
if ! jq empty "$AUTH_CACHE_FILE" 2>/dev/null; then
echo "Warning: Invalid cached token file, removing..."
rm "$AUTH_CACHE_FILE"
return 1
fi
# Check if required keys exist
REFRESH_TOKEN_EXISTS=$(jq -r 'has("refresh_token")' "$AUTH_CACHE_FILE")
PROFILE_UUID_EXISTS=$(jq -r 'has("profile_uuid")' "$AUTH_CACHE_FILE")
if [ "$REFRESH_TOKEN_EXISTS" != "true" ] || [ "$PROFILE_UUID_EXISTS" != "true" ]; then
echo "Warning: Cached token file missing required keys, removing..."
rm "$AUTH_CACHE_FILE"
return 1
fi
echo "✓ Found cached authentication tokens"
return 0
fi
return 1
}
# Function to load cached tokens (refresh_token + profile_uuid only)
load_cached_tokens() {
REFRESH_TOKEN=$(jq -r '.refresh_token' "$AUTH_CACHE_FILE")
PROFILE_UUID=$(jq -r '.profile_uuid' "$AUTH_CACHE_FILE")
# Validate required tokens are present
if [ -z "$REFRESH_TOKEN" ] || [ "$REFRESH_TOKEN" = "null" ] || \
[ -z "$PROFILE_UUID" ] || [ "$PROFILE_UUID" = "null" ]; then
echo "Error: Incomplete cached tokens, re-authenticating..."
rm "$AUTH_CACHE_FILE"
return 1
fi
echo "✓ Loaded cached refresh token + profile UUID"
return 0
}
# Function to refresh access token using cached refresh token
refresh_access_token() {
echo "Refreshing access token..."
TOKEN_RESPONSE=$(curl -s -X POST "https://oauth.accounts.hytale.com/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=hytale-server" \
-d "grant_type=refresh_token" \
-d "refresh_token=$REFRESH_TOKEN")
ERROR=$(echo "$TOKEN_RESPONSE" | jq -r '.error // empty')
if [ -n "$ERROR" ]; then
echo "Error: Failed to refresh access token: $ERROR"
return 1
fi
ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')
NEW_REFRESH_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.refresh_token // empty')
if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
echo "Error: No access token in refresh response"
return 1
fi
# Update refresh token if a new one was provided
if [ -n "$NEW_REFRESH_TOKEN" ] && [ "$NEW_REFRESH_TOKEN" != "null" ]; then
REFRESH_TOKEN="$NEW_REFRESH_TOKEN"
fi
echo "✓ Access token refreshed"
return 0
}
# Function to create a new game session
create_game_session() {
echo "Creating game server session..."
SESSION_RESPONSE=$(curl -s -X POST "https://sessions.hytale.com/game-session/new" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"uuid\": \"$PROFILE_UUID\"}")
# Validate JSON response
if ! echo "$SESSION_RESPONSE" | jq empty 2>/dev/null; then
echo "Error: Invalid JSON response from game session creation"
echo "Response: $SESSION_RESPONSE"
return 1
fi
# Extract session and identity tokens
SESSION_TOKEN=$(echo "$SESSION_RESPONSE" | jq -r '.sessionToken')
IDENTITY_TOKEN=$(echo "$SESSION_RESPONSE" | jq -r '.identityToken')
if [ -z "$SESSION_TOKEN" ] || [ "$SESSION_TOKEN" = "null" ]; then
echo "Error: Failed to create game server session"
echo "Response: $SESSION_RESPONSE"
return 1
fi
echo "✓ Game server session created successfully!"
return 0
}
# Function to save authentication tokens (refresh_token + profile_uuid only)
save_auth_tokens() {
# Create auth cache file (only in standard mode, not GSP mode)
if [ ! -f "$AUTH_CACHE_FILE" ]; then
echo "Creating auth cache file..."
touch $AUTH_CACHE_FILE
fi
cat > "$AUTH_CACHE_FILE" << EOF
{
"refresh_token": "$REFRESH_TOKEN",
"profile_uuid": "$PROFILE_UUID",
"timestamp": $(date +%s)
}
EOF
echo "✓ Refresh token cached for future use"
}
# Function to perform full authentication
perform_authentication() {
echo "Obtaining authentication tokens..."
# Step 1: Request device code
AUTH_RESPONSE=$(curl -s -X POST "https://oauth.accounts.hytale.com/oauth2/device/auth" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=hytale-server" \
-d "scope=openid offline auth:server")
# Extract device_code and verification_uri_complete using jq
DEVICE_CODE=$(echo "$AUTH_RESPONSE" | jq -r '.device_code')
VERIFICATION_URI=$(echo "$AUTH_RESPONSE" | jq -r '.verification_uri_complete')
POLL_INTERVAL=$(echo "$AUTH_RESPONSE" | jq -r '.interval')
# Display authentication banner
echo " "
echo "╔═════════════════════════════════════════════════════════════════════════════╗"
echo "║ HYTALE SERVER AUTHENTICATION REQUIRED ║"
echo "╠═════════════════════════════════════════════════════════════════════════════╣"
echo "║ ║"
echo "║ Please authenticate the server by visiting the following URL: ║"
echo "║ ║"
echo "$VERIFICATION_URI"
echo "║ ║"
echo "║ 1. Click the link above or copy it to your browser ║"
echo "║ 2. Sign in with your Hytale account ║"
echo "║ 3. Authorize the server ║"
echo "║ ║"
echo "║ Waiting for authentication... ║"
echo "║ ║"
echo "╚═════════════════════════════════════════════════════════════════════════════╝"
echo " "
# Step 2: Poll for access token
ACCESS_TOKEN=""
while [ -z "$ACCESS_TOKEN" ]; do
sleep $POLL_INTERVAL
TOKEN_RESPONSE=$(curl -s -X POST "https://oauth.accounts.hytale.com/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=hytale-server" \
-d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
-d "device_code=$DEVICE_CODE")
# Check if we got an error
ERROR=$(echo "$TOKEN_RESPONSE" | jq -r '.error // empty')
if [ "$ERROR" = "authorization_pending" ]; then
echo "Still waiting for authentication..."
continue
elif [ -n "$ERROR" ]; then
echo " Authentication error: $ERROR"
exit 1
else
# Successfully authenticated
ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')
REFRESH_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.refresh_token')
echo ""
echo "✓ Authentication successful!"
echo ""
fi
done
# Fetch available game profiles
echo "Fetching game profiles..."
PROFILES_RESPONSE=$(curl -s -X GET "https://account-data.hytale.com/my-account/get-profiles" \
-H "Authorization: Bearer $ACCESS_TOKEN")
# Check if profiles list is empty
PROFILES_COUNT=$(echo "$PROFILES_RESPONSE" | jq '.profiles | length')
if [ "$PROFILES_COUNT" -eq 0 ]; then
echo "Error: No game profiles found. You need to purchase Hytale to run a server."
exit 1
fi
# Select profile based on GAME_PROFILE variable
if [ -n "$GAME_PROFILE" ]; then
# User specified a profile username, find matching UUID
echo "Looking for profile: $GAME_PROFILE"
PROFILE_UUID=$(echo "$PROFILES_RESPONSE" | jq -r ".profiles[] | select(.username == \"$GAME_PROFILE\") | .uuid")
if [ -z "$PROFILE_UUID" ] || [ "$PROFILE_UUID" = "null" ]; then
echo "Error: Profile '$GAME_PROFILE' not found."
echo "Available profiles:"
echo "$PROFILES_RESPONSE" | jq -r '.profiles[] | " - \(.username)"'
exit 1
fi
echo "✓ Using profile: $GAME_PROFILE (UUID: $PROFILE_UUID)"
else
# Use first profile from the list
PROFILE_UUID=$(echo "$PROFILES_RESPONSE" | jq -r '.profiles[0].uuid')
PROFILE_USERNAME=$(echo "$PROFILES_RESPONSE" | jq -r '.profiles[0].username')
echo "✓ Using default profile: $PROFILE_USERNAME (UUID: $PROFILE_UUID)"
fi
echo ""
# Save refresh token + profile for future use
save_auth_tokens
# Create game server session
if ! create_game_session; then
exit 1
fi
echo ""
}
# Copy start.sh template to /home/container
echo "Copying start.sh template to /home/container..."
cp -f /usr/local/bin/start.sh start.sh
chmod 755 start.sh
# Check if the backup directory exists
if [ ! -d "backup" ]; then
echo "Backup directory does not exist. Creating it..."
mkdir -p backup
if [ $? -ne 0 ]; then
echo "Failed to create backup directory: /backup"
exit 1
fi
else
echo "Backup directory already exists."
fi
chmod -R 755 backup
# Check if the downloader exists
if [ ! -f "$DOWNLOADER" ]; then
echo "Error: Hytale downloader not found!"
echo "Please run the installation script first."
exit 1
fi
# Check if the downloader is executable
if [ ! -x "$DOWNLOADER" ]; then
echo "Setting executable permissions..."
chmod +x "$DOWNLOADER"
fi
# Create version file
if [ ! -f "$VERSION_FILE" ]; then
echo "Creating version check file..."
touch $VERSION_FILE
fi
#Fix system permissions
if [ -f "$VERSION_FILE" ] && { [ ! -r "$VERSION_FILE" ] || [ ! -w "$VERSION_FILE" ]; }; then
echo "Fixing permissions on $VERSION_FILE..."
chmod 644 "$VERSION_FILE"
fi
if [ -f "$DOWNLOAD_CRED_FILE" ] && { [ ! -r "$DOWNLOAD_CRED_FILE" ] || [ ! -w "$DOWNLOAD_CRED_FILE" ]; }; then
echo "Fixing permissions on $DOWNLOAD_CRED_FILE..."
chmod 644 "$DOWNLOAD_CRED_FILE"
fi
if [ -f "$AUTH_CACHE_FILE" ] && { [ ! -r "$AUTH_CACHE_FILE" ] || [ ! -w "$AUTH_CACHE_FILE" ]; }; then
echo "Fixing permissions on $AUTH_CACHE_FILE..."
chmod 644 "$AUTH_CACHE_FILE"
fi
INITIAL_SETUP=0
# Check if credentials file exists, if not run the updater
if [ ! -f "$DOWNLOAD_CRED_FILE" ]; then
INITIAL_SETUP=1
echo "Credentials file not found, running initial setup..."
echo "Downloading server files..."
$DOWNLOADER -check-update
echo " "
echo "════════════════════════════════════════════════════════════════"
echo " NOTE: You must have purchased Hytale on the account you are using to authenticate."
echo "════════════════════════════════════════════════════════════════"
echo " "
if ! $DOWNLOADER -patchline $PATCHLINE -download-path server.zip; then
echo ""
echo "Error: Failed to download Hytale server files."
echo "This may indicate:"
echo " - You haven't purchased Hytale"
echo " - Authentication credentials are invalid or expired"
echo ""
echo "Removing invalid credential file..."
rm -f $DOWNLOAD_CRED_FILE
exit 1
fi
# Save version info after initial setup
DOWNLOADER_VERSION=$($DOWNLOADER -print-version 2>&1)
if [ $? -eq 0 ] && [ -n "$DOWNLOADER_VERSION" ]; then
echo "$DOWNLOADER_VERSION" > $VERSION_FILE
echo "✓ Saved version info!"
fi
extract_server_files
fi
# Run automatic update if enabled
if [ "$AUTOMATIC_UPDATE" = "1" ] && [ "$INITIAL_SETUP" = "0" ]; then
echo "Checking for updates..."
# Read local version from file
if [ -f "$VERSION_FILE" ]; then
LOCAL_VERSION=$(cat $VERSION_FILE)
else
echo "version file not found, forcing update"
LOCAL_VERSION=""
fi
# Get remote/downloader version
DOWNLOADER_VERSION=$($DOWNLOADER -print-version 2>&1)
# Check if version command failed
if [ $? -ne 0 ] || [ -z "$DOWNLOADER_VERSION" ]; then
echo "Error: Failed to get downloader version. This may indicate authentication issues."
echo "Output: $DOWNLOADER_VERSION"
exit 1
else
if [ -n "$LOCAL_VERSION" ]; then
echo "Local version: $LOCAL_VERSION"
fi
echo "Downloader version: $DOWNLOADER_VERSION"
# Compare versions
if [ "$LOCAL_VERSION" != "$DOWNLOADER_VERSION" ]; then
echo " Version mismatch, running update..."
$DOWNLOADER -check-update
$DOWNLOADER -patchline $PATCHLINE -download-path server.zip
# Update version file after successful update
echo "$DOWNLOADER_VERSION" > $VERSION_FILE
echo "✓ Saved version info!"
extract_server_files
else
echo " Versions match, skipping update"
fi
fi
fi
# Check if server files were downloaded correctly
if [ ! -f "HytaleServer.jar" ]; then
echo "Error: HytaleServer.jar not found!"
echo "Server files were not downloaded correctly."
exit 1
fi
# Download the latest hytale-sourcequery plugin if enabled
if [ "$ENABLE_SOURCE_QUERY_SUPPORT" = "1" ]; then
echo "Source Query support enabled, checking for plugin..."
# Create mods directory if it doesn't exist
if [ ! -d "$MODS_FOLDER" ]; then
echo "Creating mods directory..."
mkdir -p $MODS_FOLDER
fi
if [ -d "$MODS_FOLDER" ] && { [ ! -r "$MODS_FOLDER" ] || [ ! -w "$MODS_FOLDER" ] || [ ! -x "$MODS_FOLDER" ]; }; then
echo "Fixing permissions on directory $MODS_FOLDER..."
chmod 755 "$MODS_FOLDER"
fi
echo "Downloading latest hytale-sourcequery plugin..."
LATEST_URL=$(curl -sSL https://api.github.com/repos/physgun-com/hytale-sourcequery/releases/latest | jq -r '.assets[0].browser_download_url // empty')
if [ -n "$LATEST_URL" ]; then
curl -sSL -o mods/hytale-sourcequery.jar "$LATEST_URL"
if [ $? -eq 0 ]; then
echo "✓ Successfully downloaded hytale-sourcequery plugin to mods folder"
else
echo " Failed to download hytale-sourcequery plugin"
fi
else
echo " Could not find hytale-sourcequery plugin download URL"
fi
fi
# Check if GSP mode (tokens provided externally)
if [ -n "$OVERRIDE_SESSION_TOKEN" ] && [ -n "$OVERRIDE_IDENTITY_TOKEN" ]; then
echo "Using provided session and identity tokens..."
SESSION_TOKEN="$OVERRIDE_SESSION_TOKEN"
IDENTITY_TOKEN="$OVERRIDE_IDENTITY_TOKEN"
else
# Standard mode: perform authentication
if check_cached_tokens && load_cached_tokens; then
echo "Using cached authentication..."
if refresh_access_token; then
# Update cache in case refresh token rotated
save_auth_tokens
# Create fresh game session
if ! create_game_session; then
exit 1
fi
else
# Refresh failed, need full re-auth
echo "Refresh token expired, re-authenticating..."
rm -f "$AUTH_CACHE_FILE"
perform_authentication
fi
else
# Perform full authentication if no valid cache exists
perform_authentication
fi
fi
# Export the session tokens so they're available to start.sh
export SESSION_TOKEN
export IDENTITY_TOKEN
# Enforce file and folder permissions if enabled
if [ "$ENFORCE_PERMISSIONS" = "1" ]; then
echo "Enforcing permissions... This might take a while. Please be patient."
# Set all directories to 755
find . -type d -exec chmod 755 {} \;
# Set all files to 644, excluding executables that need to remain executable
find . -type f \
! -name "hytale-downloader-linux-amd64" \
! -name "hytale-downloader-linux-arm64" \
! -name "qemu-x86_64-static" \
! -name "start.sh" \
-exec chmod 644 {} \;
echo "✓ Permissions enforced (files: 644, folders: 755)"
fi
# Now call the pterodactyl entrypoint which will execute start.sh
exec /bin/bash /entrypoint.sh