diff --git a/apps/Minecraft Java Server/install b/apps/Minecraft Java Server/install index 45279762af..3db38bceea 100755 --- a/apps/Minecraft Java Server/install +++ b/apps/Minecraft Java Server/install @@ -1,9 +1,55 @@ #!/bin/bash +# Pi-Apps style installer for Minecraft Java Server with custom server directory support. +# Default server folder: $HOME/Minecraft-Java-Server +# Service name: minecraft-server +# Screen session: Minecraft_Server +# Desktop launcher: Minecraft Java Server + status "This script helps you setup a Minecraft-Java-Server (for any version)." -#allow user to skip installation if executed from an update -if [[ "$1" == "update" ]];then +SERVICE_NAME="minecraft-server" +SCREEN_NAME="Minecraft_Server" +SERVER_FOLDER_NAME="Minecraft-Java-Server" +DESKTOP_FILE="$HOME/.local/share/applications/Minecraft-Java-Server.desktop" +CONFIG_DIR="$HOME/.config/minecraft-java-server" +CONFIG_FILE="$CONFIG_DIR/server_dir" +DEFAULT_SERVER_DIR="$HOME/$SERVER_FOLDER_NAME" + + +ensure_server_dir() { + # Create the server folder. External drives under /media sometimes need sudo for + # folder creation, but the Minecraft server still needs the regular user to be + # able to write inside the final folder. + mkdir -p "$server_dir" 2>/dev/null || sudo mkdir -p "$server_dir" || error "Could not create server directory: $server_dir" + + # Best effort ownership/permissions. On exFAT these may be ignored, because + # ownership comes from mount options, but this helps on ext4 and normal folders. + sudo chown "$USER":"$USER" "$server_dir" 2>/dev/null || true + sudo chmod 775 "$server_dir" 2>/dev/null || true + + if ! touch "$server_dir/.write-test" 2>/dev/null; then + # If the folder was created with sudo on an external drive, try once more to + # create a test file and then hand it to the user. If the user still cannot + # write, the mount permissions need fixed outside this installer. + sudo touch "$server_dir/.write-test" 2>/dev/null || true + sudo chown "$USER":"$USER" "$server_dir/.write-test" 2>/dev/null || true + sudo rm -f "$server_dir/.write-test" 2>/dev/null || true + + touch "$server_dir/.write-test" 2>/dev/null || error "The chosen server directory exists, but is not writable by $USER: +$server_dir + +Try this outside Pi-Apps: +touch \"$server_dir/test-write.txt\" + +If that fails too, the external drive is mounted without write access for $USER." + fi + + rm -f "$server_dir/.write-test" +} + +# allow user to skip installation if executed from an update +if [[ "$1" == "update" ]]; then description="The Minecraft Java Server script was executed as part of an update to improve platform support.\nWould you like to check for newer versions of Minecraft to upgrade your server to or exit now?" userinput_func "$description" "Yes, check for newer Minecraft versions and upgrade my install" "No, leave my install alone and exit now" if [ "$output" == "No, leave my install alone and exit now" ]; then @@ -15,14 +61,116 @@ fi # add jq to use in script install_packages jq || exit 1 -mkdir -p "$HOME/Minecraft-Java-Server" -cd "$HOME/Minecraft-Java-Server" || error "Could not enter Minecraft-Java-Server folder." +choose_server_directory() { + # On update, reuse the previously chosen directory when possible. + if [[ "$1" == "update" ]] && [ -f "$CONFIG_FILE" ]; then + server_dir="$(head -n 1 "$CONFIG_FILE")" + if [ -n "$server_dir" ]; then + status "Using existing Minecraft server folder:\n$server_dir" + ensure_server_dir + return + fi + fi + + description="Where do you want your Minecraft Java Server files to live?\n\nThis installer defaults to:\n$DEFAULT_SERVER_DIR\n\nYou can also choose an already-mounted drive path, such as /media/$USER/MySSD/$SERVER_FOLDER_NAME.\n\nThe selected folder will be used for server files, start-server.sh, the desktop launcher, and the systemd service." + + table=("Home folder: $DEFAULT_SERVER_DIR") + + # Add currently mounted user media drives, if any. This does not permanently mount them. + if [ -d "/media/$USER" ]; then + while IFS= read -r mount_dir; do + [ -d "$mount_dir" ] || continue + candidate="$mount_dir/$SERVER_FOLDER_NAME" + table+=("Mounted drive: $candidate") + done < <(find "/media/$USER" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort) + fi + + table+=("Custom path") + + userinput_func "$description" "${table[@]}" + selected_choice="$output" + + case "$selected_choice" in + "Home folder: "*) + server_dir="$DEFAULT_SERVER_DIR" + ;; + "Mounted drive: "*) + server_dir="${selected_choice#Mounted drive: }" + ;; + "Custom path") + server_dir=$(yad --center --wrap \ + --text "Enter the full path where the Minecraft server should be installed.\n\nExamples:\n$HOME/$SERVER_FOLDER_NAME\n/media/$USER/MySSD/$SERVER_FOLDER_NAME\n\nAvoid spaces in the path." \ + --entry \ + --entry-text "$DEFAULT_SERVER_DIR" \ + --button="Use this path":0) + ;; + *) + server_dir="$DEFAULT_SERVER_DIR" + ;; + esac + + if [ -z "$server_dir" ]; then + error "No server directory was selected. Exiting." + fi + + # Expand leading ~ if user typed it. + server_dir="${server_dir/#\~/$HOME}" + + # Normalize path if readlink supports -m. + if command -v readlink >/dev/null 2>&1; then + server_dir="$(readlink -m "$server_dir")" + fi + + # Keep this simple and safe for systemd paths. + if [[ "$server_dir" =~ [[:space:]] ]]; then + error "The chosen path contains spaces:\n$server_dir\n\nPlease choose a path without spaces." + fi + + + ensure_server_dir + + mkdir -p "$CONFIG_DIR" + printf '%s\n' "$server_dir" > "$CONFIG_FILE" + + status "Minecraft server files will be installed in:\n$server_dir" +} + +choose_server_directory "$1" + +ensure_server_dir +cd "$server_dir" || error "Could not enter Minecraft server folder." description="What type of Minecraft server\ndo you want to run?" table=("Vanilla" "Fabric" "Forge" "Paper" "Folia" "Purpur") userinput_func "$description" "${table[@]}" server_category="$output" +# Helper for PaperMC Fill v3 downloads. +# PaperMC documents Fill v3 at fill.papermc.io and recommends stable builds. +download_fill_project() { + PROJECT="$1" + VERSION="$2" + builds_response=$(wget -O- "https://fill.papermc.io/v3/projects/${PROJECT}/versions/${VERSION}/builds") || error "Could not fetch $PROJECT builds for $VERSION." + + # Prefer the newest STABLE build. If none exists, fall back to the newest build so Folia/experimental projects can still be tested. + build_json=$(echo "$builds_response" | jq -c '([.[] | select(.channel == "STABLE")] | last) // (last)') + + if [ -z "$build_json" ] || [ "$build_json" = "null" ]; then + error "No $PROJECT build was found for version $VERSION." + fi + + server_jar="$(echo "$build_json" | jq -r '.downloads."server:default".name')" + download_url="$(echo "$build_json" | jq -r '.downloads."server:default".url')" + channel="$(echo "$build_json" | jq -r '.channel // "unknown"')" + + if [ -z "$server_jar" ] || [ "$server_jar" = "null" ] || [ -z "$download_url" ] || [ "$download_url" = "null" ]; then + error "Could not determine $PROJECT server jar or download URL for $VERSION." + fi + + status "Selected $PROJECT $VERSION build channel: $channel" + wget -O "$server_jar" "$download_url" || exit 1 +} + case "$server_category" in "Fabric") available_versions=$(wget -O- https://meta.fabricmc.net/v2/versions/game | jq -r '.[] | select( .stable == true ) | .version') @@ -61,9 +209,7 @@ case "$server_category" in userinput_func "$description" "${table[@]}" server_version="$output" status "Selected $server_category version $server_version" - version_info=$(wget -O- https://fill.papermc.io/v3/projects/paper/versions/$server_version/builds/latest | jq -r '.downloads."server:default"') - server_jar="$(echo "$version_info" | jq -r '.name')" - wget -O "$server_jar" "$(echo "$version_info" | jq -r '.url')" || exit 1 + download_fill_project "paper" "$server_version" ;; "Folia") available_versions=$(wget -O- https://fill.papermc.io/v3/projects/folia/versions | jq -r '.versions.[].version.id') @@ -72,9 +218,7 @@ case "$server_category" in userinput_func "$description" "${table[@]}" server_version="$output" status "Selected $server_category version $server_version" - version_info=$(wget -O- https://fill.papermc.io/v3/projects/folia/versions/$server_version/builds/latest | jq -r '.downloads."server:default"') - server_jar="$(echo "$version_info" | jq -r '.name')" - wget -O "$server_jar" "$(echo "$version_info" | jq -r '.url')" || exit 1 + download_fill_project "folia" "$server_version" ;; "Purpur") available_versions=$(wget -O- https://api.purpurmc.org/v2/purpur/ | jq -r '.versions | .[]') @@ -103,9 +247,9 @@ case "$server_category" in ;; esac -while [[ ! -a $(echo ${server_jar}) ]]; do +while [[ ! -a "$server_jar" ]]; do description="Please use the website which has opened to download your chosen version of the Minecraft Java Server.\ - \nAdd your ${server_jar} file to the folder opened $HOME/Minecraft-Java-Server and press OK.\ + \nAdd your ${server_jar} file to the folder opened $server_dir and press OK.\ \n\nYou have not added a ${server_jar}." table=("OK") userinput_func "$description" "${table[@]}" @@ -206,10 +350,10 @@ case "$java_selection" in "Java 21") if package_available temurin-21-jre ;then install_packages temurin-21-jre || exit 1 + java_location="/usr/lib/jvm/temurin-21-jre-$dpkg_arch/bin/java" else error "Java 21 is not available for your platform from Adoptium. Exiting the script." fi - java_location="/usr/lib/jvm/temurin-21-jre-$dpkg_arch/bin/java" ;; "Java 25") if package_available temurin-25-jre ;then @@ -237,13 +381,10 @@ done if [[ "$server_category" == "Forge" ]]; then # run the server installer rm -f start-server.sh - $java_location -jar $HOME/Minecraft-Java-Server/${server_jar} --installServer || error "Failed to install forge server" + $java_location -jar "$server_dir/${server_jar}" --installServer || error "Failed to install forge server" rm -f ${server_jar} # forge has MANY ways to launch depending on the version - # for some reason they like re-inventing the wheel and doing things differently than everyone else - # the below covers all the ways I have seen from 1.6.4 - 1.18.1 - # its possible I missed an obscure format that was only used once... please create an issue if that is the case if [[ -f "minecraftforge-universal-$installer_version.jar" ]]; then server_jar="minecraftforge-universal-$installer_version.jar" elif [[ -f "forge-$installer_version-universal.jar" ]]; then @@ -254,7 +395,6 @@ if [[ "$server_category" == "Forge" ]]; then server_jar="forge-$installer_version.jar" elif [[ -f "run.sh" ]]; then # set server_jar to empty to skip start-server.sh universal script creation - # this is the most up to date forge launch format where their own run.sh file is provided server_jar="" sed -i "s:java:${java_location}:g" run.sh sed -i 's:"$@":nogui:g' run.sh @@ -267,60 +407,70 @@ if [[ "$server_category" == "Forge" ]]; then fi if [ -n "$server_jar" ]; then - sh -c "cat > $HOME/Minecraft-Java-Server/start-server.sh << _EOF_ + cat > "$server_dir/start-server.sh" < eula.txt +echo "eula=true" > "$server_dir/eula.txt" # make mods directory -mkdir mods +mkdir -p "$server_dir/mods" mkdir -p "$HOME/.local/share/applications" -tee "$HOME/.local/share/applications/Minecraft-Java-Server.desktop" < Games -> Minecraft Java Server -Attach to a server in the background with: screen -r Minecraft_Server +Attach to a server in the background with: screen -r $SCREEN_NAME Detach from a running server session with: CTRL+A then D -To start from a terminal: sudo systemctl start minecraft-server -To stop the server: sudo systemctl stop minecraft-server -To start now, start on boot, and stop on shutdown: sudo systemctl enable --now minecraft-server +To start from a terminal: sudo systemctl start $SERVICE_NAME +To stop the server: sudo systemctl stop $SERVICE_NAME +To start now, start on boot, and stop on shutdown: sudo systemctl enable --now $SERVICE_NAME " diff --git a/apps/Minecraft Java Server/uninstall b/apps/Minecraft Java Server/uninstall index bb4d9ae7b2..48ab8e8a57 100755 --- a/apps/Minecraft Java Server/uninstall +++ b/apps/Minecraft Java Server/uninstall @@ -2,19 +2,83 @@ shopt -s extglob -#skip uninstallation if executed from an update -if [[ "$1" != "update" ]];then - text="You chose to uninstall the Minecraft Java Server script.\nAll files within your $HOME/Minecraft-Java-Server folder will be deleted except the worlds, mods, and versions subfolders.\nIf you would like to backup any other files/folders do that NOW before clicking the prompt." +SERVICE_NAME="minecraft-server" +SCREEN_NAME="Minecraft_Server" +SERVER_FOLDER_NAME="Minecraft-Java-Server" +DESKTOP_FILE="$HOME/.local/share/applications/Minecraft-Java-Server.desktop" +CONFIG_DIR="$HOME/.config/minecraft-java-server" +CONFIG_FILE="$CONFIG_DIR/server_dir" +DEFAULT_SERVER_DIR="$HOME/$SERVER_FOLDER_NAME" + +get_server_dir() { + if [ -f "$CONFIG_FILE" ]; then + head -n 1 "$CONFIG_FILE" + return + fi + + if systemctl cat "$SERVICE_NAME.service" >/dev/null 2>&1; then + systemctl cat "$SERVICE_NAME.service" 2>/dev/null | sed -n 's/^WorkingDirectory=//p' | tail -n 1 + return + fi + + if [ -f "$DESKTOP_FILE" ]; then + sed -n 's/^Path=//p' "$DESKTOP_FILE" | tail -n 1 + return + fi + + echo "$DEFAULT_SERVER_DIR" +} + +# skip uninstallation if executed from an update +if [[ "$1" != "update" ]]; then + server_dir="$(get_server_dir)" + + if [ -z "$server_dir" ]; then + server_dir="$DEFAULT_SERVER_DIR" + fi + + text="You chose to uninstall the Minecraft Java Server script. + +The detected server folder is: +$server_dir + +Files within this folder will be deleted except the world, world_nether, world_the_end, mods, plugins, and versions subfolders. + +If you would like to backup any other files/folders do that NOW before clicking the prompt." userinput_func "$text" "Ok, I have made any backups I wish, continue" - cd $HOME/Minecraft-Java-Server && rm -rf !(world|mods|versions) - rm -rf $HOME/.local/share/applications/Minecraft-Java-Server.desktop || error "Failed to remove the desktop file" + # disable server from starting on boot and stop - sudo systemctl disable --now minecraft-server - + sudo systemctl disable --now "$SERVICE_NAME" 2>/dev/null || true + + # make sure any leftover screen session is closed + screen -S "$SCREEN_NAME" -X quit 2>/dev/null || true + # remove the systemd service - sudo rm -f /etc/systemd/system/minecraft-server.service + sudo rm -f "/etc/systemd/system/$SERVICE_NAME.service" sudo systemctl daemon-reload - + + rm -f "$DESKTOP_FILE" || error "Failed to remove the desktop file" + + if [ -d "$server_dir" ]; then + find "$server_dir" -mindepth 1 -maxdepth 1 -print0 | while IFS= read -r -d '' item; do + base="$(basename "$item")" + case "$base" in + world|world_nether|world_the_end|mods|plugins|versions) + echo "Preserving: $item" + ;; + *) + echo "Removing: $item" + rm -rf -- "$item" 2>/dev/null || sudo rm -rf -- "$item" + ;; + esac + done + else + echo "Server folder not found: $server_dir" + fi + + rm -f "$CONFIG_FILE" + rmdir "$CONFIG_DIR" 2>/dev/null || true + purge_packages || error "Dependencies failed to uninstall" rm_external_repo "adoptium" remove_repofile_if_unused /etc/apt/sources.list.d/adoptopenjdk.list