Files
egg-hytale/entry.sh
NATroutter 46a6c7e21c Improved the note message in initial setup
Updated readme to include new options and include caution about authentication
2026-01-14 17:37:26 +02:00

440 lines
16 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"
DOWNLOADER="./hytale-downloader-linux-amd64"
AUTH_CACHE_FILE=".hytale-auth-tokens.json"
# 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 Server/* .
rmdir 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() {
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 if it doesn't exist
echo "Copying start.sh template to /home/container..."
cp /usr/local/bin/start.sh /home/container/start.sh
chmod 755 /home/container/start.sh
# Check if the backup directory exists
if [ ! -d "/home/container/backup" ]; then
echo "Backup directory does not exist. Creating it..."
mkdir -p /home/container/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 /home/container/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
INITIAL_SETUP=0
# Check if credentials file exists, if not run the updater
if [ ! -f ".hytale-downloader-credentials.json" ]; 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 .hytale-downloader-credentials.json
exit 1
fi
extract_server_files
# Save version info after initial setup
DOWNLOADER_VERSION=$($DOWNLOADER -print-version 2>&1)
if [ $? -eq 0 ] && [ -n "$DOWNLOADER_VERSION" ]; then
echo "$DOWNLOADER_VERSION" > version.txt
echo "✓ Saved version info to version.txt!"
fi
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.txt" ]; then
LOCAL_VERSION=$(cat version.txt)
else
echo "version.txt 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
echo "Local version: $LOCAL_VERSION"
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
extract_server_files
# Update version.txt after successful update
echo "$DOWNLOADER_VERSION" > version.txt
echo "✓ Saved version info to version.txt!"
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
# Check for cached authentication tokens
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
# Export the session tokens so they're available to start.sh
export SESSION_TOKEN
export IDENTITY_TOKEN
export PROFILE_UUID
# Now call the pterodactyl entrypoint which will execute start.sh
exec /bin/bash /entrypoint.sh