#!/bin/bash set -e cd /home/container || { echo "ERROR: /home/container does not exist" exit 1 } # Install if needed if [[ "$BL_INST_FORCE" == "1" ]] || [ ! -f "Blockland.exe" ]; then source /usr/local/bin/install-bl fi # Required vars [ -z "$BL_PORT" ] && echo "Missing BL_PORT" && exit 1 [ -z "$BL_GAMEMODE" ] && echo "Missing BL_GAMEMODE" && exit 1 if [[ "$BL_DEDI_LAN" == "1" ]]; then dedi="dedicatedLAN" else [ -z "$BL_DTOKEN" ] && echo "Missing BL_DTOKEN" && exit 1 dedi="dedicated" fi # Create wine prefix if missing if [ ! -d "$HOME/.wine" ]; then echo "Creating wine prefix..." wineboot -i sleep 3 fi if [ "${BL_UNZIP_ADDONS:-0}" -eq 1 ]; then echo "Processing Add-Ons zips..." cd /home/container/Add-Ons || exit 1 for zip in *.zip; do [ -f "$zip" ] || continue addon_name="${zip%.zip}" echo "Extracting $zip..." # Extract into a temp directory first mkdir -p "__tmp_extract__" unzip -o "$zip" -d "__tmp_extract__" >/dev/null # Count top-level items in extraction item_count=$(find "__tmp_extract__" -mindepth 1 -maxdepth 1 | wc -l) if [ "$item_count" -eq 1 ]; then first_item=$(find "__tmp_extract__" -mindepth 1 -maxdepth 1) if [ -d "$first_item" ]; then # Proper folder structure, just move it mv "$first_item" . else # Single loose file — fix structure mkdir -p "$addon_name" mv "$first_item" "$addon_name"/ fi else # Multiple loose files — put them in folder mkdir -p "$addon_name" mv "__tmp_extract__"/* "$addon_name"/ fi rm -rf "__tmp_extract__" rm -f "$zip" done fi # Sync Add-On from git repository if BL_ADDON_REPO is set (only on reboot; only pulls when remote has updates) if [ -n "$BL_ADDON_REPO" ]; then # Log HTTPS userinfo redacted (password/token); SSH URLs shown as-is bl_redact_git_remote_url() { case "$1" in http://*|https://*) echo "$1" | sed -E 's#(https?://)[^/@]+@#\1***@#' ;; *) echo "$1" ;; esac } # Pelican/Wings often use a pipe for the console: stdout is block-buffered, so plain echo can # appear "stuck" while git writes to stderr. Use stderr for all addon diagnostics. bl_addon_log() { printf '%s\n' "$*" >&2 } # Periodic lines while git runs (clone/fetch can look frozen in the panel otherwise) bl_git_with_heartbeat() { local label="$1" shift ( while sleep 15; do printf '[BL_ADDON_REPO] %s — still running (%s). Slow network, huge repo, or waiting on auth/firewall.\n' \ "$label" "$(date -u +'%Y-%m-%d %H:%M:%S UTC')" >&2 done ) & local _hb=$! if command -v stdbuf >/dev/null 2>&1; then stdbuf -oL -eL "$@" else "$@" fi local _rc=$? kill "$_hb" 2>/dev/null wait "$_hb" 2>/dev/null return "$_rc" } _repo_safe=$(bl_redact_git_remote_url "$BL_ADDON_REPO") bl_addon_log "Syncing Add-On from git repository..." bl_addon_log "[BL_ADDON_REPO] Remote (credentials redacted): ${_repo_safe}" if [ -n "$BL_ADDON_REPO_BRANCH" ]; then bl_addon_log "[BL_ADDON_REPO] Branch: ${BL_ADDON_REPO_BRANCH}" else bl_addon_log "[BL_ADDON_REPO] Branch: (repository default — not set)" fi bl_addon_log "[BL_ADDON_REPO] Target folder under Add-Ons: $(basename "$BL_ADDON_REPO" .git)" bl_addon_log "[BL_ADDON_REPO] If clone/fetch hangs or fails in the panel:" bl_addon_log " • No interactive login in Docker — private HTTPS repos usually need user + token IN the URL, e.g." bl_addon_log " https://YOUR_USER:YOUR_TOKEN@github.com/org/repo.git" bl_addon_log " (GitHub/GitLab: use a personal access token or deploy token, not your web password.)" bl_addon_log " • Or use SSH (git@host:org/repo.git) with a deploy key available inside the container." # Avoid indefinite hang waiting for a TTY password prompt (headless) export GIT_TERMINAL_PROMPT=0 # Do not invoke a credential helper that might block without a TTY export GIT_ASKPASS=/bin/false # Abort if transfer stalls (bytes/sec below limit for lowSpeedTime seconds) _git_slow=( -c "http.lowSpeedLimit=500" -c "http.lowSpeedTime=120" ) mkdir -p /home/container/Add-Ons cd /home/container/Add-Ons repo_name=$(basename "$BL_ADDON_REPO" .git) if [ -d "$repo_name/.git" ]; then bl_addon_log "[BL_ADDON_REPO] Existing clone at ${repo_name}/ — fetching..." cd "$repo_name" if ! bl_git_with_heartbeat "git fetch" git "${_git_slow[@]}" fetch --progress origin; then bl_addon_log "[BL_ADDON_REPO] ERROR: git fetch failed (auth, network, or stall). Remote (redacted): ${_repo_safe}" exit 1 fi upstream=$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null) behind=$(git rev-list HEAD.."${upstream:-origin/HEAD}" --count 2>/dev/null || echo 0) if [ "${behind:-0}" -gt 0 ]; then bl_addon_log "[BL_ADDON_REPO] Remote is ahead — pulling (--ff-only)..." if ! bl_git_with_heartbeat "git pull" git "${_git_slow[@]}" pull --ff-only --progress; then bl_addon_log "[BL_ADDON_REPO] ERROR: git pull failed. Remote (redacted): ${_repo_safe}" exit 1 fi else bl_addon_log "[BL_ADDON_REPO] Add-On is up to date (no new commits on tracked branch)." fi cd /home/container/Add-Ons else bl_addon_log "[BL_ADDON_REPO] No existing clone — starting git clone (this may take a while)..." clone_opts=(--progress --verbose) [ -n "$BL_ADDON_REPO_BRANCH" ] && clone_opts+=(-b "$BL_ADDON_REPO_BRANCH") if ! bl_git_with_heartbeat "git clone" git "${_git_slow[@]}" clone "${clone_opts[@]}" "$BL_ADDON_REPO"; then bl_addon_log "[BL_ADDON_REPO] ERROR: git clone failed. Check URL, auth, branch name, and network." bl_addon_log "[BL_ADDON_REPO] Remote used (redacted): ${_repo_safe}" exit 1 fi bl_addon_log "[BL_ADDON_REPO] Clone finished successfully." fi cd /home/container || exit 1 fi # Ensure we're in the correct directory (server root where Blockland.exe lives) cd /home/container || exit 1 if [ ! -f "Blockland.exe" ]; then echo "Blockland.exe not found!" exit 1 fi echo "Launching Blockland..." server_name_args=() [ -n "$BL_SERVER_NAME" ] && server_name_args=(-serverName "$BL_SERVER_NAME") max_players_args=() [ -n "$BL_MAX_PLAYERS" ] && max_players_args=(-maxPlayers "$BL_MAX_PLAYERS") exec xvfb-run -a wine "Blockland.exe" \ ptlaaxobimwroe \ -${dedi} \ -port "$BL_PORT" \ -dtoken "$BL_DTOKEN" \ -gamemode "$BL_GAMEMODE" \ "${server_name_args[@]}" \ "${max_players_args[@]}" \ $BL_EXTRA_ARGS