@echo off setlocal enabledelayedexpansion :: ============================================================ :: Hytale F2P Dedicated Server - One-Click Starter :: ============================================================ :: Just double-click this file to start your server! :: :: The script will: :: 1. Look for game files and Java in your F2P launcher install :: 2. Auto-download anything missing :: 3. Auto-update server, assets, and agent on each launch :: 4. Fetch auth tokens and start the server :: ============================================================ :: Configuration (edit these or set as environment variables) if not defined HYTALE_AUTH_DOMAIN set "HYTALE_AUTH_DOMAIN=auth.sanasol.ws" if not defined AUTH_SERVER set "AUTH_SERVER=https://%HYTALE_AUTH_DOMAIN%" if not defined SERVER_NAME set "SERVER_NAME=My Hytale Server" if not defined ASSETS_PATH set "ASSETS_PATH=.\Assets.zip" if not defined BIND_ADDRESS set "BIND_ADDRESS=0.0.0.0:5520" if not defined AUTH_MODE set "AUTH_MODE=authenticated" if not defined DOWNLOAD_BASE set "DOWNLOAD_BASE=https://download.sanasol.ws/download" :: File names set "AGENT_JAR=dualauth-agent.jar" set "SERVER_JAR=HytaleServer.jar" set "AGENT_URL=https://github.com/sanasol/hytale-auth-server/releases/latest/download/dualauth-agent.jar" set "AGENT_VERSION_API=https://api.github.com/repos/sanasol/hytale-auth-server/releases/latest" set "VERSION_DIR=.versions" echo ============================================================ echo Hytale F2P Dedicated Server echo ============================================================ echo. :: --- Prerequisite Checks --- where curl >nul 2>&1 if errorlevel 1 ( echo [ERROR] curl is required but not found echo [ERROR] curl comes with Windows 10+. Update Windows or install curl. pause exit /b 1 ) if not exist "%VERSION_DIR%" mkdir "%VERSION_DIR%" :: --- Find Local F2P Launcher Install --- set "F2P_DIR=" set "F2P_BASE=%USERPROFILE%\AppData\Local\HytaleF2P" set "F2P_CONFIG=%F2P_BASE%\config.json" set "JAVA_CMD=java" :: Check config.json for custom installPath set "F2P_CUSTOM_BASE=" if exist "%F2P_CONFIG%" ( for /f "delims=" %%p in ('powershell -Command "try { $c = Get-Content '%F2P_CONFIG%' | ConvertFrom-Json; if ($c.installPath) { $c.installPath.Trim() + '\HytaleF2P' } } catch {}" 2^>nul') do ( set "F2P_CUSTOM_BASE=%%p" ) ) :: Search for game files: custom path first, then default if defined F2P_CUSTOM_BASE ( if exist "!F2P_CUSTOM_BASE!\release\package\game\latest" ( set "F2P_DIR=!F2P_CUSTOM_BASE!\release\package\game\latest" ) else if exist "!F2P_CUSTOM_BASE!\pre-release\package\game\latest" ( set "F2P_DIR=!F2P_CUSTOM_BASE!\pre-release\package\game\latest" ) ) if not defined F2P_DIR ( if exist "%F2P_BASE%\release\package\game\latest" ( set "F2P_DIR=%F2P_BASE%\release\package\game\latest" ) else if exist "%F2P_BASE%\pre-release\package\game\latest" ( set "F2P_DIR=%F2P_BASE%\pre-release\package\game\latest" ) ) :: --- Find Java from F2P launcher --- :: Check config.json for custom javaPath if exist "%F2P_CONFIG%" ( for /f "delims=" %%j in ('powershell -Command "try { $c = Get-Content '%F2P_CONFIG%' | ConvertFrom-Json; if ($c.javaPath -and (Test-Path $c.javaPath)) { $c.javaPath.Trim() } } catch {}" 2^>nul') do ( set "JAVA_CMD=%%j" echo [INFO] Found Java in F2P config: %%j ) ) :: Check bundled JRE if no custom javaPath found if "!JAVA_CMD!"=="java" ( set "F2P_JRE_BASE=" if defined F2P_CUSTOM_BASE ( if exist "!F2P_CUSTOM_BASE!\release\package\jre\latest\bin\java.exe" ( set "F2P_JRE_BASE=!F2P_CUSTOM_BASE!\release\package\jre\latest" ) else if exist "!F2P_CUSTOM_BASE!\pre-release\package\jre\latest\bin\java.exe" ( set "F2P_JRE_BASE=!F2P_CUSTOM_BASE!\pre-release\package\jre\latest" ) ) if not defined F2P_JRE_BASE ( if exist "%F2P_BASE%\release\package\jre\latest\bin\java.exe" ( set "F2P_JRE_BASE=%F2P_BASE%\release\package\jre\latest" ) else if exist "%F2P_BASE%\pre-release\package\jre\latest\bin\java.exe" ( set "F2P_JRE_BASE=%F2P_BASE%\pre-release\package\jre\latest" ) ) if defined F2P_JRE_BASE ( set "JAVA_CMD=!F2P_JRE_BASE!\bin\java.exe" echo [INFO] Found Java in F2P launcher: !JAVA_CMD! ) ) :: Verify java exists "!JAVA_CMD!" -version >nul 2>&1 if errorlevel 1 ( where java >nul 2>&1 if errorlevel 1 ( echo [ERROR] Java is not installed and no F2P launcher JRE found echo. echo Options: echo 1. Install the F2P launcher first ^(it includes Java^) echo 2. Download Java 25: https://download.oracle.com/java/25/latest/jdk-25_windows-x64_bin.exe echo. pause exit /b 1 ) set "JAVA_CMD=java" ) :: Check Java version for /f "tokens=3 delims= " %%v in ('"!JAVA_CMD!" -version 2^>^&1 ^| findstr /i "version"') do ( set "JAVA_VER_RAW=%%~v" ) if defined JAVA_VER_RAW ( for /f "tokens=1 delims=." %%m in ("!JAVA_VER_RAW!") do set "JAVA_MAJOR=%%m" ) echo [INFO] Java: !JAVA_VER_RAW! ^(!JAVA_CMD!^) if defined JAVA_MAJOR ( if !JAVA_MAJOR! LSS 25 ( echo [ERROR] Java !JAVA_MAJOR! detected. Java 25+ is REQUIRED. echo The DualAuth agent requires Java 25 ^(class file version 69^). echo. echo Download Java 25: https://download.oracle.com/java/25/latest/jdk-25_windows-x64_bin.exe echo. pause exit /b 1 ) ) :: --- Copy game files from F2P install --- if defined F2P_DIR ( echo [INFO] Found F2P launcher game files: !F2P_DIR! if not exist "%SERVER_JAR%" ( if exist "!F2P_DIR!\Server\HytaleServer.jar" ( echo [INFO] Found HytaleServer.jar in F2P launcher echo [INFO] Copying from: !F2P_DIR!\Server\HytaleServer.jar copy "!F2P_DIR!\Server\HytaleServer.jar" "%SERVER_JAR%" >nul echo [INFO] Copied successfully ) ) if not exist "%ASSETS_PATH%" ( if exist "!F2P_DIR!\Assets.zip" ( echo [INFO] Found Assets.zip in F2P launcher echo [INFO] Copying from: !F2P_DIR!\Assets.zip copy "!F2P_DIR!\Assets.zip" "%ASSETS_PATH%" >nul echo [INFO] Copied successfully ) ) echo. ) else ( echo [INFO] No F2P launcher install found, will download files echo. ) :: --- Download / Update HytaleServer.jar --- set "JAR_URL=%DOWNLOAD_BASE%/HytaleServer.jar" set "JAR_VERSION_FILE=%VERSION_DIR%\HytaleServer.jar.version" if not exist "%SERVER_JAR%" ( echo [INFO] HytaleServer.jar not found, downloading... echo [INFO] Expected size: ~150 MB curl -fL --progress-bar -o "%SERVER_JAR%.tmp" "%JAR_URL%" --connect-timeout 15 --max-time 3600 if exist "%SERVER_JAR%.tmp" ( move /y "%SERVER_JAR%.tmp" "%SERVER_JAR%" >nul echo [INFO] HytaleServer.jar downloaded for /f "delims=" %%h in ('powershell -Command "try { (Invoke-WebRequest -Uri '%JAR_URL%' -Method Head -UseBasicParsing).Headers['ETag'] } catch {}" 2^>nul') do ( echo %%h>"%JAR_VERSION_FILE%" ) ) else ( echo [ERROR] Failed to download HytaleServer.jar echo [ERROR] Check your internet connection pause exit /b 1 ) ) else ( echo [INFO] Checking for HytaleServer.jar updates... set "LOCAL_JAR_VER=" if exist "%JAR_VERSION_FILE%" set /p LOCAL_JAR_VER=<"%JAR_VERSION_FILE%" set "REMOTE_JAR_VER=" for /f "delims=" %%h in ('powershell -Command "try { (Invoke-WebRequest -Uri '%JAR_URL%' -Method Head -UseBasicParsing -TimeoutSec 10).Headers['ETag'] } catch { '' }" 2^>nul') do ( set "REMOTE_JAR_VER=%%h" ) if defined REMOTE_JAR_VER ( if "!LOCAL_JAR_VER!"=="!REMOTE_JAR_VER!" ( echo [INFO] HytaleServer.jar is up to date ) else ( echo [INFO] HytaleServer.jar update available, downloading... curl -fL --progress-bar -o "%SERVER_JAR%.tmp" "%JAR_URL%" --connect-timeout 15 --max-time 3600 if exist "%SERVER_JAR%.tmp" ( move /y "%SERVER_JAR%.tmp" "%SERVER_JAR%" >nul echo !REMOTE_JAR_VER!>"%JAR_VERSION_FILE%" echo [INFO] HytaleServer.jar updated ) else ( echo [WARN] Update failed, using existing HytaleServer.jar ) ) ) else ( echo [INFO] Could not check for updates, using existing HytaleServer.jar ) ) :: --- Download / Update Assets.zip --- set "ASSETS_URL=%DOWNLOAD_BASE%/Assets.zip" set "ASSETS_VERSION_FILE=%VERSION_DIR%\Assets.zip.version" if not exist "%ASSETS_PATH%" ( echo [INFO] Assets.zip not found, downloading... echo [INFO] Expected size: ~3.3 GB - this will take a while curl -fL --progress-bar -o "%ASSETS_PATH%.tmp" "%ASSETS_URL%" --connect-timeout 15 --max-time 7200 if exist "%ASSETS_PATH%.tmp" ( move /y "%ASSETS_PATH%.tmp" "%ASSETS_PATH%" >nul echo [INFO] Assets.zip downloaded for /f "delims=" %%h in ('powershell -Command "try { (Invoke-WebRequest -Uri '%ASSETS_URL%' -Method Head -UseBasicParsing).Headers['ETag'] } catch {}" 2^>nul') do ( echo %%h>"%ASSETS_VERSION_FILE%" ) ) else ( echo [ERROR] Failed to download Assets.zip echo [ERROR] Check your internet connection pause exit /b 1 ) ) else ( echo [INFO] Checking for Assets.zip updates... set "LOCAL_ASSETS_VER=" if exist "%ASSETS_VERSION_FILE%" set /p LOCAL_ASSETS_VER=<"%ASSETS_VERSION_FILE%" set "REMOTE_ASSETS_VER=" for /f "delims=" %%h in ('powershell -Command "try { (Invoke-WebRequest -Uri '%ASSETS_URL%' -Method Head -UseBasicParsing -TimeoutSec 10).Headers['ETag'] } catch { '' }" 2^>nul') do ( set "REMOTE_ASSETS_VER=%%h" ) if defined REMOTE_ASSETS_VER ( if "!LOCAL_ASSETS_VER!"=="!REMOTE_ASSETS_VER!" ( echo [INFO] Assets.zip is up to date ) else ( echo [INFO] Assets.zip update available, downloading... echo [INFO] This is a large file ^(~3.3 GB^), please be patient curl -fL --progress-bar -o "%ASSETS_PATH%.tmp" "%ASSETS_URL%" --connect-timeout 15 --max-time 7200 if exist "%ASSETS_PATH%.tmp" ( move /y "%ASSETS_PATH%.tmp" "%ASSETS_PATH%" >nul echo !REMOTE_ASSETS_VER!>"%ASSETS_VERSION_FILE%" echo [INFO] Assets.zip updated ) else ( echo [WARN] Update failed, using existing Assets.zip ) ) ) else ( echo [INFO] Could not check for updates, using existing Assets.zip ) ) :: --- Download / Update DualAuth Agent --- set "AGENT_VERSION_FILE=%VERSION_DIR%\dualauth-agent.jar.version" if not exist "%AGENT_JAR%" ( echo [INFO] Downloading DualAuth Agent... curl -fL -# -o "%AGENT_JAR%.tmp" "%AGENT_URL%" --connect-timeout 15 --max-time 120 if exist "%AGENT_JAR%.tmp" ( move /y "%AGENT_JAR%.tmp" "%AGENT_JAR%" >nul echo [INFO] DualAuth Agent downloaded for /f "delims=" %%v in ('powershell -Command "try { $r = Invoke-RestMethod -Uri '%AGENT_VERSION_API%' -TimeoutSec 10; $r.tag_name } catch { '' }" 2^>nul') do ( echo %%v>"%AGENT_VERSION_FILE%" ) ) else ( echo [ERROR] Failed to download DualAuth Agent echo [ERROR] Download manually: %AGENT_URL% pause exit /b 1 ) ) else ( echo [INFO] Checking for DualAuth Agent updates... set "LOCAL_AGENT_VER=" if exist "%AGENT_VERSION_FILE%" set /p LOCAL_AGENT_VER=<"%AGENT_VERSION_FILE%" set "REMOTE_AGENT_VER=" for /f "delims=" %%v in ('powershell -Command "try { $r = Invoke-RestMethod -Uri '%AGENT_VERSION_API%' -TimeoutSec 10; $r.tag_name } catch { '' }" 2^>nul') do ( set "REMOTE_AGENT_VER=%%v" ) if defined REMOTE_AGENT_VER ( if "!LOCAL_AGENT_VER!"=="!REMOTE_AGENT_VER!" ( echo [INFO] DualAuth Agent up to date ^(!LOCAL_AGENT_VER!^) ) else ( echo [INFO] Agent update: !LOCAL_AGENT_VER! -^> !REMOTE_AGENT_VER! curl -fL -# -o "%AGENT_JAR%.tmp" "%AGENT_URL%" --connect-timeout 15 --max-time 120 if exist "%AGENT_JAR%.tmp" ( move /y "%AGENT_JAR%.tmp" "%AGENT_JAR%" >nul echo !REMOTE_AGENT_VER!>"%AGENT_VERSION_FILE%" echo [INFO] DualAuth Agent updated ) else ( echo [WARN] Agent update failed, using existing ) ) ) else ( echo [INFO] Could not check agent updates, using existing ) ) :: --- Final Checks --- if not exist "%SERVER_JAR%" ( echo [ERROR] HytaleServer.jar not found pause exit /b 1 ) if not exist "%ASSETS_PATH%" ( echo [ERROR] Assets.zip not found pause exit /b 1 ) if not exist "%AGENT_JAR%" ( echo [ERROR] dualauth-agent.jar not found pause exit /b 1 ) :: --- Generate or Load Server ID --- set "SERVER_ID_FILE=.server-id" if exist "%SERVER_ID_FILE%" ( set /p SERVER_ID=<"%SERVER_ID_FILE%" echo [INFO] Server ID: !SERVER_ID! ) else ( for /f "delims=" %%i in ('powershell -Command "[guid]::NewGuid().ToString()"') do set "SERVER_ID=%%i" echo !SERVER_ID!>"%SERVER_ID_FILE%" echo [INFO] Generated server ID: !SERVER_ID! ) :: --- Fetch Server Tokens --- echo. echo [INFO] Fetching server tokens from %AUTH_SERVER%... set "TEMP_RESPONSE=%TEMP%\hytale_auth_%RANDOM%.json" curl -s -X POST "%AUTH_SERVER%/server/auto-auth" ^ -H "Content-Type: application/json" ^ -d "{\"server_id\": \"!SERVER_ID!\", \"server_name\": \"%SERVER_NAME%\"}" ^ --connect-timeout 10 ^ --max-time 30 ^ -o "%TEMP_RESPONSE%" 2>nul if errorlevel 1 ( echo [ERROR] Failed to connect to auth server at %AUTH_SERVER% del "%TEMP_RESPONSE%" 2>nul pause exit /b 1 ) findstr /C:"sessionToken" "%TEMP_RESPONSE%" >nul 2>&1 if errorlevel 1 ( echo [ERROR] Invalid response from auth server: type "%TEMP_RESPONSE%" del "%TEMP_RESPONSE%" 2>nul pause exit /b 1 ) :: Extract tokens using PowerShell for /f "delims=" %%i in ('powershell -Command "$j = Get-Content '%TEMP_RESPONSE%' | ConvertFrom-Json; $j.sessionToken"') do set "SESSION_TOKEN=%%i" for /f "delims=" %%i in ('powershell -Command "$j = Get-Content '%TEMP_RESPONSE%' | ConvertFrom-Json; $j.identityToken"') do set "IDENTITY_TOKEN=%%i" del "%TEMP_RESPONSE%" 2>nul if "!SESSION_TOKEN!"=="" ( echo [ERROR] Could not extract session token from response pause exit /b 1 ) if "!IDENTITY_TOKEN!"=="" ( echo [ERROR] Could not extract identity token from response pause exit /b 1 ) echo [INFO] Tokens received successfully :: --- Start Server --- set "JAVA_ARGS=" if defined JVM_XMS set "JAVA_ARGS=!JAVA_ARGS! -Xms%JVM_XMS%" if defined JVM_XMX set "JAVA_ARGS=!JAVA_ARGS! -Xmx%JVM_XMX%" echo. echo ============================================================ echo Starting Hytale Server echo Name: %SERVER_NAME% echo Bind: %BIND_ADDRESS% echo Java: !JAVA_CMD! echo Agent: %AGENT_JAR% echo ============================================================ echo. "!JAVA_CMD!" %JAVA_ARGS% -javaagent:"%AGENT_JAR%" -jar "%SERVER_JAR%" ^ --assets "%ASSETS_PATH%" ^ --bind "%BIND_ADDRESS%" ^ --auth-mode "%AUTH_MODE%" ^ --disable-sentry ^ --session-token "!SESSION_TOKEN!" ^ --identity-token "!IDENTITY_TOKEN!" ^ %* echo. echo ============================================================ echo Server stopped. Exit code: %ERRORLEVEL% echo ============================================================ pause endlocal