{"meta":{"slug":"pterodactyl-curse-forge-generic","name":"Pterodactyl Curse Forge Generic","category":"java","source":"pelican","owner":"pelican-eggs","repo":"minecraft","branch":"main","path":"java/curseforge/egg-pterodactyl-curse-forge-generic.json","rawUrl":"https://raw.githubusercontent.com/pelican-eggs/minecraft/main/java/curseforge/egg-pterodactyl-curse-forge-generic.json","indexedAt":1792246788483},"egg":{"_comment":"DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO","meta":{"version":"PTDL_v2","update_url":"https://raw.githubusercontent.com/pelican-eggs/minecraft/refs/heads/main/java/curseforge/egg-pterodactyl-curse-forge-generic.json"},"exported_at":"2025-05-27T11:05:16-07:00","name":"CurseForge Generic","author":"contact@chromozone.dev","description":"A generic egg for a CurseForge modpack.","features":["eula","java_version","pid_limit"],"docker_images":{"Java 8":"ghcr.io/pelican-eggs/yolks:java_8","Java 11":"ghcr.io/pelican-eggs/yolks:java_11","Java 16":"ghcr.io/pelican-eggs/yolks:java_16","Java 17":"ghcr.io/pelican-eggs/yolks:java_17","Java 21":"ghcr.io/pelican-eggs/yolks:java_21","Java 25":"ghcr.io/pelican-eggs/yolks:java_25"},"file_denylist":[],"startup":"java $([[ -f user_jvm_args.txt ]] && printf %s \"@user_jvm_args.txt\") -Xms128M -Xmx{{SERVER_MEMORY}}M -Dterminal.jline=false -Dterminal.ansi=true $([[ ! -f unix_args.txt ]] && printf %s \"-jar `cat .serverjar`\" || printf %s \"@unix_args.txt\")","config":{"files":"{\r\n    \"server.properties\": {\r\n        \"parser\": \"properties\",\r\n        \"find\": {\r\n            \"server-ip\": \"0.0.0.0\",\r\n            \"server-port\": \"{{server.build.default.port}}\",\r\n            \"query.port\": \"{{server.build.default.port}}\"\r\n        }\r\n    }\r\n}","startup":"{\r\n    \"done\": \")! For help, type \"\r\n}","logs":"{}","stop":"stop"},"scripts":{"installation":{"script":"#!/bin/bash\r\n# shellcheck disable=SC2155\r\n#\r\n# CurseForge Installation Script\r\n#\r\n# Server Files: /mnt/server\r\n\r\n: \"${SERVER_DIR:=/mnt/server}\"\r\n: \"${PROJECT_ID:=}\"\r\n: \"${VERSION_ID:=}\"\r\n: \"${API_KEY:=}\"\r\n\r\nif [[ ! -d $SERVER_DIR ]]; then\r\n    mkdir -p \"$SERVER_DIR\"\r\nfi\r\n\r\nif ! cd \"$SERVER_DIR\"; then\r\n    echo -e \"Failed to change directory to ${SERVER_DIR}\"\r\n    exit 1\r\nfi\r\n\r\nfunction install_required {\r\n    echo -e \"Installing required packages...\"\r\n    echo -e \"\\tRunning apt update\"\r\n    apt update > /dev/null 2>&1 || { echo \"apt update failed!\"; exit 1; }\r\n    echo -e \"\\tRunning apt install\"\r\n    apt install -y wget jq unzip > /dev/null 2>&1 || { echo \"apt install failed!\"; exit 1; }\r\n}\r\n\r\nCURSEFORGE_API_URL=\"https://api.curseforge.com/v1/mods/\"\r\nCURSEFORGE_API_HEADERS=(\"--header=Accept: application/json\" \"--header=x-api-key: ${API_KEY}\")\r\nCURSEFORGE_CDN_HEADERS=(\"--header=x-api-key: ${API_KEY}\")\r\n\r\nfunction cf_resolve_download_url {\r\n    local pid=$1 fid=$2 project_data=${3:-} response url=\"\" headers status\r\n    headers=$(mktemp) || return 1\r\n    if response=$(wget -q --server-response --timeout=30 --tries=3 \"${CURSEFORGE_API_HEADERS[@]}\" \"${CURSEFORGE_API_URL}${pid}/files/${fid}/download-url\" -O - 2>\"${headers}\"); then\r\n        url=$(printf '%s' \"${response}\" | jq -er '.data | select(type == \"string\" and test(\"^https?://\"))' 2>/dev/null) || url=\"\"\r\n    fi\r\n    status=$(awk '/^[[:space:]]*HTTP\\// { code = $2 } END { print code }' \"${headers}\")\r\n    rm -f \"${headers}\"\r\n\r\n    if [[ -n \"${url}\" ]]; then\r\n        printf '%s\\n' \"${url}\"\r\n        return 0\r\n    fi\r\n\r\n    if [[ -z \"${project_data}\" ]]; then\r\n        project_data=$(wget -q --timeout=30 --tries=3 \"${CURSEFORGE_API_HEADERS[@]}\" \"${CURSEFORGE_API_URL}${pid}\" -O -)\r\n    fi\r\n    if printf '%s' \"${project_data}\" | jq -e '.data.allowModDistribution == false' >/dev/null 2>&1; then\r\n        echo \"ERROR: Project ${pid} has disabled third-party distribution through the CurseForge API.\" >&2\r\n        echo \"       Its files must be downloaded manually from CurseForge.\" >&2\r\n    else\r\n        echo \"ERROR: Could not retrieve a download URL for project ${pid}, file ${fid} (HTTP ${status:-unavailable}).\" >&2\r\n    fi\r\n    return 1\r\n}\r\nfunction get_download {\r\n    echo -e \"Retrieving CurseForge project information...\"\r\n    local PROJECT_DATA=$(wget -q \"${CURSEFORGE_API_HEADERS[@]}\" \"${CURSEFORGE_API_URL}${PROJECT_ID}\" -O -)\r\n    local PROJECT_TITLE=$(echo \"$PROJECT_DATA\" | jq -r '.data.name // empty')\r\n\r\n    if [[ -z \"${PROJECT_DATA}\" ]]; then\r\n        echo -e \"\\tERROR: Failed to retrieve project data for project id '${PROJECT_ID}'\"\r\n        exit 1\r\n    fi\r\n\r\n    local IS_SERVER_PACK=false\r\n\r\n    if [[ -z \"${VERSION_ID}\" || \"${VERSION_ID}\" == \"latest\" ]]; then\r\n        echo -e \"\\tNo file ID specified, using latest file\"\r\n        VERSION_ID=$(echo \"$PROJECT_DATA\" | jq -r '.data.mainFileId // empty')\r\n\r\n        local VERSION_SERVER_PACK=\"$(echo -e \"${PROJECT_DATA}\" | jq -r --arg VERSION_ID \"$VERSION_ID\" '.data.latestFiles[] | select(.id|tostring==$VERSION_ID) | .isServerPack')\"\r\n        local VERSION_SERVER_ID=\"$(echo -e \"${PROJECT_DATA}\" | jq -r --arg VERSION_ID \"$VERSION_ID\" '.data.latestFiles[] | select(.id|tostring==$VERSION_ID) | .serverPackFileId | select(. != null and . != 0)')\"\r\n\r\n        if [[ \"${VERSION_SERVER_PACK}\" == \"false\" && -n \"${VERSION_SERVER_ID}\" ]]; then\r\n            echo -e \"\\tFound server pack file id '${VERSION_SERVER_ID}'\"\r\n            VERSION_ID=$VERSION_SERVER_ID\r\n            IS_SERVER_PACK=true\r\n        elif [[ \"${VERSION_SERVER_PACK}\" == \"true\" ]]; then\r\n            IS_SERVER_PACK=true\r\n        fi\r\n    else\r\n        echo -e \"\\tChecking if provided file id '${VERSION_ID}' exists\"\r\n\r\n        local FILE_DATA=$(wget -q \"${CURSEFORGE_API_HEADERS[@]}\" \"${CURSEFORGE_API_URL}${PROJECT_ID}/files/${VERSION_ID}\" -O -)\r\n\r\n        if [[ -z \"${FILE_DATA}\" ]]; then\r\n            echo -e \"\\tERROR: File id '${VERSION_ID}' not found for project '${PROJECT_TITLE}'\"\r\n            exit 1\r\n        fi\r\n\r\n        IS_SERVER_PACK=$(echo -e \"${FILE_DATA}\" | jq -r '.data.isServerPack // \"false\"')\r\n\r\n        if [[ \"${IS_SERVER_PACK}\" == \"false\" ]]; then\r\n            local VERSION_SERVER_PACK=\"$(echo -e \"${FILE_DATA}\" | jq -r '.data.serverPackFileId | select(. != null and . != 0)')\"\r\n            if [[ -n \"${VERSION_SERVER_PACK}\" ]]; then\r\n                echo -e \"\\tFound server pack file id '${VERSION_SERVER_PACK}'\"\r\n                VERSION_ID=$VERSION_SERVER_PACK\r\n                IS_SERVER_PACK=true\r\n            fi\r\n        else\r\n            IS_SERVER_PACK=true\r\n        fi\r\n    fi\r\n\r\n    # Check if version id is unset or empty string\r\n    if [[ -z \"${VERSION_ID}\" ]]; then\r\n        echo -e \"\\tERROR: No file id found for project '${PROJECT_TITLE}'\"\r\n        exit 1\r\n    fi\r\n\r\n    if [[ \"${IS_SERVER_PACK}\" == \"false\" ]]; then\r\n        echo -e \"\\tWARNING: File id '${VERSION_ID}' is not a server pack, attempting to use client files\"\r\n    fi\r\n\r\n    # get json data to work with\r\n    echo -e \"\\tRetrieving version information for '${VERSION_ID}'\"\r\n    local DOWNLOAD_URL\r\n    if ! DOWNLOAD_URL=$(cf_resolve_download_url \"${PROJECT_ID}\" \"${VERSION_ID}\" \"${PROJECT_DATA}\"); then\r\n        exit 1\r\n    fi\r\n\r\n    # download modpack files\r\n    echo -e \"\\tDownloading ${DOWNLOAD_URL}\"\r\n    if ! wget -q \"${CURSEFORGE_CDN_HEADERS[@]}\" \"${DOWNLOAD_URL}\" -O server.zip; then\r\n        echo -e \"Download failed!\"\r\n        exit 1\r\n    fi\r\n}\r\n\r\nfunction parse_loader_manifest {\r\n    local MANIFEST=\"$1\"\r\n    echo -e \"\\tParsing loader manifest\"\r\n\r\n    if ! LOADER_ID=$(jq -er '[.minecraft.modLoaders[]? | select(.primary == true) | .id] | select(length == 1) | .[0] | select(type == \"string\")' \"${MANIFEST}\") || [[ ! \"${LOADER_ID}\" =~ ^[a-z]+-[[:alnum:]][[:alnum:].+_-]*$ ]]; then\r\n        echo -e \"\\tERROR: Expected one primary loader with a valid name and version in manifest!\"\r\n        exit 1\r\n    fi\r\n    LOADER_NAME=${LOADER_ID%%-*}\r\n    LOADER_VERSION=${LOADER_ID#*-}\r\n\r\n    case \"${LOADER_NAME}\" in\r\n        forge|fabric|neoforge|quilt) ;;\r\n        *)\r\n            echo \"ERROR: Unsupported loader '${LOADER_NAME}' in manifest\"\r\n            exit 1\r\n            ;;\r\n    esac\r\n\r\n    MINECRAFT_VERSION=$(jq -r '.minecraft.version // empty' \"${MANIFEST}\")\r\n\r\n    if [[ -z \"${MINECRAFT_VERSION}\" ]]; then\r\n        echo -e \"\\tERROR: No minecraft version found in manifest!\"\r\n        exit 1\r\n    fi\r\n\r\n    echo -e \"\\tFound loader ${LOADER_NAME} ${LOADER_VERSION} for Minecraft ${MINECRAFT_VERSION}\"\r\n}\r\nfunction get_loader {\r\n    echo -e \"Retrieving loader information...\"\r\n\r\n    local PROJECT_DATA=$(wget -q \"${CURSEFORGE_API_HEADERS[@]}\" \"${CURSEFORGE_API_URL}${PROJECT_ID}\" -O -)\r\n    local PROJECT_TITLE=$(echo \"$PROJECT_DATA\" | jq -r '.data.name // empty')\r\n    if [[ -z \"${PROJECT_DATA}\" ]]; then\r\n        echo -e \"\\tERROR: Failed to retrieve project data for project id '${PROJECT_ID}'\"\r\n        exit 1\r\n    fi\r\n\r\n    local FILE_DATA=$(wget -q \"${CURSEFORGE_API_HEADERS[@]}\" \"${CURSEFORGE_API_URL}${PROJECT_ID}/files/${VERSION_ID}\" -O -)\r\n\r\n    if [[ -z \"${FILE_DATA}\" ]]; then\r\n        echo -e \"\\tERROR: File id '${VERSION_ID}' not found for project '${PROJECT_TITLE}'\"\r\n        exit 1\r\n    fi\r\n\r\n    local IS_SERVER_PACK=$(echo -e \"${FILE_DATA}\" | jq -r '.data.isServerPack // \"false\"')\r\n    local CLIENT_VERSION_ID;\r\n\r\n    if [[ \"${IS_SERVER_PACK}\" == \"true\" ]]; then\r\n        CLIENT_VERSION_ID=\"$(echo -e \"${FILE_DATA}\" | jq -r '.data.parentProjectFileId // empty')\"\r\n    else\r\n        CLIENT_VERSION_ID=$VERSION_ID\r\n    fi\r\n\r\n    if [[ -z \"${CLIENT_VERSION_ID}\" ]]; then\r\n        echo -e \"\\tERROR: File id '${VERSION_ID}' not found for project '${PROJECT_TITLE}'\"\r\n        exit 1\r\n    fi\r\n\r\n    echo -e \"\\tRetrieving file information for '${CLIENT_VERSION_ID}'\"\r\n    local DOWNLOAD_URL\r\n    if ! DOWNLOAD_URL=$(cf_resolve_download_url \"${PROJECT_ID}\" \"${CLIENT_VERSION_ID}\" \"${PROJECT_DATA}\"); then\r\n        exit 1\r\n    fi\r\n\r\n    # download modpack files\r\n    echo -e \"\\tDownloading ${DOWNLOAD_URL}\"\r\n    if ! wget -q \"${CURSEFORGE_CDN_HEADERS[@]}\" \"${DOWNLOAD_URL}\" -O client.zip; then\r\n        echo -e \"\\tERROR: Client manifest download failed!\"\r\n        exit 1\r\n    fi\r\n\r\n    echo -e \"\\tUnpacking client manifest\"\r\n    if ! unzip -p client.zip manifest.json > \"${SERVER_DIR}/client.manifest.json\" 2>/dev/null || [[ ! -s \"${SERVER_DIR}/client.manifest.json\" ]]; then\r\n        echo \"ERROR: Could not extract a nonempty manifest.json from the downloaded client archive.\"\r\n        echo \"This egg requires a CurseForge manifest to determine the mod loader.\"\r\n        echo \"If the archive only contains an FTB installer, use the FTB egg instead.\"\r\n        exit 1\r\n    fi\r\n    rm -f client.zip\r\n\r\n    parse_loader_manifest \"${SERVER_DIR}/client.manifest.json\"\r\n}\r\n\r\nfunction unzip-strip() (\r\n    set -u\r\n\r\n    local archive=$1\r\n    local destdir=${2:-}\r\n    shift; shift || :\r\n    echo -e \"\\tUnpacking ${archive} to ${destdir}\"\r\n\r\n    echo -e \"\\tCreating temporary directory\"\r\n    local tmpdir=/mnt/server/tmp\r\n    if ! mkdir -p \"${tmpdir}\"; then\r\n        echo -e \"\\tERROR: mkdir failed to create temporary directory\"\r\n        return 1\r\n    fi\r\n\r\n    trap 'rm -rf -- \"$tmpdir\"' EXIT\r\n\r\n    echo -e \"\\tUnpacking archive\"\r\n\r\n    if ! unzip -q \"$archive\" -d \"$tmpdir\"; then\r\n        echo -e \"\\tERROR: unzip failed to unpack archive\"\r\n        return 1\r\n    fi\r\n\r\n    echo -e \"\\tSetting glob settings\"\r\n\r\n    shopt -s dotglob\r\n\r\n    echo -e \"\\tCleaning up directory structure\"\r\n\r\n    local files=(\"$tmpdir\"/*) name i=1\r\n\r\n    if (( ${#files[@]} == 1 )) && [[ -d \"${files[0]}\" ]]; then\r\n        name=$(basename \"${files[0]}\")\r\n        files=(\"$tmpdir\"/*/*)\r\n    else\r\n        name=$(basename \"$archive\"); name=${archive%.*}\r\n        files=(\"$tmpdir\"/*)\r\n    fi\r\n\r\n    if [[ -z \"$destdir\" ]]; then\r\n        destdir=./\"$name\"\r\n    fi\r\n\r\n    while [[ -f \"$destdir\" ]]; do\r\n        destdir=${destdir}-$((i++));\r\n    done\r\n\r\n    echo -e \"\\tCopying files to ${destdir}\"\r\n\r\n    mkdir -p \"$destdir\"\r\n    if ! cp -ar \"$@\" -t \"$destdir\" -- \"${files[@]}\"; then\r\n        echo -e \"\\tERROR: Failed to copy unpacked files\"\r\n        return 1\r\n    fi\r\n    rm -rf \"$tmpdir\"\r\n)\r\n\r\nfunction unpack_zip {\r\n    echo -e \"Unpacking server files...\"\r\n    if ! unzip-strip server.zip \"${SERVER_DIR}\"; then\r\n        echo -e \"\\tERROR: Failed to unpack server.zip\"\r\n        exit 1\r\n    fi\r\n    rm -rf server.zip\r\n}\r\n\r\nfunction json_download_mods() (\r\n    set -o pipefail\r\n    echo \"Downloading mods...\"\r\n\r\n    local MANIFEST=\"${SERVER_DIR}/manifest.json\"\r\n    jq -c '.files[]? | select(.required == true) | {project: .projectID, file: .fileID}' \"${MANIFEST}\" | while read -r mod; do\r\n        local MOD_PROJECT_ID=$(echo \"${mod}\" | jq -r '.project // empty')\r\n        local MOD_FILE_ID=$(echo \"${mod}\" | jq -r '.file // empty')\r\n\r\n        if [[ -z \"${MOD_PROJECT_ID}\" || -z \"${MOD_FILE_ID}\" ]]; then\r\n            echo -e \"\\tERROR: Failed to parse project id or file id for mod '${mod}'\"\r\n            exit 1\r\n        fi\r\n\r\n        local FILE_URL\r\n        if ! FILE_URL=$(cf_resolve_download_url \"${MOD_PROJECT_ID}\" \"${MOD_FILE_ID}\"); then\r\n            echo\r\n            echo \"ERROR: Required mod ${MOD_PROJECT_ID} (file ${MOD_FILE_ID}) cannot be downloaded.\"\r\n            echo \"       Automatic installation is incomplete.\"\r\n            echo\r\n            exit 1\r\n        fi\r\n\r\n        echo -e \"\\tDownloading ${FILE_URL}\"\r\n\r\n        if ! wget -q -nc \"${CURSEFORGE_CDN_HEADERS[@]}\" \"${FILE_URL}\" -P \"${SERVER_DIR}/mods\"; then\r\n            echo -e \"\\tERROR: Failed to download mod ${MOD_PROJECT_ID} ${MOD_FILE_ID}\"\r\n            exit 1\r\n        fi\r\n    done\r\n)\r\n\r\nfunction json_download_overrides {\r\n    echo \"Copying overrides...\"\r\n    local OVERRIDES SERVER_ROOT SOURCE_DIR\r\n    if ! OVERRIDES=$(jq -er '.overrides // \"overrides\" | select(type == \"string\")' \"${SERVER_DIR}/manifest.json\"); then\r\n        echo \"ERROR: Invalid overrides directory in manifest\"\r\n        return 1\r\n    fi\r\n    [[ -z \"${OVERRIDES}\" ]] && return 0\r\n\r\n    SERVER_ROOT=$(realpath -e -- \"${SERVER_DIR}\") || return 1\r\n    SOURCE_DIR=$(realpath -m -- \"${SERVER_ROOT}/${OVERRIDES}\") || return 1\r\n    if [[ \"${OVERRIDES}\" == /* || \"/${OVERRIDES}/\" == */../* || \"${SOURCE_DIR}\" != \"${SERVER_ROOT}/\"* ]]; then\r\n        echo \"ERROR: Overrides must be a directory within the server directory\"\r\n        return 1\r\n    fi\r\n    if [[ ! -e \"${SOURCE_DIR}\" ]]; then\r\n        return 0\r\n    fi\r\n    if [[ ! -d \"${SOURCE_DIR}\" ]]; then\r\n        echo \"ERROR: Overrides path is not a directory\"\r\n        return 1\r\n    fi\r\n    if ! cp -a -- \"${SOURCE_DIR}/.\" \"${SERVER_ROOT}/\"; then\r\n        echo \"ERROR: Failed to copy overrides; the source directory has been kept\"\r\n        return 1\r\n    fi\r\n    rm -r -- \"${SOURCE_DIR}\"\r\n}\r\n\r\nFORGE_INSTALLER_URL=\"https://maven.minecraftforge.net/net/minecraftforge/forge/\"\r\n\r\nfunction json_download_forge {\r\n    echo \"Downloading Forge...\"\r\n\r\n    local MC_VERSION=$MINECRAFT_VERSION\r\n    local FORGE_VERSION=$LOADER_VERSION\r\n\r\n    FORGE_VERSION=\"${MC_VERSION}-${FORGE_VERSION}\"\r\n    if [[ \"${MC_VERSION}\" == \"1.7.10\" || \"${MC_VERSION}\" == \"1.8.9\" ]]; then\r\n        FORGE_VERSION=\"${FORGE_VERSION}-${MC_VERSION}\"\r\n    fi\r\n\r\n    local FORGE_JAR=\"forge-${FORGE_VERSION}.jar\"\r\n    if [[ \"${MC_VERSION}\" == \"1.7.10\" ]]; then\r\n        FORGE_JAR=\"forge-${FORGE_VERSION}-universal.jar\"\r\n    fi\r\n\r\n    local FORGE_URL=\"${FORGE_INSTALLER_URL}${FORGE_VERSION}/forge-${FORGE_VERSION}\"\r\n\r\n    echo -e \"\\tUsing Forge ${FORGE_VERSION} from ${FORGE_URL}\"\r\n\r\n    local FORGE_INSTALLER=\"${FORGE_URL}-installer.jar\"\r\n    echo -e \"\\tDownloading Forge Installer ${FORGE_VERSION} from ${FORGE_INSTALLER}\"\r\n\r\n    if ! wget -q -O forge-installer.jar \"${FORGE_INSTALLER}\"; then\r\n        echo -e \"\\tERROR: Failed to download Forge Installer ${FORGE_VERSION}\"\r\n        exit 1\r\n    fi\r\n\r\n    # Remove old Forge files so we can safely update\r\n    rm -rf libraries/net/minecraftforge/forge/\r\n    rm -f unix_args.txt\r\n\r\n    echo -e \"\\tInstalling Forge Server ${FORGE_VERSION}\"\r\n    if ! java -jar forge-installer.jar --installServer > /dev/null 2>&1; then\r\n        echo -e \"\\tERROR: Failed to install Forge Server ${FORGE_VERSION}\"\r\n        exit 1\r\n    fi\r\n\r\n    if [[ $MC_VERSION =~ ^1\\.(17|18|19|20|21|22|23) || $FORGE_VERSION =~ ^1\\.(17|18|19|20|21|22|23) ]]; then\r\n        echo -e \"\\tDetected Forge 1.17 or newer version. Setting up Forge Unix arguments\"\r\n        ln -sf libraries/net/minecraftforge/forge/*/unix_args.txt unix_args.txt\r\n    else\r\n        mv \"$FORGE_JAR\" forge-server-launch.jar\r\n        echo \"forge-server-launch.jar\" > \".serverjar\"\r\n    fi\r\n\r\n    rm -f forge-installer.jar\r\n}\r\n\r\nFABRIC_INSTALLER_URL=\"https://meta.fabricmc.net/v2/versions/installer\"\r\n\r\nfunction json_download_fabric {\r\n    echo \"Downloading Fabric...\"\r\n\r\n    local MC_VERSION=$MINECRAFT_VERSION\r\n    local FABRIC_VERSION=$LOADER_VERSION\r\n\r\n    local INSTALLER_JSON=$(wget -q -O - ${FABRIC_INSTALLER_URL} )\r\n    local INSTALLER_VERSION=$(echo \"$INSTALLER_JSON\" | jq -r '.[0].version // empty')\r\n    local INSTALLER_URL=$(echo \"$INSTALLER_JSON\" | jq -r '.[0].url // empty')\r\n\r\n    if [[ -z \"${INSTALLER_VERSION}\" ]]; then\r\n        echo -e \"\\tERROR: No Fabric installer version found\"\r\n        exit 1\r\n    fi\r\n\r\n    if [[ -z \"${INSTALLER_URL}\" ]]; then\r\n        echo -e \"\\tERROR: No Fabric installer url found\"\r\n        exit 1\r\n    fi\r\n\r\n    echo -e \"\\tDownloading Fabric Installer ${MC_VERSION}-${FABRIC_VERSION} (${INSTALLER_VERSION}) from ${INSTALLER_URL}\"\r\n\r\n    if ! wget -q -O fabric-installer.jar \"${INSTALLER_URL}\"; then\r\n        echo -e \"\\tERROR: Failed to download Fabric Installer ${MC_VERSION}-${FABRIC_VERSION} (${INSTALLER_VERSION})\"\r\n        exit 1\r\n    fi\r\n\r\n    echo -e \"\\tInstalling Fabric Server ${MC_VERSION}-${FABRIC_VERSION} (${INSTALLER_VERSION})\"\r\n    if ! java -jar fabric-installer.jar server -mcversion \"${MC_VERSION}\" -loader \"${FABRIC_VERSION}\" -downloadMinecraft; then\r\n        echo -e \"\\tERROR: Failed to install Fabric Server ${MC_VERSION}-${FABRIC_VERSION} (${INSTALLER_VERSION})\"\r\n        exit 1\r\n    fi\r\n\r\n    echo \"fabric-server-launch.jar\" > \".serverjar\"\r\n\r\n    rm -f fabric-installer.jar\r\n}\r\n\r\nQUILT_INSTALLER_URL=\"https://meta.quiltmc.org/v3/versions/installer\"\r\n\r\nfunction json_download_quilt {\r\n    echo \"Downloading Quilt...\"\r\n\r\n    local MC_VERSION=$MINECRAFT_VERSION\r\n    local QUILT_VERSION=$LOADER_VERSION\r\n\r\n    local INSTALLER_JSON=$(wget -q -O - ${QUILT_INSTALLER_URL} )\r\n    local INSTALLER_VERSION=$(echo \"$INSTALLER_JSON\" | jq -r '.[0].version // empty')\r\n    local INSTALLER_URL=$(echo \"$INSTALLER_JSON\" | jq -r '.[0].url // empty')\r\n\r\n    if [[ -z \"${INSTALLER_VERSION}\" ]]; then\r\n        echo -e \"\\tERROR: No Quilt installer version found\"\r\n        exit 1\r\n    fi\r\n\r\n    if [[ -z \"${INSTALLER_URL}\" ]]; then\r\n        echo -e \"\\tERROR: No Quilt installer URL found\"\r\n        exit 1\r\n    fi\r\n\r\n    echo -e \"\\tDownloading Quilt Installer ${MC_VERSION}-${QUILT_VERSION} (${INSTALLER_VERSION}) from ${INSTALLER_URL}\"\r\n\r\n    if ! wget -q -O quilt-installer.jar \"${INSTALLER_URL}\"; then\r\n        echo -e \"\\tERROR: Failed to download Quilt Installer ${MC_VERSION}-${QUILT_VERSION} (${INSTALLER_VERSION})\"\r\n        exit 1\r\n    fi\r\n\r\n    echo -e \"\\tInstalling Quilt Server ${MC_VERSION}-${QUILT_VERSION} (${INSTALLER_VERSION})\"\r\n    if ! java -jar quilt-installer.jar install server \"${MC_VERSION}\" \"${QUILT_VERSION}\" --download-server --install-dir=./; then\r\n        echo -e \"\\tERROR: Failed to install Quilt Server ${MC_VERSION}-${QUILT_VERSION} (${INSTALLER_VERSION})\"\r\n        exit 1\r\n    fi\r\n\r\n    echo \"quilt-server-launch.jar\" > \".serverjar\"\r\n\r\n    rm quilt-installer.jar\r\n}\r\n\r\nNEOFORGE_DOWNLOAD_URL=\"https://maven.neoforged.net/releases/net/neoforged/\"\r\n\r\nfunction json_download_neoforge {\r\n    echo \"Downloading neoforge\"\r\n    \r\n    local MC_VERSION=$MINECRAFT_VERSION\r\n    local NEOFORGE_VERSION=$LOADER_VERSION\r\n    \r\n    if [[ -n ${NEOFORGE_VERSION} ]]; then\r\n        # The 1.20.1 release lives in a different repository and is called \"forge\" instead of \"neoforge\"\r\n        if [[ \"${NEOFORGE_VERSION}\" == 1.20.1-* ]]; then\r\n            DOWNLOAD_LINK=$NEOFORGE_DOWNLOAD_URL/forge/${NEOFORGE_VERSION}/forge-${NEOFORGE_VERSION}\r\n            ARTIFACT_NAME=\"forge\"\r\n        else\r\n            DOWNLOAD_LINK=$NEOFORGE_DOWNLOAD_URL/neoforge/${NEOFORGE_VERSION}/neoforge-${NEOFORGE_VERSION}\r\n            ARTIFACT_NAME=\"neoforge\"\r\n        fi\r\n    else\r\n        echo \"No Neoforge version specified in manifest\"\r\n        exit 1\r\n    fi\r\n    \r\n    echo \"Downloading NeoForge version ${NEOFORGE_VERSION}\"\r\n    echo \"Download link is ${DOWNLOAD_LINK}\"\r\n\r\n    # Check if the download link we generated is valid\r\n    if [[ -n \"${DOWNLOAD_LINK}\" ]]; then\r\n        if wget -q --spider \"${DOWNLOAD_LINK}-installer.jar\"; then\r\n            echo -e \"Installer jar download link is valid.\"\r\n        else\r\n            echo -e \"Link is invalid. Exiting now\"\r\n            exit 2\r\n        fi\r\n    else\r\n        echo -e \"No download link provided. Exiting now\"\r\n        exit 3\r\n    fi\r\n    \r\n    # If so, go ahead and download the installer\r\n    wget -O installer.jar \"${DOWNLOAD_LINK}-installer.jar\"\r\n    \r\n    if [[ ! -f ./installer.jar ]]; then\r\n        echo \"!!! Error downloading NeoForge version ${NEOFORGE_VERSION} !!!\"\r\n        exit 4\r\n    fi\r\n    \r\n    # Delete args to support downgrading/upgrading\r\n    rm -rf libraries/net/neoforged/${ARTIFACT_NAME}\r\n    rm unix_args.txt\r\n\r\n    # Installing server\r\n    echo -e \"Installing NeoForge server.\\n\"\r\n    java -jar installer.jar --installServer || {\r\n        echo -e \"\\nInstall failed using NeoForge version ${NEOFORGE_VERSION} and Minecraft version ${MINECRAFT_VERSION}.\"\r\n        echo -n   \"Should you be using unlimited memory value of 0, make sure to increase the default install resource limits in the Wings\"\r\n        echo      \"config or specify exact allocated memory in the server Build Configuration instead of 0!\"\r\n        echo      \"Otherwise, the NeoForge installer will not have enough memory.\";\r\n        exit 5;\r\n        }\r\n\r\n    # Symlink the startup arguments to the server directory\r\n    ln -sf libraries/net/neoforged/${ARTIFACT_NAME}/*/unix_args.txt unix_args.txt\r\n\r\n    # And finally clean up\r\n    echo -e \"Deleting installer.jar file.\\n\"\r\n    rm -rf installer.jar\r\n}\r\n\r\nfunction validate_startup {\r\n    if [[ -s \"${SERVER_DIR}/unix_args.txt\" ]]; then\r\n        return 0\r\n    fi\r\n\r\n    local SERVER_JAR\r\n    if [[ -f \"${SERVER_DIR}/.serverjar\" ]]; then\r\n        SERVER_JAR=$(cat \"${SERVER_DIR}/.serverjar\")\r\n        # The startup command expects a relative JAR path without whitespace.\r\n        if [[ -n \"${SERVER_JAR}\" && \"${SERVER_JAR}\" != /* && \"${SERVER_JAR}\" != *[[:space:]]* && \"/${SERVER_JAR}/\" != */../* && -s \"${SERVER_DIR}/${SERVER_JAR}\" ]]; then\r\n            return 0\r\n        fi\r\n    fi\r\n\r\n    echo \"ERROR: No usable server startup target was found.\"\r\n    echo \"The server needs a nonempty unix_args.txt, or a .serverjar file\"\r\n    echo \"containing the relative path to its server JAR (without spaces).\"\r\n    echo \"For an uploaded ZIP, include these files or a supported manifest.json.\"\r\n    exit 1\r\n}\r\n\r\ninstall_required\r\n\r\nif [[ -z \"${PROJECT_ID}\" ]]; then\r\n    echo \"ERROR: You must specify a PROJECT_ID environment variable!\"\r\n    exit 1\r\nfi\r\n\r\nif [[ \"${PROJECT_ID}\" == \"zip\" ]]; then\r\n    if [[ ! -f \"${SERVER_DIR}/server.zip\" ]]; then\r\n        echo -e \"\\tERROR: PROJECT_ID is 'zip', but ${SERVER_DIR}/server.zip was not found\"\r\n        exit 1\r\n    fi\r\n\r\n    unpack_zip\r\n\r\n    if [[ -f \"${SERVER_DIR}/manifest.json\" ]]; then\r\n        parse_loader_manifest \"${SERVER_DIR}/manifest.json\"\r\n    else\r\n        echo -e \"\\tNo manifest.json found; checking prepared server startup files\"\r\n        validate_startup\r\n    fi\r\nelse\r\n    get_download\r\n    get_loader\r\n    unpack_zip\r\nfi\r\n\r\nif [[ -f \"${SERVER_DIR}/manifest.json\" ]]; then\r\n    echo \"Found manifest.json, installing mods\"\r\n    json_download_mods || exit 1\r\n    json_download_overrides || exit 1\r\nfi\r\n\r\nif [[ -n \"${LOADER_NAME:-}\" ]]; then\r\n    echo -e \"\\nLoader name: ${LOADER_NAME}\"\r\n\r\n    if [[ $LOADER_NAME == \"forge\" ]]; then\r\n        json_download_forge\r\n    fi\r\n\r\n    if [[ $LOADER_NAME == \"fabric\" ]]; then\r\n        json_download_fabric\r\n    fi\r\n    \r\n    if [[ $LOADER_NAME == \"neoforge\" ]]; then\r\n        json_download_neoforge\r\n    fi\r\n\r\n    if [[ $LOADER_NAME == \"quilt\" ]]; then\r\n        json_download_quilt\r\n    fi\r\nfi\r\n\r\nvalidate_startup\r\necho -e \"\\nInstall completed successfully, enjoy!\"","container":"ghcr.io/pelican-eggs/installers:java_8","entrypoint":"bash"}},"variables":[{"name":"Modpack Project ID","description":"The modpack project ID from the CurseForge site on the pack page, or 'zip' if installing from an uploaded server.zip file.\r\n\r\nFor example, the project ID of https://www.curseforge.com/minecraft/modpacks/bofa-mods is 375152","env_variable":"PROJECT_ID","default_value":"","user_viewable":true,"user_editable":true,"rules":"required|string","field_type":"text"},{"name":"Modpack File ID","description":"The file ID belonging to a specific version of a modpack. Can either be the main file or the file ID associated with the server pack.\r\n\r\nIf not provided the script will attempt to retrieve the latest server pack or client pack available.","env_variable":"VERSION_ID","default_value":"","user_viewable":true,"user_editable":true,"rules":"string","field_type":"text"},{"name":"CurseForge API Key","description":"A CurseForge API key is required to use this egg. This key is not visible to the server owner. You can get an API key here: https://console.curseforge.com","env_variable":"API_KEY","default_value":"","user_viewable":false,"user_editable":false,"rules":"required|string|max:60","field_type":"text"}]},"readme":"# CurseForge Generic\n\n## This is a generic egg for CurseForge modpacks\n\nYou will need to give it a modpack project ID. The project ID for [All the Mods 8 - ATM8](https://www.curseforge.com/minecraft/modpacks/all-the-mods-8) is `520914` for example.\nThis can be found on the modpack page in the `About Project` section in the right sidebar.\n\nYou can also optionally specify a file ID. If you do not specify a file ID, the latest version will be used. \nThe file ID for the server pack for [All the Mods 8 - ATM8](https://www.curseforge.com/minecraft/modpacks/all-the-mods-8) version `1.0.17` is `4504876` for example. \nThis can be found on the modpack page by clicking the wanted file and copying the id at the end of the URL (the number after `/files/`).\n\nThe script will automatically setup of Forge, Fabric, or Quilt depending on the modpack.\n\nYou *must* specify a CurseForge API key. \nYou can obtain an API key by creating a developer account [here](https://console.curseforge.com/) and then clicking on the \"API keys\" tab.\n\n## Installing an uploaded ZIP\n\nSet the project ID to `zip` and upload `server.zip` before reinstalling. The archive can contain a CurseForge `manifest.json`, including one exported by Packwiz, or an already prepared server. Native Packwiz `pack.toml` files are not supported.\n\nA prepared server must include a nonempty `unix_args.txt`, or a `.serverjar` file containing the relative path to its server JAR without spaces. For example, a ZIP with `server.jar` should also contain `.serverjar` with `server.jar` as its contents. A ZIP with neither a supported manifest nor usable startup files fails installation.\n\nManifest installs support Forge, Fabric, NeoForge, and Quilt. All required mods must download successfully; a missing required mod fails the installation.\n\n## Server Ports\n\nThe minecraft server requires a single port for access (default 25565) but plugins may require extra ports to enabled for the server.\n\n| Port  | Default |\n|-------|---------|\n| Game  | 25565   |\n"}