Spaces:
Runtime error
Runtime error
| set -euo pipefail | |
| JAVA11="/usr/lib/jvm/temurin-11-jre-amd64/bin/java" | |
| JAVA17="/usr/lib/jvm/java-17-openjdk-amd64/bin/java" | |
| BUNGEE_DIR="/opt/server/bungee" | |
| BACKEND_DIR="/opt/server/backend" | |
| WORLD_DIR="$BACKEND_DIR/world" | |
| PAPER_PIPE="$BACKEND_DIR/paper_input" | |
| TOTAL_MEM_MB=$(free -m | awk '/^Mem:/{print $2}') | |
| BUNGEE_MAX_MB=192 | |
| PAPER_MAX_MB=$(( TOTAL_MEM_MB - BUNGEE_MAX_MB - 768 )) | |
| [ "$PAPER_MAX_MB" -gt 4096 ] && PAPER_MAX_MB=4096 | |
| [ "$PAPER_MAX_MB" -lt 1024 ] && PAPER_MAX_MB=1024 | |
| PAPER_MIN_MB=1024 | |
| CPU_CORES=$(nproc 2>/dev/null || echo 2) | |
| GC_THREADS=$CPU_CORES | |
| CONC_GC=$(( CPU_CORES / 2 )) | |
| [ "$CONC_GC" -lt 1 ] && CONC_GC=1 | |
| NETTY_THREADS=$CPU_CORES | |
| echo "============================================" | |
| echo " Eaglercraft 1.8.8 β Vanilla" | |
| echo " Mem: ${PAPER_MIN_MB}-${PAPER_MAX_MB}MB | CPUs: ${CPU_CORES}" | |
| echo " Setup UI: port 7860 (only if no world)" | |
| echo "============================================" | |
| PAPER_JVM_FLAGS=( | |
| -Xmx${PAPER_MAX_MB}M -Xms${PAPER_MIN_MB}M | |
| -XX:+UseG1GC | |
| -XX:+ParallelRefProcEnabled | |
| -XX:MaxGCPauseMillis=15 | |
| -XX:+UnlockExperimentalVMOptions | |
| -XX:+DisableExplicitGC | |
| -XX:G1NewSizePercent=40 | |
| -XX:G1MaxNewSizePercent=50 | |
| -XX:G1HeapRegionSize=16M | |
| -XX:G1ReservePercent=20 | |
| -XX:G1HeapWastePercent=5 | |
| -XX:G1MixedGCCountTarget=4 | |
| -XX:InitiatingHeapOccupancyPercent=50 | |
| -XX:G1MixedGCLiveThresholdPercent=90 | |
| -XX:G1RSetUpdatingPauseTimePercent=5 | |
| -XX:SurvivorRatio=32 | |
| -XX:+PerfDisableSharedMem | |
| -XX:MaxTenuringThreshold=1 | |
| -XX:+OptimizeStringConcat | |
| -XX:+UseCompressedOops | |
| -XX:+AlwaysPreTouch | |
| -XX:ParallelGCThreads=${GC_THREADS} | |
| -XX:ConcGCThreads=${CONC_GC} | |
| -XX:MaxMetaspaceSize=256M | |
| -XX:CompressedClassSpaceSize=128M | |
| -XX:ReservedCodeCacheSize=128M | |
| -XX:-UseCodeCacheFlushing | |
| -Xss1m | |
| -Dio.netty.allocator.maxCachedBufferCapacity=524288 | |
| -Dio.netty.recycler.maxCapacityPerThread=0 | |
| -Dio.netty.eventLoopThreads=${NETTY_THREADS} | |
| -Dio.netty.allocator.numDirectArenas=${NETTY_THREADS} | |
| -Dio.netty.allocator.numHeapArenas=${NETTY_THREADS} | |
| -Dcom.mojang.eula.agree=true | |
| -DIReallyKnowWhatIAmDoingISwear | |
| -Dspigot.disable-firewall-check=true | |
| -Djava.net.preferIPv4Stack=true | |
| ) | |
| BUNGEE_JVM_FLAGS=( | |
| -Xmx${BUNGEE_MAX_MB}M -Xms128M | |
| -XX:+UseG1GC | |
| -XX:+ParallelRefProcEnabled | |
| -XX:MaxGCPauseMillis=30 | |
| -XX:+UnlockExperimentalVMOptions | |
| -XX:+DisableExplicitGC | |
| -XX:+PerfDisableSharedMem | |
| -XX:+OptimizeStringConcat | |
| -XX:+UseCompressedOops | |
| -XX:MaxMetaspaceSize=128M | |
| -XX:ReservedCodeCacheSize=64M | |
| -Xss1m | |
| -XX:ParallelGCThreads=${GC_THREADS} | |
| -XX:ConcGCThreads=${CONC_GC} | |
| -Dio.netty.allocator.maxCachedBufferCapacity=524288 | |
| -Dio.netty.recycler.maxCapacityPerThread=0 | |
| -Dio.netty.eventLoopThreads=${NETTY_THREADS} | |
| -Dio.netty.allocator.numDirectArenas=${NETTY_THREADS} | |
| -Dio.netty.allocator.numHeapArenas=${NETTY_THREADS} | |
| -Djava.net.preferIPv4Stack=true | |
| ) | |
| PAPER_PID="" | |
| BUNGEE_PID="" | |
| WEB_PID="" | |
| NGINX_PID="" | |
| echo "127.0.0.1 checkip.amazonaws.com" >> /etc/hosts | |
| echo "127.0.0.1 api.ipify.org" >> /etc/hosts | |
| echo "127.0.0.1 icanhazip.com" >> /etc/hosts | |
| echo "127.0.0.1 portcheck.twizmwazin.com" >> /etc/hosts | |
| cleanup() { | |
| echo "" | |
| echo "[SHUTDOWN] Saving world to HuggingFace..." | |
| python3 /opt/server/sync_world.py upload 2>/dev/null || true | |
| echo "[SHUTDOWN] Stopping WindSpigot..." | |
| if [ -p "$PAPER_PIPE" ]; then | |
| echo "stop" >&3 2>/dev/null || true | |
| fi | |
| sleep 5 | |
| [ -n "$PAPER_PID" ] && kill "$PAPER_PID" 2>/dev/null || true | |
| [ -n "$BUNGEE_PID" ] && kill "$BUNGEE_PID" 2>/dev/null || true | |
| [ -n "$WEB_PID" ] && kill "$WEB_PID" 2>/dev/null || true | |
| [ -n "$NGINX_PID" ] && kill "$NGINX_PID" 2>/dev/null || true | |
| exec 3>&- 2>/dev/null || true | |
| wait 2>/dev/null || true | |
| } | |
| trap cleanup SIGTERM SIGINT EXIT | |
| echo "[0/7] Starting nginx (port 7860) and web panel (port 5000)..." | |
| nginx -g "daemon off;" >> /tmp/nginx_error.log 2>&1 & | |
| NGINX_PID=$! | |
| python3 /opt/server/web.py >> /tmp/web.log 2>&1 & | |
| WEB_PID=$! | |
| echo " nginx PID=$NGINX_PID web PID=$WEB_PID" | |
| sleep 1 | |
| echo "[1/7] Downloading world from HuggingFace..." | |
| python3 /opt/server/sync_world.py download 2>&1 | |
| WORLD_OK=$? | |
| if [ "$WORLD_OK" -ne 0 ] || [ ! -d "$WORLD_DIR" ]; then | |
| echo "" | |
| echo "ββββββββββββββββββββββββββββββββββββββββββββ" | |
| echo "β NO WORLD FOUND β Setup UI is active β" | |
| echo "β Open port 7860 and upload world.zip β" | |
| echo "ββββββββββββββββββββββββββββββββββββββββββββ" | |
| touch /tmp/setup_mode | |
| wait "$WEB_PID" | |
| exit 0 | |
| fi | |
| rm -f /tmp/setup_mode | |
| echo "[2/7] Generating server icon..." | |
| if command -v convert &>/dev/null; then | |
| if [ -f /etc/ImageMagick-6/policy.xml ]; then | |
| sed -i 's/rights="none" pattern="PNG"/rights="read|write" pattern="PNG"/' \ | |
| /etc/ImageMagick-6/policy.xml | |
| fi | |
| convert -size 64x64 xc:"#1a1a2e" \ | |
| \( -size 64x64 gradient:"#1a1a2e"-"#16213e" \) -composite \ | |
| -fill "#fffde7" -draw "circle 52,10 58,10" \ | |
| -fill "#16213e" -draw "circle 55,8 61,8" \ | |
| -fill "#ffffff" \ | |
| -draw "point 10,5" -draw "point 25,3" \ | |
| -draw "point 40,8" -draw "point 15,15" \ | |
| -draw "point 35,12" -draw "point 5,20" \ | |
| -fill "#4caf50" -draw "rectangle 0,48 63,63" \ | |
| -fill none -stroke "#000000" -strokewidth 1 \ | |
| -draw "rectangle 0,0 63,63" \ | |
| PNG:"$BACKEND_DIR/server-icon.png" \ | |
| && cp "$BACKEND_DIR/server-icon.png" "$BUNGEE_DIR/server-icon.png" \ | |
| && echo " Icon generated." \ | |
| || echo " Warning: icon generation failed." | |
| else | |
| echo " Warning: ImageMagick not found." | |
| fi | |
| echo "[3/7] Writing backend config..." | |
| cd "$BACKEND_DIR" | |
| echo "eula=true" > eula.txt | |
| cat > server.properties << 'EOF' | |
| server-port=25565 | |
| server-ip=127.0.0.1 | |
| online-mode=false | |
| spawn-protection=0 | |
| max-players=50 | |
| view-distance=8 | |
| motd=\u00A7aEaglercraft 1.8.8 | |
| pvp=true | |
| allow-flight=false | |
| level-name=world | |
| gamemode=2 | |
| default-player-permission-level=visitor | |
| difficulty=1 | |
| use-native-transport=true | |
| network-compression-threshold=-1 | |
| max-tick-time=-1 | |
| generate-structures=true | |
| allow-nether=true | |
| spawn-animals=true | |
| spawn-monsters=true | |
| prevent-proxy-connections=false | |
| enable-query=false | |
| enable-rcon=false | |
| EOF | |
| cat > spigot.yml << 'EOF' | |
| config-version: 8 | |
| settings: | |
| bungeecord: true | |
| timeout-time: 60 | |
| netty-threads: 2 | |
| async-catcher-enabled: false | |
| save-user-cache-on-stop-only: true | |
| check-firewall: false | |
| commands: | |
| tab-complete: 0 | |
| log: false | |
| world-settings: | |
| default: | |
| verbose: false | |
| view-distance: 8 | |
| mob-spawn-range: 4 | |
| ticks-per: | |
| hopper-transfer: 8 | |
| hopper-check: 1 | |
| arrow-despawn-rate: 300 | |
| item-despawn-rate: 6000 | |
| EOF | |
| cat > bukkit.yml << 'EOF' | |
| settings: | |
| connection-throttle: -1 | |
| allow-end: true | |
| spawn-npcs: true | |
| spawn-animals: true | |
| spawn-monsters: true | |
| chunk-gc: | |
| period-in-ticks: 600 | |
| ticks-per: | |
| animal-spawns: 400 | |
| monster-spawns: 1 | |
| autosave: 6000 | |
| spawn: | |
| location: | |
| world: world | |
| x: 0.0 | |
| y: 64.0 | |
| z: 0.0 | |
| yaw: 0.0 | |
| pitch: 0.0 | |
| EOF | |
| python3 -c " | |
| import uuid, json | |
| players = [ | |
| {'name': 'Xx_N01i_xX', 'level': 4}, | |
| {'name': 'EXCUSE_ME_SIR', 'level': 4} | |
| ] | |
| ops = [] | |
| for p in players: | |
| # Offline-mode UUID = UUID v3 of 'OfflinePlayer:<name>' | |
| offline_uuid = uuid.uuid3(uuid.NAMESPACE_URL, 'OfflinePlayer:' + p['name']) | |
| # Minecraft uses UUID v3 but with a specific namespace | |
| # Actually the correct method: | |
| import hashlib | |
| h = hashlib.md5(('OfflinePlayer:' + p['name']).encode('utf-8')).digest() | |
| b = bytearray(h) | |
| b[6] = (b[6] & 0x0f) | 0x30 # version 3 | |
| b[8] = (b[8] & 0x3f) | 0x80 # variant 1 | |
| u = uuid.UUID(bytes=bytes(b)) | |
| ops.append({ | |
| 'uuid': str(u), | |
| 'name': p['name'], | |
| 'level': p['level'], | |
| 'bypassesPlayerLimit': False | |
| }) | |
| print(f\" {p['name']} -> {u}\") | |
| with open('ops.json', 'w') as f: | |
| json.dump(ops, f, indent=2) | |
| print(' ops.json written!') | |
| " 2>&1 | |
| echo "[4/7] Writing BungeeCord config..." | |
| cd "$BUNGEE_DIR" | |
| cat > config.yml << 'EOF' | |
| server_connect_timeout: 5000 | |
| player_limit: 50 | |
| permissions: | |
| default: | |
| - bungeecord.command.server | |
| - bungeecord.command.list | |
| timeout: 30000 | |
| online_mode: false | |
| throttle: 4000 | |
| throttle_limit: 3 | |
| servers: | |
| main: | |
| motd: '&aEaglercraft 1.8.8' | |
| address: 127.0.0.1:25565 | |
| restricted: false | |
| listeners: | |
| - query_port: 25567 | |
| motd: '&aEaglercraft 1.8.8' | |
| tab_list: GLOBAL_PING | |
| query_enabled: false | |
| proxy_protocol: false | |
| priorities: | |
| - main | |
| bind_local_address: true | |
| host: 127.0.0.1:25567 | |
| max_players: 50 | |
| force_default_server: true | |
| ip_forward: true | |
| connection_throttle: -1 | |
| stats: none | |
| log_commands: false | |
| log_pings: false | |
| network_compression_threshold: -1 | |
| EOF | |
| echo "[5/7] Generating EaglerXServer config..." | |
| rm -rf \ | |
| "$BUNGEE_DIR/plugins/EaglercraftXServer" \ | |
| "$BUNGEE_DIR/plugins/EaglercraftXBungee" \ | |
| "$BUNGEE_DIR/plugins/EaglerXServer" | |
| > /tmp/bungee-gen.log | |
| timeout 20s $JAVA17 "${BUNGEE_JVM_FLAGS[@]}" \ | |
| -cp "sqlite-jdbc.jar:BungeeCord.jar" net.md_5.bungee.Bootstrap \ | |
| >> /tmp/bungee-gen.log 2>&1 || true | |
| pkill -f 'net.md_5.bungee.Bootstrap' 2>/dev/null || true | |
| sleep 3 | |
| EAGLER_CFG="$(find "$BUNGEE_DIR/plugins" -maxdepth 2 -name listeners.yml | head -n1 || true)" | |
| if [ -z "$EAGLER_CFG" ]; then | |
| echo " ERROR: EaglerXServer did not generate listeners.yml" | |
| echo " Last 120 lines of bungee-gen.log:" | |
| tail -120 /tmp/bungee-gen.log | |
| echo " Files under plugins/:" | |
| find "$BUNGEE_DIR/plugins" -maxdepth 2 -type f | sort | |
| exit 1 | |
| fi | |
| sed -i 's|inject_address:.*|inject_address: "127.0.0.1:25567"|' "$EAGLER_CFG" || true | |
| echo " Using Eagler config: $EAGLER_CFG" | |
| echo "[6/7] Starting WindSpigot (backend)..." | |
| cd "$BACKEND_DIR" | |
| rm -f "$PAPER_PIPE" | |
| mkfifo "$PAPER_PIPE" | |
| > /tmp/paper.log | |
| $JAVA11 "${PAPER_JVM_FLAGS[@]}" \ | |
| -jar server.jar nogui \ | |
| < "$PAPER_PIPE" >> /tmp/paper.log 2>&1 & | |
| PAPER_PID=$! | |
| sleep 1 | |
| exec 3>"$PAPER_PIPE" | |
| echo " WindSpigot PID: $PAPER_PID" | |
| echo " Waiting for WindSpigot to be ready (up to 120s)..." | |
| READY=0 | |
| for i in $(seq 1 120); do | |
| if grep -q "Done" /tmp/paper.log 2>/dev/null; then | |
| READY=1 | |
| echo " WindSpigot READY! (${i}s)" | |
| break | |
| fi | |
| if ! kill -0 "$PAPER_PID" 2>/dev/null; then | |
| echo " ERROR: WindSpigot died early" | |
| tail -60 /tmp/paper.log | |
| exit 1 | |
| fi | |
| [ $(( i % 20 )) -eq 0 ] && echo " Still waiting... (${i}s)" && tail -5 /tmp/paper.log | |
| sleep 1 | |
| done | |
| if [ "$READY" -eq 0 ]; then | |
| echo " WARNING: WindSpigot did not print Done within 120s" | |
| tail -20 /tmp/paper.log | |
| fi | |
| sleep 2 | |
| echo "[7/7] Starting BungeeCord + EaglerXServer..." | |
| cd "$BUNGEE_DIR" | |
| > /tmp/bungee.log | |
| $JAVA17 "${BUNGEE_JVM_FLAGS[@]}" \ | |
| -cp "sqlite-jdbc.jar:BungeeCord.jar" net.md_5.bungee.Bootstrap \ | |
| >> /tmp/bungee.log 2>&1 & | |
| BUNGEE_PID=$! | |
| echo " BungeeCord PID: $BUNGEE_PID" | |
| echo " Waiting for Bungee/Eagler listener on 25567 (up to 30s)..." | |
| WS_READY=0 | |
| for i in $(seq 1 30); do | |
| if nc -z 127.0.0.1 25567 2>/dev/null; then | |
| WS_READY=1 | |
| echo " Eagler listener READY! (${i}s)" | |
| break | |
| fi | |
| if ! kill -0 "$BUNGEE_PID" 2>/dev/null; then | |
| echo " ERROR: BungeeCord died early" | |
| tail -80 /tmp/bungee.log | |
| exit 1 | |
| fi | |
| sleep 1 | |
| done | |
| if [ "$WS_READY" -eq 0 ]; then | |
| echo " ERROR: 25567 never opened" | |
| echo " Last 120 lines of bungee.log:" | |
| tail -120 /tmp/bungee.log | |
| exit 1 | |
| fi | |
| echo "" | |
| echo "============================================" | |
| echo " SERVER STATUS" | |
| echo "============================================" | |
| echo " WindSpigot :25565 $(nc -z 127.0.0.1 25565 2>/dev/null && echo 'β ONLINE' || echo 'β OFFLINE')" | |
| echo " Eagler/Bungee:25567 $(nc -z 127.0.0.1 25567 2>/dev/null && echo 'β ONLINE' || echo 'β OFFLINE')" | |
| echo " Nginx :7860 $(nc -z 127.0.0.1 7860 2>/dev/null && echo 'β ONLINE' || echo 'β OFFLINE')" | |
| echo " Flask :5000 $(nc -z 127.0.0.1 5000 2>/dev/null && echo 'β ONLINE' || echo 'β OFFLINE')" | |
| echo "============================================" | |
| echo " JOIN URL: wss://YOUR-ACTUAL-SPACE.hf.space/ws/" | |
| echo "============================================" | |
| echo "" | |
| LAST_LOG_LINE=0 | |
| LAST_SAVE_TIME=$(date +%s) | |
| SAVE_INTERVAL=300 | |
| LOOP_COUNT=0 | |
| while true; do | |
| LOOP_COUNT=$(( LOOP_COUNT + 1 )) | |
| sleep 10 | |
| if [ $(( LOOP_COUNT % 6 )) -eq 0 ]; then | |
| CUR=$(wc -l < /tmp/paper.log 2>/dev/null || echo 0) | |
| if [ "$CUR" -gt "$LAST_LOG_LINE" ]; then | |
| ERR=$(tail -n +"$(( LAST_LOG_LINE + 1 ))" /tmp/paper.log | grep -c "ERROR\|SEVERE" 2>/dev/null || echo 0) | |
| [ "$ERR" -gt 0 ] && echo "[$(date '+%H:%M:%S')] $ERR new error(s) in paper.log" | |
| LAST_LOG_LINE=$CUR | |
| fi | |
| fi | |
| NOW=$(date +%s) | |
| if [ $(( NOW - LAST_SAVE_TIME )) -ge "$SAVE_INTERVAL" ]; then | |
| echo "[$(date '+%H:%M:%S')] Autosaving world to HuggingFace..." | |
| echo "save-all" >&3 2>/dev/null || true | |
| sleep 3 | |
| python3 /opt/server/sync_world.py upload 2>&1 & | |
| LAST_SAVE_TIME=$NOW | |
| fi | |
| done |