Advertisement
KC9UZR

Definitive Minecraft Linux Deployment & Automated Maintenance

Jun 16th, 2025
362
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 15.12 KB | Gaming | 0 0
  1. #!/bin/bash
  2.  
  3. # --- Configuration ---
  4. SERVER_DIR="$HOME/minecraft_server"
  5. BACKUP_DIR="$HOME/minecraft_backups"
  6. MEMORY="4G"
  7. SERVER_JAR_NAME="paper.jar"
  8. SCREEN_NAME="mc_server"
  9.  
  10. # --- Globals ---
  11. JAVA_EXECUTABLE=""
  12. CURL_USER_AGENT="Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0"
  13.  
  14. # --- Colors ---
  15. RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; CYAN='\033[0;36m'; NC='\033[0m'
  16.  
  17. # --- Helper Functions ---
  18. print_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
  19. print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
  20. print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
  21. print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
  22.  
  23. # --- CORE SYSTEM FUNCTIONS ---
  24.  
  25. download_file() {
  26.     local url="$1"
  27.     local output_path="$2"
  28.     local curl_output; curl_output=$(curl --user-agent "$CURL_USER_AGENT" -L -o "$output_path" --fail --show-error "$url" 2>&1)
  29.     if [ $? -ne 0 ]; then
  30.         print_error "Download failed for: $url"; print_warning "Curl Error: $curl_output"; return 1
  31.     fi
  32.     return 0
  33. }
  34.  
  35. run_system_diagnostics() {
  36.     for pkg in curl wget jq screen nano; do
  37.         if ! command -v "$pkg" &> /dev/null; then print_error "Required command '$pkg' is not installed. Please run 'sudo apt-get install $pkg'"; exit 1; fi
  38.     done
  39. }
  40.  
  41. install_and_find_java() {
  42.     if [ -z "$JAVA_EXECUTABLE" ] || [ ! -f "$JAVA_EXECUTABLE" ]; then
  43.         print_info "Ensuring Java 21 is installed..."; sudo apt-get update -y >/dev/null 2>&1
  44.         if ! sudo apt-get install -y openjdk-21-jre-headless >/dev/null 2>&1; then print_error "Failed to install Java 21."; exit 1; fi
  45.         local discovered_path; discovered_path=$(dpkg -L openjdk-21-jre-headless | grep '/bin/java$' | head -n 1)
  46.         if [ -n "$discovered_path" ] && [ -f "$discovered_path" ]; then
  47.             JAVA_EXECUTABLE="$discovered_path"; print_success "Java 21 executable located."
  48.         else
  49.             print_error "Java installed, but executable could not be located."; exit 1
  50.         fi
  51.     fi
  52. }
  53.  
  54. # --- SOFTWARE INSTALLATION FUNCTIONS ---
  55.  
  56. update_paper() {
  57.     if is_server_running; then print_error "Server is running! Stop it before updating."; return; fi
  58.     print_info "Checking for PaperMC updates..."; mkdir -p "$SERVER_DIR"
  59.     local latest_version; latest_version=$(curl -sfL "$CURL_USER_AGENT" https://api.papermc.io/v2/projects/paper | jq -r '.versions[-1]')
  60.     local version_url="https://api.papermc.io/v2/projects/paper/versions/${latest_version}"
  61.     local latest_build; latest_build=$(curl -sfL "$CURL_USER_AGENT" "$version_url" | jq -r '.builds[-1]')
  62.     local build_url="${version_url}/builds/${latest_build}"
  63.     local jar_name; jar_name=$(curl -sfL "$CURL_USER_AGENT" "$build_url" | jq -r '.downloads.application.name')
  64.     if download_file "${build_url}/downloads/${jar_name}" "$SERVER_DIR/$SERVER_JAR_NAME"; then
  65.         print_success "PaperMC is up to date."
  66.     else
  67.         print_error "Could not update PaperMC."; return 1
  68.     fi
  69. }
  70.  
  71. update_geyser() {
  72.     if is_server_running; then print_error "Server is running! Stop it before updating."; return; fi
  73.     print_info "Updating Geyser & Floodgate with verification..."; local PLUGINS_DIR="${SERVER_DIR}/plugins"; mkdir -p "$PLUGINS_DIR"
  74.    
  75.     # Geyser (GitHub API is primary for stability)
  76.     print_info "Downloading latest Geyser..."
  77.     local geyser_url; geyser_url=$(curl -sfL "$CURL_USER_AGENT" "https://api.github.com/repos/GeyserMC/Geyser/releases/latest" | jq -r '.assets[] | select(.name | contains("Geyser-Spigot")) | .browser_download_url')
  78.     download_file "$geyser_url" "${PLUGINS_DIR}/Geyser-Spigot.jar"
  79.     if [ -f "${PLUGINS_DIR}/Geyser-Spigot.jar" ]; then print_success "Geyser download successful and verified."; else print_error "Geyser download failed verification."; fi
  80.  
  81.     # Floodgate (GitHub API is primary for stability)
  82.     print_info "Downloading latest Floodgate..."
  83.     local floodgate_url; floodgate_url=$(curl -sfL "$CURL_USER_AGENT" "https://api.github.com/repos/GeyserMC/Floodgate/releases/latest" | jq -r '.assets[] | select(.name | contains("floodgate-spigot")) | .browser_download_url')
  84.     download_file "$floodgate_url" "${PLUGINS_DIR}/floodgate-spigot.jar"
  85.     if [ -f "${PLUGINS_DIR}/floodgate-spigot.jar" ]; then print_success "Floodgate download successful and verified."; else print_error "Floodgate download failed verification."; fi
  86. }
  87.  
  88. # --- New Interactive Plugin Installer ---
  89. install_plugins_interactive() {
  90.     if is_server_running; then print_error "Server is running! Stop it before installing plugins."; return; fi
  91.     local PLUGINS_DIR="${SERVER_DIR}/plugins"; mkdir -p "$PLUGINS_DIR"
  92.    
  93.     declare -A plugins=(
  94.         ["EssentialsX"]="https://ci.essentialsx.net/job/EssentialsX/lastSuccessfulBuild/artifact/target/EssentialsX.jar"
  95.         ["EssentialsX Spawn"]="https://ci.essentialsx.net/job/EssentialsX/lastSuccessfulBuild/artifact/target/EssentialsXSpawn.jar"
  96.         ["EssentialsX Chat"]="https://ci.essentialsx.net/job/EssentialsX/lastSuccessfulBuild/artifact/target/EssentialsXChat.jar"
  97.         ["LuckPerms"]="https://api.luckperms.net/v2/downloader"
  98.         ["WorldEdit"]="https://api.modrinth.com/v2/project/worldedit/version?loaders=%5B%22paper%22%5D"
  99.         ["WorldGuard"]="https://api.modrinth.com/v2/project/worldguard/version?loaders=%5B%22paper%22%5D"
  100.         ["CoreProtect"]="https://api.github.com/repos/PlayPro/CoreProtect/releases/latest"
  101.         ["ProtocolLib"]="https://api.github.com/repos/dmulloy2/ProtocolLib/releases/latest"
  102.         ["ViaVersion"]="https://ci.viaversion.com/job/ViaVersion/lastSuccessfulBuild/api/json"
  103.         ["ViaBackwards"]="https://ci.viaversion.com/job/ViaBackwards/lastSuccessfulBuild/api/json"
  104.         ["Vault"]="https://api.github.com/repos/MilkBowl/Vault/releases/latest"
  105.         ["Dynmap"]="https://api.modrinth.com/v2/project/dynmap/version?loaders=%5B%22paper%22%5D"
  106.         ["ChestShop"]="https://api.github.com/repos/ChestShop-authors/ChestShop-3/releases/latest"
  107.         ["GriefPrevention"]="https://api.github.com/repos/TechFortress/GriefPrevention/releases/latest"
  108.         ["spark"]="https://api.github.com/repos/lucko/spark/releases/latest"
  109.         ["Chunky"]="https://api.modrinth.com/v2/project/chunky/version?loaders=%5B%22paper%22%5D"
  110.         ["GSit"]="https://api.github.com/repos/Gecolay/GSit/releases/latest"
  111.         ["SimpleTPA"]="https://api.github.com/repos/imDalton/SimpleTPA/releases/latest"
  112.         ["PlayerWarps"]="https://api.modrinth.com/v2/project/playerwarps/version?loaders=%5B%22paper%22%5D"
  113.         ["ImageMaps"]="https://api.modrinth.com/v2/project/imagemaps/version?loaders=%5B%22paper%22%5D"
  114.     )
  115.     local plugin_names=("${!plugins[@]}"); local selections=()
  116.  
  117.     while true; do
  118.         clear; echo -e "${CYAN}--- Interactive Plugin Installer ---${NC}"
  119.         echo "Enter a number to select/deselect a plugin. Select 'Done' when finished."
  120.        
  121.         for i in "${!plugin_names[@]}"; do
  122.             local name="${plugin_names[$i]}"; local installed_mark=""
  123.             for s in "${selections[@]}"; do if [[ "$s" == "$name" ]]; then installed_mark="${GREEN} [Selected]${NC}"; fi; done
  124.             printf "  %-2s. %-20s %s\n" "$((i+1))" "$name" "$installed_mark"
  125.         done
  126.         echo -e "\n  ${GREEN}d. Done Installing${NC}"
  127.         echo -e "  ${RED}c. Cancel${NC}"
  128.         read -p "Your choice: " choice
  129.  
  130.         case "$choice" in
  131.             d|D) break;;
  132.             c|C) selections=(); break;;
  133.             *)
  134.                 if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "${#plugin_names[@]}" ]; then
  135.                     local selected_name="${plugin_names[$((choice-1))]}"; local found=0
  136.                     for i in "${!selections[@]}"; do
  137.                         if [[ "${selections[$i]}" == "$selected_name" ]]; then
  138.                             unset 'selections[i]'; found=1; break
  139.                         fi
  140.                     done
  141.                     if [ $found -eq 0 ]; then selections+=("$selected_name"); fi
  142.                 else
  143.                     print_warning "Invalid input. Please try again."; sleep 1
  144.                 fi
  145.                 ;;
  146.         esac
  147.     done
  148.    
  149.     if [ ${#selections[@]} -gt 0 ]; then
  150.         print_info "Installing selected plugins..."
  151.         for name in "${selections[@]}"; do
  152.             local api_url="${plugins[$name]}"; local download_url=""
  153.             print_info "Fetching ${name}..."
  154.             if [[ "$api_url" == *modrinth.com* ]]; then
  155.                 download_url=$(curl -sfL "$CURL_USER_AGENT" "$api_url" | jq -r '.[0].files[0].url')
  156.             elif [[ "$api_url" == *luckperms.net* ]]; then
  157.                 download_url=$(curl -sfL "$CURL_USER_AGENT" "$api_url" | jq -r '.bukkit.url')
  158.             elif [[ "$api_url" == *viaversion.com* ]]; then
  159.                 local filename=$(curl -sfL "$CURL_USER_AGENT" "$api_url" | jq -r '.artifacts[0].fileName')
  160.                 download_url="${api_url//\/api\/json/}/artifact/build/libs/$filename"
  161.             elif [[ "$api_url" == *essentialsx.net* ]]; then
  162.                 download_url="$api_url" # Direct download link
  163.             elif [[ "$api_url" == *github.com* ]]; then
  164.                 download_url=$(curl -sfL "$CURL_USER_AGENT" "$api_url" | jq -r '.assets[] | select(.name | endswith(".jar")) | .browser_download_url' | head -n 1)
  165.             fi
  166.            
  167.             if [ -n "$download_url" ]; then
  168.                 local filename; filename=$(basename "$download_url"); filename="${filename// /_}" # Sanitize filename
  169.                 download_file "$download_url" "${PLUGINS_DIR}/${name}.jar"
  170.             else
  171.                 print_error "Could not determine download URL for ${name} from API."
  172.             fi
  173.         done
  174.         print_success "Plugin installation complete."
  175.     fi
  176. }
  177.  
  178. install_server() {
  179.     print_info "Starting new Minecraft Server installation..."; install_and_find_java
  180.     mkdir -p "$SERVER_DIR"; cd "$SERVER_DIR" || exit
  181.     update_paper || exit 1
  182.    
  183.     print_info "Starting server once to generate eula.txt..."
  184.     "$JAVA_EXECUTABLE" -Xmx1024M -Xms1024M -jar "$SERVER_JAR_NAME" nogui > /dev/null 2>&1
  185.     if [ ! -f "eula.txt" ]; then print_error "EULA file not created."; exit 1; fi
  186.     sed -i 's/eula=false/eula=true/g' eula.txt
  187.     print_success "EULA accepted. Server is ready."
  188.     print_warning "Use the menu to install plugins and Geyser as needed."
  189. }
  190.  
  191. uninstall_script() {
  192.     print_error "!!! DANGER ZONE - UNINSTALL !!!"; print_warning "This option will permanently delete the following:"
  193.     echo -e "  - Server Directory:  ${RED}${SERVER_DIR}${NC}\n  - Backups Directory: ${RED}${BACKUP_DIR}${NC}\n  - This Script:       ${RED}$(realpath "$0")${NC}"
  194.     read -p "Type 'uninstall' to confirm: " confirm
  195.     if [[ "$confirm" != "uninstall" ]]; then print_info "Uninstall cancelled."; return; fi
  196.     print_info "Proceeding with uninstallation..."; if is_server_running; then stop_server; fi
  197.     print_info "Deleting server directory..."; rm -rf "$SERVER_DIR"
  198.     print_info "Deleting backups directory..."; rm -rf "$BACKUP_DIR"
  199.     print_success "Uninstallation complete. This script will now delete itself."; echo "Goodbye!"; rm -- "$0"; exit 0
  200. }
  201.  
  202. # --- MENU-DRIVEN FUNCTIONS ---
  203. is_server_running() { screen -list | grep -q "$SCREEN_NAME"; }
  204. start_server() { if is_server_running; then print_warning "Already running."; return; fi; if [ ! -f "$SERVER_DIR/$SERVER_JAR_NAME" ]; then print_error "Not installed!"; return; fi; install_and_find_java; cd "$SERVER_DIR" || exit; print_info "Starting server..."; screen -dmS "$SCREEN_NAME" "$JAVA_EXECUTABLE" -Xms${MEMORY} -Xmx${MEMORY} -jar "$SERVER_JAR_NAME" nogui; sleep 2; if is_server_running; then print_success "Server started."; else print_error "Server failed to start."; fi; }
  205. stop_server() { if ! is_server_running; then print_warning "Not running."; return; fi; screen -S "$SCREEN_NAME" -p 0 -X stuff "stop\n"; print_info "Waiting for shutdown..."; count=0; while is_server_running; do sleep 1; count=$((count+1)); if [ $count -gt 20 ]; then print_warning "Server not responding."; break; fi; done; if ! is_server_running; then print_success "Server stopped."; fi; }
  206. console() { if ! is_server_running; then print_warning "Not running."; return; fi; print_info "Attaching. To exit: ${CYAN}Ctrl+A then D${NC}."; sleep 1; screen -r "$SCREEN_NAME"; }
  207. backup_server() { if is_server_running; then read -p "$(echo -e ${YELLOW}"Stop server for a safe backup? (y/n): "${NC})" c; if [[ "$c" =~ ^[Yy]$ ]]; then stop_server; fi; fi; print_info "Creating backup..."; mkdir -p "$BACKUP_DIR"; TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S"); tar -czf "${BACKUP_DIR}/backup_${TIMESTAMP}.tar.gz" -C "$SERVER_DIR" .; print_success "Backup created in ${BACKUP_DIR}"; }
  208. edit_properties() { if [ -f "$SERVER_DIR/server.properties" ]; then nano "$SERVER_DIR/server.properties"; else print_error "server.properties not found!"; fi; }
  209. view_log() { if [ -f "$SERVER_DIR/logs/latest.log" ]; then print_info "Live log. Press Ctrl+C to exit."; tail -f "$SERVER_DIR/logs/latest.log"; else print_error "latest.log not found!"; fi; }
  210.  
  211. main_menu() {
  212.    while true; do
  213.        clear
  214.        echo -e "${CYAN}================================================${NC}"
  215.        echo -e "${YELLOW}      Minecraft Server Manager (Definitive)     ${NC}"
  216.        echo -e "${CYAN}================================================${NC}"
  217.        echo -e "Status: $(if is_server_running; then echo -e "${GREEN}RUNNING${NC}"; else echo -e "${RED}STOPPED${NC}"; fi)"
  218.        echo -e "\n  --- Server Control ---"
  219.        echo -e "  ${GREEN}1.${NC} Start Server         ${BLUE}2.${NC} View Console"
  220.        echo -e "  ${RED}3.${NC} Stop Server          ${BLUE}4.${NC} View Live Log"
  221.        echo -e "\n  --- Software Management ---"
  222.        echo -e "  ${CYAN}5.${NC} Update Paper Jar"
  223.        echo -e "  ${CYAN}6.${NC} Install Popular Plugins (Interactive Menu)"
  224.        echo -e "  ${CYAN}7.${NC} Install/Update Geyser & Floodgate"
  225.        echo -e "\n  --- File Management & Uninstallation ---"
  226.        echo -e "  ${YELLOW}8.${NC} Edit server.properties   ${YELLOW}9.${NC} Create Backup"
  227.        echo -e "  ${BLUE}10.${NC} Clean Install Server    ${RED}11.${NC} UNINSTALL EVERYTHING"
  228.        echo -e "  ${RED}12.${NC} Exit\n"
  229.        read -p "Enter your choice [1-12]: " choice
  230.  
  231.        case $choice in
  232.            1) start_server;; 2) console;; 3) stop_server;; 4) view_log;;
  233.            5) update_paper;; 6) install_plugins_interactive;; 7) update_geyser;;
  234.            8) edit_properties;; 9) backup_server;;
  235.            10) read -p "$(echo -e ${RED}"This will DELETE the current server. Are you sure? (y/n): "${NC})" c; if [[ "$c" =~ ^[Yy]$ ]]; then rm -rf "$SERVER_DIR"; install_server; fi;;
  236.            11) uninstall_script;;
  237.            12) if is_server_running; then print_warning "Stop server before exiting."; else break; fi;;
  238.            *) print_warning "Invalid option.";;
  239.        esac
  240.        if [[ "$choice" != "11" ]]; then echo -e "\n${YELLOW}Press [Enter] to return to the menu...${NC}"; read -p ""; fi
  241.    done
  242.    print_success "Goodbye!"
  243. }
  244.  
  245. # --- Script Entry Point ---
  246. if [[ $EUID -eq 0 ]]; then print_error "This script must not be run as root."; exit 1; fi
  247. run_system_diagnostics
  248. main_menu
Advertisement
Comments
  • KC9UZR
    1 day
    Comment was deleted
Add Comment
Please, Sign In to add comment
Advertisement