Spaces:
Paused
Paused
Download start.sh from smodusermc/digpvp: direct link, hf CLI and curl.
- Browser
- Download file 41.8 kB
-
https://huggingface.co/spaces/smodusermc/digpvp/resolve/main/start.sh
- Command line
-
hf download hf://spaces/smodusermc/digpvp/start.sh
-
curl -L -o start.sh https://huggingface.co/spaces/smodusermc/digpvp/resolve/main/start.sh
41.8 kB
| JAVA_HOME_DIR=$(find /usr/lib/jvm -maxdepth 1 -name "java-17-openjdk-*" -type d 2>/dev/null | head -1) | |
| if [ -z "$JAVA_HOME_DIR" ]; then | |
| echo "ERROR: Java 17 not found!" | |
| exit 1 | |
| fi | |
| JAVA="$JAVA_HOME_DIR/bin/java" | |
| BUNGEE_DIR="/opt/server/bungee" | |
| BACKEND_DIR="/opt/server/backend" | |
| PLUGIN_DIR="$BACKEND_DIR/plugins" | |
| mkdir -p "$PLUGIN_DIR" | |
| HF_BUCKET_HANDLE="hf://buckets/smodusermc/digpvp" | |
| SAVE_DIRS="world world_nether world_the_end players banned-ips.json banned-players.json ops.json whitelist.json plugins/WorldGuard plugins/Shopkeepers plugins/MineResetLite plugins/SafeTrade plugins/Skript plugins/WorldEdit plugins/PvPManager plugins/HolographicDisplays"SYNC_INTERVAL="${SYNC_INTERVAL:-300}" | |
| SYNC_INTERVAL="${SYNC_INTERVAL:-300}" | |
| IDLE_MODE=false | |
| # ============================================= | |
| # OP ACCOUNT | |
| # ============================================= | |
| OP_USERNAME="CreppyBitch" | |
| CPU_CORES=$(nproc 2>/dev/null || echo 2) | |
| NETTY_THREADS=$CPU_CORES | |
| TOTAL_MEM_MB=$(free -m | awk '/^Mem:/{print $2}') | |
| BUNGEE_MAX_MB=1024 | |
| PAPER_MAX_MB=$(( TOTAL_MEM_MB - BUNGEE_MAX_MB - 768 )) | |
| [ "$PAPER_MAX_MB" -gt 8192 ] && PAPER_MAX_MB=8192 | |
| [ "$PAPER_MAX_MB" -lt 1024 ] && PAPER_MAX_MB=1024 | |
| PAPER_MIN_MB=8192 | |
| echo "========================================" | |
| echo " Eaglercraft 1.8.8 PVP Server" | |
| echo " WindSpigot + HuggingFace Buckets" | |
| echo "========================================" | |
| echo "" | |
| echo " CPUs: $CPU_CORES | RAM: ${TOTAL_MEM_MB}MB" | |
| echo " Server: ${PAPER_MIN_MB}-${PAPER_MAX_MB}MB | Bungee: ${BUNGEE_MAX_MB}MB" | |
| echo " Java: $($JAVA -version 2>&1 | head -1)" | |
| echo " Bucket: $HF_BUCKET_HANDLE" | |
| [ -n "$OP_USERNAME" ] && echo " OP Account: $OP_USERNAME" | |
| echo " Plugins: WorldEdit, WorldGuard, MineResetLite, Shopkeepers, SafeTrade, Skript, PvPManager" | |
| echo "" | |
| # ============================================= | |
| # JVM FLAGS | |
| # ============================================= | |
| PAPER_JVM_FLAGS=( | |
| -Xmx${PAPER_MAX_MB}M | |
| -Xms${PAPER_MIN_MB}M | |
| -XX:+UseG1GC | |
| -XX:+ParallelRefProcEnabled | |
| -XX:MaxGCPauseMillis=25 | |
| -XX:+UnlockExperimentalVMOptions | |
| -XX:+DisableExplicitGC | |
| -XX:G1NewSizePercent=40 | |
| -XX:G1MaxNewSizePercent=50 | |
| -XX:G1HeapRegionSize=8M | |
| -XX:G1ReservePercent=15 | |
| -XX:G1HeapWastePercent=10 | |
| -XX:G1MixedGCCountTarget=8 | |
| -XX:InitiatingHeapOccupancyPercent=60 | |
| -XX:G1MixedGCLiveThresholdPercent=90 | |
| -XX:G1RSetUpdatingPauseTimePercent=5 | |
| -XX:SurvivorRatio=32 | |
| -XX:+PerfDisableSharedMem | |
| -XX:MaxTenuringThreshold=1 | |
| -XX:+OptimizeStringConcat | |
| -XX:+UseCompressedOops | |
| -XX:MaxMetaspaceSize=256M | |
| -XX:CompressedClassSpaceSize=128M | |
| -XX:ReservedCodeCacheSize=128M | |
| -XX:-UseCodeCacheFlushing | |
| -Xss256k | |
| -Djline.terminal=jline.UnsupportedTerminal | |
| -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 | |
| -Dusing.aikars.flags=https://mcflags.emc.gs | |
| -Daikars.new.flags=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 | |
| -Xss256k | |
| -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} | |
| -Deaglerxbungee.stfu=true | |
| ) | |
| # ============================================================= | |
| # RCON — each command sent individually to avoid mangling | |
| # ============================================================= | |
| RCON_PASS="chunkystart" | |
| get_player_count() { | |
| local RESULT | |
| RESULT=$(mcrcon -H 127.0.0.1 -P 25575 -p "$RCON_PASS" "list" 2>/dev/null) | |
| echo "$RESULT" | grep -oE 'are [0-9]+' | grep -oE '[0-9]+' || echo "0" | |
| } | |
| mc_command() { | |
| for cmd in "$@"; do | |
| mcrcon -H 127.0.0.1 -P 25575 -p "$RCON_PASS" "$cmd" 2>/dev/null | |
| done | |
| } | |
| # ============================================================= | |
| # WindSpigot starter | |
| # ============================================================= | |
| start_windspigot() { | |
| cd "$BACKEND_DIR" | |
| $JAVA "${PAPER_JVM_FLAGS[@]}" -jar server.jar nogui --noconsole >> /tmp/paper.log 2>&1 & | |
| BACKEND_PID=$! | |
| } | |
| # ============================================================= | |
| # OP Account Setup | |
| # ============================================================= | |
| setup_op_account() { | |
| if [ -z "$OP_USERNAME" ]; then | |
| return | |
| fi | |
| echo " Setting up OP for: $OP_USERNAME" | |
| local OFFLINE_UUID | |
| OFFLINE_UUID=$(echo -n "OfflinePlayer:${OP_USERNAME}" | md5sum | sed 's/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)/\1\2\3\4-\5\6-\7\8-\9\10-\11\12\13\14\15\16/') | |
| local V3_UUID | |
| V3_UUID=$(echo "$OFFLINE_UUID" | sed 's/.\{1\}\(.\{3\}-\)/3\1/' | sed 's/\(.\{14\}-\).\(.\{3\}-\)/\1'"$(echo "$OFFLINE_UUID" | cut -c15 | tr '0-9a-f' '89ab89ab89ab89ab')"'\2/') | |
| cat > "$BACKEND_DIR/ops.json" << OPEOF | |
| [ | |
| { | |
| "uuid": "${V3_UUID}", | |
| "name": "${OP_USERNAME}", | |
| "level": 4, | |
| "bypassesPlayerLimit": true | |
| } | |
| ] | |
| OPEOF | |
| echo " ops.json written (level 4, UUID: ${V3_UUID})" | |
| mc_command "op ${OP_USERNAME}" | |
| echo " RCON op command sent" | |
| } | |
| # ============================================================= | |
| # IDLE MODE — safe version that does NOT kill entities | |
| # ============================================================= | |
| enter_idle_mode() { | |
| [ "$IDLE_MODE" = true ] && return | |
| IDLE_MODE=true | |
| mc_command "gamerule randomTickSpeed 0" | |
| mc_command "gamerule doMobSpawning false" | |
| mc_command "kill @e[type=Zombie]" | |
| mc_command "kill @e[type=Skeleton]" | |
| mc_command "kill @e[type=Spider]" | |
| mc_command "kill @e[type=Creeper]" | |
| mc_command "kill @e[type=Enderman]" | |
| mc_command "kill @e[type=Witch]" | |
| mc_command "kill @e[type=Slime]" | |
| mc_command "kill @e[type=CaveSpider]" | |
| mc_command "kill @e[type=Silverfish]" | |
| mc_command "kill @e[type=Guardian]" | |
| mc_command "kill @e[type=Endermite]" | |
| mc_command "kill @e[type=Blaze]" | |
| mc_command "kill @e[type=Ghast]" | |
| mc_command "kill @e[type=MagmaCube]" | |
| mc_command "kill @e[type=WitherSkeleton]" | |
| mc_command "kill @e[type=ZombiePigman]" | |
| echo "[IDLE] Active — hostile mobs cleared, ticks paused" | |
| } | |
| exit_idle_mode() { | |
| [ "$IDLE_MODE" = false ] && return | |
| IDLE_MODE=false | |
| mc_command "gamerule randomTickSpeed 3" | |
| mc_command "gamerule doMobSpawning true" | |
| echo "[IDLE] Gameplay restored" | |
| } | |
| # ============================================================= | |
| # Port fix | |
| # ============================================================= | |
| find_listeners_yml() { | |
| find "$BUNGEE_DIR/plugins" -name "listeners.yml" -type f 2>/dev/null | head -1 | |
| } | |
| patch_eagler_port() { | |
| local FILE=$(find_listeners_yml) | |
| [ -z "$FILE" ] && return 1 | |
| grep -q ":7860" "$FILE" && return 0 | |
| sed -i 's/\(address:[[:space:]]*"[^:]*:\)[0-9]*/\17860/' "$FILE" | |
| sed -i "s/\(address:[[:space:]]*[^\"][^:]*:\)[0-9]*/\17860/" "$FILE" | |
| echo " Port -> 7860" | |
| } | |
| start_bungee() { | |
| cd "$BUNGEE_DIR" | |
| $JAVA "${BUNGEE_JVM_FLAGS[@]}" \ | |
| -cp "sqlite-jdbc.jar:BungeeCord.jar" \ | |
| net.md_5.bungee.Bootstrap >> /tmp/bungee.log 2>&1 & | |
| BUNGEE_PID=$! | |
| } | |
| # ============================================================= | |
| # HuggingFace Bucket | |
| # ============================================================= | |
| hf_authenticate() { | |
| if [ -n "$HF_TOKEN" ]; then | |
| hf auth login --token "$HF_TOKEN" --add-to-git-credential 2>/dev/null || true | |
| echo " Authenticated" | |
| else | |
| echo " No HF_TOKEN" | |
| fi | |
| } | |
| hf_ensure_bucket() { | |
| local BUCKET_ID=$(echo "$HF_BUCKET_HANDLE" | sed 's|hf://buckets/||') | |
| hf buckets create "$BUCKET_ID" --exist-ok 2>/dev/null | |
| } | |
| hf_restore_saves() { | |
| echo " Restoring game data..." | |
| hf buckets sync "${HF_BUCKET_HANDLE}/game-data" "$BACKEND_DIR" 2>&1 | tail -5 | |
| for dir in $SAVE_DIRS; do | |
| [ -e "$BACKEND_DIR/$dir" ] && echo " Found: $dir" | |
| done | |
| } | |
| hf_push_saves() { | |
| local STAGING="/tmp/hf-staging" | |
| rm -rf "$STAGING" && mkdir -p "$STAGING" | |
| for item in $SAVE_DIRS; do | |
| if [ -e "$BACKEND_DIR/$item" ]; then | |
| mkdir -p "$STAGING/$(dirname "$item")" | |
| cp -a "$BACKEND_DIR/$item" "$STAGING/$item" | |
| fi | |
| done | |
| hf buckets sync "$STAGING" "${HF_BUCKET_HANDLE}/game-data" --delete 2>&1 | tail -3 | |
| [ $? -eq 0 ] && echo "[SYNC] OK $(date '+%H:%M:%S')" \ | |
| || echo "[SYNC] FAIL $(date '+%H:%M:%S')" | |
| rm -rf "$STAGING" | |
| } | |
| hf_sync_loop() { | |
| while true; do | |
| sleep "$SYNC_INTERVAL" | |
| hf_push_saves | |
| done | |
| } | |
| # ============================================================= | |
| # STEP 0: Bucket | |
| # ============================================================= | |
| echo "[0/7] Bucket setup..." | |
| hf_authenticate | |
| hf_ensure_bucket | |
| hf_restore_saves | |
| echo "" | |
| # ============================================================= | |
| # STEP 1: World size | |
| # ============================================================= | |
| echo "[1/7] World analysis..." | |
| for WORLD_DIR in world world_nether world_the_end; do | |
| if [ -d "$BACKEND_DIR/$WORLD_DIR" ]; then | |
| SIZE=$(du -sh "$BACKEND_DIR/$WORLD_DIR" 2>/dev/null | awk '{print $1}') | |
| REGIONS=$(find "$BACKEND_DIR/$WORLD_DIR" -name "*.mca" 2>/dev/null | wc -l) | |
| echo " $WORLD_DIR: $SIZE ($REGIONS region files)" | |
| fi | |
| done | |
| echo "" | |
| # ============================================================= | |
| # STEP 2: Core server configs + Start WindSpigot | |
| # ============================================================= | |
| cd "$BACKEND_DIR" | |
| echo "eula=true" > eula.txt | |
| echo "[2/7] Writing core server configs + starting WindSpigot..." | |
| cat > server.properties << 'EOF' | |
| server-port=25565 | |
| server-ip=127.0.0.1 | |
| online-mode=false | |
| spawn-protection=0 | |
| max-players=20 | |
| view-distance=6 | |
| gamemode=0 | |
| difficulty=1 | |
| level-name=world | |
| level-type=FLAT | |
| level-seed=8678942899319966093 | |
| generate-structures=true | |
| motd=DigPvP Eaglercraft | |
| pvp=true | |
| allow-flight=false | |
| white-list=false | |
| spawn-npcs=true | |
| spawn-animals=false | |
| spawn-monsters=false | |
| enable-command-block=false | |
| allow-nether=false | |
| use-native-transport=true | |
| network-compression-threshold=-1 | |
| entity-broadcast-range-percentage=50 | |
| max-tick-time=-1 | |
| enable-rcon=true | |
| rcon.port=25575 | |
| rcon.password=chunkystart | |
| EOF | |
| cat > bukkit.yml << 'EOF' | |
| settings: | |
| allow-end: true | |
| warn-on-overload: true | |
| connection-throttle: -1 | |
| shutdown-message: Server closed | |
| save-user-cache-on-stop-only: true | |
| spawn-limits: | |
| monsters: 30 | |
| animals: 6 | |
| water-animals: 2 | |
| ambient: 1 | |
| chunk-gc: | |
| period-in-ticks: 600 | |
| ticks-per: | |
| animal-spawns: 600 | |
| monster-spawns: 4 | |
| autosave: 12000 | |
| 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 | |
| moved-wrongly-threshold: 0.0625 | |
| moved-too-quickly-multiplier: 10.0 | |
| item-dirty-ticks: 20 | |
| player-shuffle: 0 | |
| commands: | |
| tab-complete: 0 | |
| log: false | |
| world-settings: | |
| default: | |
| verbose: false | |
| view-distance: 4 | |
| mob-spawn-range: 3 | |
| entity-activation-range: | |
| animals: 12 | |
| monsters: 20 | |
| misc: 6 | |
| tick-inactive-villagers: false | |
| entity-tracking-range: | |
| players: 48 | |
| animals: 32 | |
| monsters: 32 | |
| misc: 16 | |
| other: 48 | |
| ticks-per: | |
| hopper-transfer: 8 | |
| hopper-check: 1 | |
| hopper-amount: 1 | |
| max-entity-collisions: 2 | |
| merge-radius: | |
| exp: 6.0 | |
| item: 4.0 | |
| arrow-despawn-rate: 60 | |
| item-despawn-rate: 3000 | |
| nerf-spawner-mobs: true | |
| zombie-aggressive-towards-villager: false | |
| enable-zombie-pigmen-portal-spawns: false | |
| EOF | |
| cat > windspigot.yml << 'EOF' | |
| settings: | |
| async: | |
| entity-tracking: true | |
| path-searching: true | |
| tnt-explosions: true | |
| lighting: true | |
| tnt: | |
| optimize-movement: true | |
| optimize-liquid-explosions: true | |
| fast-operators: true | |
| stop-decoding-itemstack-on-place: true | |
| modern-tick-loop: | |
| enabled: false | |
| tcp-fast-open: true | |
| reduced-chunk-loads: true | |
| lag-compensated-potions: false | |
| anti-xray: | |
| enabled: false | |
| hit-detection: | |
| enabled: true | |
| threshold: 1.0 | |
| combat: | |
| knockback: | |
| friction: 2.0 | |
| horizontal: 0.35 | |
| vertical: 0.35 | |
| vertical-limit: 0.4 | |
| extra-horizontal: 0.425 | |
| extra-vertical: 0.085 | |
| chunk: | |
| threads: 1 | |
| players-per-thread: 25 | |
| EOF | |
| cat > nacho.yml << 'EOF' | |
| settings: | |
| stop-notify-bungee: true | |
| anti-malware: true | |
| use-tcp-nodelay: true | |
| brand-name: "PVP" | |
| EOF | |
| setup_op_account | |
| > /tmp/paper.log | |
| start_windspigot | |
| echo " WindSpigot PID: $BACKEND_PID" | |
| for i in $(seq 1 120); do | |
| if grep -q "Done" /tmp/paper.log 2>/dev/null; then | |
| echo " WindSpigot READY (~${i}s)" | |
| break | |
| fi | |
| if ! kill -0 $BACKEND_PID 2>/dev/null; then | |
| echo " WINDSPIGOT CRASHED!" | |
| tail -30 /tmp/paper.log | |
| exit 1 | |
| fi | |
| [ $((i % 15)) -eq 0 ] && echo " Loading... (${i}s)" | |
| sleep 1 | |
| done | |
| for i in $(seq 1 30); do | |
| nc -z 127.0.0.1 25575 2>/dev/null && break | |
| sleep 1 | |
| done | |
| if [ -n "$OP_USERNAME" ]; then | |
| mc_command "op ${OP_USERNAME}" | |
| echo " OP granted to ${OP_USERNAME} via RCON" | |
| fi | |
| echo "" | |
| echo " === PLUGINS LOADED ===" | |
| grep -i "Enabling" /tmp/paper.log | grep -oP "Enabling \K[^\s]+" 2>/dev/null | while read p; do | |
| echo " - $p" | |
| done | |
| echo " ======================" | |
| echo "" | |
| # ============================================================= | |
| # STEP 3: PVP gamerules — each command sent separately | |
| # ============================================================= | |
| echo "[3/7] Setting PVP gamerules..." | |
| mc_command "gamerule pvp true" | |
| mc_command "gamerule keepInventory false" | |
| mc_command "gamerule naturalRegeneration true" | |
| mc_command "gamerule doFireTick true" | |
| mc_command "gamerule mobGriefing false" | |
| mc_command "gamerule announceAdvancements false" | |
| mc_command "difficulty 2" | |
| mc_command "defaultgamemode survival" | |
| echo " PVP gamerules set" | |
| echo "" | |
| # ============================================================= | |
| # STEP 4: Idle mode — safe version | |
| # ============================================================= | |
| echo "[4/7] Applying idle mode (no players)..." | |
| enter_idle_mode | |
| echo "" | |
| # ============================================================= | |
| # STEP 5: Write ALL plugin configs + BungeeCord config | |
| # ============================================================= | |
| echo "[5/7] Writing ALL plugin configs + BungeeCord config..." | |
| # =============================== | |
| # BUNGEE CONFIG | |
| # =============================== | |
| cd "$BUNGEE_DIR" | |
| cat > config.yml << 'EOF' | |
| server_connect_timeout: 5000 | |
| remote_ping_cache: -1 | |
| forge_support: false | |
| player_limit: 10 | |
| permissions: | |
| default: | |
| - bungeecord.command.server | |
| admin: | |
| - bungeecord.command.alert | |
| timeout: 30000 | |
| log_commands: false | |
| network_compression_threshold: 256 | |
| online_mode: false | |
| disabled_commands: | |
| - disabledcommandhere | |
| servers: | |
| lobby: | |
| motd: '&aEaglercraft PVP' | |
| address: 127.0.0.1:25565 | |
| restricted: false | |
| listeners: | |
| - query_port: 25577 | |
| motd: '&6Eaglercraft 1.8.8 PVP' | |
| tab_list: GLOBAL_PING | |
| query_enabled: false | |
| proxy_protocol: false | |
| forced_hosts: {} | |
| ping_passthrough: false | |
| priorities: | |
| - lobby | |
| bind_local_address: true | |
| host: 127.0.0.1:25577 | |
| max_players: 10 | |
| tab_size: 60 | |
| force_default_server: true | |
| ip_forward: true | |
| remote_ping_timeout: 5000 | |
| prevent_proxy_connections: false | |
| groups: | |
| default: | |
| - default | |
| connection_throttle: -1 | |
| connection_throttle_limit: 0 | |
| stats: none | |
| log_pings: false | |
| EOF | |
| echo " BungeeCord config.yml written" | |
| # =============================== | |
| # WORLDEDIT CONFIG | |
| # =============================== | |
| mkdir -p "$PLUGIN_DIR/WorldEdit" | |
| cat > "$PLUGIN_DIR/WorldEdit/config.yml" << 'WEEOF' | |
| limits: | |
| allow-extra-data-values: false | |
| max-blocks-changed: | |
| default: 150000 | |
| maximum: 200000 | |
| max-polygonal-points: | |
| default: -1 | |
| maximum: 20 | |
| max-radius: -1 | |
| max-super-pickaxe-size: 5 | |
| max-brush-radius: 6 | |
| butcher-radius: | |
| default: -1 | |
| maximum: -1 | |
| disallowed-blocks: [] | |
| use-inventory: | |
| enable: false | |
| allow-override: true | |
| creative-mode-overrides: false | |
| logging: | |
| log-commands: false | |
| file: worldedit.log | |
| super-pickaxe: | |
| drop-items: true | |
| many-drop-items: true | |
| snapshots: | |
| directory: | |
| navigation-wand: | |
| item: 345 | |
| max-distance: 100 | |
| scripting: | |
| timeout: 3000 | |
| dir: craftscripts | |
| saving: | |
| dir: schematics | |
| files: | |
| allow-symbolic-links: false | |
| history: | |
| size: 15 | |
| expiration: 10 | |
| wand-item: 271 | |
| no-double-slash: false | |
| no-op-permissions: false | |
| debug: false | |
| show-help-on-first-use: true | |
| server-side-cui: true | |
| WEEOF | |
| echo " WorldEdit config written" | |
| # =============================== | |
| # WORLDGUARD CONFIG | |
| # =============================== | |
| mkdir -p "$PLUGIN_DIR/WorldGuard" | |
| cat > "$PLUGIN_DIR/WorldGuard/config.yml" << 'WGEOF' | |
| op-permissions: true | |
| summary-on-start: true | |
| auto-invincible: false | |
| auto-invincible-group: false | |
| auto-no-drowning-group: false | |
| use-player-move-event: true | |
| use-player-teleport-event: true | |
| regions: | |
| enable: true | |
| wand: 287 | |
| max-region-count-per-player: | |
| default: 7 | |
| claim-only-inside-existing-regions: false | |
| max-claim-volume: 30000 | |
| cancel-chat-without-recipients: false | |
| npc-flag-handling: RELATIONSHIP_API | |
| use-creature-spawn-event: true | |
| invincibility-removes-mobs: false | |
| high-frequency-flags: true | |
| protect-against-liquid-flow: false | |
| sql: | |
| use: false | |
| dsn: jdbc:mysql://localhost/worldguard | |
| username: worldguard | |
| password: worldguard | |
| table-prefix: '' | |
| driver: '' | |
| build-permission-nodes: | |
| enable: false | |
| deny-message: '&cSorry, you cannot build here!' | |
| event-handling: | |
| block-entity-spawns-with-mob-reasons: false | |
| interaction-whitelist: [] | |
| security: | |
| deop-everyone-on-join: false | |
| block-in-game-op-command: false | |
| mobs: | |
| block-creeper-explosions: false | |
| block-creeper-block-damage: true | |
| block-wither-explosions: false | |
| block-wither-block-damage: true | |
| block-wither-skull-explosions: false | |
| block-wither-skull-block-damage: true | |
| block-enderdragon-block-damage: true | |
| block-enderdragon-portal-creation: false | |
| block-fireball-explosions: false | |
| block-fireball-block-damage: true | |
| anti-wolf-dumbness: false | |
| allow-tamed-spawns: true | |
| disable-enderman-griefing: true | |
| block-painting-destroy: false | |
| block-item-frame-destroy: false | |
| block-plugin-spawning: true | |
| block-above-ground-slimes: false | |
| block-other-explosions: false | |
| block-zombie-door-destruction: true | |
| block-vehicle-entry: false | |
| block-creature-spawn: [] | |
| player-damage: | |
| disable-fall-damage: false | |
| disable-lava-damage: false | |
| disable-fire-damage: false | |
| disable-lightning-damage: false | |
| disable-drowning-damage: false | |
| disable-suffocation-damage: false | |
| disable-contact-damage: false | |
| disable-void-damage: false | |
| teleport-on-void-falling: false | |
| disable-explosion-damage: false | |
| disable-mob-damage: false | |
| disable-death-messages: false | |
| ignition: | |
| block-tnt: false | |
| block-tnt-block-damage: false | |
| block-lighter: false | |
| fire: | |
| disable-lava-fire-spread: false | |
| disable-all-fire-spread: false | |
| disable-fire-spread-blocks: [] | |
| lava-spread-blocks: [] | |
| weather: | |
| prevent-lightning-strike-fire: false | |
| disable-lightning-strike-fire: false | |
| disable-thunderstorm: false | |
| disable-weather: false | |
| disable-pig-zombification: false | |
| disable-powered-creepers: false | |
| always-raining: false | |
| always-thundering: false | |
| dynamics: | |
| disable-mushroom-spread: false | |
| disable-ice-melting: false | |
| disable-snow-melting: false | |
| disable-snow-formation: false | |
| disable-ice-formation: false | |
| disable-leaf-decay: false | |
| disable-grass-growth: false | |
| disable-mycelium-spread: false | |
| disable-vine-growth: false | |
| disable-crop-growth: false | |
| disable-soil-dehydration: false | |
| chest-protection: | |
| enable: false | |
| disable-off-check: false | |
| blacklist: | |
| use: false | |
| override-wildcard: true | |
| log: | |
| console: false | |
| database: false | |
| file: false | |
| file-path: worldguard/logs/%Y-%m-%d.log | |
| WGEOF | |
| mkdir -p "$PLUGIN_DIR/WorldGuard/worlds/world" | |
| cat > "$PLUGIN_DIR/WorldGuard/worlds/world/config.yml" << 'WGWEOF' | |
| regions: | |
| enable: true | |
| invincibility-removes-mobs: false | |
| mobs: | |
| block-creeper-block-damage: true | |
| block-enderdragon-block-damage: true | |
| block-wither-block-damage: true | |
| disable-enderman-griefing: true | |
| block-zombie-door-destruction: true | |
| player-damage: | |
| disable-fall-damage: false | |
| disable-void-damage: false | |
| fire: | |
| disable-all-fire-spread: false | |
| weather: | |
| disable-weather: false | |
| WGWEOF | |
| echo " WorldGuard config written" | |
| # =============================== | |
| # MINERESETLITE CONFIG | |
| # =============================== | |
| mkdir -p "$PLUGIN_DIR/MineResetLite" | |
| mkdir -p "$PLUGIN_DIR/MineResetLite/mines" | |
| cat > "$PLUGIN_DIR/MineResetLite/config.yml" << 'MRLEOF' | |
| broadcast-in-world-only: false | |
| broadcast-nearby-only: false | |
| default-mine-reset-delay: 15 | |
| MRLEOF | |
| echo " MineResetLite config written" | |
| # =============================== | |
| # SHOPKEEPERS CONFIG | |
| # =============================== | |
| mkdir -p "$PLUGIN_DIR/Shopkeepers" | |
| cat > "$PLUGIN_DIR/Shopkeepers/config.yml" << 'SKEOF' | |
| debug: false | |
| enable-metrics: false | |
| bypass-spawn-blocking: true | |
| bypass-shop-interaction-blocking: false | |
| save-instantly: true | |
| enable-living-shops: true | |
| disable-living-shops: false | |
| enable-sign-shops: true | |
| enable-citizen-shops: false | |
| name-regex: "[A-Za-z0-9 ]{3,25}" | |
| max-shops-per-player: 0 | |
| require-creation-permission: false | |
| create-player-shop-with-command: false | |
| currency-item: EMERALD | |
| currency-item-spawn-egg-entity-type: '' | |
| zero-currency-item: AIR | |
| high-currency-item: EMERALD_BLOCK | |
| high-currency-value: 9 | |
| high-currency-min-cost: 20 | |
| zero-high-currency-item: AIR | |
| disable-other-villagers: true | |
| block-villager-spawns: false | |
| hire-other-villagers: false | |
| tax-rate: 0 | |
| tax-round-up: false | |
| protect-chests: true | |
| prevent-item-movement: true | |
| delete-shopkeeper-on-break-chest: false | |
| editor-max-column: 8 | |
| enable-villager-shops: true | |
| enable-sign-shops-only: false | |
| enable-witch-shops: true | |
| msg-creation-item-selected: "&aShop creation item selected." | |
| msg-shop-create-fail: "&cCannot create shop." | |
| msg-button-name: "&aNaming" | |
| msg-button-type: "&aType" | |
| msg-button-delete: "&4Delete" | |
| msg-button-hire: "&aHire" | |
| SKEOF | |
| echo " Shopkeepers config written" | |
| # =============================== | |
| # SAFETRADE CONFIG | |
| # =============================== | |
| mkdir -p "$PLUGIN_DIR/SafeTrade" | |
| cat > "$PLUGIN_DIR/SafeTrade/config.yml" << 'STEOF' | |
| enabled: true | |
| prefix: "&8[&6Trade&8] " | |
| allow-cross-world: true | |
| request-timeout: 30 | |
| gui-title: "&8Safe Trade" | |
| money-trading: false | |
| log-trades: true | |
| log-file: "trades.log" | |
| sounds: | |
| trade-request: true | |
| trade-complete: true | |
| trade-cancel: true | |
| messages: | |
| trade-request-sent: "&aTrade request sent to &e%player%&a!" | |
| trade-request-received: "&e%player% &awants to trade! Use &e/trade accept &ato accept." | |
| trade-accepted: "&aTrade accepted!" | |
| trade-denied: "&cTrade denied." | |
| trade-cancelled: "&cTrade cancelled." | |
| trade-complete: "&aTrade complete!" | |
| no-pending-request: "&cYou have no pending trade requests." | |
| already-trading: "&cYou are already in a trade." | |
| cannot-trade-self: "&cYou cannot trade with yourself." | |
| player-not-found: "&cPlayer not found." | |
| player-busy: "&c%player% is busy." | |
| request-expired: "&cTrade request expired." | |
| STEOF | |
| echo " SafeTrade config written" | |
| # =============================== | |
| # SKRIPT CONFIG + SCRIPTS | |
| # =============================== | |
| mkdir -p "$PLUGIN_DIR/Skript" | |
| mkdir -p "$PLUGIN_DIR/Skript/scripts" | |
| cat > "$PLUGIN_DIR/Skript/config.sk" << 'SKRIPTEOF' | |
| language: english | |
| color codes: true | |
| script loader: | |
| use new script loader: true | |
| databases: | |
| database 1: | |
| type: CSV | |
| pattern: .* | |
| file: ./plugins/Skript/variables.csv | |
| backup interval: 2 hours | |
| log: | |
| verbosity: NORMAL | |
| enable effect commands: true | |
| allow function calls from expressions: true | |
| disable variable will not be saved warnings: false | |
| disable missing and/or warnings: false | |
| disable variable conflict warnings: false | |
| date format: default | |
| SKRIPTEOF | |
| echo " Skript config written" | |
| # ---- PVP core script ---- | |
| cat > "$PLUGIN_DIR/Skript/scripts/pvp-core.sk" << 'PVPSKEOF' | |
| # ============================ | |
| # PVP Core — Kills, Penalties, & Downgrades | |
| # ============================ | |
| command /setcustomspawn: | |
| permission: op | |
| trigger: | |
| set {serverSpawn} to player's location | |
| set spawn of world "world" to player's location | |
| send "&a&lExact spawn point set! Players will now spawn EXACTLY here." to player | |
| on death of player: | |
| # 1. Update Streaks | |
| if attacker is a player: | |
| set {deathsInARow::%attacker%} to 0 | |
| add 1 to {kills::%attacker%} | |
| send "&c&l☠ &e%victim% &7was slain by &c%attacker% &7(&e%{kills::%attacker%}% kills&7)" to all players | |
| add 1 to {deathsInARow::%victim%} | |
| # 2. Drop Ores & Currency on the ground (Keep Gear) | |
| loop all items in victim's inventory: | |
| if loop-item is coal or iron ore or gold ore or lapis lazuli or redstone or diamond or emerald or cobblestone or stone: | |
| drop loop-item at victim's location | |
| remove loop-item from victim's inventory | |
| # 3. XP Floor Logic (Drop to the bottom of their tier) | |
| set {_lvl} to victim's level | |
| if {_lvl} >= 26: | |
| set victim's level to 26 | |
| else if {_lvl} >= 11: | |
| set victim's level to 11 | |
| else: | |
| set victim's level to 0 | |
| # 4. Downgrade Gear Logic (5 Deaths in a row) | |
| if {deathsInARow::%victim%} >= 5: | |
| set {deathsInARow::%victim%} to 0 | |
| send "&c&l☠ You died 5 times in a row! Your gear was downgraded!" to victim | |
| # Downgrade Helmet | |
| if victim's helmet is diamond helmet: | |
| set victim's helmet to iron helmet | |
| else if victim's helmet is iron helmet or chainmail helmet: | |
| set victim's helmet to leather helmet | |
| # Downgrade Chestplate | |
| if victim's chestplate is diamond chestplate: | |
| set victim's chestplate to iron chestplate | |
| else if victim's chestplate is iron chestplate or chainmail chestplate: | |
| set victim's chestplate to leather chestplate | |
| # Downgrade Leggings | |
| if victim's leggings is diamond leggings: | |
| set victim's leggings to iron leggings | |
| else if victim's leggings is iron leggings or chainmail leggings: | |
| set victim's leggings to leather leggings | |
| # Downgrade Boots | |
| if victim's boots is diamond boots: | |
| set victim's boots to iron boots | |
| else if victim's boots is iron boots or chainmail boots: | |
| set victim's boots to leather boots | |
| # Downgrade Pickaxe | |
| if victim has a diamond pickaxe: | |
| remove all diamond pickaxes from victim's inventory | |
| give iron pickaxe to victim | |
| else if victim has an iron pickaxe: | |
| remove all iron pickaxes from victim's inventory | |
| give stone pickaxe to victim | |
| command /stats [<player>]: | |
| trigger: | |
| if arg-1 is set: | |
| set {_target} to arg-1 | |
| else: | |
| set {_target} to player | |
| set {_k} to {kills::%{_target}%} ? 0 | |
| set {_d} to {deaths::%{_target}%} ? 0 | |
| send "&8&m " | |
| send "&6&l⚔ &ePVP Stats for &c%{_target}%" | |
| send "&7Kills: &a%{_k}%" | |
| send "&7Deaths: &c%{_d}%" | |
| send "&7Streak: &e%{streak::%{_target}%} ? 0%" | |
| send "&8&m " | |
| on respawn: | |
| if {serverSpawn} is set: | |
| set respawn location to {serverSpawn} | |
| wait 1 tick | |
| if {serverSpawn} is set: | |
| teleport player to {serverSpawn} | |
| if player does not have a sword: | |
| give player wooden sword named "&7Starter Sword" | |
| if player does not have a pickaxe: | |
| give player wooden pickaxe named "&7Starter Pickaxe" | |
| on first join: | |
| wait 1 tick | |
| if {serverSpawn} is set: | |
| teleport player to {serverSpawn} | |
| give player wooden sword named "&7Starter Sword" | |
| give player wooden pickaxe named "&7Starter Pickaxe" | |
| give player 8 of cooked beef named "&6Steak" | |
| send "&a&lWelcome to the DigPvP!" to player | |
| send "&7Use &e/stats &7to check your kills and deaths." to player | |
| send "&7Use &e/trade <player> &7to trade items safely." to player | |
| send "&7How to play:&7 Mine in the beginner zone to get ores." to player | |
| send "Buy weapons, tools, armor and more for PVP and mining." to player | |
| on join: | |
| set join message to "&8[&a+&8] &7%player%" | |
| on quit: | |
| set quit message to "&8[&c-&8] &7%player%" | |
| command /spawn: | |
| trigger: | |
| # 1. Check if they are in combat | |
| if {combatTag::%player%} is set: | |
| if difference between now and {combatTag::%player%} is less than 15 seconds: | |
| send "&c&lERROR! &7You cannot use /spawn while in combat!" to player | |
| stop | |
| # 2. Start the 3-second timer | |
| send "&eTeleporting in 3 seconds... Do not move!" to player | |
| set {_loc} to player's location | |
| wait 3 seconds | |
| # 3. Check if they moved | |
| if distance between player's location and {_loc} > 0.5: | |
| send "&cTeleport cancelled because you moved!" to player | |
| stop | |
| # 4. Teleport them safely | |
| teleport player to spawn of world "world" | |
| send "&aTeleported to spawn!" to player | |
| PVPSKEOF | |
| echo " Skript pvp-core.sk written" | |
| # ---- Spawn protection script ---- | |
| cat > "$PLUGIN_DIR/Skript/scripts/spawn-protect.sk" << 'SPAWNEOF' | |
| on damage of player: | |
| attacker is a player | |
| distance between victim's location and spawn of world "world" is less than 20 | |
| cancel event | |
| send "&cNo PVP in spawn area!" to attacker | |
| SPAWNEOF | |
| echo " Skript spawn-protect.sk written" | |
| # ---- Combat tag script ---- | |
| cat > "$PLUGIN_DIR/Skript/scripts/combat-tag.sk" << 'COMBATEOF' | |
| on damage of player: | |
| attacker is a player | |
| set {combatTag::%victim%} to now | |
| set {combatTag::%attacker%} to now | |
| send "&c&l⚔ You are now in combat! Don't log out!" to victim | |
| send "&c&l⚔ You are now in combat! Don't log out!" to attacker | |
| on quit: | |
| {combatTag::%player%} is set | |
| difference between now and {combatTag::%player%} is less than 15 seconds | |
| kill player | |
| broadcast "&c%player% combat logged and was killed!" | |
| on region enter: | |
| # Make sure to replace "spawn" with the exact name of your WorldGuard safezone! | |
| if "%region%" contains "spawn": | |
| if {combatTag::%player%} is set: | |
| if difference between now and {combatTag::%player%} is less than 15 seconds: | |
| # This creates the physical barrier / forcefield bounce | |
| cancel event | |
| push player horizontally backward at speed 1.5 | |
| push player upwards at speed 0.3 | |
| send "&c&l⚔ &7You cannot enter the Safezone while in combat!" to player | |
| every 1 second: | |
| loop all players: | |
| {combatTag::%loop-player%} is set | |
| difference between now and {combatTag::%loop-player%} is greater than 15 seconds | |
| delete {combatTag::%loop-player%} | |
| COMBATEOF | |
| echo " Skript combat-tag.sk written" | |
| cat > "$PLUGIN_DIR/Skript/scripts/zones.sk" << 'ZONEEOF' | |
| on region enter: | |
| # ----------------------------- | |
| # 1. THE BEGINNER MINE (Max Lvl 10) | |
| # ----------------------------- | |
| if "%region%" contains "beginnermine": | |
| # Check if they are over level 10 | |
| if player's level > 10: | |
| cancel event | |
| send "&c&lLOCKED! &7You are over Level 10. You must use the Pro Mine!" to player | |
| # Check if they are wearing Iron, Diamond, or Emerald (Leather/Gold is allowed) | |
| if player is wearing iron helmet or iron chestplate or iron leggings or iron boots: | |
| cancel event | |
| send "&c&lLOCKED! &7Your armor is too strong for the Beginner Mine!" to player | |
| if player is wearing diamond helmet or diamond chestplate or diamond leggings or diamond boots: | |
| cancel event | |
| send "&c&lLOCKED! &7Your armor is too strong for the Beginner Mine!" to player | |
| # ----------------------------- | |
| # 2. THE PRO MINE (Max Lvl 25) | |
| # ----------------------------- | |
| if "%region%" contains "promine": | |
| # Check if they are over level 25 | |
| if player's level > 25: | |
| cancel event | |
| send "&c&lLOCKED! &7You are over Level 25. You must use the Expert Mine!" to player | |
| # Check if they are wearing Diamond or Emerald (Chainmail/Iron is allowed) | |
| if player is wearing diamond helmet or diamond chestplate or diamond leggings or diamond boots: | |
| cancel event | |
| send "&c&lLOCKED! &7Your armor is too strong for the Pro Mine!" to player | |
| # ----------------------------- | |
| # 3. THE EXPERT MINE (Level 26+) | |
| # ----------------------------- | |
| if "%region%" contains "expertmine": | |
| # Check if they are under level 26 | |
| if player's level < 26: | |
| cancel event | |
| send "&c&lLOCKED! &7You need to be Level 26+ to enter the Expert Mine!" to player | |
| ZONEEOF | |
| echo " Skript spawn-protect.sk written" | |
| # ---- Emerald economy script ---- | |
| cat > "$PLUGIN_DIR/Skript/scripts/economy.sk" << 'ECONEOF' | |
| command /balance [<player>]: | |
| aliases: /bal, /money | |
| trigger: | |
| if arg-1 is set: | |
| set {_target} to arg-1 | |
| else: | |
| set {_target} to player | |
| set {_emeralds} to number of emeralds in {_target}'s inventory | |
| send "&8&m " | |
| send "&6&l$ &eBalance for &c%{_target}%" | |
| send "&7Emeralds: &a%{_emeralds}%" | |
| send "&8&m " | |
| command /pay <player> <integer>: | |
| trigger: | |
| if arg-2 is less than 1: | |
| send "&cAmount must be at least 1!" | |
| stop | |
| if arg-1 is player: | |
| send "&cYou can't pay yourself!" | |
| stop | |
| set {_has} to number of emeralds in player's inventory | |
| if {_has} is less than arg-2: | |
| send "&cYou don't have enough emeralds!" | |
| stop | |
| remove arg-2 of emerald from player's inventory | |
| give arg-2 of emerald to arg-1 | |
| send "&aSent &e%arg-2% emeralds &ato &e%arg-1%&a!" | |
| send "&a%player% &esent you &a%arg-2% emeralds&e!" to arg-1 | |
| ECONEOF | |
| echo " Skript economy.sk written" | |
| echo "" | |
| echo " All configs written — reloading plugins via RCON..." | |
| # =============================== | |
| # RELOAD ALL PLUGINS so configs take effect | |
| # =============================== | |
| sleep 2 | |
| mc_command "worldguard reload" | |
| echo " WorldGuard reloaded" | |
| mc_command "skript reload all" | |
| echo " Skript scripts reloaded" | |
| mc_command "worldedit reload" | |
| echo " WorldEdit reloaded" | |
| mc_command "reload confirm" | |
| echo " Full server reload done — all plugin configs applied" | |
| echo "" | |
| # ============================================================= | |
| # STEP 6: EaglerXBungee generation + Start BungeeCord | |
| # ============================================================= | |
| LISTENERS_FILE=$(find_listeners_yml) | |
| if [ -z "$LISTENERS_FILE" ]; then | |
| echo "[6/7] Generating EaglerXBungee config..." | |
| cd "$BUNGEE_DIR" | |
| $JAVA "${BUNGEE_JVM_FLAGS[@]}" \ | |
| -cp "sqlite-jdbc.jar:BungeeCord.jar" \ | |
| net.md_5.bungee.Bootstrap >> /tmp/bungee-gen.log 2>&1 & | |
| GEN_PID=$! | |
| for i in $(seq 1 60); do | |
| if nc -z 127.0.0.1 8081 2>/dev/null || nc -z 127.0.0.1 7860 2>/dev/null; then | |
| echo " EaglerXBungee started (~$((i*2))s)" | |
| break | |
| fi | |
| if ! kill -0 $GEN_PID 2>/dev/null; then | |
| echo " Generation failed" | |
| tail -20 /tmp/bungee-gen.log | |
| break | |
| fi | |
| sleep 2 | |
| done | |
| sleep 3 | |
| kill $GEN_PID 2>/dev/null | |
| wait $GEN_PID 2>/dev/null | |
| for i in $(seq 1 15); do | |
| nc -z 127.0.0.1 8081 2>/dev/null || break | |
| sleep 1 | |
| done | |
| sleep 2 | |
| else | |
| echo "[6/7] EaglerXBungee config exists" | |
| fi | |
| echo " Starting BungeeCord..." | |
| patch_eagler_port | |
| EAGLER_DIR=$(dirname "$(find_listeners_yml)" 2>/dev/null) | |
| if [ -n "$EAGLER_DIR" ]; then | |
| mkdir -p "$EAGLER_DIR/drivers" | |
| cp -f "$BUNGEE_DIR/sqlite-jdbc.jar" "$EAGLER_DIR/drivers/sqlite-jdbc.jar" 2>/dev/null | |
| fi | |
| > /tmp/bungee.log | |
| start_bungee | |
| echo " BungeeCord PID: $BUNGEE_PID" | |
| PORT_READY=false | |
| for i in $(seq 1 45); do | |
| if nc -z 127.0.0.1 7860 2>/dev/null; then | |
| PORT_READY=true | |
| echo " Port 7860 OPEN (~$((i*2))s)" | |
| break | |
| fi | |
| if ! kill -0 $BUNGEE_PID 2>/dev/null; then | |
| echo " BungeeCord crashed!" | |
| tail -20 /tmp/bungee.log | |
| break | |
| fi | |
| sleep 2 | |
| done | |
| if [ "$PORT_READY" = true ]; then | |
| echo "" | |
| echo "============================================" | |
| echo " PVP SERVER READY — EaglerCraft on :7860" | |
| [ -n "$OP_USERNAME" ] && echo " OP: $OP_USERNAME (level 4)" | |
| echo " Plugins: WE, WG, MRL, Shopkeepers, SafeTrade, Skript" | |
| echo "============================================" | |
| else | |
| echo " Port 7860 NOT open!" | |
| for port in 7860 8081 25565 25577; do | |
| nc -z 127.0.0.1 $port 2>/dev/null && echo " OK $port" || echo " FAIL $port" | |
| done | |
| LISTENERS_NOW=$(find_listeners_yml) | |
| if [ -n "$LISTENERS_NOW" ] && grep -q ":8081" "$LISTENERS_NOW"; then | |
| kill $BUNGEE_PID 2>/dev/null | |
| wait $BUNGEE_PID 2>/dev/null | |
| sleep 3 | |
| patch_eagler_port | |
| start_bungee | |
| sleep 20 | |
| nc -z 127.0.0.1 7860 2>/dev/null && echo " Port 7860 open!" || echo " Failed" | |
| fi | |
| fi | |
| # ============================================================= | |
| # STEP 7: Final confirmation | |
| # ============================================================= | |
| echo "" | |
| echo "[7/7] Final status check..." | |
| echo " === ACTIVE PLUGINS ===" | |
| RELOAD_CHECK=$(mc_command "plugins") | |
| echo " $RELOAD_CHECK" | |
| echo " ======================" | |
| echo "" | |
| # ============================================================= | |
| # Sync loop | |
| # ============================================================= | |
| hf_sync_loop & | |
| SYNC_PID=$! | |
| # ============================================================= | |
| # Shutdown — save world properly, then push, then stop processes | |
| # ============================================================= | |
| graceful_shutdown() { | |
| echo " Shutting down..." | |
| mc_command "gamerule doMobSpawning true" | |
| mc_command "gamerule randomTickSpeed 3" | |
| mc_command "save-all" | |
| sleep 5 | |
| hf_push_saves | |
| kill $SYNC_PID 2>/dev/null | |
| # Stop the MC server gracefully via RCON before killing PIDs | |
| mc_command "stop" | |
| sleep 5 | |
| kill $BUNGEE_PID 2>/dev/null | |
| # Only force-kill if still alive | |
| kill -0 $BACKEND_PID 2>/dev/null && kill $BACKEND_PID 2>/dev/null | |
| exit 0 | |
| } | |
| trap graceful_shutdown SIGTERM SIGINT SIGHUP | |
| # ============================================================= | |
| # Monitor loop | |
| # ============================================================= | |
| echo "" | |
| echo "Monitor loop started..." | |
| LAST_LOG_LINE=$(wc -l < /tmp/paper.log 2>/dev/null || echo 0) | |
| LOOP_COUNT=0 | |
| while true; do | |
| LOOP_COUNT=$((LOOP_COUNT + 1)) | |
| if ! kill -0 $BACKEND_PID 2>/dev/null; then | |
| echo "[$(date '+%H:%M:%S')] WindSpigot crashed — restarting..." | |
| hf_push_saves | |
| IDLE_MODE=false | |
| start_windspigot | |
| sleep 45 | |
| # Wait for RCON to be available before sending commands | |
| for i in $(seq 1 30); do | |
| nc -z 127.0.0.1 25575 2>/dev/null && break | |
| sleep 1 | |
| done | |
| if [ -n "$OP_USERNAME" ]; then | |
| mc_command "op ${OP_USERNAME}" | |
| fi | |
| mc_command "gamerule pvp true" | |
| mc_command "gamerule keepInventory false" | |
| mc_command "gamerule mobGriefing false" | |
| enter_idle_mode | |
| fi | |
| if ! kill -0 $BUNGEE_PID 2>/dev/null; then | |
| echo "[$(date '+%H:%M:%S')] BungeeCord crashed — restarting..." | |
| patch_eagler_port | |
| start_bungee | |
| fi | |
| if ! kill -0 $SYNC_PID 2>/dev/null; then | |
| hf_sync_loop & | |
| SYNC_PID=$! | |
| fi | |
| if kill -0 $BACKEND_PID 2>/dev/null; then | |
| PLAYER_COUNT=$(get_player_count) | |
| if [ "$PLAYER_COUNT" != "0" ] && [ "$IDLE_MODE" = true ]; then | |
| exit_idle_mode | |
| elif [ "$PLAYER_COUNT" = "0" ] && [ "$IDLE_MODE" = false ]; then | |
| enter_idle_mode | |
| fi | |
| fi | |
| if [ $((LOOP_COUNT % 5)) -eq 0 ]; then | |
| CURRENT_LINE=$(wc -l < /tmp/paper.log 2>/dev/null || echo 0) | |
| if [ "$CURRENT_LINE" -gt "$LAST_LOG_LINE" ]; then | |
| NEW_ERRORS=$(tail -n +"$((LAST_LOG_LINE + 1))" /tmp/paper.log | grep -c "ERROR\|SEVERE" || echo 0) | |
| [ "$NEW_ERRORS" -gt 0 ] && echo "[$(date '+%H:%M:%S')] $NEW_ERRORS errors" && \ | |
| tail -n +"$((LAST_LOG_LINE + 1))" /tmp/paper.log | grep "ERROR\|SEVERE" | tail -3 | |
| LAST_LOG_LINE=$CURRENT_LINE | |
| fi | |
| fi | |
| if [ $((LOOP_COUNT % 30)) -eq 0 ]; then | |
| for LF in /tmp/paper.log /tmp/bungee.log; do | |
| LS=$(stat -c%s "$LF" 2>/dev/null || echo 0) | |
| if [ "$LS" -gt 10485760 ]; then | |
| tail -1000 "$LF" > "${LF}.tmp" && mv "${LF}.tmp" "$LF" | |
| echo "[$(date '+%H:%M:%S')] Trimmed $(basename $LF)" | |
| fi | |
| done | |
| LAST_LOG_LINE=$(wc -l < /tmp/paper.log 2>/dev/null || echo 0) | |
| fi | |
| if [ $((LOOP_COUNT % 5)) -eq 0 ]; then | |
| RSS=$(ps -p $BACKEND_PID -o rss= 2>/dev/null | awk '{printf "%.0f", $1/1024}') | |
| echo "[STATUS] Players: ${PLAYER_COUNT:-?} | RAM: ${RSS:-?}MB | $([ "$IDLE_MODE" = true ] && echo IDLE || echo ACTIVE)" | |
| fi | |
| sleep 60 | |
| done |