diff --git a/.github/workflows/build-multiarch.yml b/.github/workflows/build-multiarch.yml index 7f7ea7a1..07e911c9 100644 --- a/.github/workflows/build-multiarch.yml +++ b/.github/workflows/build-multiarch.yml @@ -8,8 +8,7 @@ on: - java11* - java16* - java17* - - test/* - - fix/* + - test/** tags: - "[0-9]+.[0-9]+.[0-9]+" - "[0-9]+.[0-9]+.[0-9]+-java8-multiarch" @@ -58,7 +57,7 @@ jobs: - name: Build for test uses: docker/build-push-action@v2.7.0 - if: github.ref_name == 'master' + if: github.ref_name == 'master' || startsWith(github.ref_name, 'test/') with: context: . platforms: linux/amd64 @@ -72,7 +71,7 @@ jobs: - name: Run tests # It is assumed that image variants are merged from master and tested there - if: github.ref_name == 'master' + if: github.ref_name == 'master' || startsWith(github.ref_name, 'test/') run: | tests/test.sh @@ -83,7 +82,7 @@ jobs: context: . # No arm64/armv7 support: https://hub.docker.com/_/ibm-semeru-runtimes?tab=tags&page=1&name=open-17-jdk platforms: linux/amd64 - push: ${{ github.event_name != 'pull_request' }} + push: true tags: ${{ steps.meta.outputs.tags }} # ensure latest base image is used pull: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..5ec5052f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,43 @@ +name: ContinuousIntegration + +on: + push: + branches: + - 'dev*' + - '!master' + - '!java*' + - '!multi*' + paths-ignore: + - "*.md" + - "docs/**" + - "examples/**" + +env: + IMAGE_TO_TEST: ${{ secrets.IMAGE_ORG }}/minecraft-server:test-${{ github.repository_owner }}-${{ github.run_id }} + MODS_FORGEAPI_KEY: ${{ secrets.MODS_FORGEAPI_KEY }} + +jobs: + test: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2.4.0 + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Build + uses: docker/build-push-action@v2.7.0 + with: + context: . + platforms: linux/amd64 + tags: ${{ env.IMAGE_TO_TEST }} + load: true + cache-from: type=gha + + - name: Run Setup Only Tests + run: sh tests/setuponlytests/test.sh + + # - name: Run Full Minecraft Service Tests + # run: | + # tests/fulltests/test.sh diff --git a/.github/workflows/discord.yml b/.github/workflows/discord.yml new file mode 100644 index 00000000..e4e6b42b --- /dev/null +++ b/.github/workflows/discord.yml @@ -0,0 +1,26 @@ +name: discord + +on: + workflow_run: + workflows: ["ContinuousIntegration", "PullRequest", "Build and Publish", "Build and publish multiarch" ] + types: + - completed + +env: + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} + +jobs: + discord: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: on-success + if: ${{ github.event.workflow_run.conclusion == 'success' }} + uses: Ilshidur/action-discord@master + with: + args: "Github repo: ${{ github.repository }}\n- Branch: ${{ github.event.workflow_run.head_branch }}\n- [Link: to Actions](<${{ github.event.workflow_run.html_url }}>)\n- Status: 🎉 ${{ github.event.workflow_run.conclusion }} 🍏" + - name: on-failure + if: ${{ github.event.workflow_run.conclusion == 'failure' }} + uses: Ilshidur/action-discord@master + with: + args: "Github repo: ${{ github.repository }}\n- Branch: ${{ github.event.workflow_run.head_branch }}\n- [Link: to Actions](<${{ github.event.workflow_run.html_url }}>)\n- Status: 🤔 ${{ github.event.workflow_run.conclusion }} 💣💥" diff --git a/.github/workflows/generate-toc.yml b/.github/workflows/generate-toc.yml index d5f6e76c..2f9bb1ab 100644 --- a/.github/workflows/generate-toc.yml +++ b/.github/workflows/generate-toc.yml @@ -16,6 +16,6 @@ jobs: curl https://raw.githubusercontent.com/ekalinin/github-markdown-toc/master/gh-md-toc -o gh-md-toc chmod a+x gh-md-toc ./gh-md-toc --insert --no-backup README.md - - uses: stefanzweifel/git-auto-commit-action@v4.12.0 + - uses: stefanzweifel/git-auto-commit-action@v4.13.1 with: commit_message: "docs: Auto update markdown TOC" \ No newline at end of file diff --git a/.github/workflows/issue-label.yml b/.github/workflows/issue-label.yml new file mode 100644 index 00000000..4d4df210 --- /dev/null +++ b/.github/workflows/issue-label.yml @@ -0,0 +1,17 @@ +on: + issues: + types: [labeled] + +env: + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} + +jobs: + labelNotify: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: notify + if: github.event.label.name == 'enhancement' || github.event.label.name == 'bug' + uses: Ilshidur/action-discord@master + with: + args: "[${{ github.event.issue.title }}](<${{ github.event.issue.html_url }}>) added `${{ github.event.label.name }}` label" diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 0dfce114..79521468 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -1,12 +1,17 @@ -name: Validate PR +name: PullRequest on: pull_request: branches: [ master ] types: [assigned, opened, synchronize, labeled] + paths-ignore: + - "*.md" + - "docs/**" + - "examples/**" env: IMAGE_TO_TEST: itzg/minecraft-server:test-${{ github.repository_owner }}-${{ github.run_id }} + MODS_FORGEAPI_KEY: ${{ secrets.MODS_FORGEAPI_KEY }} jobs: test: diff --git a/.github/workflows/stale-check.yml b/.github/workflows/stale-check.yml index 8aa6e41b..cd4459c2 100644 --- a/.github/workflows/stale-check.yml +++ b/.github/workflows/stale-check.yml @@ -21,5 +21,5 @@ jobs: Please add a comment describing the reason to keep this issue open. days-before-stale: 30 days-before-close: 5 - exempt-issue-labels: 'enhancement,keep,status/needs triage' + exempt-issue-labels: 'enhancement,keep,status/needs triage,priority/high' diff --git a/.gitignore b/.gitignore index 574e0d91..11fd3002 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ +.vscode /data/ /.idea/ *.iml -/gh-md-toc \ No newline at end of file +/gh-md-toc diff --git a/Dockerfile b/Dockerfile index d9d2f774..bcc809ad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,23 +5,23 @@ LABEL org.opencontainers.image.authors="Geoff Bourne " RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive \ apt-get install -y \ - imagemagick \ - gosu \ - sudo \ - net-tools \ - iputils-ping \ - curl wget \ - git \ - jq \ - dos2unix \ - mysql-client \ - tzdata \ - rsync \ - nano \ - unzip \ - knockd \ - ttf-dejavu \ - && apt-get clean + imagemagick \ + gosu \ + sudo \ + net-tools \ + iputils-ping \ + curl \ + git \ + jq \ + dos2unix \ + mysql-client \ + tzdata \ + rsync \ + nano \ + unzip \ + knockd \ + ttf-dejavu \ + && apt-get clean RUN addgroup --gid 1000 minecraft \ && adduser --system --shell /bin/false --uid 1000 --ingroup minecraft --home /data minecraft @@ -45,26 +45,26 @@ RUN easy-add --var os=${TARGETOS} --var arch=${TARGETARCH}${TARGETVARIANT} \ --from https://github.com/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz RUN easy-add --var os=${TARGETOS} --var arch=${TARGETARCH}${TARGETVARIANT} \ - --var version=1.5.1 --var app=rcon-cli --file {{.app}} \ - --from https://github.com/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz + --var version=1.5.1 --var app=rcon-cli --file {{.app}} \ + --from https://github.com/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz RUN easy-add --var os=${TARGETOS} --var arch=${TARGETARCH}${TARGETVARIANT} \ - --var version=0.10.3 --var app=mc-monitor --file {{.app}} \ - --from https://github.com/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz + --var version=0.10.3 --var app=mc-monitor --file {{.app}} \ + --from https://github.com/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz RUN easy-add --var os=${TARGETOS} --var arch=${TARGETARCH}${TARGETVARIANT} \ - --var version=1.8.0 --var app=mc-server-runner --file {{.app}} \ - --from https://github.com/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz + --var version=1.8.0 --var app=mc-server-runner --file {{.app}} \ + --from https://github.com/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz RUN easy-add --var os=${TARGETOS} --var arch=${TARGETARCH}${TARGETVARIANT} \ - --var version=0.1.1 --var app=maven-metadata-release --file {{.app}} \ - --from https://github.com/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz + --var version=0.1.1 --var app=maven-metadata-release --file {{.app}} \ + --from https://github.com/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz ARG MC_HELPER_VERSION=1.11.0 ARG MC_HELPER_BASE_URL=https://github.com/itzg/mc-image-helper/releases/download/v${MC_HELPER_VERSION} RUN curl -fsSL ${MC_HELPER_BASE_URL}/mc-image-helper-${MC_HELPER_VERSION}.tgz \ - | tar -C /usr/share -zxf - \ - && ln -s /usr/share/mc-image-helper-${MC_HELPER_VERSION}/bin/mc-image-helper /usr/bin + | tar -C /usr/share -zxf - \ + && ln -s /usr/share/mc-image-helper-${MC_HELPER_VERSION}/bin/mc-image-helper /usr/bin VOLUME ["/data"] WORKDIR /data diff --git a/README.md b/README.md index c1fe70d6..ddff40dd 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ By default, the container will download the latest version of the "vanilla" [Min **TABLE OF CONTENTS** + * [Mitigated Log4jShell Vulnerability](#mitigated-log4jshell-vulnerability) * [Looking for a Bedrock Dedicated Server](#looking-for-a-bedrock-dedicated-server) * [Interacting with the server](#interacting-with-the-server) * [Data Directory](#data-directory) @@ -45,6 +46,7 @@ By default, the container will download the latest version of the "vanilla" [Min * [Running a Bukkit/Spigot server](#running-a-bukkitspigot-server) * [Running a Paper server](#running-a-paper-server) * [Running an Airplane server](#running-an-airplane-server) + * [Running a Pufferfish server](#running-a-pufferfish-server) * [Running a Purpur server](#running-a-purpur-server) * [Running a Magma server](#running-a-magma-server) * [Running a Mohist server](#running-a-mohist-server) @@ -66,6 +68,7 @@ By default, the container will download the latest version of the "vanilla" [Min * [Optional plugins, mods, and config attach points](#optional-plugins-mods-and-config-attach-points) * [Auto-downloading SpigotMC/Bukkit/PaperMC plugins](#auto-downloading-spigotmcbukkitpapermc-plugins) * [Downloadable mod/plugin pack for Forge, Fabric, and Bukkit-like Servers](#downloadable-modplugin-pack-for-forge-fabric-and-bukkit-like-servers) + * [ForgeAPI usage to use non-version specific projects](#forgeapi-usage-to-use-non-version-specific-projects) * [Generic pack file](#generic-pack-file) * [Mod/Plugin URL Listing File](#modplugin-url-listing-file) * [Remove old mods/plugins](#remove-old-modsplugins) @@ -74,6 +77,7 @@ By default, the container will download the latest version of the "vanilla" [Min * [Cloning world from a container path](#cloning-world-from-a-container-path) * [Overwrite world on start](#overwrite-world-on-start) * [Datapacks](#datapacks) + * [VanillaTweaks](#vanillatweaks) * [Server configuration](#server-configuration) * [Message of the Day](#message-of-the-day) * [Difficulty](#difficulty) @@ -129,6 +133,7 @@ By default, the container will download the latest version of the "vanilla" [Min * [Explicitly disable GUI](#explicitly-disable-gui) * [Stop Duration](#stop-duration) * [Setup only](#setup-only) + * [Enable Flare Flags](#enable-flare-flags) * [Autopause](#autopause) * [Description](#description) * [Enabling Autopause](#enabling-autopause) @@ -136,10 +141,14 @@ By default, the container will download the latest version of the "vanilla" [Min * [Running on RaspberryPi](#running-on-raspberrypi) * [Contributing](#contributing) - + +## Mitigated Log4jShell Vulnerability + +**Please ensure you have pulled the latest image** since [all official mitigations](https://www.minecraft.net/en-us/article/important-message--security-vulnerability-java-edition) are automatically applied by the container startup process. + ## Looking for a Bedrock Dedicated Server For Minecraft clients running on consoles, mobile, or native Windows, you'll need to @@ -283,8 +292,8 @@ When using the image `itzg:/minecraft-server` without a tag, the `latest` image | java8-openj9 | 8 | Debian | OpenJ9 | amd64 | | java11 | 11 | Debian | Hotspot | amd64,arm64,armv7 | | java11-openj9 | 11 | Debian | OpenJ9 | amd64 | -| java16-openj9 | 16 | Debian | OpenJ9 | amd64 | | java17 | 17 | Ubuntu | Hotspot | amd64,arm64,armv7 | +| java17-openj9 | 17 | Debian | OpenJ9 | amd64 | For example, to use Java version 8 on any supported architecture: @@ -302,7 +311,7 @@ The following image tags have been deprecated and are no longer receiving update - adopt15 - openj9-nightly - multiarch-latest -- java16 +- java16/java16-openj9 ## Healthcheck @@ -477,7 +486,20 @@ An [Airplane](https://airplane.gg) server, which is "a stable, optimized, well s Extra variables: - `AIRPLANE_BUILD=lastSuccessfulBuild` : set a specific Airplane build to use - `FORCE_REDOWNLOAD=false` : set to true to force the located server jar to be re-downloaded -- `USE_FLARE_FLAGS=false` : set to true to add appropriate flags for the [Flare](https://blog.airplane.gg/flare) profiler +- `USE_FLARE_FLAGS=false` : set to true to add appropriate flags for the built-in [Flare](https://blog.airplane.gg/flare) profiler + +### Running a Pufferfish server + +A [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) server, which is "a highly optimized Paper fork designed for large servers requiring both maximum performance, stability, and "enterprise" features." + + -e TYPE=PUFFERFISH + +> NOTE: The `VERSION` variable is used to select a Pufferfish branch to download from. The available options are "LATEST" and "1.18" + +Extra variables: +- `PUFFERFISH_BUILD=lastSuccessfulBuild` : set a specific Pufferfish build to use +- `FORCE_REDOWNLOAD=false` : set to true to force the located server jar to be re-downloaded +- `USE_FLARE_FLAGS=false` : set to true to add appropriate flags for the built-in [Flare](https://blog.airplane.gg/flare) profiler ### Running a Purpur server @@ -490,7 +512,7 @@ A [Purpur](https://purpur.pl3x.net/) server, which is "drop-in replacement for P Extra variables: - `PURPUR_BUILD=LATEST` : set a specific Purpur build to use - `FORCE_REDOWNLOAD=false` : set to true to force the located server jar to be re-downloaded -- `USE_FLARE_FLAGS=false` : set to true to add appropriate flags for the [Flare](https://blog.airplane.gg/flare) profiler +- `USE_FLARE_FLAGS=false` : set to true to add appropriate flags for the built-in [Flare](https://blog.airplane.gg/flare) profiler ### Running a Magma server @@ -567,6 +589,8 @@ Configuration options with defaults: - `LIMBO_SCHEMA_FILENAME`=default.schem - `LEVEL`="Default;${LIMBO_SCHEMA_FILENAME}" +> NOTE: instead of using format codes in the MOTD, Limbo requires [JSON chat content](https://minecraft.fandom.com/wiki/Raw_JSON_text_format#Java_Edition). If a plain string is provided, which is the default, then it gets converted into the required JSON structure. + ### Running a Crucible server A [Crucible](https://github.com/CrucibleMC/Crucible) server can be run by setting `TYPE` to `CRUCIBLE`. @@ -721,6 +745,47 @@ You may also download or copy over individual mods using the `MODS` environment docker run -d -e MODS=https://www.example.com/mods/mod1.jar,/plugins/common,/plugins/special/mod2.jar ... +### **ForgeAPI** usage to use non-version specific projects + +**NOTE:** This potentially could lead to unexpected behavior if the Mod recieves an update with unexpected behavior. + +This is more complicated because you will be pulling/using the latest mod for the release of your game. To get started make sure you have a [CursedForge API Key](https://docs.curseforge.com/#getting-started). Then use the environmental parameters in your docker build. + +Parameters to use the ForgeAPI: + +* `MODS_FORGEAPI_KEY` - Required +* `MODS_FORGEAPI_FILE` - Required or use MODS_FORGEAPI_PROJECTIDS (Overrides MODS_FORGEAPI_PROJECTIDS) +* `MODS_FORGEAPI_PROJECTIDS` - Required or use MODS_FORGEAPI_FILE +* `MODS_FORGEAPI_RELEASES` - Default is release, Options: [Release|Beta|Alpha] +* `REMOVE_OLD_FORGEAPI_MODS` - Default is False +* `REMOVE_OLD_DATAPACKS_DEPTH` - Default is 1 +* `REMOVE_OLD_DATAPACKS_INCLUDE` - Default is *.jar + +Example of expected forge api project ids, releases, and key: + +```yaml + MODS_FORGEAPI_PROJECTIDS: 306612,256717 + MODS_FORGEAPI_RELEASES: Release + MODS_FORGEAPI_KEY: $WRX... +``` + +Example of expected ForgeAPI file format: **Note**: name is currently unused, but can be used to document each entry. + +```json +[ + { + "name": "fabric api", + "projectId": "306612", + "releaseType": "release" + }, + { + "name": "fabric voice mod", + "projectId": "416089", + "releaseType": "beta" + } +] +``` + ### Generic pack file To install all of the server content (jars, mods, plugins, configs, etc) from a zip file, such as a CurseForge modpack that is missing a server start script, then set `GENERIC_PACK` to the container path of the zip file. That, combined with `TYPE`, allows for custom content along with container managed server download and install. @@ -804,6 +869,47 @@ Datapacks can be installed in a similar manner to mods/plugins. There are many e * `REMOVE_OLD_DATAPACKS_EXCLUDE` Datapacks will be placed in `/data/$LEVEL/datapacks` +### VanillaTweaks + +VanillaTweaks datapacks can be installed with a share code from the website UI **OR** a json file to specify packs to download and install. + +Accepted Parameters: + +- `VANILLATWEAKS_FILE` +- `VANILLATWEAKS_SHARECODE` +- `REMOVE_OLD_VANILLATWEAKS` +- `REMOVE_OLD_VANILLATWEAKS_DEPTH` +- `REMOVE_OLD_VANILLATWEAKS_INCLUDE` +- `REMOVE_OLD_VANILLATWEAKS_EXCLUDE` + +Example of expected Vanillatweaks sharecode: + +```yaml +VANILLATWEAKS_SHARECODE: MGr52E +``` + +Example of expected Vanillatweaks file format: + +```json +{ + "version": "1.18", + "packs": { + "survival": [ + "graves", + "multiplayer sleep", + "afk display", + "armor statues", + "unlock all recipes", + "fast leaf decay", + "coordinates hud" + ], + "items": ["armored elytra"] + } +} +``` + +Datapacks will be placed in `/data/$LEVEL/datapacks` + ## Server configuration By default, the server configuration will be created and set based on the following environment variables, but only the first time the server is started. If the `server.properties` file already exists, the values in them will not be changed. @@ -1400,10 +1506,6 @@ To enable remote JMX, such as for profiling with VisualVM or JMC, add the enviro When `MEMORY` is greater than or equal to 12G, then the Aikar flags will be adjusted according to the article. -Large page support can also be enabled by adding - - -e USE_LARGE_PAGES=true - ### HTTP Proxy You may configure the use of an HTTP/HTTPS proxy by passing the proxy's URL via the `PROXY` @@ -1428,6 +1530,14 @@ When the container is signalled to stop, the Minecraft process wrapper will atte ### Setup only If you are using a host-attached data directory, then you can have the image setup the Minecraft server files and stop prior to launching the server process by setting `SETUP_ONLY` to `true`. + +### Enable Flare Flags + +To enable the JVM flags required to fully support the [Flare profiling suite](https://blog.airplane.gg/flare), set the following variable: + + -e USE_FLARE_FLAGS=true + +Flare is built-in to Airplane/Pufferfish/Purpur, and is available in [plugin form](https://github.com/TECHNOVE/FlarePlugin) for other server types. ## Autopause @@ -1459,6 +1569,8 @@ Enable the Autopause functionality by setting: Autopause is not compatible with `EXEC_DIRECTLY=true` and the two cannot be set together. +> When configuring kubernetes readiness/liveness health checks with auto-pause enabled, be sure to reference the `mc-health` wrapper script rather than `mc-status` directly. + The following environment variables define the behaviour of auto-pausing: * `AUTOPAUSE_TIMEOUT_EST`, default `3600` (seconds) describes the time between the last client disconnect and the pausing of the process (read as timeout established) diff --git a/docker-versions-create.sh b/docker-versions-create.sh index a5c07e7e..9abdfd22 100755 --- a/docker-versions-create.sh +++ b/docker-versions-create.sh @@ -7,8 +7,8 @@ branches_list=( 'java8-openj9' 'java11' 'java11-openj9' - 'java16-openj9' 'java17' + 'java17-openj9' ) function TrapExit { diff --git a/examples/docker-compose-generic-pack.yml b/examples/docker-compose-generic-pack.yml index 314a8083..dfb2d152 100644 --- a/examples/docker-compose-generic-pack.yml +++ b/examples/docker-compose-generic-pack.yml @@ -13,6 +13,7 @@ services: VERSION: ${VERSION:-1.17.1} FORGEVERSION: ${FORGEVERSION:-37.0.90} GENERIC_PACK: /modpacks/${MODPACK:-Server-Files-0.0.21.zip} + REMOVE_OLD_MODS: "${REMOVE_OLD_MODS:-false}" ports: - "25565:25565" diff --git a/examples/vanilla-tweaks/docker-compose.yml b/examples/vanilla-tweaks/docker-compose.yml new file mode 100644 index 00000000..6adf5dce --- /dev/null +++ b/examples/vanilla-tweaks/docker-compose.yml @@ -0,0 +1,27 @@ +version: "3.3" + +services: + vanillatweaks_file: + restart: "no" + image: itzg/minecraft-server + ports: + - "25565:25565/tcp" + environment: + EULA: "TRUE" + VERSION: ${MINECRAFT_VERSION:-LATEST} + VANILLATWEAKS_FILE: /config/vanillatweaks-datapacks.json + REMOVE_OLD_VANILLATWEAKS: "TRUE" + volumes: + - data:/data + - ./vanillatweaks-datapacks.json:/config/vanillatweaks-datapacks.json:ro + vanillatweaks_sharecode: + # port is set to 25566 to not conflict with vanillatweaks_file example + ports: + - "25566:25565/tcp" + restart: "no" + image: itzg/minecraft-server + environment: + EULA: "TRUE" + VERSION: ${MINECRAFT_VERSION:-LATEST} + VANILLATWEAKS_SHARECODE: MGr52E + REMOVE_OLD_VANILLATWEAKS: "TRUE" diff --git a/examples/vanilla-tweaks/vanillatweaks-datapacks.json b/examples/vanilla-tweaks/vanillatweaks-datapacks.json new file mode 100644 index 00000000..aa19970d --- /dev/null +++ b/examples/vanilla-tweaks/vanillatweaks-datapacks.json @@ -0,0 +1,15 @@ +{ + "version": "1.18", + "packs": { + "survival": [ + "graves", + "multiplayer sleep", + "afk display", + "armor statues", + "unlock all recipes", + "fast leaf decay", + "coordinates hud" + ], + "items": ["armored elytra"] + } +} diff --git a/scripts/start-configuration b/scripts/start-configuration index cfeb0fcb..a52af4f2 100755 --- a/scripts/start-configuration +++ b/scripts/start-configuration @@ -183,6 +183,10 @@ case "${TYPE^^}" in exec ${SCRIPTS:-/}start-deployAirplane "$@" ;; + PUFFERFISH) + exec ${SCRIPTS:-/}start-deployPufferfish "$@" + ;; + CANYON) exec ${SCRIPTS:-/}start-deployCanyon "$@" ;; @@ -205,7 +209,7 @@ case "${TYPE^^}" in log "Invalid type: '$TYPE'" log "Must be: VANILLA, FORGE, BUKKIT, SPIGOT, PAPER, FTBA (multiarch-only)," log " CURSEFORGE, SPONGEVANILLA, PURPUR, CUSTOM," - log " MAGMA, MOHIST, CATSERVER, AIRPLANE, CANYON, LIMBO, CRUCIBLE" + log " MAGMA, MOHIST, CATSERVER, AIRPLANE, PUFFERFISH, CANYON, LIMBO, CRUCIBLE" exit 1 ;; diff --git a/scripts/start-deployAirplane b/scripts/start-deployAirplane index 05e33202..cb36aa81 100755 --- a/scripts/start-deployAirplane +++ b/scripts/start-deployAirplane @@ -14,7 +14,7 @@ fi : ${AIRPLANE_BUILD:=lastSuccessfulBuild} : ${AIRPLANE_TYPE:=airplane} -if [ "${VERSION}" = "LATEST" ] || [ "${VERSION}" = "1.17" ]; then +if [ "${VERSION}" = "LATEST" ] || [ "${VERSION}" = "1.17" ]; then AIRPLANE_BRANCH="1.17" fi @@ -36,16 +36,14 @@ done if [ ! -f "$SERVER" ] || isTrue "${FORCE_REDOWNLOAD:-false}"; then downloadUrl="https://ci.tivy.ca/job/Airplane-${AIRPLANE_BRANCH}/${AIRPLANE_BUILD}/artifact/launcher-${AIRPLANE_TYPE}.jar" log "Downloading Airplane from $downloadUrl ..." - curl -fsSL -o "$SERVER" "$downloadUrl" - if [ ! -f "$SERVER" ]; then + if ! get -o "$SERVER" "$downloadUrl"; then log "ERROR: failed to download from $downloadUrl (status=$?)" exit 3 fi fi # Normalize on Spigot for later operations -export TYPE=SPIGOT export FAMILY=SPIGOT export SKIP_LOG4J_CONFIG=true -exec ${SCRIPTS:-/}start-spiget "$@" +exec "${SCRIPTS:-/}start-spiget" "$@" diff --git a/scripts/start-deployBukkitSpigot b/scripts/start-deployBukkitSpigot index 120da778..a185e98f 100755 --- a/scripts/start-deployBukkitSpigot +++ b/scripts/start-deployBukkitSpigot @@ -126,7 +126,6 @@ else fi # Normalize on Spigot for operations below -export TYPE=SPIGOT export FAMILY=SPIGOT export SKIP_LOG4J_CONFIG=true diff --git a/scripts/start-deployCF b/scripts/start-deployCF index 6a788393..ff2a72e8 100755 --- a/scripts/start-deployCF +++ b/scripts/start-deployCF @@ -35,11 +35,37 @@ FTB_SERVER_MOD=${FTB_SERVER_MOD:-$CF_SERVER_MOD} log "Looking for Feed-The-Beast / CurseForge server modpack." requireVar FTB_SERVER_MOD -if ! isTrue "${USE_MODPACK_START_SCRIPT:-true}"; then - if ! [ -f "${FTB_SERVER_MOD}" ]; then - log "ERROR unable to find requested modpack file ${FTB_SERVER_MOD}" - exit 2 +downloadModpack() { + srv_modpack=${FTB_SERVER_MOD} + if isURL "${srv_modpack}"; then + log "Downloading modpack from ${srv_modpack}..." + if ! srv_modpack=$(get -o /data --output-filename --skip-existing "${srv_modpack}"); then + log "ERROR: failed to download modpack" + exit 1 + fi fi + if [[ "${srv_modpack:0:5}" == "data/" ]]; then + # Prepend with "/" + srv_modpack="/${srv_modpack}" + fi + if [[ ! "${srv_modpack:0:1}" == "/" ]]; then + # If not an absolute path, assume file is in "/data" + srv_modpack=/data/${srv_modpack} + fi + if [[ ! -f "${srv_modpack}" ]]; then + log "FTB server modpack ${srv_modpack} not found." + exit 2 + fi + if [[ ! "${srv_modpack: -4}" == ".zip" ]]; then + log "FTB server modpack ${srv_modpack} is not a zip archive." + log "Please set FTB_SERVER_MOD to a file with a .zip extension." + exit 2 + fi + FTB_SERVER_MOD=${srv_modpack} +} + +if ! isTrue "${USE_MODPACK_START_SCRIPT:-true}"; then + downloadModpack needsInstall=true installMarker=/data/.curseforge-installed @@ -132,32 +158,9 @@ fi # also check for the start script rather than just the folder # this allows saving just the world separate from the rest of the data directory if [[ $startScriptCount = 0 ]]; then + downloadModpack srv_modpack=${FTB_SERVER_MOD} - if isURL "${srv_modpack}"; then - log "Downloading modpack from ${srv_modpack}..." - if ! srv_modpack=$(get -o /data --output-filename --skip-existing "${srv_modpack}"); then - log "ERROR: failed to download modpack" - exit 1 - fi - fi - if [[ "${srv_modpack:0:5}" == "data/" ]]; then - # Prepend with "/" - srv_modpack="/${srv_modpack}" - fi - if [[ ! "${srv_modpack:0:1}" == "/" ]]; then - # If not an absolute path, assume file is in "/data" - srv_modpack=/data/${srv_modpack} - fi - if [[ ! -f "${srv_modpack}" ]]; then - log "FTB server modpack ${srv_modpack} not found." - exit 2 - fi - if [[ ! "${srv_modpack: -4}" == ".zip" ]]; then - log "FTB server modpack ${srv_modpack} is not a zip archive." - log "Please set FTB_SERVER_MOD to a file with a .zip extension." - exit 2 - fi - + log "Unpacking FTB server modpack ${srv_modpack} ..." mkdir -p "${FTB_BASE_DIR}" unzip -o "${srv_modpack}" -d "${FTB_BASE_DIR}" | awk '{printf "."} END {print ""}' diff --git a/scripts/start-deployCanyon b/scripts/start-deployCanyon index 0aa17331..dab75946 100755 --- a/scripts/start-deployCanyon +++ b/scripts/start-deployCanyon @@ -43,7 +43,6 @@ if [ ! -f "$SERVER" ]; then fi # Normalize on Spigot for later operations -export TYPE=SPIGOT export FAMILY=SPIGOT export SKIP_LOG4J_CONFIG=true diff --git a/scripts/start-deployPaper b/scripts/start-deployPaper index 69406b3c..fe606dcc 100755 --- a/scripts/start-deployPaper +++ b/scripts/start-deployPaper @@ -73,7 +73,6 @@ else fi # Normalize on Spigot for downstream operations -export TYPE=SPIGOT export FAMILY=SPIGOT export SKIP_LOG4J_CONFIG=true diff --git a/scripts/start-deployPufferfish b/scripts/start-deployPufferfish new file mode 100755 index 00000000..0b0ea52c --- /dev/null +++ b/scripts/start-deployPufferfish @@ -0,0 +1,45 @@ +#!/bin/bash + +. ${SCRIPTS:-/}start-utils +set -euo pipefail +isDebugging && set -x + +IFS=$'\n\t' + +if [ "${VERSION}" != "LATEST" ] && [ "${VERSION}" != "1.18" ] ; then + log "ERROR: Pufferfish server type only supports VERSION=LATEST, VERSION=1.18. Note that these are branches, not #.#.# versions." + exit 1 +fi + +: ${PUFFERFISH_BUILD:=lastSuccessfulBuild} + +if [ "${VERSION}" = "LATEST" ] || [ "${VERSION}" = "1.18" ]; then + PUFFERFISH_BRANCH="1.18" + PUFFERFISH_VERSION="1.18.1-R0.1" +fi + +log "Using Pufferfish-${PUFFERFISH_BRANCH} branch" + +export SERVER=pufferfish-${PUFFERFISH_BRANCH}-${PUFFERFISH_BUILD}.jar + +log "Removing old Pufferfish versions ..." +shopt -s nullglob +for f in pufferfish-*.jar; do + [[ $f != $SERVER ]] && rm $f +done + +if [ ! -f "$SERVER" ] || isTrue "${FORCE_REDOWNLOAD:-false}"; then + artifact="build/libs/pufferfish-paperclip-${PUFFERFISH_VERSION}-SNAPSHOT-reobf.jar" + downloadUrl="https://ci.pufferfish.host/job/Pufferfish-${PUFFERFISH_BRANCH}/${PUFFERFISH_BUILD}/artifact/${artifact}" + log "Downloading Pufferfish from $downloadUrl ..." + if ! get -o "$SERVER" "$downloadUrl"; then + log "ERROR: failed to download from $downloadUrl (status=$?)" + exit 3 + fi +fi + +# Normalize on Spigot for later operations +export FAMILY=SPIGOT +export SKIP_LOG4J_CONFIG=true + +exec ${SCRIPTS:-/}start-spiget "$@" diff --git a/scripts/start-deployPurpur b/scripts/start-deployPurpur index e8b38424..156920b8 100755 --- a/scripts/start-deployPurpur +++ b/scripts/start-deployPurpur @@ -2,7 +2,8 @@ set -euo pipefail IFS=$'\n\t' -. ${SCRIPTS:-/}start-utils +# shellcheck source=start-utils +. "${SCRIPTS:-/}start-utils" isDebugging && set -x : ${VANILLA_VERSION:?} @@ -10,12 +11,10 @@ isDebugging && set -x : ${FORCE_REDOWNLOAD:=false} if [[ ${PURPUR_BUILD} == LATEST ]]; then - PURPUR_BUILD=$(curl -fsSL "https://api.purpurmc.org/v2/purpur/${VANILLA_VERSION}" | - jq -r '.builds.latest' || echo "") - if [[ -z ${PURPUR_BUILD} ]]; then + if ! PURPUR_BUILD=$(get --json-path=".builds.latest" "https://api.purpurmc.org/v2/purpur/${VANILLA_VERSION}"); then log "ERROR: Failed to locate a Purpur build for ${VANILLA_VERSION}." log " Please check if a download is available at https://purpur.pl3x.net/downloads/" - exit 3 + exit 1 fi fi @@ -24,15 +23,14 @@ export SERVER="purpur-${VANILLA_VERSION}-${PURPUR_BUILD}.jar" if [ ! -f "$SERVER" ] || isTrue "$FORCE_REDOWNLOAD"; then downloadUrl="https://api.purpurmc.org/v2/purpur/${VANILLA_VERSION}/${PURPUR_BUILD}/download" log "Downloading Purpur from $downloadUrl ..." - if ! curl -fsSL -o "$SERVER" "$downloadUrl"; then + if ! get -o "$SERVER" "$downloadUrl"; then log "ERROR: failed to download from $downloadUrl (status=$?)" exit 3 fi fi # Normalize on Spigot for later operations -export TYPE=SPIGOT export FAMILY=SPIGOT export SKIP_LOG4J_CONFIG=true -exec ${SCRIPTS:-/}start-spiget "$@" +exec "${SCRIPTS:-/}start-spiget" "$@" diff --git a/scripts/start-finalExec b/scripts/start-finalExec index 8e684d97..e5178446 100755 --- a/scripts/start-finalExec +++ b/scripts/start-finalExec @@ -128,6 +128,11 @@ elif isFamily VANILLA && versionLessThan 1.12; then patchLog4jConfig log4j2_17-111.xml https://launcher.mojang.com/v1/objects/dd2b723346a8dcd48e7f4d245f6bf09e98db9696/log4j2_17-111.xml elif isFamily VANILLA && versionLessThan 1.17; then patchLog4jConfig log4j2_112-116.xml https://launcher.mojang.com/v1/objects/02937d122c86ce73319ef9975b58896fc1b491d1/log4j2_112-116.xml +# See https://purpurmc.org/docs/Log4j/ +elif isType PURPUR && versionLessThan 1.17; then + patchLog4jConfig purpur_log4j2_1141-1165.xml https://purpurmc.org/docs/xml/purpur_log4j2_1141-1165.xml +elif isType PURPUR && versionLessThan 1.18.1; then + patchLog4jConfig purpur_log4j2_117.xml https://purpurmc.org/docs/xml/purpur_log4j2_117.xml elif versionLessThan 1.18.1; then JVM_OPTS="-Dlog4j2.formatMsgNoLookups=true ${JVM_OPTS}" fi @@ -236,12 +241,6 @@ if isTrue "${USE_AIKAR_FLAGS}"; then " fi -if isTrue "${USE_LARGE_PAGES}"; then - JVM_XX_OPTS="${JVM_XX_OPTS} - -XX:+UseLargePagesInMetaspace - " -fi - if isTrue "${USE_FLARE_FLAGS}"; then JVM_XX_OPTS="${JVM_XX_OPTS} -XX:+UnlockDiagnosticVMOptions @@ -329,10 +328,10 @@ EOF else exec mc-server-runner "${mcServerRunnerArgs[@]}" "${finalArgs[@]}" fi -elif [[ -x run.sh ]]; then +elif [[ $SERVER =~ run.sh ]]; then log "Using Forge supplied run.sh script..." echo $JVM_XX_OPTS $JVM_OPTS $expandedDOpts > user_jvm_args.txt - exec mc-server-runner "${mcServerRunnerArgs[@]}" --shell bash run.sh + exec mc-server-runner "${mcServerRunnerArgs[@]}" --shell bash "${SERVER}" else # If we have a bootstrap.txt file... feed that in to the server stdin if [ -f /data/bootstrap.txt ]; then diff --git a/scripts/start-setupDatapack b/scripts/start-setupDatapack index b8134191..07559421 100755 --- a/scripts/start-setupDatapack +++ b/scripts/start-setupDatapack @@ -74,4 +74,4 @@ elif [[ "$DATAPACKS_FILE" ]]; then fi fi -exec "${SCRIPTS:-/}start-setupModpack" "$@" +exec "${SCRIPTS:-/}start-setupForgeApiMods" "$@" diff --git a/scripts/start-setupForgeApiMods b/scripts/start-setupForgeApiMods new file mode 100644 index 00000000..069ebc52 --- /dev/null +++ b/scripts/start-setupForgeApiMods @@ -0,0 +1,177 @@ +#!/bin/bash + +set -e -o pipefail + +: "${MODS_FORGEAPI_KEY:=}" +: "${REMOVE_OLD_FORGEAPI_MODS:=false}" +: "${MODS_FORGEAPI_PROJECTIDS:=}" +: "${MODS_FORGEAPI_FILE:=}" +: "${MODS_FORGEAPI_RELEASES:=RELEASE}" +: "${REMOVE_OLD_MODS_DEPTH:=1} " +: "${REMOVE_OLD_MODS_INCLUDE:=*.jar}" + +# FORGEAPI_BASE_URL used in manifest downloads below +FORGEAPI_BASE_URL=${FORGEAPI_BASE_URL:-https://api.curseforge.com/v1} +RELEASE_NUMBER_FILTER=1 +MINECRAFT_GAME_ID=432 +out_dir=/data/mods + +# shellcheck source=start-utils +. "${SCRIPTS:-/}start-utils" +isDebugging && set -x + +# Remove old mods/plugins +if isTrue "${REMOVE_OLD_FORGEAPI_MODS}" && [ -z "${MODS_FORGEAPI_FILE}" ]; then + removeOldMods /data/mods +fi + +ensureModKey(){ + if [ -z "$MODS_FORGEAPI_KEY" ]; then + log "ERROR: MODS_FORGEAPI_KEY REQUIRED to Connect to FORGE API, you supplied: ${MODS_FORGEAPI_KEY}" + exit 2 + fi +} + +# Set the global release type per the text. +# NOTE: downcasing release type for comparing types. +updateReleaseNumber(){ + releaseType=$1 + if [ "release" = "${releaseType,,}" ]; then + RELEASE_NUMBER_FILTER=1 + elif [ "beta" = "${releaseType,,}" ]; then + RELEASE_NUMBER_FILTER=2 + elif [ "alpha" = "${releaseType,,}" ]; then + RELEASE_NUMBER_FILTER=3 + fi +} + +retrieveVersionTypeNumber(){ + VERSION_NAME="Minecraft ${VANILLA_VERSION%.*}" + minecraft_types=$(curl -X GET -s \ + "${FORGEAPI_BASE_URL}/games/${MINECRAFT_GAME_ID}/version-types" \ + -H 'Accept: application/json' -H 'x-api-key: '${MODS_FORGEAPI_KEY}'') + + if [ ! "$minecraft_types" ]; then + log "ERROR: unable to retrieve version types for ${VERSION_NAME} from ForgeAPI" + exit 2 + fi + + TYPE_ID=$(jq -n "$minecraft_types" | jq --arg VERSION_NAME "$VERSION_NAME" -jc ' + .data[] | select(.name==$VERSION_NAME) | .id') + + if [ ! "$TYPE_ID" ]; then + log "ERROR: unable to retrieve version types for ${VERSION_NAME} from ForgeAPI" + exit 2 + fi +} + +modFileByProjectID(){ + project_id=$(echo $1 | tr -d '"') + project_id_release_type=$2 + unset PROJECT_FILE + + # if Type id isn't defined use minecraft version to go get it. + if [ ! "$TYPE_ID" ]; then + retrieveVersionTypeNumber + fi + + # JQ is struggling with larger page sizes so having to pagination for mods with a lot of releases + pageSize=42 + index=0 + total_count=1 + + while [ $index -lt $total_count ]; do + project_files=$(curl -X GET -s \ + "${FORGEAPI_BASE_URL}/mods/${project_id}/files?gameVersionTypeId=${TYPE_ID}&index=${index}&pageSize=${pageSize}" \ + -H 'Accept: application/json' -H 'x-api-key: '${MODS_FORGEAPI_KEY}'') + + if [ ! "$project_files" ]; then + log "ERROR: unable to retrieve any project id files for ${project_id} from ForgeAPI" + exit 2 + fi + # Use project files to grab out the total count of mods. + total_count=$(jq -n "$project_files" | jq -c '.pagination.totalCount' ) + + # Checking for a individual release type input, if not use global + if [ $project_id_release_type ]; then + updateReleaseNumber $project_id_release_type + else + updateReleaseNumber $MODS_FORGEAPI_RELEASES + fi + # grabs the highest ID of the releaseTypes selected. + # Default is 1 for Release, Beta is 2, and Alpha is 3. Using less than we can validate highest release. + current_project_file=$(jq -n "$project_files" | jq --arg RELEASE_FILTER "$RELEASE_NUMBER_FILTER" -jc ' + .data | sort_by(.id) | reverse | map(select(.releaseType<=($RELEASE_FILTER|tonumber))) | .[0]') + + # Logic to grab the latest release over the entire pagination + if [ ! "$PROJECT_FILE" ]; then + PROJECT_FILE=$current_project_file + elif [ "$current_project_file" ]; then + current_project_file_id=$(jq -n "$current_project_file" | jq -jc '.id' ) + PROJECT_FILE_ID=$(jq -n "$PROJECT_FILE" | jq -jc '.id' ) + if (( current_project_file_id > PROJECT_FILE_ID )); then + PROJECT_FILE=$current_project_file + fi + fi + + # check to see if we have gone to far or lost our index and exit with an error + if [ -z "$index" ] || [ -z "$total_count" ] || [ $index -ge $total_count ]; then + log "ERROR: Unable to retrieve any files for ${project_id} from ForgeAPI also Validate files have release type associated with no. ${RELEASE_NUMBER_FILTER}" + exit 2 + fi + # Increment start index to new set. + index=$(($index + $pageSize)) + done +} + +downloadModPackfromModFile() { + if [ ! "$PROJECT_FILE" ]; then + log "ERROR: Project File not found from the ForgeAPI" + exit 2 + fi + + # grabs needed values from our json return + file_name=$(jq -n "$PROJECT_FILE" | jq -jc '.fileName' ) + download_url=$(jq -n "$PROJECT_FILE" | jq -jc '.downloadUrl' ) + + # trys to make the output directory incase it doesnt exist. + mkdir -p "$out_dir" + echo "Downloading ${download_url}" + if ! get -o "${out_dir}/${file_name}" $download_url ; then + log "ERROR: failed to download from ${download_url}" + exit 2 + fi +} + +# Use forge api json file to filter and download the correct mods +if [ "$MODS_FORGEAPI_FILE" ] && [ -z "$MODS_FORGEAPI_PROJECTIDS" ]; then + ensureModKey + if [ ! -f "$MODS_FORGEAPI_FILE" ]; then + log "ERROR: given MODS_FORGEAPI_FILE file does not exist" + exit 2 + fi + MODS_FORGEAPI_PROJECTIDS=$(jq --raw-output '[.[] | .projectId] | join(",")' $MODS_FORGEAPI_FILE) + if [ ! "$MODS_FORGEAPI_PROJECTIDS" ]; then + log "ERROR: unable to retrieve packs from $MODS_FORGEAPI_FILE" + exit 2 + fi + + # Needs loop here to look up release types befor calling download. + for project_id in ${MODS_FORGEAPI_PROJECTIDS//,/ }; do + current_release_type=$(jq --arg PROJECT_ID "$project_id" -jc ' + .[] | select(.projectId==$PROJECT_ID) | .releaseType' "$MODS_FORGEAPI_FILE") + modFileByProjectID $project_id $current_release_type + downloadModPackfromModFile + done +fi + +# Use only project ids and global release data. +if [ "$MODS_FORGEAPI_PROJECTIDS" ] && [ -z "$MODS_FORGEAPI_FILE" ]; then + ensureModKey + for project_id in ${MODS_FORGEAPI_PROJECTIDS//,/ }; do + modFileByProjectID $project_id + downloadModPackfromModFile + done +fi + +exec "${SCRIPTS:-/}start-setupModpack" "$@" diff --git a/scripts/start-setupModconfig b/scripts/start-setupModconfig index 82d4c1b5..aeab818d 100755 --- a/scripts/start-setupModconfig +++ b/scripts/start-setupModconfig @@ -1,6 +1,7 @@ #!/bin/bash -. ${SCRIPTS:-/}start-utils +# shellcheck source=start-utils +. "${SCRIPTS:-/}start-utils" # If supplied with a URL for a config (simple zip of configurations), download it and unpack if [[ "$MODCONFIG" ]]; then @@ -9,7 +10,7 @@ case "X$MODCONFIG" in log "Downloading mod/plugin configs via HTTP" log " from $MODCONFIG ..." curl -sSL -o /tmp/modconfig.zip "$MODCONFIG" - if [ "$TYPE" = "SPIGOT" ]; then + if [ "$FAMILY" = "SPIGOT" ]; then mkdir -p /data/plugins unzip -o -d /data/plugins /tmp/modconfig.zip else @@ -24,4 +25,4 @@ case "X$MODCONFIG" in esac fi -exec ${SCRIPTS:-/}start-setupMounts $@ +exec "${SCRIPTS:-/}start-setupMounts" "$@" diff --git a/scripts/start-setupModpack b/scripts/start-setupModpack index 02036b46..0c71c676 100755 --- a/scripts/start-setupModpack +++ b/scripts/start-setupModpack @@ -6,6 +6,7 @@ set -e -o pipefail : "${MODS_FILE:=}" : "${REMOVE_OLD_MODS_DEPTH:=1} " : "${REMOVE_OLD_MODS_INCLUDE:=*.jar}" +sum_file=/data/.generic_pack.sum # shellcheck source=start-utils . "${SCRIPTS:-/}start-utils" @@ -18,12 +19,13 @@ CURSE_URL_BASE=${CURSE_URL_BASE:-https://minecraft.curseforge.com/projects} if isTrue "${REMOVE_OLD_MODS}" && [ -z "${MODS_FILE}" ]; then removeOldMods /data/mods removeOldMods /data/plugins + rm -f "$sum_file" fi # If packwiz url passed, bootstrap packwiz and update mods before other modpack processing if [[ "${PACKWIZ_URL}" ]]; then # Ensure we have the latest packwiz bootstrap installer - latestPackwiz=$(curl -fsSL https://api.github.com/repos/comp500/packwiz-installer-bootstrap/releases/latest) + latestPackwiz=$(curl -fsSL https://api.github.com/repos/packwiz/packwiz-installer-bootstrap/releases/latest) if [[ -z "${latestPackwiz}" ]]; then log "WARNING: Could not retrieve Packwiz bootstrap installer release information" else @@ -65,7 +67,7 @@ if [[ "$MODPACK" ]]; then exit 1 fi - if [ "$TYPE" = "SPIGOT" ]; then + if [ "$FAMILY" = "SPIGOT" ]; then mkdir -p /data/plugins if ! unzip -o -d /data/plugins /tmp/modpack.zip; then log "ERROR: failed to unzip the modpack from ${MODPACK}" @@ -79,7 +81,7 @@ if [[ "$MODPACK" ]]; then rm -f /tmp/modpack.zip elif [[ "$MODS" ]]; then - if [ "$TYPE" = "SPIGOT" ]; then + if [ "$FAMILY" = "SPIGOT" ]; then out_dir=/data/plugins else out_dir=/data/mods @@ -116,7 +118,7 @@ elif [[ "$MODS_FILE" ]]; then exit 2 fi - if [ "$TYPE" = "SPIGOT" ]; then + if [ "$FAMILY" = "SPIGOT" ]; then out_dir=/data/plugins else out_dir=/data/mods @@ -204,7 +206,6 @@ if [[ "${GENERIC_PACKS}" ]]; then fi done - sum_file=/data/.generic_pack.sum isDebugging && [ -f "$sum_file}" ] && cat "$sum_file" if ! sha256sum -c "${sum_file}" --status 2> /dev/null; then base_dir=/tmp/generic_pack_base diff --git a/scripts/start-setupMounts b/scripts/start-setupMounts index b2037a6a..6650a38b 100755 --- a/scripts/start-setupMounts +++ b/scripts/start-setupMounts @@ -1,14 +1,15 @@ #!/bin/bash -. ${SCRIPTS:-/}start-utils +# shellcheck source=start-utils +. "${SCRIPTS:-/}start-utils" -: ${SYNC_SKIP_NEWER_IN_DESTINATION:=${PLUGINS_SYNC_UPDATE:-true}} -: ${REPLACE_ENV_DURING_SYNC:=true} -: ${REPLACE_ENV_SUFFIXES:=yml,yaml,txt,cfg,conf,properties,hjson,json,tml,toml} -: ${REPLACE_ENV_VARIABLE_PREFIX:=${ENV_VARIABLE_PREFIX:-CFG_}} -: ${REPLACE_ENV_VARIABLES_EXCLUDES:=} -: ${REPLACE_ENV_VARIABLES_EXCLUDE_PATHS:=} -: ${DEBUG:=false} +: "${SYNC_SKIP_NEWER_IN_DESTINATION:=${PLUGINS_SYNC_UPDATE:-true}}" +: "${REPLACE_ENV_DURING_SYNC:=true}" +: "${REPLACE_ENV_SUFFIXES:=yml,yaml,txt,cfg,conf,properties,hjson,json,tml,toml}" +: "${REPLACE_ENV_VARIABLE_PREFIX:=${ENV_VARIABLE_PREFIX:-CFG_}}" +: "${REPLACE_ENV_VARIABLES_EXCLUDES:=}" +: "${REPLACE_ENV_VARIABLES_EXCLUDE_PATHS:=}" +: "${DEBUG:=false}" set -e isDebugging && set -x @@ -24,8 +25,8 @@ else fi if [ -d /plugins ]; then - case ${TYPE} in - SPIGOT|BUKKIT|PAPER|MAGMA) + case ${FAMILY} in + SPIGOT|HYBRID) mkdir -p /data/plugins log "Copying plugins over..." mc-image-helper \ @@ -40,7 +41,7 @@ if [ -d /plugins ]; then fi # If any modules have been provided, copy them over -: ${COPY_MODS_DEST:="/data/mods"} +: "${COPY_MODS_DEST:="/data/mods"}" if [ -d /mods ]; then log "Copying any mods over..." @@ -53,7 +54,7 @@ if [ -d /mods ]; then /mods "${COPY_MODS_DEST}" fi -: ${COPY_CONFIG_DEST:="/data/config"} +: "${COPY_CONFIG_DEST:="/data/config"}" if [ -d /config ]; then log "Copying any configs from /config to ${COPY_CONFIG_DEST}" @@ -66,4 +67,4 @@ if [ -d /config ]; then /config "${COPY_CONFIG_DEST}" fi -exec ${SCRIPTS:-/}start-setupServerProperties $@ +exec "${SCRIPTS:-/}start-setupServerProperties" "$@" diff --git a/scripts/start-setupServerProperties b/scripts/start-setupServerProperties index 0febd096..0a70273a 100755 --- a/scripts/start-setupServerProperties +++ b/scripts/start-setupServerProperties @@ -48,7 +48,7 @@ function customizeServerProps { # If not provided, generate a reasonable default message-of-the-day, # which shows up in the server listing in the client - if [ -z "$MOTD" ]; then + if ! [ -v MOTD ]; then # snapshot is the odd case where we have to look at version to identify that label if [[ ${ORIGINAL_TYPE} == "VANILLA" && ${VERSION} == "SNAPSHOT" ]]; then label=SNAPSHOT @@ -62,6 +62,13 @@ function customizeServerProps { MOTD="A ${label} Minecraft Server powered by Docker" fi + # normalize MOTD + if [[ ${TYPE^^} = LIMBO ]]; then + if [[ $MOTD ]] && ! [[ $MOTD =~ ^{ ]]; then + MOTD="{\"text\":\"${MOTD}\"}" + fi + fi + setServerProp "server-name" SERVER_NAME setServerProp "server-ip" SERVER_IP setServerProp "server-port" SERVER_PORT diff --git a/scripts/start-setupVanillaTweaks b/scripts/start-setupVanillaTweaks new file mode 100644 index 00000000..4da67423 --- /dev/null +++ b/scripts/start-setupVanillaTweaks @@ -0,0 +1,84 @@ +#!/bin/bash + +set -e -o pipefail + +: "${REMOVE_OLD_VANILLATWEAKS:=false}" +: "${VANILLATWEAKS_FILE:=}" +: "${VANILLATWEAKS_SHARECODE:=}" +: "${REMOVE_OLD_VANILLATWEAKS_DEPTH:=1} " +: "${REMOVE_OLD_VANILLATWEAKS_INCLUDE:=*.zip}" + +# shellcheck source=start-utils +. "${SCRIPTS:-/}start-utils" +isDebugging && set -x + +out_dir=/data/${LEVEL:-world}/datapacks + +# Remove old VANILLATWEAKS +if isTrue "${REMOVE_OLD_VANILLATWEAKS}" && [ -z "${VANILLATWEAKS_FILE}" ]; then + if [ -d "$out_dir" ]; then + find "$out_dir" -mindepth 1 -maxdepth ${REMOVE_OLD_VANILLATWEAKS_DEPTH:-16} -wholename "${REMOVE_OLD_VANILLATWEAKS_INCLUDE:-*}" -not -wholename "${REMOVE_OLD_VANILLATWEAKS_EXCLUDE:-}" -delete + fi +fi + +# Example: VANILLATWEAKS_SHARECODE=MGr52E +# Code generated from the UI website, typically a alphanumeric 6 digit code. +if [[ "$VANILLATWEAKS_SHARECODE" ]]; then + VANILLATWEAKS_FILE=/tmp/vanillatweaksfile.json + SHARECODE_LOOKUP_URL="https://vanillatweaks.net/assets/server/sharecode.php?code=${VANILLATWEAKS_SHARECODE}" + curl -f $SHARECODE_LOOKUP_URL -o $VANILLATWEAKS_FILE + if [ ! -f "$VANILLATWEAKS_FILE" ]; then + log "ERROR: Unable to use share code provided to retreive vanillatweaks file" + exit 2 + fi +fi + +# Use vanillatweaks file to specify VT and datapacks +if [[ "$VANILLATWEAKS_FILE" ]]; then + if [ ! -f "$VANILLATWEAKS_FILE" ]; then + log "ERROR: given VANILLATWEAKS_FILE file does not exist" + exit 2 + fi + + PACKS=$(jq -jc '.packs' $VANILLATWEAKS_FILE) + if [ ! "$PACKS" ]; then + log "ERROR: unable to retrieve packs from $VANILLATWEAKS_FILE" + exit 2 + fi + + VT_VERSION=$(jq -jc '.version' $VANILLATWEAKS_FILE) + if [ ! "$VT_VERSION" ]; then + log "ERROR: unable to retrieve version from $VANILLATWEAKS_FILE" + exit 2 + fi +fi + +# Download and unzip packs +if [[ "$PACKS" ]] && [[ "$VT_VERSION" ]]; then + VT_ZIPDATA_URL=https://vanillatweaks.net/assets/server/zipdatapacks.php + DOWNLOAD_URL=$(curl -X POST -F "packs=${PACKS}" -F "version=${VT_VERSION}" $VT_ZIPDATA_URL | jq -r '.link') + if [ ! "$DOWNLOAD_URL" ]; then + log "ERROR: unable to retrieve DOWNLOAD_URL from vanillatweaks.net!" + exit 2 + fi + + TEMPZIP=/tmp/vanillatweaks.zip + if ! get -o $TEMPZIP "https://vanillatweaks.net${DOWNLOAD_URL}"; then + log "ERROR: failed to download from ${DOWNLOAD_URL}" + exit 2 + fi + + mkdir -p "$out_dir" + if ! unzip -o -d "$out_dir" $TEMPZIP; then + log "ERROR: failed to unzip the ${PACKS} from ${$TEMPZIP}" + fi + + # clean up files time! + rm -f $TEMPZIP + # cleans up temp vanilla tweaks file download to get stored packs + if [[ "$VANILLATWEAKS_SHARECODE" ]]; then + rm -f $VANILLATWEAKS_FILE + fi +fi + +exec "${SCRIPTS:-/}start-setupDatapack" "$@" diff --git a/scripts/start-setupWorld b/scripts/start-setupWorld index 3484b46d..db14635f 100755 --- a/scripts/start-setupWorld +++ b/scripts/start-setupWorld @@ -33,7 +33,7 @@ if [[ "$WORLD" ]] && ( isTrue "${FORCE_WORLD_COPY}" || [ ! -d "$worldDest" ] ); mkdir -p /tmp/world-data (cd /tmp/world-data && unzip -o -q "$zipSrc") - if [ "$TYPE" = "SPIGOT" ]; then + if [ "$FAMILY" = "SPIGOT" ]; then baseDirs=$(find /tmp/world-data -name "level.dat" -not -path "*_nether*" -not -path "*_the_end*" -exec dirname "{}" \;) else baseDirs=$(find /tmp/world-data -name "level.dat" -exec dirname "{}" \;) @@ -51,7 +51,7 @@ if [[ "$WORLD" ]] && ( isTrue "${FORCE_WORLD_COPY}" || [ ! -d "$worldDest" ] ); exit 1 fi rsync --remove-source-files --recursive --delete "$baseDir/" "$worldDest" - if [ "$TYPE" = "SPIGOT" ]; then + if [ "$FAMILY" = "SPIGOT" ]; then log "Copying end and nether ..." [ -d "${baseDir}_nether" ] && rsync --remove-source-files --recursive --delete "${baseDir}_nether/" "${worldDest}_nether" [ -d "${baseDir}_the_end" ] && rsync --remove-source-files --recursive --delete "${baseDir}_the_end/" "${worldDest}_the_end" @@ -61,7 +61,7 @@ if [[ "$WORLD" ]] && ( isTrue "${FORCE_WORLD_COPY}" || [ ! -d "$worldDest" ] ); rsync --recursive --delete "${WORLD%/}"/ "$worldDest" fi - if [ "$TYPE" = "SPIGOT" ]; then + if [ "$FAMILY" = "SPIGOT" ]; then # Reorganise if a Spigot server log "Moving End and Nether maps to Spigot location" [ -d "$worldDest/DIM1" ] && mv -f "$worldDest/DIM1" "${worldDest}_the_end" @@ -69,4 +69,4 @@ if [[ "$WORLD" ]] && ( isTrue "${FORCE_WORLD_COPY}" || [ ! -d "$worldDest" ] ); fi fi -exec "${SCRIPTS:-/}start-setupDatapack" "$@" +exec "${SCRIPTS:-/}start-setupVanillaTweaks" "$@" diff --git a/scripts/start-utils b/scripts/start-utils index 8c77cbca..0861e24e 100755 --- a/scripts/start-utils +++ b/scripts/start-utils @@ -178,5 +178,14 @@ function isFamily() { return 0 fi done + + return 1 +} +function isType() { + for t in "${@}"; do + if [[ $TYPE == "$t" ]]; then + return 0 + fi + done return 1 } \ No newline at end of file diff --git a/tests/fulltests/test.sh b/tests/fulltests/test.sh new file mode 100644 index 00000000..4a9a4891 --- /dev/null +++ b/tests/fulltests/test.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# go to script root directory +cd "$(dirname "$0")" || exit 1 + +# compose down function for reuse +down() { + docker-compose down -v --remove-orphans +} + +checkandExitOnFailure(){ + failed=$1 + # docker-compose logs outputs messages from the specified container + if $failed; then + docker-compose logs mc + down + cd .. + exit 2 + fi +} + +# tests to completely spin up Minecraft and use the monitor to validate the service is running. +fullMinecraftUpTest(){ + folder=$1 + cd "$folder" + failed=false + # run the monitor to validate the Minecraft image is healthy + docker-compose run monitor || failed=true + echo "${folder} Result: failed=$failed" + checkandExitOnFailure $failed + down + cd .. +} + +# go through each folder in fulltests and run fullbuilds +FOLDERS=$(ls) +for folder in $FOLDERS; do + # If folder is a directory + if [ -d "$folder" ]; then + echo "Starting Tests on ${folder}" + fullMinecraftUpTest $folder + fi +done diff --git a/tests/docker-compose.yml b/tests/fulltests/vanilla-latest/docker-compose.yml similarity index 95% rename from tests/docker-compose.yml rename to tests/fulltests/vanilla-latest/docker-compose.yml index f3a0bb0a..61c6f289 100644 --- a/tests/docker-compose.yml +++ b/tests/fulltests/vanilla-latest/docker-compose.yml @@ -1,4 +1,4 @@ -version: "3.8" +version: "3" services: monitor: diff --git a/tests/setuponlytests/forgeapimods_file/docker-compose.yml b/tests/setuponlytests/forgeapimods_file/docker-compose.yml new file mode 100644 index 00000000..11686a4b --- /dev/null +++ b/tests/setuponlytests/forgeapimods_file/docker-compose.yml @@ -0,0 +1,17 @@ +version: "3" + +services: + mc: + restart: "no" + image: ${IMAGE_TO_TEST:-itzg/minecraft-server} + environment: + EULA: "TRUE" + SETUP_ONLY: "TRUE" + VERSION: ${MINECRAFT_VERSION:-LATEST} + MODS_FORGEAPI_FILE: /config/example.json + # Key is defined in .github/workflows/pr.yml and ci.yml + # This should be coming from github secrets. + MODS_FORGEAPI_KEY: ${MODS_FORGEAPI_KEY} + REMOVE_OLD_FORGEAPI_MODS: "TRUE" + volumes: + - ./example.json:/config/example.json:ro diff --git a/tests/setuponlytests/forgeapimods_file/example.json b/tests/setuponlytests/forgeapimods_file/example.json new file mode 100644 index 00000000..b7c9a304 --- /dev/null +++ b/tests/setuponlytests/forgeapimods_file/example.json @@ -0,0 +1,11 @@ +[{ + "name": "fabric api", + "projectId": "306612", + "releaseType": "release" + }, + { + "name": "Fabric Voice Mod", + "projectId": "416089", + "releaseType": "beta" + } +] \ No newline at end of file diff --git a/tests/setuponlytests/forgeapimods_projectids/docker-compose.yml b/tests/setuponlytests/forgeapimods_projectids/docker-compose.yml new file mode 100644 index 00000000..2bc794dc --- /dev/null +++ b/tests/setuponlytests/forgeapimods_projectids/docker-compose.yml @@ -0,0 +1,15 @@ +version: "3" + +services: + mc: + restart: "no" + image: ${IMAGE_TO_TEST:-itzg/minecraft-server} + environment: + EULA: "TRUE" + SETUP_ONLY: "TRUE" + VERSION: ${MINECRAFT_VERSION:-LATEST} + MODS_FORGEAPI_PROJECTIDS: 306612,416089 + # Allows for Beta releases of 416089 the Fabric Voice Mod + MODS_FORGEAPI_RELEASES: BETA + MODS_FORGEAPI_KEY: ${MODS_FORGEAPI_KEY} + REMOVE_OLD_FORGEAPI_MODS: "TRUE" diff --git a/tests/generic-packs/docker-compose.test.yml b/tests/setuponlytests/generic-packs/docker-compose.yml similarity index 75% rename from tests/generic-packs/docker-compose.test.yml rename to tests/setuponlytests/generic-packs/docker-compose.yml index 210c54dd..46dd8344 100644 --- a/tests/generic-packs/docker-compose.test.yml +++ b/tests/setuponlytests/generic-packs/docker-compose.yml @@ -2,14 +2,13 @@ version: "3" services: mc: - image: itzg/minecraft-server + image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "true" + SETUP_ONLY: "TRUE" GENERIC_PACKS: https://github.com/itzg/mc-image-helper/releases/download/v1.9.5/mc-image-helper-1.9.5.zip,/packs/testing.zip - DEBUG: "true" volumes: - ./packs:/packs - data:/data - volumes: - data: {} \ No newline at end of file + data: {} diff --git a/tests/generic-packs/packs/testing.zip b/tests/setuponlytests/generic-packs/packs/testing.zip similarity index 100% rename from tests/generic-packs/packs/testing.zip rename to tests/setuponlytests/generic-packs/packs/testing.zip diff --git a/tests/setuponlytests/test.sh b/tests/setuponlytests/test.sh new file mode 100644 index 00000000..169cf0b6 --- /dev/null +++ b/tests/setuponlytests/test.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# go to script root directory +cd "$(dirname "$0")" || exit 1 + +# compose down function for reuse +down() { + docker-compose down -v --remove-orphans +} + +checkandExitOnFailure(){ + failed=$1 + # docker-compose logs outputs messages from the specified container + if $failed; then + docker-compose logs mc + down + cd .. + exit 2 + fi +} + +# tests that only run the setup files for things like downloads and configuration. +setupOnlyMinecraftTest(){ + folder=$1 + cd "$folder" + failed=false + # run the monitor to validate the Minecraft image is healthy + docker-compose --log-level ERROR up --quiet-pull --exit-code-from mc 2>/dev/null || failed=true + echo "${folder} Result: failed=$failed" + checkandExitOnFailure $failed + down + cd .. +} + +# go through each folder in setuponly and test setups +FOLDERS=$(ls) +for folder in $FOLDERS; do + # If folder is a directory + if [ -d "$folder" ]; then + echo "Starting Tests on ${folder}" + setupOnlyMinecraftTest $folder + fi +done diff --git a/tests/setuponlytests/vanillatweaks_file/docker-compose.yml b/tests/setuponlytests/vanillatweaks_file/docker-compose.yml new file mode 100644 index 00000000..b33292ee --- /dev/null +++ b/tests/setuponlytests/vanillatweaks_file/docker-compose.yml @@ -0,0 +1,14 @@ +version: "3" + +services: + mc: + restart: "no" + image: ${IMAGE_TO_TEST:-itzg/minecraft-server} + environment: + EULA: "TRUE" + SETUP_ONLY: "TRUE" + VERSION: ${MINECRAFT_VERSION:-LATEST} + VANILLATWEAKS_FILE: /config/vanillatweaks-datapacks.json + REMOVE_OLD_VANILLATWEAKS: "TRUE" + volumes: + - ./vanillatweaks-datapacks.json:/config/vanillatweaks-datapacks.json:ro diff --git a/tests/setuponlytests/vanillatweaks_file/vanillatweaks-datapacks.json b/tests/setuponlytests/vanillatweaks_file/vanillatweaks-datapacks.json new file mode 100644 index 00000000..80604c49 --- /dev/null +++ b/tests/setuponlytests/vanillatweaks_file/vanillatweaks-datapacks.json @@ -0,0 +1,17 @@ +{ + "type": "datapacks", + "version": "1.18", + "packs": { + "survival": [ + "graves", + "multiplayer sleep", + "afk display", + "armor statues", + "unlock all recipes", + "fast leaf decay", + "coordinates hud" + ], + "items": ["armored elytra"] + }, + "result": "ok" +} diff --git a/tests/setuponlytests/vanillatweaks_sharecode/docker-compose.yml b/tests/setuponlytests/vanillatweaks_sharecode/docker-compose.yml new file mode 100644 index 00000000..66eac5b5 --- /dev/null +++ b/tests/setuponlytests/vanillatweaks_sharecode/docker-compose.yml @@ -0,0 +1,12 @@ +version: "3" + +services: + mc: + restart: "no" + image: ${IMAGE_TO_TEST:-itzg/minecraft-server} + environment: + EULA: "TRUE" + SETUP_ONLY: "TRUE" + VERSION: ${MINECRAFT_VERSION:-LATEST} + VANILLATWEAKS_SHARECODE: MGr52E + REMOVE_OLD_VANILLATWEAKS: "TRUE" diff --git a/tests/test.sh b/tests/test.sh index 9db35dec..a538ddc4 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -1,22 +1,18 @@ #!/bin/bash +# go to script root directory cd "$(dirname "$0")" || exit 1 -failed=false - -down() { - docker-compose down -v -} - -docker-compose run monitor || failed=true -echo " -Result: failed=$failed" - -if $failed; then - docker-compose logs mc - down - exit 1 -else - down -fi - +# go through top level folders and trigger the tests in the subfolders +FOLDERS=$(ls) +for folder in $FOLDERS; do + # If folder is a directory + if [ -d "$folder" ]; then + cd "$folder" + if [ -f "./test.sh" ]; then + echo "Starting ${folder} Tests" + sh ./test.sh + fi + cd .. + fi +done