Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/bin/bash
- # --- Configuration ---
- SERVER_DIR="$HOME/minecraft_server"
- BACKUP_DIR="$HOME/minecraft_backups"
- MEMORY="4G"
- SERVER_JAR_NAME="paper.jar"
- SCREEN_NAME="mc_server"
- # --- Globals ---
- JAVA_EXECUTABLE=""
- CURL_USER_AGENT="Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0"
- # --- Colors ---
- RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; CYAN='\033[0;36m'; NC='\033[0m'
- # --- Helper Functions ---
- print_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
- print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
- print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
- print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
- # --- CORE SYSTEM FUNCTIONS ---
- download_file() {
- local url="$1"
- local output_path="$2"
- local curl_output; curl_output=$(curl --user-agent "$CURL_USER_AGENT" -L -o "$output_path" --fail --show-error "$url" 2>&1)
- if [ $? -ne 0 ]; then
- print_error "Download failed for: $url"; print_warning "Curl Error: $curl_output"; return 1
- fi
- return 0
- }
- run_system_diagnostics() {
- for pkg in curl wget jq screen nano; do
- 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
- done
- }
- install_and_find_java() {
- if [ -z "$JAVA_EXECUTABLE" ] || [ ! -f "$JAVA_EXECUTABLE" ]; then
- print_info "Ensuring Java 21 is installed..."; sudo apt-get update -y >/dev/null 2>&1
- 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
- local discovered_path; discovered_path=$(dpkg -L openjdk-21-jre-headless | grep '/bin/java$' | head -n 1)
- if [ -n "$discovered_path" ] && [ -f "$discovered_path" ]; then
- JAVA_EXECUTABLE="$discovered_path"; print_success "Java 21 executable located."
- else
- print_error "Java installed, but executable could not be located."; exit 1
- fi
- fi
- }
- # --- SOFTWARE INSTALLATION FUNCTIONS ---
- update_paper() {
- if is_server_running; then print_error "Server is running! Stop it before updating."; return; fi
- print_info "Checking for PaperMC updates..."; mkdir -p "$SERVER_DIR"
- local latest_version; latest_version=$(curl -sfL "$CURL_USER_AGENT" https://api.papermc.io/v2/projects/paper | jq -r '.versions[-1]')
- local version_url="https://api.papermc.io/v2/projects/paper/versions/${latest_version}"
- local latest_build; latest_build=$(curl -sfL "$CURL_USER_AGENT" "$version_url" | jq -r '.builds[-1]')
- local build_url="${version_url}/builds/${latest_build}"
- local jar_name; jar_name=$(curl -sfL "$CURL_USER_AGENT" "$build_url" | jq -r '.downloads.application.name')
- if download_file "${build_url}/downloads/${jar_name}" "$SERVER_DIR/$SERVER_JAR_NAME"; then
- print_success "PaperMC is up to date."
- else
- print_error "Could not update PaperMC."; return 1
- fi
- }
- update_geyser() {
- if is_server_running; then print_error "Server is running! Stop it before updating."; return; fi
- print_info "Updating Geyser & Floodgate with verification..."; local PLUGINS_DIR="${SERVER_DIR}/plugins"; mkdir -p "$PLUGINS_DIR"
- # Geyser (GitHub API is primary for stability)
- print_info "Downloading latest Geyser..."
- 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')
- download_file "$geyser_url" "${PLUGINS_DIR}/Geyser-Spigot.jar"
- if [ -f "${PLUGINS_DIR}/Geyser-Spigot.jar" ]; then print_success "Geyser download successful and verified."; else print_error "Geyser download failed verification."; fi
- # Floodgate (GitHub API is primary for stability)
- print_info "Downloading latest Floodgate..."
- 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')
- download_file "$floodgate_url" "${PLUGINS_DIR}/floodgate-spigot.jar"
- if [ -f "${PLUGINS_DIR}/floodgate-spigot.jar" ]; then print_success "Floodgate download successful and verified."; else print_error "Floodgate download failed verification."; fi
- }
- # --- New Interactive Plugin Installer ---
- install_plugins_interactive() {
- if is_server_running; then print_error "Server is running! Stop it before installing plugins."; return; fi
- local PLUGINS_DIR="${SERVER_DIR}/plugins"; mkdir -p "$PLUGINS_DIR"
- declare -A plugins=(
- ["EssentialsX"]="https://ci.essentialsx.net/job/EssentialsX/lastSuccessfulBuild/artifact/target/EssentialsX.jar"
- ["EssentialsX Spawn"]="https://ci.essentialsx.net/job/EssentialsX/lastSuccessfulBuild/artifact/target/EssentialsXSpawn.jar"
- ["EssentialsX Chat"]="https://ci.essentialsx.net/job/EssentialsX/lastSuccessfulBuild/artifact/target/EssentialsXChat.jar"
- ["LuckPerms"]="https://api.luckperms.net/v2/downloader"
- ["WorldEdit"]="https://api.modrinth.com/v2/project/worldedit/version?loaders=%5B%22paper%22%5D"
- ["WorldGuard"]="https://api.modrinth.com/v2/project/worldguard/version?loaders=%5B%22paper%22%5D"
- ["CoreProtect"]="https://api.github.com/repos/PlayPro/CoreProtect/releases/latest"
- ["ProtocolLib"]="https://api.github.com/repos/dmulloy2/ProtocolLib/releases/latest"
- ["ViaVersion"]="https://ci.viaversion.com/job/ViaVersion/lastSuccessfulBuild/api/json"
- ["ViaBackwards"]="https://ci.viaversion.com/job/ViaBackwards/lastSuccessfulBuild/api/json"
- ["Vault"]="https://api.github.com/repos/MilkBowl/Vault/releases/latest"
- ["Dynmap"]="https://api.modrinth.com/v2/project/dynmap/version?loaders=%5B%22paper%22%5D"
- ["ChestShop"]="https://api.github.com/repos/ChestShop-authors/ChestShop-3/releases/latest"
- ["GriefPrevention"]="https://api.github.com/repos/TechFortress/GriefPrevention/releases/latest"
- ["spark"]="https://api.github.com/repos/lucko/spark/releases/latest"
- ["Chunky"]="https://api.modrinth.com/v2/project/chunky/version?loaders=%5B%22paper%22%5D"
- ["GSit"]="https://api.github.com/repos/Gecolay/GSit/releases/latest"
- ["SimpleTPA"]="https://api.github.com/repos/imDalton/SimpleTPA/releases/latest"
- ["PlayerWarps"]="https://api.modrinth.com/v2/project/playerwarps/version?loaders=%5B%22paper%22%5D"
- ["ImageMaps"]="https://api.modrinth.com/v2/project/imagemaps/version?loaders=%5B%22paper%22%5D"
- )
- local plugin_names=("${!plugins[@]}"); local selections=()
- while true; do
- clear; echo -e "${CYAN}--- Interactive Plugin Installer ---${NC}"
- echo "Enter a number to select/deselect a plugin. Select 'Done' when finished."
- for i in "${!plugin_names[@]}"; do
- local name="${plugin_names[$i]}"; local installed_mark=""
- for s in "${selections[@]}"; do if [[ "$s" == "$name" ]]; then installed_mark="${GREEN} [Selected]${NC}"; fi; done
- printf " %-2s. %-20s %s\n" "$((i+1))" "$name" "$installed_mark"
- done
- echo -e "\n ${GREEN}d. Done Installing${NC}"
- echo -e " ${RED}c. Cancel${NC}"
- read -p "Your choice: " choice
- case "$choice" in
- d|D) break;;
- c|C) selections=(); break;;
- *)
- if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "${#plugin_names[@]}" ]; then
- local selected_name="${plugin_names[$((choice-1))]}"; local found=0
- for i in "${!selections[@]}"; do
- if [[ "${selections[$i]}" == "$selected_name" ]]; then
- unset 'selections[i]'; found=1; break
- fi
- done
- if [ $found -eq 0 ]; then selections+=("$selected_name"); fi
- else
- print_warning "Invalid input. Please try again."; sleep 1
- fi
- ;;
- esac
- done
- if [ ${#selections[@]} -gt 0 ]; then
- print_info "Installing selected plugins..."
- for name in "${selections[@]}"; do
- local api_url="${plugins[$name]}"; local download_url=""
- print_info "Fetching ${name}..."
- if [[ "$api_url" == *modrinth.com* ]]; then
- download_url=$(curl -sfL "$CURL_USER_AGENT" "$api_url" | jq -r '.[0].files[0].url')
- elif [[ "$api_url" == *luckperms.net* ]]; then
- download_url=$(curl -sfL "$CURL_USER_AGENT" "$api_url" | jq -r '.bukkit.url')
- elif [[ "$api_url" == *viaversion.com* ]]; then
- local filename=$(curl -sfL "$CURL_USER_AGENT" "$api_url" | jq -r '.artifacts[0].fileName')
- download_url="${api_url//\/api\/json/}/artifact/build/libs/$filename"
- elif [[ "$api_url" == *essentialsx.net* ]]; then
- download_url="$api_url" # Direct download link
- elif [[ "$api_url" == *github.com* ]]; then
- download_url=$(curl -sfL "$CURL_USER_AGENT" "$api_url" | jq -r '.assets[] | select(.name | endswith(".jar")) | .browser_download_url' | head -n 1)
- fi
- if [ -n "$download_url" ]; then
- local filename; filename=$(basename "$download_url"); filename="${filename// /_}" # Sanitize filename
- download_file "$download_url" "${PLUGINS_DIR}/${name}.jar"
- else
- print_error "Could not determine download URL for ${name} from API."
- fi
- done
- print_success "Plugin installation complete."
- fi
- }
- install_server() {
- print_info "Starting new Minecraft Server installation..."; install_and_find_java
- mkdir -p "$SERVER_DIR"; cd "$SERVER_DIR" || exit
- update_paper || exit 1
- print_info "Starting server once to generate eula.txt..."
- "$JAVA_EXECUTABLE" -Xmx1024M -Xms1024M -jar "$SERVER_JAR_NAME" nogui > /dev/null 2>&1
- if [ ! -f "eula.txt" ]; then print_error "EULA file not created."; exit 1; fi
- sed -i 's/eula=false/eula=true/g' eula.txt
- print_success "EULA accepted. Server is ready."
- print_warning "Use the menu to install plugins and Geyser as needed."
- }
- uninstall_script() {
- print_error "!!! DANGER ZONE - UNINSTALL !!!"; print_warning "This option will permanently delete the following:"
- echo -e " - Server Directory: ${RED}${SERVER_DIR}${NC}\n - Backups Directory: ${RED}${BACKUP_DIR}${NC}\n - This Script: ${RED}$(realpath "$0")${NC}"
- read -p "Type 'uninstall' to confirm: " confirm
- if [[ "$confirm" != "uninstall" ]]; then print_info "Uninstall cancelled."; return; fi
- print_info "Proceeding with uninstallation..."; if is_server_running; then stop_server; fi
- print_info "Deleting server directory..."; rm -rf "$SERVER_DIR"
- print_info "Deleting backups directory..."; rm -rf "$BACKUP_DIR"
- print_success "Uninstallation complete. This script will now delete itself."; echo "Goodbye!"; rm -- "$0"; exit 0
- }
- # --- MENU-DRIVEN FUNCTIONS ---
- is_server_running() { screen -list | grep -q "$SCREEN_NAME"; }
- 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; }
- 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; }
- 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"; }
- 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}"; }
- edit_properties() { if [ -f "$SERVER_DIR/server.properties" ]; then nano "$SERVER_DIR/server.properties"; else print_error "server.properties not found!"; fi; }
- 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; }
- main_menu() {
- while true; do
- clear
- echo -e "${CYAN}================================================${NC}"
- echo -e "${YELLOW} Minecraft Server Manager (Definitive) ${NC}"
- echo -e "${CYAN}================================================${NC}"
- echo -e "Status: $(if is_server_running; then echo -e "${GREEN}RUNNING${NC}"; else echo -e "${RED}STOPPED${NC}"; fi)"
- echo -e "\n --- Server Control ---"
- echo -e " ${GREEN}1.${NC} Start Server ${BLUE}2.${NC} View Console"
- echo -e " ${RED}3.${NC} Stop Server ${BLUE}4.${NC} View Live Log"
- echo -e "\n --- Software Management ---"
- echo -e " ${CYAN}5.${NC} Update Paper Jar"
- echo -e " ${CYAN}6.${NC} Install Popular Plugins (Interactive Menu)"
- echo -e " ${CYAN}7.${NC} Install/Update Geyser & Floodgate"
- echo -e "\n --- File Management & Uninstallation ---"
- echo -e " ${YELLOW}8.${NC} Edit server.properties ${YELLOW}9.${NC} Create Backup"
- echo -e " ${BLUE}10.${NC} Clean Install Server ${RED}11.${NC} UNINSTALL EVERYTHING"
- echo -e " ${RED}12.${NC} Exit\n"
- read -p "Enter your choice [1-12]: " choice
- case $choice in
- 1) start_server;; 2) console;; 3) stop_server;; 4) view_log;;
- 5) update_paper;; 6) install_plugins_interactive;; 7) update_geyser;;
- 8) edit_properties;; 9) backup_server;;
- 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;;
- 11) uninstall_script;;
- 12) if is_server_running; then print_warning "Stop server before exiting."; else break; fi;;
- *) print_warning "Invalid option.";;
- esac
- if [[ "$choice" != "11" ]]; then echo -e "\n${YELLOW}Press [Enter] to return to the menu...${NC}"; read -p ""; fi
- done
- print_success "Goodbye!"
- }
- # --- Script Entry Point ---
- if [[ $EUID -eq 0 ]]; then print_error "This script must not be run as root."; exit 1; fi
- run_system_diagnostics
- main_menu
Advertisement
Advertisement