diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml
index f8158ae67b9271a9826564ebdf3c74f3adfc9476..d6358ffb4894becf8622d6bcffa37abe684f4bf3 100644
--- a/.github/workflows/gh-pages.yml
+++ b/.github/workflows/gh-pages.yml
@@ -18,20 +18,20 @@ jobs:
 
       # BUILD PROJECT AND PUBLISH TO PACKAGES
       - name: Set up JDK 8
-        uses: actions/setup-java@v3
+        uses: actions/setup-java@v2
         with:
           distribution: 'adopt'
           java-version: '8'
       - name: Grant execute permission for gradlew
         run: chmod +x gradlew
       - name: Build documentation
-        run: ./gradlew about build -x test
+        run: ./gradlew javadoc -Pversion='' --info --full-stacktrace -x test
       - name: List files
         run: ls -ahl build/libs
 
       # DEPLOY TO GH-PAGES
       - name: Deploy to GitHub-Pages
-        uses: JamesIves/github-pages-deploy-action@v4.4.0
+        uses: JamesIves/github-pages-deploy-action@v4.2.2
         with:
           branch: gh-pages
           folder: build/docs/javadoc
diff --git a/.github/workflows/github-prerelease.yml b/.github/workflows/github-prerelease.yml
deleted file mode 100644
index ed2fcff3ce47005dada07b98242c1da1234f5500..0000000000000000000000000000000000000000
--- a/.github/workflows/github-prerelease.yml
+++ /dev/null
@@ -1,248 +0,0 @@
-name: Create GitHub Pre-Release after GitLab tag mirror
-
-on:
-  push:
-    tags:
-      - '[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+'
-      - '[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+'
-      - '[0-9]+.[0-9]+.[0-9]+-pre.[0-9]+'
-
-jobs:
-  release:
-    runs-on: ubuntu-latest
-    # OUTPUTS
-    outputs:
-      newtag: ${{ steps.tag.outputs.tag }}
-      uploadurl: ${{ steps.create_release.outputs.upload_url }}
-    steps:
-      # GET LATEST CODE
-      - name: Checkout latest code
-        uses: actions/checkout@master
-        with:
-          fetch-depth: 0
-
-      # EXTRACT TAG FROM PUSH
-      - name: Get tag
-        id: tag
-        uses: dawidd6/action-get-tag@v1
-      - name: Use tag
-        run: echo ${{steps.tag.outputs.tag}}
-
-      # GENERATE CHANGELOG, RELEASE
-      - id: conventional_changelog
-        uses: ardalanamini/auto-changelog@master
-        with:
-          token: ${{ secrets.GITHUB_TOKEN }}
-
-      - name: Create Release
-        id: create_release
-        uses: actions/create-release@latest
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          tag_name: ${{ github.ref }}
-          release_name: Release ${{steps.tag.outputs.tag}}
-          body: |
-            Automatic release of version: **${{steps.tag.outputs.tag}}**
-            **Changes in this release:**
-            ${{ steps.conventional_changelog.outputs.changelog }}
-          draft: false
-          prerelease: true
-
-  main:
-    needs: release
-    runs-on: ubuntu-latest
-    steps:
-      # GET LATEST CODE
-      - name: Checkout latest code
-        uses: actions/checkout@master
-        with:
-          ref: main
-          fetch-depth: 0
-
-      # BUILD PROJECT AND PUBLISH TO PACKAGES
-      - name: Set up JDK 8
-        uses: actions/setup-java@v3
-        with:
-          distribution: 'adopt'
-          java-version: '8'
-
-      - name: Grant execute permission for gradlew
-        run: chmod +x gradlew
-
-      - name: Publish to GitHub Packages
-        env:
-          GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }}
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        run: |
-          NEW_VERSION=$(echo "${{ needs.release.outputs.newtag }}")
-          echo "New version: ${NEW_VERSION}"
-          echo "Github username: ${GITHUB_ACTOR}"
-          ./gradlew about
-          ./gradlew -Pversion=${NEW_VERSION} build --info -x test
-
-      # UPLOAD ASSETS TO RELEASE
-      - name: Upload Release Asset Main Jar
-        id: upload-release-asset
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/ExampleAddon-${{ needs.release.outputs.newtag }}.jar
-          asset_name: ExampleAddon-${{ needs.release.outputs.newtag }}.jar
-          asset_content_type: application/jar
-
-      - name: Upload Release Asset Sources Jar
-        id: upload-release-asset-sources
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/ExampleAddon-${{ needs.release.outputs.newtag }}-sources.jar
-          asset_name: ExampleAddon-${{ needs.release.outputs.newtag }}-sources.jar
-          asset_content_type: application/jar
-
-      - name: Upload Release Asset Javadoc Jar
-        id: upload-release-asset-javadoc
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/ExampleAddon-${{ needs.release.outputs.newtag }}-javadoc.jar
-          asset_name: ExampleAddon-${{ needs.release.outputs.newtag }}-javadoc.jar
-          asset_content_type: application/jar
-
-  alternative:
-    needs: release
-    runs-on: ubuntu-latest
-    steps:
-      # GET LATEST CODE
-      - name: Checkout latest code
-        uses: actions/checkout@master
-        with:
-          ref: alternativeExtensionDeclaration
-          fetch-depth: 0
-
-      # BUILD PROJECT AND PUBLISH TO PACKAGES
-      - name: Set up JDK 8
-        uses: actions/setup-java@v3
-        with:
-          distribution: 'adopt'
-          java-version: '8'
-
-      - name: Grant execute permission for gradlew
-        run: chmod +x gradlew
-
-      - name: Publish to GitHub Packages
-        env:
-          GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }}
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        run: |
-          NEW_VERSION=$(echo "${{ needs.release.outputs.newtag }}")
-          echo "New version: ${NEW_VERSION}"
-          echo "Github username: ${GITHUB_ACTOR}"
-          ./gradlew about
-          ./gradlew -Pversion=${NEW_VERSION} build --info -x test
-
-      # UPLOAD ASSETS TO RELEASE
-      - name: Upload Release Asset Main Jar
-        id: upload-release-asset
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/AlternativeExtensionDeclaration-${{ needs.release.outputs.newtag }}.jar
-          asset_name: AlternativeExtensionDeclaration-${{ needs.release.outputs.newtag }}.jar
-          asset_content_type: application/jar
-
-      - name: Upload Release Asset Sources Jar
-        id: upload-release-asset-sources
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/AlternativeExtensionDeclaration-${{ needs.release.outputs.newtag }}-sources.jar
-          asset_name: AlternativeExtensionDeclaration-${{ needs.release.outputs.newtag }}-sources.jar
-          asset_content_type: application/jar
-
-      - name: Upload Release Asset Javadoc Jar
-        id: upload-release-asset-javadoc
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/AlternativeExtensionDeclaration-${{ needs.release.outputs.newtag }}-javadoc.jar
-          asset_name: AlternativeExtensionDeclaration-${{ needs.release.outputs.newtag }}-javadoc.jar
-          asset_content_type: application/jar
-
-  tetris:
-    needs: release
-    runs-on: ubuntu-latest
-    steps:
-      # GET LATEST CODE
-      - name: Checkout latest code
-        uses: actions/checkout@master
-        with:
-          ref: tetris
-          fetch-depth: 0
-
-      # BUILD PROJECT AND PUBLISH TO PACKAGES
-      - name: Set up JDK 8
-        uses: actions/setup-java@v3
-        with:
-          distribution: 'adopt'
-          java-version: '8'
-
-      - name: Grant execute permission for gradlew
-        run: chmod +x gradlew
-
-      - name: Publish to GitHub Packages
-        env:
-          GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }}
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        run: |
-          NEW_VERSION=$(echo "${{ needs.release.outputs.newtag }}")
-          echo "New version: ${NEW_VERSION}"
-          echo "Github username: ${GITHUB_ACTOR}"
-          ./gradlew about
-          ./gradlew -Pversion=${NEW_VERSION} build --info -x test
-
-      # UPLOAD ASSETS TO RELEASE
-      - name: Upload Release Asset Main Jar
-        id: upload-release-asset
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/MiniGame-${{ needs.release.outputs.newtag }}.jar
-          asset_name: MiniGame-${{ needs.release.outputs.newtag }}.jar
-          asset_content_type: application/jar
-
-      - name: Upload Release Asset Sources Jar
-        id: upload-release-asset-sources
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/MiniGame-${{ needs.release.outputs.newtag }}-sources.jar
-          asset_name: MiniGame-${{ needs.release.outputs.newtag }}-sources.jar
-          asset_content_type: application/jar
-
-      - name: Upload Release Asset Javadoc Jar
-        id: upload-release-asset-javadoc
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/MiniGame-${{ needs.release.outputs.newtag }}-javadoc.jar
-          asset_name: MiniGame-${{ needs.release.outputs.newtag }}-javadoc.jar
-          asset_content_type: application/jar
\ No newline at end of file
diff --git a/.github/workflows/github_release.yml b/.github/workflows/github_release.yml
index 6e7cc7a856c66639152addfe69720594bd05cbc6..7fba6ec32c8a8096f3bf5713d52f52a6784c8319 100644
--- a/.github/workflows/github_release.yml
+++ b/.github/workflows/github_release.yml
@@ -3,9 +3,10 @@ name: Create GitHub Release after GitLab tag mirror
 on:
   push:
     tags:
-      - '[0-9]+.[0-9]+.[0-9]+'
+      - '*'
 
 jobs:
+
   release:
     runs-on: ubuntu-latest
     # OUTPUTS
@@ -26,12 +27,22 @@ jobs:
       - name: Use tag
         run: echo ${{steps.tag.outputs.tag}}
 
-      # GENERATE CHANGELOG, RELEASE
-      - id: conventional_changelog
-        uses: ardalanamini/auto-changelog@master
+      # GET RELEASE INFO
+      - name: Get Release Info
+        run: |
+          curl --header \
+          'PRIVATE-TOKEN: ${{ secrets.GITLAB_TOKEN }}' \
+          'https://git.griefed.de/api/v4/projects/95/releases/${{steps.tag.outputs.tag}}' >> ./version.json
+
+      # GET DESCRIPTION
+      - name: Extract version from package.json
+        uses: sergeysova/jq-action@v2
+        id: description
         with:
-          token: ${{ secrets.GITHUB_TOKEN }}
+          cmd: 'jq .description version.json -r'
+          multiline: true
 
+      # GENERATE RELEASE
       - name: Create Release
         id: create_release
         uses: actions/create-release@latest
@@ -40,10 +51,7 @@ jobs:
         with:
           tag_name: ${{ github.ref }}
           release_name: Release ${{steps.tag.outputs.tag}}
-          body: |
-            Automatic release of version: **${{steps.tag.outputs.tag}}**
-            **Changes in this release:**
-            ${{ steps.conventional_changelog.outputs.changelog }}
+          body: ${{ steps.description.outputs.value }}
           draft: false
           prerelease: false
 
@@ -62,140 +70,10 @@ jobs:
       - name: Set up JDK 8
         uses: actions/setup-java@v3
         with:
-          distribution: 'adopt'
-          java-version: '8'
-
-      - name: Grant execute permission for gradlew
-        run: chmod +x gradlew
-
-      - name: Publish to GitHub Packages
-        env:
-          GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }}
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        run: |
-          NEW_VERSION=$(echo "${{ needs.release.outputs.newtag }}")
-          echo "New version: ${NEW_VERSION}"
-          echo "Github username: ${GITHUB_ACTOR}"
-          ./gradlew about
-          ./gradlew -Pversion=${NEW_VERSION} build --info -x test
-
-      # UPLOAD ASSETS TO RELEASE
-      - name: Upload Release Asset Main Jar
-        id: upload-release-asset
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/ExampleAddon-${{ needs.release.outputs.newtag }}.jar
-          asset_name: ExampleAddon-${{ needs.release.outputs.newtag }}.jar
-          asset_content_type: application/jar
-
-      - name: Upload Release Asset Sources Jar
-        id: upload-release-asset-sources
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/ExampleAddon-${{ needs.release.outputs.newtag }}-sources.jar
-          asset_name: ExampleAddon-${{ needs.release.outputs.newtag }}-sources.jar
-          asset_content_type: application/jar
-
-      - name: Upload Release Asset Javadoc Jar
-        id: upload-release-asset-javadoc
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/ExampleAddon-${{ needs.release.outputs.newtag }}-javadoc.jar
-          asset_name: ExampleAddon-${{ needs.release.outputs.newtag }}-javadoc.jar
-          asset_content_type: application/jar
-
-  alternative:
-    needs: release
-    runs-on: ubuntu-latest
-    steps:
-      # GET LATEST CODE
-      - name: Checkout latest code
-        uses: actions/checkout@master
-        with:
-          ref: alternativeExtensionDeclaration
-          fetch-depth: 0
-
-      # BUILD PROJECT AND PUBLISH TO PACKAGES
-      - name: Set up JDK 8
-        uses: actions/setup-java@v3
-        with:
-          distribution: 'adopt'
-          java-version: '8'
-
-      - name: Grant execute permission for gradlew
-        run: chmod +x gradlew
-
-      - name: Publish to GitHub Packages
-        env:
-          GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }}
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        run: |
-          NEW_VERSION=$(echo "${{ needs.release.outputs.newtag }}")
-          echo "New version: ${NEW_VERSION}"
-          echo "Github username: ${GITHUB_ACTOR}"
-          ./gradlew about
-          ./gradlew -Pversion=${NEW_VERSION} build --info -x test
-
-      # UPLOAD ASSETS TO RELEASE
-      - name: Upload Release Asset Main Jar
-        id: upload-release-asset
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/AlternativeExtensionDeclaration-${{ needs.release.outputs.newtag }}.jar
-          asset_name: AlternativeExtensionDeclaration-${{ needs.release.outputs.newtag }}.jar
-          asset_content_type: application/jar
-
-      - name: Upload Release Asset Sources Jar
-        id: upload-release-asset-sources
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/AlternativeExtensionDeclaration-${{ needs.release.outputs.newtag }}-sources.jar
-          asset_name: AlternativeExtensionDeclaration-${{ needs.release.outputs.newtag }}-sources.jar
-          asset_content_type: application/jar
-
-      - name: Upload Release Asset Javadoc Jar
-        id: upload-release-asset-javadoc
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/AlternativeExtensionDeclaration-${{ needs.release.outputs.newtag }}-javadoc.jar
-          asset_name: AlternativeExtensionDeclaration-${{ needs.release.outputs.newtag }}-javadoc.jar
-          asset_content_type: application/jar
-
-  tetris:
-    needs: release
-    runs-on: ubuntu-latest
-    steps:
-      # GET LATEST CODE
-      - name: Checkout latest code
-        uses: actions/checkout@master
-        with:
-          ref: tetris
-          fetch-depth: 0
-
-      # BUILD PROJECT AND PUBLISH TO PACKAGES
-      - name: Set up JDK 8
-        uses: actions/setup-java@v3
-        with:
-          distribution: 'adopt'
+          distribution: 'zulu'
           java-version: '8'
+          check-latest: true
+          cache: 'gradle'
 
       - name: Grant execute permission for gradlew
         run: chmod +x gradlew
@@ -205,11 +83,7 @@ jobs:
           GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }}
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         run: |
-          NEW_VERSION=$(echo "${{ needs.release.outputs.newtag }}")
-          echo "New version: ${NEW_VERSION}"
-          echo "Github username: ${GITHUB_ACTOR}"
-          ./gradlew about
-          ./gradlew -Pversion=${NEW_VERSION} build --info -x test
+          ./gradlew -Pversion=${{ needs.release.outputs.newtag }} build --info --full-stacktrace -x test
 
       # UPLOAD ASSETS TO RELEASE
       - name: Upload Release Asset Main Jar
@@ -219,8 +93,8 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
           upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/MiniGame-${{ needs.release.outputs.newtag }}.jar
-          asset_name: MiniGame-${{ needs.release.outputs.newtag }}.jar
+          asset_path: ./build/libs/ServerPackCreatorExampleAddon-${{ needs.release.outputs.newtag }}.jar
+          asset_name: ServerPackCreatorExampleAddon-${{ needs.release.outputs.newtag }}.jar
           asset_content_type: application/jar
 
       - name: Upload Release Asset Sources Jar
@@ -230,8 +104,8 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
           upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/MiniGame-${{ needs.release.outputs.newtag }}-sources.jar
-          asset_name: MiniGame-${{ needs.release.outputs.newtag }}-sources.jar
+          asset_path: ./build/libs/ServerPackCreatorExampleAddon-${{ needs.release.outputs.newtag }}-sources.jar
+          asset_name: ServerPackCreatorExampleAddon-${{ needs.release.outputs.newtag }}-sources.jar
           asset_content_type: application/jar
 
       - name: Upload Release Asset Javadoc Jar
@@ -241,6 +115,6 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
           upload_url:  ${{ needs.release.outputs.uploadurl }}
-          asset_path: ./build/libs/MiniGame-${{ needs.release.outputs.newtag }}-javadoc.jar
-          asset_name: MiniGame-${{ needs.release.outputs.newtag }}-javadoc.jar
+          asset_path: ./build/libs/ServerPackCreatorExampleAddon-${{ needs.release.outputs.newtag }}-javadoc.jar
+          asset_name: ServerPackCreatorExampleAddon-${{ needs.release.outputs.newtag }}-javadoc.jar
           asset_content_type: application/jar
\ No newline at end of file
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index f722c2fa6d32b38ba0d4db4bb155a2d525faf137..d148779261746f2614f7f4385bb1c239aa474626 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -6,104 +6,28 @@ on:
   workflow_dispatch:
 
 jobs:
-  main:
+  test:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v3
-        with:
-          ref: main
-      - name: Set up JDK 8
-        uses: actions/setup-java@v3
-        with:
-          distribution: 'adopt'
-          java-version: '8'
-      - name: Where is Java
-        run: which java
-      - name: Grant execute permission for gradlew
-        run: chmod +x gradlew
-      - name: Build with Gradle
-        run: ./gradlew about build --no-daemon --info
-      - name: List files in libs
-        run: ls -ahl build/libs
-      - uses: actions/upload-artifact@v3
-        with:
-          name: build-artifacts-gradle
-          path: |
-            build/libs/
-            !build/libs/libraries/
-            frontend/dist/spa
-          if-no-files-found: warn # 'warn' or 'ignore' are also available, defaults to `warn`
-      - uses: actions/upload-artifact@v3
-        if: failure()
-        with:
-          name: report
-          path: build/reports/tests/test/
-          if-no-files-found: warn # 'warn' or 'ignore' are also available, defaults to `warn`
-
-  alternative:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v3
-        with:
-          ref: alternativeExtensionDeclaration
+      - uses: actions/checkout@v2
       - name: Set up JDK 8
         uses: actions/setup-java@v3
         with:
-          distribution: 'adopt'
+          distribution: 'zulu'
           java-version: '8'
+          check-latest: true
+          cache: 'gradle'
       - name: Where is Java
         run: which java
       - name: Grant execute permission for gradlew
         run: chmod +x gradlew
       - name: Build with Gradle
-        run: ./gradlew about build --no-daemon --info
+        run: ./gradlew build --info --full-stacktrace
       - name: List files in libs
         run: ls -ahl build/libs
-      - uses: actions/upload-artifact@v3
+      - uses: actions/upload-artifact@v2
         with:
           name: build-artifacts-gradle
           path: |
-            build/libs/
-            !build/libs/libraries/
-            frontend/dist/spa
-          if-no-files-found: warn # 'warn' or 'ignore' are also available, defaults to `warn`
-      - uses: actions/upload-artifact@v3
-        if: failure()
-        with:
-          name: report
-          path: build/reports/tests/test/
-          if-no-files-found: warn # 'warn' or 'ignore' are also available, defaults to `warn`
-
-  tetris:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v3
-        with:
-          ref: tetris
-      - name: Set up JDK 8
-        uses: actions/setup-java@v3
-        with:
-          distribution: 'adopt'
-          java-version: '8'
-      - name: Where is Java
-        run: which java
-      - name: Grant execute permission for gradlew
-        run: chmod +x gradlew
-      - name: Build with Gradle
-        run: ./gradlew about build --no-daemon --info
-      - name: List files in libs
-        run: ls -ahl build/libs
-      - uses: actions/upload-artifact@v3
-        with:
-          name: build-artifacts-gradle
-          path: |
-            build/libs/
-            !build/libs/libraries/
-            frontend/dist/spa
-          if-no-files-found: warn # 'warn' or 'ignore' are also available, defaults to `warn`
-      - uses: actions/upload-artifact@v3
-        if: failure()
-        with:
-          name: report
-          path: build/reports/tests/test/
-          if-no-files-found: warn # 'warn' or 'ignore' are also available, defaults to `warn`
\ No newline at end of file
+            build
+          if-no-files-found: warn
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 58facc9e64f289360d252693f6d3854c3920ff47..ab27e223aac8b3995202f88e9d15372fc41824e1 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,11 +4,11 @@ stages:
   - Release
   - Build Release
 
-services:
-  - name: ghcr.io/griefed/gitlab-ci-cd:2.0.9
-    alias: docker
-
-image: ghcr.io/griefed/gitlab-ci-cd:2.0.9
+variables:
+  project_name: "$CI_PROJECT_NAME"
+  SEMANTIC_RELEASE_PACKAGE: "$CI_PROJECT_NAME"
+  GRADLE_OPTS: "-Dorg.gradle.daemon=false"
+  GRADLE_USER_HOME: "$CI_PROJECT_DIR/.gradle"
 
 workflow:
   rules:
@@ -16,17 +16,13 @@ workflow:
       when: never
     - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
       when: never
+    - if: '$CI_COMMIT_TITLE =~ /^RELEASE:.+$/ && $CI_COMMIT_BRANCH' # && $CI_COMMIT_TAG == null
+      when: never
     - when: always
 
 Gradle Test:
-  image: griefed/baseimage-ubuntu-jdk-8:2.0.13
+  image: griefed/baseimage-ubuntu-jdk-8:2.0.4
   stage: Gradle Test
-  services:
-    - name: griefed/gitlab-ci-cd:2.2.1
-      alias: docker
-  variables:
-    project_name: $CI_PROJECT_NAME
-    SEMANTIC_RELEASE_PACKAGE: $CI_PROJECT_NAME
   before_script:
     - echo "**** Running in $CI_JOB_ID ****"
     - echo "**** Java location ****"
@@ -35,60 +31,48 @@ Gradle Test:
     - java -version
     - echo "**** Allowing execution of gradlew ****"
     - chmod +x gradlew
-    - echo "**** Ensure clean environment ****"
-    - ./gradlew about
   script:
     - echo "**** Building ServerPackCreator ****"
-    - ./gradlew build --info
-  except:
-    refs:
-      - tags
-      - webservice
-    variables:
-      - $CI_COMMIT_TITLE =~ /^RELEASE:.+$/
+    - ./gradlew build --info --full-stacktrace
+  artifacts:
+    when: always
+    name: "$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA-$CI_JOB_NAME"
+    expose_as: "Gradle-Test-Artifacts"
+    paths:
+      - build
+    expire_in: 1 week
+  cache:
+    - key:
+        files:
+          - build.gradle
+          - gradle/gradle-wrapper.properties
+      paths:
+        - .gradle
 
 Release:
   needs:
     - job: 'Gradle Test'
       artifacts: false
   stage: Release
-  image: griefed/gitlab-ci-cd:2.2.1
-  services:
-    - name: griefed/gitlab-ci-cd:2.2.1
-      alias: docker
-  variables:
-    project_name: $CI_PROJECT_NAME
-    SEMANTIC_RELEASE_PACKAGE: $CI_PROJECT_NAME
+  image: ghcr.io/griefed/gitlab-ci-cd:2.0.9
   script:
     - npx semantic-release
-  only:
-    - main
-  except:
-    refs:
-      - tags
-    variables:
-      - $CI_COMMIT_TITLE =~ /^RELEASE:.+$/
+  rules:
+    - if: '$CI_COMMIT_BRANCH == "alpha" && $CI_COMMIT_TITLE !~ /^RELEASE:.+$/ && $CI_SERVER_HOST == "git.griefed.de"'
+    - if: '$CI_COMMIT_BRANCH == "beta" && $CI_COMMIT_TITLE !~ /^RELEASE:.+$/ && $CI_SERVER_HOST == "git.griefed.de"'
+    - if: '$CI_COMMIT_BRANCH == "main" && $CI_COMMIT_TITLE !~ /^RELEASE:.+$/ && $CI_SERVER_HOST == "git.griefed.de"'
 
 pages:
-  image: griefed/baseimage-ubuntu-jdk-8:2.0.13
+  image: griefed/baseimage-ubuntu-jdk-8:2.0.4
   stage: Documentation
-  services:
-    - name: griefed/gitlab-ci-cd:2.2.1
-      alias: docker
-  variables:
-    project_name: $CI_PROJECT_NAME
-    SEMANTIC_RELEASE_PACKAGE: $CI_PROJECT_NAME
   before_script:
     - which java
     - chmod +x gradlew
-    - ./gradlew about
   script:
-    - ./gradlew build --info -x test
+    - "./gradlew javaDoc -Pversion='' --info --full-stacktrace -x test"
     - cp -Rf build/docs/javadoc public
-    - LC_COLLATE=C ls -ahl --group-directories-first --color=auto
-      public
+    - LC_COLLATE=C ls -ahl --group-directories-first --color=auto public
   only:
-    - master
     - main
   artifacts:
     paths:
diff --git a/.releaserc.yml b/.releaserc.yml
index 3a50737410bf84deca3e69b0b19e70ce5f0d5241..1d68f44d04a7baac547e5c18e4b2fedfcb1d9519 100644
--- a/.releaserc.yml
+++ b/.releaserc.yml
@@ -64,7 +64,7 @@ generateNotes:
           section: '🧨 Breaking changes!'
           hidden: false
         - type: 'build'
-          section: 'âš— Build and Dependencies'
+          section: '🦊 CI/CD'
           hidden: false
         - type: 'chore'
           section: 'Other'
diff --git a/.run/Build -x test.run.xml b/.run/Build -x test.run.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ee3fed6625db3199ffa4d768ab3e73aab6a1f532
--- /dev/null
+++ b/.run/Build -x test.run.xml	
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="Build -x test" type="GradleRunConfiguration" factoryName="Gradle">
+    <ExternalSystemSettings>
+      <option name="executionName" />
+      <option name="externalProjectPath" value="$PROJECT_DIR$" />
+      <option name="externalSystemIdString" value="GRADLE" />
+      <option name="scriptParameters" value="--info --stacktrace -x test" />
+      <option name="taskDescriptions">
+        <list />
+      </option>
+      <option name="taskNames">
+        <list>
+          <option value="clean" />
+          <option value="build" />
+        </list>
+      </option>
+      <option name="vmOptions" />
+    </ExternalSystemSettings>
+    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
+    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
+    <DebugAllEnabled>false</DebugAllEnabled>
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.run/Build.run.xml b/.run/Build.run.xml
new file mode 100644
index 0000000000000000000000000000000000000000..dd93e0c680473240c7fe206bb30667055b6614be
--- /dev/null
+++ b/.run/Build.run.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="Build" type="GradleRunConfiguration" factoryName="Gradle">
+    <ExternalSystemSettings>
+      <option name="executionName" />
+      <option name="externalProjectPath" value="$PROJECT_DIR$" />
+      <option name="externalSystemIdString" value="GRADLE" />
+      <option name="scriptParameters" value="--info --stacktrace" />
+      <option name="taskDescriptions">
+        <list />
+      </option>
+      <option name="taskNames">
+        <list>
+          <option value="clean" />
+          <option value="build" />
+        </list>
+      </option>
+      <option name="vmOptions" />
+    </ExternalSystemSettings>
+    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
+    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
+    <DebugAllEnabled>false</DebugAllEnabled>
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b3b64da25a21db09e0c459e701e7a111c1d2a08..69c4df38821b0f7c4884698c7d0190c43dc0bb12 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,189 +1,3 @@
-## [3.0.16](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.15...3.0.16) (2022-08-30)
-
-
-### âš— Build and Dependencies
-
-* **Dependencies:** Update Mockito Core to 4.7.0 ([6f5be52](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/6f5be52a0c5872b35aae53ee0ef98dc7d9321723))
-* **Dependencies:** Update ServerPackCreator to 3.12.0 ([28aa2e0](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/28aa2e00b34837ee399115f86c076f3156c2eea2))
-
-## [3.0.15](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.14...3.0.15) (2022-08-10)
-
-
-### âš— Build and Dependencies
-
-* **deps:** bump junit-jupiter-api from 5.8.2 to 5.9.0 ([cd5526c](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/cd5526cad69bdff9bbf0e398c3662230e552ae92))
-* **deps:** bump junit-jupiter-engine from 5.8.2 to 5.9.0 ([987c90d](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/987c90d9150714c4f44e651f648572dae89d0c70))
-
-## [3.0.14](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.13...3.0.14) (2022-08-06)
-
-
-### 🛠 Fixes
-
-* **deps:** update junit5 monorepo to v5.9.0 ([f32dbf4](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/f32dbf435eff97564653e5992580f338b220805e))
-
-
-### Other
-
-* **deps:** update dependency ghcr.io/griefed/gitlab-ci-cd to v2.0.9 ([be309fb](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/be309fb259906174cb31b7f468dab98e36c7ff5f))
-* **deps:** update dependency gradle to v7.5 ([ca9ebb8](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/ca9ebb8d4cbf1b5faf89afaefa4f5b4d80665f3a))
-* **deps:** update dependency griefed/baseimage-ubuntu-jdk-8 to v2.0.12 ([c31bd6b](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/c31bd6b3b12ffc7f76779dfe62c1087f4fde25b8))
-* **deps:** update dependency griefed/gitlab-ci-cd to v2.0.9 ([77e0e72](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/77e0e72290fcb0e0cceb6c59c7ba76a0ca8d8571))
-
-## [3.0.13](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.12...3.0.13) (2022-07-25)
-
-
-### âš— Build and Dependencies
-
-* **Dependencies:** Update dependency ServerPackCreator to 3.10.1 ([401f194](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/401f1949b7f27364e460cb01a428c67318b98dca))
-* **deps:** bump JamesIves/github-pages-deploy-action ([c49c29c](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/c49c29cdaa1329683a7dd06a8e81e1399bb60b0d))
-
-## [3.0.12](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.11...3.0.12) (2022-07-16)
-
-
-### âš— Build and Dependencies
-
-* **Dependencies:** Update dependency ServerPackCreator to 3.9.0. ([4f402c0](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/4f402c0fcb822a0dba4c7fa1b452e3d241edae00))
-
-
-### Other
-
-* **deps:** update dependency griefed/baseimage-ubuntu-jdk-8 to v2.0.11 ([e24ce0e](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/e24ce0e0b5de447f092de10b7c88344ed005fea4))
-* **deps:** update dependency griefed/gitlab-ci-cd to v2.0.8 ([e9a98c7](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/e9a98c7f0e780ff7ed7ae835a98503533b2cdb1e))
-* **deps:** update dependency org.apache.logging.log4j:log4j-api to v2.18.0 ([aa2b824](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/aa2b824d8ef8fcc39400ce97accacc5809277b49))
-* **deps:** update dependency org.pf4j:pf4j to v3.7.0 ([3a0a9da](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/3a0a9da49fffb0da13bf47f4b463e4b264e7e90a))
-* **deps:** update jamesives/github-pages-deploy-action action to v4.3.4 ([fd9a81a](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/fd9a81afd57b2cbf290a9c02266ac4dc2a21bbbb))
-
-## [3.0.11](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.10...3.0.11) (2022-06-26)
-
-
-### âš— Build and Dependencies
-
-* **Dependencies:** Update dependency ServerPackCreator to 3.6.0. ([1196adc](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/1196adc7daa729ce9e3c1280499baef4cd7e7108))
-
-## [3.0.10](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.9...3.0.10) (2022-06-22)
-
-
-### âš— Build and Dependencies
-
-* **Dependencies:** Update dependency ServerPackCreator to 3.5.0. ([9ec4dfc](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/9ec4dfc6fde3bd77940633e1e9607ad5afdd9c02))
-
-## [3.0.9](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.8...3.0.9) (2022-06-18)
-
-
-### âš— Build and Dependencies
-
-* **Dependencies:** Update dependencies ([5935d8a](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/5935d8a0724a46e9c8332bc36a729201eb8c1e08))
-
-
-### Other
-
-* **deps:** update dependency de.griefed:serverpackcreator to v3.4.1 ([82e5f3b](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/82e5f3b44e977144ddd8d5320e21a98a99ca3a99))
-* **deps:** update dependency griefed/baseimage-ubuntu-jdk-8 to v2.0.10 ([2396cff](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/2396cff155d937817ce3146b1d7afc63d0290da6))
-* **deps:** update dependency griefed/baseimage-ubuntu-jdk-8 to v2.0.9 ([aacf2f8](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/aacf2f8b49851006ae86beb648800516b5f3bf75))
-* **deps:** update dependency griefed/gitlab-ci-cd to v2.0.6 ([304f169](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/304f169d0109a8f62a63667e88bb0033c81e1b50))
-* **deps:** update dependency griefed/gitlab-ci-cd to v2.0.7 ([1613c63](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/1613c633dcc55bdc803da8df2af232732e246358))
-* **deps:** update dependency org.mockito:mockito-core to v4.5.1 ([7f7cc1c](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/7f7cc1ce38e00941805d3657a08b60f5ba286aa5))
-* **deps:** update jamesives/github-pages-deploy-action action to v4.3.3 ([829433c](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/829433c2984fb190a878900a86f09d918fe76ae2))
-
-### [3.0.8](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.7...3.0.8) (2022-04-24)
-
-
-### âš— Build and Dependencies
-
-* **deps:** bump actions/upload-artifact from 2 to 3 ([4b39e58](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/4b39e586947e919fe4ec300e06ef71a3be1820d2))
-* **deps:** bump JamesIves/github-pages-deploy-action ([84948ea](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/84948eab9c81dc25bdd055163543f6ca9faae5d6))
-* **ServerPackCreator:** Update to 3.3.0 ([fd86bee](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/fd86bee12170ae19e324a584c7046c54ba56bcb8))
-
-### [3.0.7](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.6...3.0.7) (2022-04-15)
-
-
-### âš— Build and Dependencies
-
-* **ServerPackCreator:** Update to 3.1.0 ([8602f24](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/8602f24c6b9aa746f2907c693e0de79e85977bfa))
-
-
-### Other
-
-* **deps:** update actions/checkout action to v3 ([37f52e4](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/37f52e4154da7bcf37fd36153378be8d211683ff))
-* **deps:** update actions/upload-artifact action to v3 ([7b82039](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/7b820397e2c578ec2de1fbde2e4ac9d9d3f4ef46))
-* **deps:** update dependency gradle to v7.4.2 ([1294ba8](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/1294ba80e3c954ffedaa9968ffd79c504c170078))
-* **deps:** update dependency griefed/baseimage-ubuntu-jdk-8 to v2.0.8 ([fa73205](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/fa73205349c5dbeb2242eae3ec88825421915a4d))
-* **deps:** update dependency griefed/gitlab-ci-cd to v2.0.5 ([632e7a7](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/632e7a71190327ec00b52ce4ff41a33b55fbfb65))
-
-### [3.0.6](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.5...3.0.6) (2022-03-27)
-
-
-### âš— Build and Dependencies
-
-* **deps:** bump log4j-api from 2.17.1 to 2.17.2 ([95ffab5](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/95ffab5156663e9d0709ba1c1f146295a9667822))
-* **deps:** bump mockito-core from 4.3.1 to 4.4.0 ([d42748c](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/d42748c8e234972b67bc3fa350e492baff294fea))
-* **deps:** bump serverpackcreator from 3.0.0-beta.7 to 3.0.0-beta.9 ([ecc0ed3](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/ecc0ed37aa9305a714bd2f4cb641e561f1420a52))
-* **deps:** bump serverpackcreator from 3.0.0-beta.9 to 3.0.0-beta.10 ([3707498](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/3707498dfe61dc753856431c14a1817843dc21a8))
-* **deps:** Update ServerPackCreator to 3.0.1, amongst other dependency updates. ([08c4780](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/08c478054df7d36f5aef7a67c39da6f48d2e86a7))
-
-
-### Other
-
-* **deps:** update dependency de.griefed:serverpackcreator to v3.0.0-beta.9 ([d489876](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/d4898766dcbedd754242fc8960e52df4948276d0))
-* **deps:** update dependency griefed/baseimage-ubuntu-jdk-8 to v2.0.7 ([ccdafde](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/ccdafdeb1ef0e59f99660db1fdb980be91451110))
-
-### [3.0.5](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.4...3.0.5) (2022-02-27)
-
-
-### âš— Build and Dependencies
-
-* **ServerPackCreator:** Update to beta.7 ([b28d80b](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/b28d80b4fec0323cdc39aea75f49678a4e3060b5))
-
-
-### 🦊 CI/CD
-
-* **GitHub:** Correctly execute (pre)release actions when tags are pushed. ([34f5e0e](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/34f5e0ed694609f10ebac4c722badf299fc9176e))
-
-
-### Other
-
-* **deps:** update actions/setup-java action to v3 ([2daeabe](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/2daeabe7b2afba6ade6e04b24b7104eb316b35de))
-* **deps:** update dependency de.griefed:serverpackcreator to v3.0.0-beta.3 ([c2858c5](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/c2858c5747d2fd17e625528f0e508a224fbeec06))
-* **deps:** update dependency de.griefed:serverpackcreator to v3.0.0-beta.6 ([69429b0](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/69429b03879038861747d90199a8100b45057852))
-* **deps:** update dependency griefed/baseimage-ubuntu-jdk-8 to v2.0.6 ([d9273af](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/d9273afc07c421e21258fd4d55c22d05604908f8))
-* **deps:** update dependency griefed/gitlab-ci-cd to v2.0.3 ([e1c9704](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/e1c97043ab53764afa914bff12d2f0c77fd8831a))
-* **deps:** update griefed/baseimage-ubuntu-jdk-8 docker tag to v2.0.5 ([5b63cb8](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/5b63cb84097663f22971ea29b4fc84405492fce2))
-* **deps:** update jamesives/github-pages-deploy-action action to v4.2.5 ([6f9b52e](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/6f9b52e85aa2d2183d524ca73e71efc7e0357b8d))
-* **Icon:** Add icon ([b874a32](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/b874a321f9e505bb576771949d747d8737df0ca3))
-
-### [3.0.4](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.3...3.0.4) (2022-02-19)
-
-
-### âš— Build and Dependencies
-
-* **ServerPackCreator:** Update to beta.3 ([61689a2](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/61689a27bc2a4c2a6d0620774a4ab014806b01af))
-
-
-### 🦊 CI/CD
-
-* Fix GitHub release job asset upload for MiniGame branch ([79be391](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/79be3918b57a74e09428dcc75f65aacdc4a1a99c))
-
-### [3.0.3](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.2...3.0.3) (2022-02-15)
-
-
-### 🦊 CI/CD
-
-* **deps:** Bump ServerPackCreator to 3.0.0-beta.1 ([9d2ea8f](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/9d2ea8f4b36675e2cd6de16dd71c15a63c057048))
-* **branches:** Add different branches to releases and tests, so people can download the different examples and use them. ([43ac00d](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/43ac00df930107f578817a4fb5f84a9e62ea0868))
-* **release:** Don't build release assets on GitLab. Don't upload artifacts from test job. ([016d69c](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/016d69c0abcd1b237751787f2815c1465d6e5948))
-* **release:** On tag, build all three branches and upload assets to generated release. ([f15205a](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/f15205aa3c9f5df1ca2643a553b93e001376e6b4))
-
-
-### Other
-
-* Add info about additional example in alternativeExtensionDeclaration branch ([acbf95e](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/acbf95ef4a8cbc9514fb4fd281a5c906ef5ad4ce))
-* **deps:** update dependency gradle to v7.4 ([f87176b](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/f87176bf16db596c803f152443e49191a2bc8fd5))
-* **deps:** update griefed/baseimage-ubuntu-jdk-8 docker tag to v2.0.3 ([0a41972](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/0a419721a720e47ac83729f6ead8d7532bbbe5c3))
-* **deps:** update griefed/baseimage-ubuntu-jdk-8 docker tag to v2.0.4 ([b102243](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/b1022430720088830a696ecb70a7347d80f33b21))
-* **deps:** update griefed/gitlab-ci-cd docker tag to v2.0.1 ([f6096fe](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/f6096fea21b140465432dbe87c5e4d8380ad849d))
-* **deps:** update jamesives/github-pages-deploy-action action to v4.2.3 ([7e220fd](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/7e220fde79c2833a191d41cf314bd604de215b4a))
-* **README:** Rephrase addons section and include link to new addons overview website ([31b291a](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/31b291aceab2f40393b418259c87050cdaee1cb9))
-
 ### [3.0.2](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.1...3.0.2) (2022-01-31)
 
 
diff --git a/README.md b/README.md
index 83a6c2538bc5a255813b9b64f446f1564b283001..30857af64e2dcb01e907ab2b54ad78ab2d95b004 100644
--- a/README.md
+++ b/README.md
@@ -1,210 +1,304 @@
-# Example Addon for ServerPackCreator
+# 1. Example Addon for ServerPackCreator
 
-[![Homepage](https://img.shields.io/badge/Griefed.de-Homepage-c0ffee?style=for-the-badge&labelColor=325358&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAACylBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v6OsnIvAAAA7XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiQlJicoKSorLC0uLzAxMjU2Nzg5Ojs8Pj9AQUJERUZHSElLTE9QUVJTVFVWV1hZXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5/gIGCg4SGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6Cio6WmqKmqq6ytrrCxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jKy8zNzs/Q0dLT1NXW2Nrb3N3e3+Dh4uPk5ebn6Onq6+zt7u/w8fLz9PX29/j5+vv8/f4O/wLaAAAGUklEQVR42o3WBVsb2xoF4G9PQqgXubktUnd3d3fFqjjHQt2Vg9Xd3TXU3d1b3KEugczM+g8nY5VAmrxYZGaxtj1A9jgfcoS1SdKTUx73gg2kYZzOTcdIYQjKeVWZnPJ7a91bh2zc6/SJWbNj16F9G+aNa12VkXGjBYU+5FTTz0DqmPphB9MsIjRCgXnRXRH42pSc6lQCgLdCIYoifsL3IqcG8FB8e34sISpkUnjUjKRj2VpeIDmjmweFdYeRkcr/mtYjK9CNfstnt1pAyFxmJA3XIi5VUDucqEeOsV5p6u2v5tbl6Cecb9RtC2T5Qxk5wE35rN5u8uPKhHuMPPZB/FgClEzXUbl0s3jg6S3+Ubgnlati1w1XB74ExPhyJ4KbzQNHasQHVyeHdH56nxQpoZwOLIyHuLkC6ciJSrtEiNMZ2ev3BdjgTi6ouAsoGUJ2amUCh9zJJZVSgHy71XQ7CDzwJhf5vQRO/DqR4wR87qYn1+gS7gHi0gGdmvp5cKSv4tOsd1A68PaaH7loAg8bvuTz23s+lPS66BsvAhDX66rWlDlt4vEYitIgojbZULzvwqSH/OOPy8iZCBGyPQYiFmSB7GgFBpuNNPrtWfOZW2bzlcvmlFtnzGdvms3XLppTbqeYz18/15YkXq8heVObbAwb5DjLCJID4qjfmTBDyNTuxpi/vHtOCTGEhrarE/FX1cGTR1eMiuxxsTVJYgWp7GiSGe/A5rYHMVy68eGU35wddXc3o/EJ7u4rxlKLXXVYxHx91Y0DqNv2/7E5C5QOflIFoSkpZktPIhkxPMzNB7AzpHdYjcjh3XsOifl/aO+QOlPHtRs84E/vqL6jmwZP6HFO6sDNlyr8STJ2GECqLxHDLTmgcINpxZNZplPHTbOeJps23ZlhOn/AtOD5UtOua7GxZxMfMCKql8UDpxhJKmdCFOI4Iia+KCoGYO5rZ/qGn591Ihtu+V4euVVJ0qwUqZdakM3UE6mb41Lj2pOdfn9TGcYa6bC2JMkwEfv8dSQJmBDtYSZyJYDYGWAUSf4BlpAiIMJ/4R0XA2g1MJMkiUCUFrCs34bnrgaYgLVyle0QA7SAwMAGZlcDJgC75YC9EMdqAXcXBDxyNWA8cJBsdCeBUC1gZijnuIFnTVVFkgQAR8iGOwxEawHjfUPPkL1mJ+/0JKKW36C6TpIQYA9JtgLztYB1AcnvyE717LeFX5sT9YMmlyR/qZNIC4Et3xs0DCjToGeSu25KZNmARO0XBwKXOTVgaQAzk53aNYn07ewDdLWOAQEkac8jx9+oBFyOL2cVmPxJjd6LqtPSjFzOBt9emdwi8IeXccoQRraxazDYpAqqvnqnagoRFy8ARZ4k4S4AQmY9ZRlbL3hFP2Ol0ERDk0PklwbgPEeymQCEeZwUMKNt/INfA/DdPz/NAYsW8OMItRYBvPKTAoKm+JpdCfC4A5s7RpKNldKEWCng2Sy7SWQiNFHQZNAoC2zE9QayqZMKyStPooA/ovVyA6Yh0w5VfOWl2sNhFY9DZglkRIZ9kAnh0ip4RF8gonioSgc9hKpggADVo24foMhuQxRiheJxdWkrrywmohS7gds9zN8oAhD5r4WvEsnn3tvPJcofyxBbg7bDU5wHfLr2EcgO7NnUp7KeuOq+TTr2TxCB+/G6gFUjmAsBeUNKIQTSz9wvAGm1KeDE2vHSKqyHih/5GqqPnZ/nqi6mAUfc6BdN3gKXqgSMH9DNTETBO1RrqoZrDxdzpKpyGciuTXbGWYGDocv7xWcQUS40fQRompOi0gHg2wCyxy0EkBXhP/cWEeVD0w/ftSIbHVXeBwjRjMrQrxOBq9OMV3SOAzwmJPifBwR1OHbc1wJIH5q2sWvFcofQwivyCX/3NSAu0VO59AutgNUifji2f+O/cbL5VYLiFPHJM16LkFj+5sgBbtxbyCx3on79f52rP/+12iVvMCPHGp8TIRNS/22hI40xLku9XTxel37LMDUPCvGKP6k4n708FEvdyJlgEYqis2tmhE+dGDZr/dl0KxR8f3KqN49yCTyAkg7kVLNvgHh72YViAd9Z81JMLcakAp+bkFM+RbCsMxLn1Wnism1Hjh/YsSp2ZMtqjIjq7LW+9SWnqrzODjCQijFGPxiC71Ynp/RJrZnjehzZ+Q9fNggLmTcf8AAAAABJRU5ErkJggg==)](https://www.griefed.de)
-[![Blog](https://img.shields.io/badge/Griefed.de-Blog-c0ffee?style=for-the-badge&labelColor=325358&logo=wordpress)](https://blog.griefed.de)
-[![Fleet](https://img.shields.io/badge/Griefed.de-Fleet-c0ffee?style=for-the-badge&labelColor=325358&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABiVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8XJWL///8gNCTuAAAAgXRSTlMAAgMEBQYHCAkLDA4PEBESFhgZGh0eISIlKC8zNDU3OT0+P0BBQkhKS01QU1RVVltcXWJjZGlrbG1wcnN0eHl6e4CBhYyOkJWWmZ6foaKkqaqsrrKztLe8vsHExcbHy8zNz9DT1NXX2Nna293e3+Tm5+nq7O3u7/Hy9fb4+fr7/f4zgtRAAAABWklEQVR42mKgHhCFMbg0dHW1xNGlBcw5wTS7tkcJoNV5UJItBoMA3GPjcm3btm3btq1+8s1JxijuV0p+X5NzVcfNRoT4O26ClEZykdydI5ddCHDt+EfqthiwYoXfRH++yb9r8IZ+o/CpeB1hE/zMtXf0KYKkP9p8ZSaCfq1S2ddDU84zrsuXYXZjYy0VcBxQKYVmgQ9slS8HhUbxSP+kNAPB+kIyO1ggr5mn9GoX70wKydA4/RNQQSVPvGspZEHQ5ZKHK5lu8UymUi/enRQuxYjcN5LPH+RTQfcVlQ4AbfJ1n4JKRusG0ErpzCn3vjFMe8g9/eUU2himHEAilc+Vq6XJ4Zyx6eXTk3f6/IOww7i2oOliXNXQ/H5kHBcWSO2MowyKJc4VU/BLOGcMm04EJF3FyHsQImkvMj/tQBhrz0to+rwSUf70XvjT23VWxKLLaBkYGuqr+Y8f9Q3q0fzzGED8cgAAAABJRU5ErkJggg==)](https://fleet.griefed.de)
-[![GitHub](https://img.shields.io/badge/Griefed.de-Github-c0ffee?style=for-the-badge&labelColor=325358&logo=github)](https://github.com/Griefed)
-[![DockerHub](https://img.shields.io/badge/Griefed.de-DockerHub-c0ffee?style=for-the-badge&labelColor=325358&logo=docker&logoColor=white)](https://hub.docker.com/u/griefed)
-[![Discord](https://img.shields.io/badge/Griefed.de-Discord-c0ffee?style=for-the-badge&labelColor=325358&logo=discord&logoColor=white)](https://discord.griefed.de)
+This is an example server pack addon for [ServerPackCreator](https://github.com/Griefed/ServerPackCreator)
 
----
+ServerPackCreator provides several extension endpoints for [pf4j plugins](https://github.com/pf4j/pf4j), from hereon out called **addons**, to add
+additional functionality. This example addon demonstrates an implementation for all available extension endpoints of ServerPackCreator.
 
-# Sources, GitHub, GitLab and Mirroring and all that good stuff
+This repository demonstrates how extension for ServerPackCreator are implemented, one small example for every extension
+point available in ServerPackCreator.
 
-Repositories on GitHub are now for issues only. I've set up my own installation of GitLab and moved all my repositories over to [Git.Griefed.de](https://git.griefed.de/users/Griefed/projects). Make sure to check there first for the latest code before opening an issue on GitHub.
+## 1.1 Addon details
 
-For questions, you can always join my [Discord server](https://discord.griefed.de) and talk to me there.
+Take care to edit this section in the `build.gradle`-file if you forked, or intent on forking, this repository.
 
-###### This repository is available at:
+```groovy
+/*
+ CHANGE THESE VALUES
+    FOR YOUR OWN
+       ADDON
 
-- Source: https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon
-- Mirror: https://gitlab.com/Griefed/ServerPackCreatorExampleAddon
-- Mirror: https://github.com/Griefed/ServerPackCreatorExampleAddon
-- Mirror: https://gitea.com/Griefed/ServerPackCreatorExampleAddon
+ Addon ID must be unique.
+    Set it carefully!
+ */
+def pluginClass = 'de.griefed.exampleaddon.Example'
+def addon_id = 'example'
+def addon_name = 'Example Addon'
+def addon_description = 'An example addon for ServerPackCreator'
+def addon_author = 'Griefed'
+group 'de.griefed'
+version = "1.0.0"
+```
 
----
+`pluginClass` must point at the Addon/Plugin class of your addon. Think of it as the Main-Class-attribute from a regular JARs manifest.
 
-[![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/Griefed/ServerPackCreatorExampleAddon?include_prereleases&label=Latest%20Release&logo=Github&style=for-the-badge&color=c0ffee&labelColor=325358)](https://github.com/Griefed/ServerPackCreatorExampleAddon/releases/latest)
-[![GitHub](https://img.shields.io/github/license/Griefed/ServerPackCreatorExampleAddon?logo=GitHub&style=for-the-badge&color=c0ffee&labelColor=325358)](https://github.com/Griefed/ServerPackCreatorExampleAddon/blob/main/LICENSE)
+`addon_id` Is the ID used by ServerPackCreator to identify your addon, extensions in your addon, the global configuration-file of your addon (if you provide one) and
+for identifying extension-configurations from or given to a serverpackcreator.conf. Make it as **unique** as possible.
+[pf4j](https://pf4j.org/doc/plugins.html) does **not** allow for two addons with the same ID to co-exist in a given environment. The more unique
+your addon-ID is, the more likely it will be able to co-exist with any other addon in a given users ServerPackCreator environment.
 
-[![GitHub Repo stars](https://img.shields.io/github/stars/Griefed/ServerPackCreatorExampleAddon?label=GitHub%20Stars&style=for-the-badge&logo=Github&labelColor=325358&color=c0ffee)](https://github.com/Griefed/ServerPackCreatorExampleAddon)
-[![GitHub forks](https://img.shields.io/github/forks/Griefed/ServerPackCreatorExampleAddon?label=GitHub%20Forks&style=for-the-badge&logo=Github&labelColor=325358&color=c0ffee)](https://github.com/Griefed/ServerPackCreatorExampleAddon)
-[![GitHub contributors](https://img.shields.io/github/contributors/Griefed/ServerPackCreatorExampleAddon?color=c0ffee&label=Contributors&logo=GitHub&logoColor=white&style=for-the-badge&labelColor=325358)](https://github.com/Griefed/ServerPackCreatorExampleAddon/graphs/contributors)
-[![GitHub all releases](https://img.shields.io/github/downloads/Griefed/ServerPackCreatorExampleAddon/total?color=c0ffee&logo=GitHub&logoColor=white&labelColor=325358&style=for-the-badge)](https://github.com/Griefed/ServerPackCreatorExampleAddon/releases)
+`addon_name` Is good for identifying a troublesome addon in the logs.
 
----
+`addon_description`, `addon_author` and `version` are fancy to have and should contain a value, but they are not used by ServerPackCreator for any vital or sensitive operations.
 
+`group` well yeah, this should obviously be changed to **your** group as you're **not** me 😅
 
-This is an example server pack addon for [ServerPackCreator](https://github.com/Griefed/ServerPackCreator)
+### 1.1.1 Updating the implemented version of ServerPackCreator
 
-The following endpoints are available in ServerPackCreator to add you extra functionality with a plugin. This example plugin provides examples for all four of them:
+It's as simple as changing the version specified in the `dependencies`-section of the `build.gradle`:
 
-- TabExtension: Allows you to add your own JComponent in the form of an additional tab to the GUI. You can run whatever code you want in that tab.
+```groovy
+dependencies {
+    ...
+    implementation 'de.griefed:serverpackcreator:3.14.0'
+    ...
+}
+```
 
-- PreGenExtension: Will allow you to run your code before generation of a server pack starts
+## 1.2 Configuration Panel Extension
 
-- PreZipExtension: Will allow you to run your code after a server pack was generated, but before the ZIP-archive is created
+The configuration panel is intended to let you add a panel in which you, or the user of your addon, may
+configure something for any of the extensions added by your addon.
 
-- PostGenExtension: Will allow you to run your code after the server pack ZIP-archive was generated, so right at the on of the SPC process, basically.
+![configpanel](img/configpanel.png)
 
-Using this example plugin would result in the following or similar output in ServerPackCreator itself:
+The above example lets you configure four textfields, one for each extension point used during server pack 
+configuration checking and server pack generation. More on this in **Configuration Check Extension**.
 
-serverpackcreator.log:
-```
-INFO [main] (AbstractPluginManager.java:814) - Plugin 'example-plugin@0.0.1' resolved
-INFO [main] (AbstractPluginManager.java:357) - Start plugin 'example-plugin@0.0.1'
-INFO [main] (ApplicationPlugins.java:74) - Available PreGenExtension plugins:
-INFO [main] (ApplicationPlugins.java:76) - Name:       ServerPackCreatorExampleAddon
-INFO [main] (ApplicationPlugins.java:77) - Description:This is an example addon for ServerPackCreator showcasing all, currently 4, aspects ofServerPackCreators addon functionality. In this example you see code which gets executed before a server pack is generated,code that gets executed after a server pack was generated but before the ZIP-archive is created, code that gets executedafter this ZIP-archive was created, as well as code adding a new tabbed pane to the GUI.
-INFO [main] (ApplicationPlugins.java:78) - Version:    0.0.1
-INFO [main] (ApplicationPlugins.java:79) - Author:     Griefed
-INFO [main] (ApplicationPlugins.java:83) - Available PreZipExtension plugins:
-INFO [main] (ApplicationPlugins.java:85) - Name:       ServerPackCreatorExampleAddon
-INFO [main] (ApplicationPlugins.java:86) - Description:This is an example addon for ServerPackCreator showcasing all, currently 4, aspects ofServerPackCreators addon functionality. In this example you see code which gets executed before a server pack is generated,code that gets executed after a server pack was generated but before the ZIP-archive is created, code that gets executedafter this ZIP-archive was created, as well as code adding a new tabbed pane to the GUI.
-INFO [main] (ApplicationPlugins.java:87) - Version:    0.0.1
-INFO [main] (ApplicationPlugins.java:88) - Author:     Griefed
-INFO [main] (ApplicationPlugins.java:92) - Available PostGenExtension plugins:
-INFO [main] (ApplicationPlugins.java:94) - Name:       ServerPackCreatorExampleAddon
-INFO [main] (ApplicationPlugins.java:95) - Description:This is an example addon for ServerPackCreator showcasing all, currently 4, aspects ofServerPackCreators addon functionality. In this example you see code which gets executed before a server pack is generated,code that gets executed after a server pack was generated but before the ZIP-archive is created, code that gets executedafter this ZIP-archive was created, as well as code adding a new tabbed pane to the GUI.
-INFO [main] (ApplicationPlugins.java:96) - Version:    0.0.1
-INFO [main] (ApplicationPlugins.java:97) - Author:     Griefed
-INFO [main] (ApplicationPlugins.java:101) - Available TabExtension plugins:
-INFO [main] (ApplicationPlugins.java:103) - Name:       ServerPackCreatorExampleAddon
-INFO [main] (ApplicationPlugins.java:104) - Description:This is an example addon for ServerPackCreator showcasing all, currently 4, aspects ofServerPackCreators addon functionality. In this example you see code which gets executed before a server pack is generated,code that gets executed after a server pack was generated but before the ZIP-archive is created, code that gets executedafter this ZIP-archive was created, as well as code adding a new tabbed pane to the GUI.
-INFO [main] (ApplicationPlugins.java:105) - Version:    0.0.1
-INFO [main] (ApplicationPlugins.java:106) - Author:     Griefed
-```
+Extension configurations are saved to the serverpackcreator.conf of the server pack and re-loaded along
+with everything else, like the Minecraft version, modloader and modloader version etc.
 
-addons.log:
-```
-2022-01-26T18:20:45,896  INFO [main] (ExamplePlugin.java:45) - Starting ExamplePlugin...
-2022-01-26T18:20:45,897  INFO [main] (ExamplePlugin.java:46) - This methods should prepare the environment for anything you want to do with it.
-2022-01-26T18:20:45,897  INFO [main] (ExamplePlugin.java:47) - You could download some files. Create or replace some files. Basically you can do whatever you want.
-2022-01-26T18:21:55,874  INFO [pool-2-thread-1] (ServerPackHandler.java:253) - Executing PreGenExtension addons
-2022-01-26T18:21:55,874  INFO [pool-2-thread-1] (ServerPackHandler.java:255) - Executing plugin ServerPackCreatorExampleAddon
-2022-01-26T18:21:55,874  INFO [pool-2-thread-1] (ExamplePlugin.java:58) - This would run before a server pack generation.
-2022-01-26T18:21:55,875  INFO [pool-2-thread-1] (ExamplePlugin.java:59) - Received destination: G:/GitLab/ServerPackCreator/testruns/server-packs/Deathdusk
-2022-01-26T18:21:55,875  INFO [pool-2-thread-1] (ExamplePlugin.java:60) - We recieved the following configurationModel: ConfigurationModel{clientMods=[AdvancementPlaques-, AmbientSounds_, backtools-, BetterAdvancements-, BetterAnimationsCollection-, BetterDarkMode-, betterf3-, BetterF3-, BetterFoliage-, BetterPingDisplay-, BetterPlacement-, Blur-, catalogue-, cherishedworlds-, classicbar-, clickadv-, ClientTweaks_, configured-, Controlling-, CraftPresence-, CTM-, customdiscordrpc-, CustomMainMenu-, defaultoptions-, DefaultOptions_, desiredservers-, Ding-, drippyloadingscreen-, drippyloadingscreen_, Durability101-, dynmus-, dynamic-music-, DynamicSurroundings-, DynamicSurroundingsHuds-, eiramoticons-, EiraMoticons_, EnchantmentDescriptions-, EquipmentCompare-, extremesoundmuffler-, extremeSoundMuffler-, Fallingleaves-, fallingleaves-, fancymenu_, findme-, flickerfix-, FpsReducer-, FullscreenWindowed-, WindowedFullscreen-, InventoryEssentials_, InventorySpam-, invtweaks-, InventoryTweaks-, ItemBorders-, itemzoom, itlt-, jeed-, jeiintegration_, JustEnoughProfessions-, JEITweaker-, justenoughbeacons-, JustEnoughCalculation-, jehc-, just-enough-harvestcraft-, JustEnoughProfessions-, JustEnoughResources-, keywizard-, konkrete_, lazydfu-, LegendaryTooltips-, LightOverlay-, light-overlay-, LLOverlayReloaded-, loadmyresources_, lootbeams-, mcbindtype-, modnametooltip_, modnametooltip-, moreoverlays-, MouseTweaks-, multihotbar-, MyServerIsCompatible-, Neat, NotifMod-, OldJavaWarning-, ornaments-, overloadedarmorbar-, PackMenu-, PickUpNotifier-, Ping-, preciseblockplacing-, presencefootsteps-, PresenceFootsteps-, ReAuth-, ResourceLoader-, shutupexperimentalsettings-, SimpleDiscordRichPresence-, smoothboot-, sounddeviceoptions-, SpawnerFix-, spoticraft-, tconplanner-, timestamps-, Tips-, TipTheScales-, Toast Control-, Toast-Control-, torohealth-, toughnessbar-, TravelersTitles-, WorldNameRandomizer-], copyDirs=[config, mods], modpackDir='C:/Minecraft/Game/Instances/Deathdusk', javaPath='C:/Program Files/Java/jdk1.8.0_291/jre/bin/java.exe', minecraftVersion='1.18.1', modLoader='Fabric', modLoaderVersion='0.12.12', javaArgs='empty', serverPackSuffix='', serverIconPath='', serverPropertiesPath='', includeServerInstallation=true, includeServerIcon=true, includeServerProperties=true, includeZipCreation=true, curseModpack=null, projectName='null', fileName='null', fileDiskName='null', projectID=0, fileID=0}
-2022-01-26T18:21:55,875  INFO [pool-2-thread-1] (ExamplePlugin.java:61) - We received the following applicationProperties: {de.griefed.serverpackcreator.serverpack.autodiscoverenabled=true, de.griefed.serverpackcreator.spring.schedules.database.cleanup=0 0 24 * *, de.griefed.serverpackcreator.configuration.hastebinserver=https://haste.zneix.eu/documents, de.griefed.serverpackcreator.spring.artemis.queue.max_disk_usage=90, de.griefed.serverpackcreator.serverpack.overwrite.enabled=true, de.griefed.serverpackcreator.configuration.saveloadedconfig=false, de.griefed.serverpackcreator.gui.darkmode=true, de.griefed.serverpackcreator.configuration.directories.shouldexclude=overrides,packmenu,resourcepacks,server_pack,fancymenu, de.griefed.serverpackcreator.language=en_us, de.griefed.serverpackcreator.configuration.directories.serverpacks=server-packs, homeDir=G:/GitLab/ServerPackCreator/build/classes/java, de.griefed.serverpackcreator.curseforge.cleanup.enabled=true, de.griefed.serverpackcreator.plugins.directory=./plugins, de.griefed.serverpackcreator.versioncheck.prerelease=true, de.griefed.serverpackcreator.configuration.directories.mustinclude=mods,config,defaultconfigs,scripts,saves,seeds,libraries, de.griefed.serverpackcreator.serverpack.cleanup.enabled=true, de.griefed.serverpackcreator.spring.cursecontroller.regenerate.enabled=false, de.griefed.serverpackcreator.configuration.fallbackmodslist=AdvancementPlaques-,AmbientSounds_,backtools-,BetterAdvancements-,BetterAnimationsCollection-,BetterDarkMode-,betterf3-,BetterF3-,BetterFoliage-,BetterPingDisplay-,BetterPlacement-,Blur-,catalogue-,cherishedworlds-,classicbar-,clickadv-,ClientTweaks_,configured-,Controlling-,CraftPresence-,CTM-,customdiscordrpc-,CustomMainMenu-,defaultoptions-,DefaultOptions_,desiredservers-,Ding-,drippyloadingscreen-,drippyloadingscreen_,Durability101-,dynmus-,dynamic-music-,DynamicSurroundings-,DynamicSurroundingsHuds-,eiramoticons-,EiraMoticons_,EnchantmentDescriptions-,EquipmentCompare-,extremesoundmuffler-,extremeSoundMuffler-,Fallingleaves-,fallingleaves-,fancymenu_,findme-,flickerfix-,FpsReducer-,FullscreenWindowed-,WindowedFullscreen-,InventoryEssentials_,InventorySpam-,invtweaks-,InventoryTweaks-,ItemBorders-,itemzoom,itlt-,jeed-,jeiintegration_,JustEnoughProfessions-,JEITweaker-,justenoughbeacons-,JustEnoughCalculation-,jehc-,just-enough-harvestcraft-,JustEnoughProfessions-,JustEnoughResources-,keywizard-,konkrete_,lazydfu-,LegendaryTooltips-,LightOverlay-,light-overlay-,LLOverlayReloaded-,loadmyresources_,lootbeams-,mcbindtype-,modnametooltip_,modnametooltip-,moreoverlays-,MouseTweaks-,multihotbar-,MyServerIsCompatible-,Neat,NotifMod-,OldJavaWarning-,ornaments-,overloadedarmorbar-,PackMenu-,PickUpNotifier-,Ping-,preciseblockplacing-,presencefootsteps-,PresenceFootsteps-,ReAuth-,ResourceLoader-,shutupexperimentalsettings-,SimpleDiscordRichPresence-,smoothboot-,sounddeviceoptions-,SpawnerFix-,spoticraft-,tconplanner-,timestamps-,Tips-,TipTheScales-,Toast Control-,Toast-Control-,torohealth-,toughnessbar-,TravelersTitles-,WorldNameRandomizer-}
-2022-01-26T18:22:06,414  INFO [pool-2-thread-1] (ServerPackHandler.java:303) - Executing PreZipExtension addons
-2022-01-26T18:22:06,414  INFO [pool-2-thread-1] (ServerPackHandler.java:305) - Executing plugin ServerPackCreatorExampleAddon
-2022-01-26T18:22:06,414  INFO [pool-2-thread-1] (ExamplePlugin.java:98) - This would run after a server pack was generated, but BEFORE the ZIP-archive would be generated.
-2022-01-26T18:22:06,414  INFO [pool-2-thread-1] (ExamplePlugin.java:99) - Received destination: G:/GitLab/ServerPackCreator/testruns/server-packs/Deathdusk
-2022-01-26T18:22:06,414  INFO [pool-2-thread-1] (ExamplePlugin.java:100) - We recieved the following configurationModel: ConfigurationModel{clientMods=[AdvancementPlaques-, AmbientSounds_, backtools-, BetterAdvancements-, BetterAnimationsCollection-, BetterDarkMode-, betterf3-, BetterF3-, BetterFoliage-, BetterPingDisplay-, BetterPlacement-, Blur-, catalogue-, cherishedworlds-, classicbar-, clickadv-, ClientTweaks_, configured-, Controlling-, CraftPresence-, CTM-, customdiscordrpc-, CustomMainMenu-, defaultoptions-, DefaultOptions_, desiredservers-, Ding-, drippyloadingscreen-, drippyloadingscreen_, Durability101-, dynmus-, dynamic-music-, DynamicSurroundings-, DynamicSurroundingsHuds-, eiramoticons-, EiraMoticons_, EnchantmentDescriptions-, EquipmentCompare-, extremesoundmuffler-, extremeSoundMuffler-, Fallingleaves-, fallingleaves-, fancymenu_, findme-, flickerfix-, FpsReducer-, FullscreenWindowed-, WindowedFullscreen-, InventoryEssentials_, InventorySpam-, invtweaks-, InventoryTweaks-, ItemBorders-, itemzoom, itlt-, jeed-, jeiintegration_, JustEnoughProfessions-, JEITweaker-, justenoughbeacons-, JustEnoughCalculation-, jehc-, just-enough-harvestcraft-, JustEnoughProfessions-, JustEnoughResources-, keywizard-, konkrete_, lazydfu-, LegendaryTooltips-, LightOverlay-, light-overlay-, LLOverlayReloaded-, loadmyresources_, lootbeams-, mcbindtype-, modnametooltip_, modnametooltip-, moreoverlays-, MouseTweaks-, multihotbar-, MyServerIsCompatible-, Neat, NotifMod-, OldJavaWarning-, ornaments-, overloadedarmorbar-, PackMenu-, PickUpNotifier-, Ping-, preciseblockplacing-, presencefootsteps-, PresenceFootsteps-, ReAuth-, ResourceLoader-, shutupexperimentalsettings-, SimpleDiscordRichPresence-, smoothboot-, sounddeviceoptions-, SpawnerFix-, spoticraft-, tconplanner-, timestamps-, Tips-, TipTheScales-, Toast Control-, Toast-Control-, torohealth-, toughnessbar-, TravelersTitles-, WorldNameRandomizer-], copyDirs=[config, mods], modpackDir='C:/Minecraft/Game/Instances/Deathdusk', javaPath='C:/Program Files/Java/jdk1.8.0_291/jre/bin/java.exe', minecraftVersion='1.18.1', modLoader='Fabric', modLoaderVersion='0.12.12', javaArgs='empty', serverPackSuffix='', serverIconPath='', serverPropertiesPath='', includeServerInstallation=true, includeServerIcon=true, includeServerProperties=true, includeZipCreation=true, curseModpack=null, projectName='null', fileName='null', fileDiskName='null', projectID=0, fileID=0}
-2022-01-26T18:22:06,414  INFO [pool-2-thread-1] (ExamplePlugin.java:101) - We received the following applicationProperties: {de.griefed.serverpackcreator.serverpack.autodiscoverenabled=true, de.griefed.serverpackcreator.spring.schedules.database.cleanup=0 0 24 * *, de.griefed.serverpackcreator.configuration.hastebinserver=https://haste.zneix.eu/documents, de.griefed.serverpackcreator.spring.artemis.queue.max_disk_usage=90, de.griefed.serverpackcreator.serverpack.overwrite.enabled=true, de.griefed.serverpackcreator.configuration.saveloadedconfig=false, de.griefed.serverpackcreator.gui.darkmode=true, de.griefed.serverpackcreator.configuration.directories.shouldexclude=overrides,packmenu,resourcepacks,server_pack,fancymenu, de.griefed.serverpackcreator.language=en_us, de.griefed.serverpackcreator.configuration.directories.serverpacks=server-packs, homeDir=G:/GitLab/ServerPackCreator/build/classes/java, de.griefed.serverpackcreator.curseforge.cleanup.enabled=true, de.griefed.serverpackcreator.plugins.directory=./plugins, de.griefed.serverpackcreator.versioncheck.prerelease=true, de.griefed.serverpackcreator.configuration.directories.mustinclude=mods,config,defaultconfigs,scripts,saves,seeds,libraries, de.griefed.serverpackcreator.serverpack.cleanup.enabled=true, de.griefed.serverpackcreator.spring.cursecontroller.regenerate.enabled=false, de.griefed.serverpackcreator.configuration.fallbackmodslist=AdvancementPlaques-,AmbientSounds_,backtools-,BetterAdvancements-,BetterAnimationsCollection-,BetterDarkMode-,betterf3-,BetterF3-,BetterFoliage-,BetterPingDisplay-,BetterPlacement-,Blur-,catalogue-,cherishedworlds-,classicbar-,clickadv-,ClientTweaks_,configured-,Controlling-,CraftPresence-,CTM-,customdiscordrpc-,CustomMainMenu-,defaultoptions-,DefaultOptions_,desiredservers-,Ding-,drippyloadingscreen-,drippyloadingscreen_,Durability101-,dynmus-,dynamic-music-,DynamicSurroundings-,DynamicSurroundingsHuds-,eiramoticons-,EiraMoticons_,EnchantmentDescriptions-,EquipmentCompare-,extremesoundmuffler-,extremeSoundMuffler-,Fallingleaves-,fallingleaves-,fancymenu_,findme-,flickerfix-,FpsReducer-,FullscreenWindowed-,WindowedFullscreen-,InventoryEssentials_,InventorySpam-,invtweaks-,InventoryTweaks-,ItemBorders-,itemzoom,itlt-,jeed-,jeiintegration_,JustEnoughProfessions-,JEITweaker-,justenoughbeacons-,JustEnoughCalculation-,jehc-,just-enough-harvestcraft-,JustEnoughProfessions-,JustEnoughResources-,keywizard-,konkrete_,lazydfu-,LegendaryTooltips-,LightOverlay-,light-overlay-,LLOverlayReloaded-,loadmyresources_,lootbeams-,mcbindtype-,modnametooltip_,modnametooltip-,moreoverlays-,MouseTweaks-,multihotbar-,MyServerIsCompatible-,Neat,NotifMod-,OldJavaWarning-,ornaments-,overloadedarmorbar-,PackMenu-,PickUpNotifier-,Ping-,preciseblockplacing-,presencefootsteps-,PresenceFootsteps-,ReAuth-,ResourceLoader-,shutupexperimentalsettings-,SimpleDiscordRichPresence-,smoothboot-,sounddeviceoptions-,SpawnerFix-,spoticraft-,tconplanner-,timestamps-,Tips-,TipTheScales-,Toast Control-,Toast-Control-,torohealth-,toughnessbar-,TravelersTitles-,WorldNameRandomizer-}
-2022-01-26T18:22:45,053  INFO [pool-2-thread-1] (ServerPackHandler.java:333) - Executing PostGenExtension addons
-2022-01-26T18:22:45,053  INFO [pool-2-thread-1] (ServerPackHandler.java:335) - Executing plugin ServerPackCreatorExampleAddon
-2022-01-26T18:22:45,053  INFO [pool-2-thread-1] (ExamplePlugin.java:143) - This would run after the server pack ZIP-archive was created.
-2022-01-26T18:22:45,053  INFO [pool-2-thread-1] (ExamplePlugin.java:144) - Received destination: G:/GitLab/ServerPackCreator/testruns/server-packs/Deathdusk
-2022-01-26T18:22:45,053  INFO [pool-2-thread-1] (ExamplePlugin.java:145) - We recieved the following configurationModel: ConfigurationModel{clientMods=[AdvancementPlaques-, AmbientSounds_, backtools-, BetterAdvancements-, BetterAnimationsCollection-, BetterDarkMode-, betterf3-, BetterF3-, BetterFoliage-, BetterPingDisplay-, BetterPlacement-, Blur-, catalogue-, cherishedworlds-, classicbar-, clickadv-, ClientTweaks_, configured-, Controlling-, CraftPresence-, CTM-, customdiscordrpc-, CustomMainMenu-, defaultoptions-, DefaultOptions_, desiredservers-, Ding-, drippyloadingscreen-, drippyloadingscreen_, Durability101-, dynmus-, dynamic-music-, DynamicSurroundings-, DynamicSurroundingsHuds-, eiramoticons-, EiraMoticons_, EnchantmentDescriptions-, EquipmentCompare-, extremesoundmuffler-, extremeSoundMuffler-, Fallingleaves-, fallingleaves-, fancymenu_, findme-, flickerfix-, FpsReducer-, FullscreenWindowed-, WindowedFullscreen-, InventoryEssentials_, InventorySpam-, invtweaks-, InventoryTweaks-, ItemBorders-, itemzoom, itlt-, jeed-, jeiintegration_, JustEnoughProfessions-, JEITweaker-, justenoughbeacons-, JustEnoughCalculation-, jehc-, just-enough-harvestcraft-, JustEnoughProfessions-, JustEnoughResources-, keywizard-, konkrete_, lazydfu-, LegendaryTooltips-, LightOverlay-, light-overlay-, LLOverlayReloaded-, loadmyresources_, lootbeams-, mcbindtype-, modnametooltip_, modnametooltip-, moreoverlays-, MouseTweaks-, multihotbar-, MyServerIsCompatible-, Neat, NotifMod-, OldJavaWarning-, ornaments-, overloadedarmorbar-, PackMenu-, PickUpNotifier-, Ping-, preciseblockplacing-, presencefootsteps-, PresenceFootsteps-, ReAuth-, ResourceLoader-, shutupexperimentalsettings-, SimpleDiscordRichPresence-, smoothboot-, sounddeviceoptions-, SpawnerFix-, spoticraft-, tconplanner-, timestamps-, Tips-, TipTheScales-, Toast Control-, Toast-Control-, torohealth-, toughnessbar-, TravelersTitles-, WorldNameRandomizer-], copyDirs=[config, mods], modpackDir='C:/Minecraft/Game/Instances/Deathdusk', javaPath='C:/Program Files/Java/jdk1.8.0_291/jre/bin/java.exe', minecraftVersion='1.18.1', modLoader='Fabric', modLoaderVersion='0.12.12', javaArgs='empty', serverPackSuffix='', serverIconPath='', serverPropertiesPath='', includeServerInstallation=true, includeServerIcon=true, includeServerProperties=true, includeZipCreation=true, curseModpack=null, projectName='null', fileName='null', fileDiskName='null', projectID=0, fileID=0}
-2022-01-26T18:22:45,053  INFO [pool-2-thread-1] (ExamplePlugin.java:146) - We received the following applicationProperties: {de.griefed.serverpackcreator.serverpack.autodiscoverenabled=true, de.griefed.serverpackcreator.spring.schedules.database.cleanup=0 0 24 * *, de.griefed.serverpackcreator.configuration.hastebinserver=https://haste.zneix.eu/documents, de.griefed.serverpackcreator.spring.artemis.queue.max_disk_usage=90, de.griefed.serverpackcreator.serverpack.overwrite.enabled=true, de.griefed.serverpackcreator.configuration.saveloadedconfig=false, de.griefed.serverpackcreator.gui.darkmode=true, de.griefed.serverpackcreator.configuration.directories.shouldexclude=overrides,packmenu,resourcepacks,server_pack,fancymenu, de.griefed.serverpackcreator.language=en_us, de.griefed.serverpackcreator.configuration.directories.serverpacks=server-packs, homeDir=G:/GitLab/ServerPackCreator/build/classes/java, de.griefed.serverpackcreator.curseforge.cleanup.enabled=true, de.griefed.serverpackcreator.plugins.directory=./plugins, de.griefed.serverpackcreator.versioncheck.prerelease=true, de.griefed.serverpackcreator.configuration.directories.mustinclude=mods,config,defaultconfigs,scripts,saves,seeds,libraries, de.griefed.serverpackcreator.serverpack.cleanup.enabled=true, de.griefed.serverpackcreator.spring.cursecontroller.regenerate.enabled=false, de.griefed.serverpackcreator.configuration.fallbackmodslist=AdvancementPlaques-,AmbientSounds_,backtools-,BetterAdvancements-,BetterAnimationsCollection-,BetterDarkMode-,betterf3-,BetterF3-,BetterFoliage-,BetterPingDisplay-,BetterPlacement-,Blur-,catalogue-,cherishedworlds-,classicbar-,clickadv-,ClientTweaks_,configured-,Controlling-,CraftPresence-,CTM-,customdiscordrpc-,CustomMainMenu-,defaultoptions-,DefaultOptions_,desiredservers-,Ding-,drippyloadingscreen-,drippyloadingscreen_,Durability101-,dynmus-,dynamic-music-,DynamicSurroundings-,DynamicSurroundingsHuds-,eiramoticons-,EiraMoticons_,EnchantmentDescriptions-,EquipmentCompare-,extremesoundmuffler-,extremeSoundMuffler-,Fallingleaves-,fallingleaves-,fancymenu_,findme-,flickerfix-,FpsReducer-,FullscreenWindowed-,WindowedFullscreen-,InventoryEssentials_,InventorySpam-,invtweaks-,InventoryTweaks-,ItemBorders-,itemzoom,itlt-,jeed-,jeiintegration_,JustEnoughProfessions-,JEITweaker-,justenoughbeacons-,JustEnoughCalculation-,jehc-,just-enough-harvestcraft-,JustEnoughProfessions-,JustEnoughResources-,keywizard-,konkrete_,lazydfu-,LegendaryTooltips-,LightOverlay-,light-overlay-,LLOverlayReloaded-,loadmyresources_,lootbeams-,mcbindtype-,modnametooltip_,modnametooltip-,moreoverlays-,MouseTweaks-,multihotbar-,MyServerIsCompatible-,Neat,NotifMod-,OldJavaWarning-,ornaments-,overloadedarmorbar-,PackMenu-,PickUpNotifier-,Ping-,preciseblockplacing-,presencefootsteps-,PresenceFootsteps-,ReAuth-,ResourceLoader-,shutupexperimentalsettings-,SimpleDiscordRichPresence-,smoothboot-,sounddeviceoptions-,SpawnerFix-,spoticraft-,tconplanner-,timestamps-,Tips-,TipTheScales-,Toast Control-,Toast-Control-,torohealth-,toughnessbar-,TravelersTitles-,WorldNameRandomizer-}
+To see how this is achieved, please see
+
+```java
+@Override
+public ArrayList<CommentedConfig> serverPackExtensionConfig() {...}
+``` 
+
+and 
+
+```java
+@Override
+public void setServerPackExtensionConfig(ArrayList<CommentedConfig> serverPackExtensionConfig) {...}
 ```
 
-Additionally, the following files/directories would be created in the server pack-directory:
+in the `ConfigurationPanel`-class.
+
+## 1.3 Tab Extension
+
+Tab extensions allow you to add whole tabs to the GUI of ServerPackCreator. These additional tabs are intended
+to let you add textfields and such, which allow you to configure your global addon configuration.
+You may add anything you want to it. The sky is the limit!
+
+![tab](img/tabextension.png)
+
+The above example adds a button which, when pressed, opens a minimalistic Tetris game in a new window.
+It's not supposed to be actually that entertaining, but rather to demonstrate that you can do what you want inside
+your tab.
+
+Below the big button are some textfields which allow you to change some values of the global addon-wide configuration.
+Global addon-configurations are handed to you by ServerPackCreator when the tab is instantiated. The only thing
+you need to take care of is to call `saveConfiguration()` from within your tab to save the configuration.
+The example above simply adds a button `Set values` which does just that. 
+
+Global addon-configurations are passed to every extension, along with any available extension-specific configuration,
+automatically, so you don't have to worry about anything other than actually saving changes you made in the tab.
+
+Maybe have a timer auto-save every few seconds? Your tab, your choice! 😁
+
+## 1.4 Configuration Check Extension
+
+The configuration check extension point allows you to run your own config checks, be that on any of the
+already available data from the server pack config tab, or your own data from the configuration panel, or your
+own tab, or whatever else you may want to check.
+
+![check](img/configcheck.png)
+
+The above example simply checks whether the string in `text` of the passed `CommentedConfig` in a list
+of passed configs contains text. If it does, then we add a custom error message to the list of errors encountered
+during configuration checks.
+That list is then displayed to the user after the configurations checks have all run.
+
+For details see `runCheck(...) {...}` in the `ConfigurationCheck`-class.
+
+Keep in mind that the method must return `true` in order to trigger a config check failure on ServerPackCreators part.
+Only if any one configuration check, be that ServerPackCreator native or from addons, returns `true` will the
+error messages be displayed to the user.
+
+Make use of this extension point in combination with the **Configuration Panel Extension** and/or **Tab Extension** in order to
+check user input for any errors!
+
+## 1.5 Pre Server Pack Generation Extension
+
+The Pre Server Pack Generation extensions run, as the name implies, *right before* the generation of a server pack really begins.
+You may use this to prepare the environment for any of the tailing extensions.
+
+![pregen](img/pregen.png)
+
+The above example shows the run of a PreGen extension, with the global addon configuration as well as the extension-specific
+extension passed to it by ServerPackCreator.
+
+See the `PreGeneration`-class for details on how the example above was achieved.
+
+## 1.6 Pre Server Pack ZIP-archive Creation Extension
+
+The Pre Server Pack ZIP-archive Creation extensions run, as the name implies, *right before* the creation of the server packs ZIP-archive is, or would be,
+started. Want to add any files to the ZIP-archive? Or make sure some file doesn't make it into the ZIP-archive?
+
+![prezip](img/prezip.png)
+
+The above example shows the run of a PreZip extension, with the global addon configuration as well as the extension-specific
+extension passed to it by ServerPackCreator.
+
+See the `PreZipArchive`-class for details on how the example above was achieved.
+
+## 1.7 Post Server Pack Generation Extension
+
+The Post Server Pack Generation extensions run, as the name implies, *after* the generation of a server pack has finished.
+Want to add any files to the server pack, but don't want them to end up in the ZIP-archive? Maybe download,
+install and configure DynMap with some renderdata? This would be the place to do that!
+
+![postgen](img/postgen.png)
+
+The above example shows the run of a PreGen extension, with the global addon configuration as well as the extension-specific
+extension passed to it by ServerPackCreator.
 
-- `./ExampleStartExtension`
-- `./ExampleArchiveExtension`
-- `serverpackcreator.conf` as it was used to generate the server pack
-- `./some/folder/with/a/name`
+See the `PostGeneration`-class for details on how the example above was achieved.
 
-| Example at boot time       | Example after generation finished |
-|----------------------------|-----------------------------------|
-| ![boot](img/boot.png)      | ![preGen](img/afterGen.png)       |
+See now why the ConfigPanel, ConfigCheck and Tab extensions are so nice to have?
+The possibilities are (almost) **endless**!😁 
 
-**Example tab**
+# 2. The reason for allowing ServerPackCreator to run addons:
 
-![tab](img/exampleTab.png)
+Some people need additional functionality for their server packs, or have some additional wishes for
+them. Some of those
+things may not fit into the core functionality of ServerPackCreator itself.
 
-# 1. Addons
+It may also be that it is such a niche feature, that I either don't have the time to code it in, or
+simply don't want to.
+Maybe it doesn't fit into the overall design of ServerPackCreator, too. Who knows, it could be any
+of those reasons or another.
 
-## 1.1 Why
+**Hence, the addon functionality.**
 
-There are things which people want to do with their server packs which could most certainly be automated. Some of those
-things so special, or maybe out of place, that they would not warrant a separate feature for ServerPackCreator itself.
+This allows people to write their own addons to expand the functionality of ServerPackCreator with
+their own features as
+they see fit.
 
-It may also be that it is such a niche feature, that I either don't have the time to code it in, or simply don't want to.
-Maybe it doesn't fit into the overall design of ServerPackCreator, too. Who knows, it could be any of those reasons or another.
+# 3. How
 
-**Hence, the addon functionality!**
+During the start of ServerPackCreator, all addons are loaded and then started. As per the pf4j
+plugin lifecycle, you may use the `start()` method of your addon-class to run any operations needed
+to ensure your environment is set up.
+ServerPackCreator provides a nice `ServerPackCreatorExampleAddon`-class which you can extend if you so
+desire. 
+What it does is it prints some information about your addon during the start of ServerPackCreator,
+which in turn helps you, any other addon-dev and me with debugging things. You may, of course,
+choose not to extend the aforementioned class, and simply extend pf4j's `Plugin`-class, setting up
+your very own addon from scratch.
 
-This allows people to write their own addons to expand the functionality of ServerPackCreator with their own features as
-they see fit or want.
+For detailed documentation about Pf4j, visit https://pf4j.org/
 
-For documentation about Pf4j, visit the [Pf4j documentation](https://pf4j.org/)
+After addons have been loaded an started by ServerPackCreator, it will list all available extensions. Depending on the mode in which ServerPackCreator is run
 
-## 1.2 Adding your own
+- additional tabs may get added the GUI
+- additional panels may get added to the server pack configuration tab
+- additional extensions for configuration checking and server pack generation may be added
 
-How to get your own addon into this list:
+## 3.1 Extension Endpoints
 
-If you have written your own addon or plugin for ServerPackCreator and you would like to see it added here, please open an issue over at ServerPackCreatoron GitHub, using the Documentation template.
+One plugin can have multiple extensions. You may add and provide as many as you like.
+Currently existing endpoint and their intended purpose:
 
-For an addon to be accepted, you must at least provide:
-- The name of the repository, and therefore the addon.
-- The owner of the repository, and therefore the addon.
-- The branch of the repository where the main code resides in.
-- A description of the plugin or addon.
+| **Extension Endpoint**                 | **Description**                                                                                                                                                                                                                                                                                                                  |
+|----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Configuration Check                    | Allows you to run your own, additional, configuration checks. Useful if you provide any of the other extensions and want to make them configurable.<br>The Configuration Check Extension of your addon receives the global configuration for your addon, as well as any server pack specific extension configurations available. |
+| Tab Extension                          | Allows you to add an entirely new tab to the GUI of ServerPackCreator. A usage example for this: Provide configuration possibilities in case you have a global configuration for your addon.                                                                                                                                     |
+| Configuration Panel                    | Allows you to add additional panels to the server pack configuration tab. A usage example for this: Your extensions have configuration you would like to be configurable on a server pack - by - server pack basis.                                                                                                              |
+| Pre Server Pack Generation             | Allows you to run your own operations before the generation of a server pack starts. You may provide configurations via the previously mentioned Tab- and Configuration Panel extensions.                                                                                                                                        |
+| Pre Server Pack ZIP-archive Generation | Allows you to run your own operations after the server pack was generated, but before the ZIP-archive creation would start. You may provide configurations via the previously mentioned Tab- and Configuration Panel extensions.                                                                                                 |
+| Post Server Pack Generation            | Allows you to run your own operations after the generation of a server pack has finished. You may provide configurations via the previously mentioned Tab- and Configuration Panel extensions.                                                                                                                                   |
 
-A curated list of officially acknowledged addons/plugins can be found at [addons.griefed.de](https://addons.griefed.de) (redirects to [GitHub Pages](https://griefed.github.io/ServerPackCreator-Addons-Overview/#/))
+## 3.2 Data provided to extensions
 
-## 1.3 Examples for potential addons
+Depending on the extension, different data will be supplied by ServerPackCreator, to the extension, automatically.
 
-Some examples for potential addons can be found [in this discussion thread](https://github.com/Griefed/ServerPackCreator/discussions/201).
+| **Extension Endpoint**                                                                                | **Description**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
+|-------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Configuration Check                                                                                   | 1. The Version Meta ServerpackCreator uses for Minecraft, Forge, Fabric, LegacyFabric and Quilt related operations.<br>2. The configuration of ServerPackCreator, ApplicationProperties.<br>3. Commonly used utilities across ServerPackCreator<br>4. Receives the configuration model which was just checked by ServerPackCreator.<br>5. A list of encountered errors during previous configuration checks to which you may add your own encountered errors, if any.<br>6. A global, addon-specific, configuration, if you've provided one.<br>6. A list of server pack-specific configurations. You may provide as many as you like, one for each of your extensions for example. |
+| Tab Extension                                                                                         | 1. The Version Meta ServerpackCreator uses for Minecraft, Forge, Fabric, LegacyFabric and Quilt related operations.<br>2. The configuration of ServerPackCreator, ApplicationProperties.<br>3. Commonly used utilities across ServerPackCreator<br>4. A global, addon-specific, configuration, if you've provided one.<br>4.1. The configuration-file for your addon-wide configuration, used to load and save.                                                                                                                                                                                                                                                                     |
+| Configuration Panel                                                                                   | 1. The Version Meta ServerpackCreator uses for Minecraft, Forge, Fabric, LegacyFabric and Quilt related operations.<br>2. The configuration of ServerPackCreator, ApplicationProperties.<br>3. Commonly used utilities across ServerPackCreator<br>4. The tab in which the panel(s) reside in, giving you access to its data.<br>5. A global, addon-specific, configuration, if you've provided one.<br>6. The name of your extension, used by the `ExtensionConfigPanel`-class to add a title to a titled border around your panel.<br>7. The ID of the addon which holds the extension. This is the, unique to a given environment, unique identifier of your addon.              |
+| Pre Server Pack Generation,<br>Pre Server Pack ZIP-archive Generation,<br>Post Server Pack Generation | 1. The Version Meta ServerpackCreator uses for Minecraft, Forge, Fabric, LegacyFabric and Quilt related operations.<br>2. Commonly used utilities across ServerPackCreator<br>3. The configuration of ServerPackCreator, ApplicationProperties.<br>4. Receives the configuration model which was just checked by ServerPackCreator.<br>5. The destination at which the server pack was/is/will be/being generated.<br>6. A global, addon-specific, configuration, if you've provided one.<br>7. A list of server pack-specific configurations. You may provide as many as you like, one for each of your extensions for example.                                                    |
 
-Some excerpts:
-1. Changelog generator, by @TheButterbrotMan at [Feature request]: Changelog generator #198
-   - A changelog generator that checks the differences to the previous version and generates a changelog.
+As you can see, a lot of data and information is provided by ServerPackCreator for your convenience.
+If your addon provides a global configuration, you do not need to take care of it, manage it, ensure
+it exists or load it. ServerPackCreator will extract it and provide any and all of your extensions
+with it, so you may use, change, overwrite and save it as you so desire.
 
-2. Bundle Adoptium Java with server packs, by @kreezxil at [Feature request]: Bundle Adoptium Java #199
-   - Because modpacks need one of the either Java 8, 16, or 17, it would be nice to have the corresponding https://adoptium.net java prebundled with the server pack.
+Furthermore, any server pack specific configuration provided by you via a Configuration Panel will
+be stored in the server pack related configuration file, along with any other default fields of
+ServerPackCreator. When said configuration file is then loaded, your Configuration Panel extension
+is again, automatically, provided with your extension configurations. No need to load it yourself,
+all you have to make sure is to provide the changed data back to ServerPackCreator for when a given
+user saves their configuration.
 
-3. Automatic setup of a server pack for [BlueMap](https://www.curseforge.com/minecraft/mc-mods/bluemap)
-   - Check all mods in the specified modpacks mods-directory for textures, and if any are found, add the mod to
-     BlueMap's resourcepack folder `config/bluemap/resourcepacks`, install BlueMap for the specified Minecraft and Forge/Fabric
-     version and voilà!
+See [pf4j documentation](https://pf4j.org/doc/extensions) on extensions.
 
-# 2. How
+## 3.3 Global addon configuration
 
-During the start of ServerPackCreator, all plugins are loaded and started. If you have anything you need to run then and there,
-use `public void start() {...}` and do your thing.
+This example contains a `config.toml`-file which serves as a global configuration for your addon and
+any extensions it provides. As mentioned previously, every one of your extensions will receive this
+global configuration and in order for it to be changeable, you need to provide a suitable TabExtension.
 
-For documentation about Pf4j, visit https://pf4j.org/
+If you intend on using the global configuration, bear in mind:
+1. to **not** rename the file yourself. ServerPackCreator extracts it from your addon and stores it under a different filename, matching your addons ID
+2. that your addon ID should be as unique as possible. Duplicate IDs will cause conflicts with other addons which have the same ID as yours
+3. pf4j relies on each addon to have a unique ID, otherwise pf4j will encounter issues if multiple addons with the same ID are provided
+4. Change the ID in `build.grdle`, in the `def addon_id`-section. Change the other values accordingly, too
 
-If you have ideas and/or suggestions for improvements to the addon-system in ServerPackCreator, open an improvement-issue over at the ServerPackCreator [issues page](https://github.com/Griefed/ServerPackCreator/issues/new?assignees=Griefed&labels=enhancement&template=improvement.yml&title=%5BImprovement+request%5D%3A+)
+ServerPackCreator will manage and provide the global configuration itself. You simply have to take
+care to provide, change and use the values therein.
 
-## 2.1 Extensions
+## 3.4 Addon identification
 
-One plugin can have multiple extensions.
+The `addon.toml`-file contains values by which to identify your addon in the logs. Do not edit this
+file manually, as the `processResources`-task will automatically replace the values with the ones
+you set in the `def <key>`-section.
 
-ServerPackCreator passes the `ConfigurationModel` and `ApplicationProperties`, used to create a server pack, to all `PreGenExtension`, `PreZipExtension` and `PostGenExtension` extensions.
+As well as with the `addon.toml`-file, your addon#s manifest must contain specific information. This
+information is also automatically replace.
 
-If you have a plugin with multiple extensions of the same type, you can set the priority in which they are executed by specifiying and ordinal for your extension: `@Extension(ordinal = 1)`
-The higher the ordinal, the lower the priority. See [pf4j documentation](https://pf4j.org/doc/extensions) on extensions.
+Again, make sure to change the values in the `def <key>`-section!
 
-## 2.2 Logs
+## 3.5 Logs
 
-If you want to print log messages, then use the `LOG_ADDONS` logger as shown in this example. This will result in ServerPackCreator writing any and all log entries from this logger to the `addons.log`-file.
+If you want to print log messages, then use the `LOG_ADDONS` logger as shown in this example. This
+will result in ServerPackCreator writing any and all log entries from this logger to
+the `addons.log`-file.
 
-# 3. Building
+# 4. Building
 
 1. Fork and clone this repository
 2. Make your changes and additions.
-3. Build with `gradlew about build --info`.
+3. Build with `gradlew clean build --info --stacktrace`.
 4. Copy the JAR-file from `build/libs` to the plugins-directory created by ServerPackCreator.
 5. Run ServerPackCreator!
 
-## 4. Contributing
+# 5. Contributing
 
-If you have written an addon, let me know by creating an issue in this repository. Provide a short description of what your
-addon does and a link to the GitHub repository as well. I will add it to a list in the README of ServerPackCreator.
+If you have written an addon, let me know by creating an issue in this repository. Provide a short
+description of what your
+addon does and a link to the GitHub repository as well. I will add it to a list in the README of
+ServerPackCreator.
 
-**NOTE: I only add addons which are open source. I will NOT add any direct download links to any file. People must be able
-to check your code before they download and install your addon, and as such, I will only add a link to your addon-respository
+**NOTE: I only add addons which are open source. I will NOT add any direct download links to any
+file. People must be able
+to check your code before they download and install your addon, and as such, I will only add a link
+to your addon-respository
 along with a small description, if you provided one.
 
 Example:
 
-| Addon                                                                                                                                           | Creator | Description                                                                                                                                                                                         |
-|:------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| [ExampleAddon](https://github.com/Griefed/ServerPackCreatorExampleAddon)                                                                        | Griefed | An example addon providing a starting point for addon development.                                                                                                                                  |
-| [ExampleAddon Alternative Extension Declaration](https://github.com/Griefed/ServerPackCreatorExampleAddon/tree/alternativeExtensionDeclaration) | Griefed | An example addon providing a starting point for addon development. This addon provides an example for a different way of declaring extensions as well as reading entries from the plugins manifest. |
-| [Example MiniGame](https://github.com/Griefed/ServerPackCreatorExampleAddon/tree/tetris)                                                        | Griefed | Play Tetris in a new window whilst your server packs generate!                                                                                                                                      |
+| Addon                                                                                    | Creator | Description                                                                                             |
+|:-----------------------------------------------------------------------------------------|:--------|:--------------------------------------------------------------------------------------------------------|
+| [ExampleAddon](https://github.com/Griefed/ServerPackCreatorExampleAddon)                 | Griefed | An example addon providing a starting point for addon development with one example for every extension. |
diff --git a/build.gradle b/build.gradle
index 6b18c5d247ff271e0398e9ebe61f1dcdc3acabfb..36dfba658a570eba912c69ebbf93217c64e2d26f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,10 +5,24 @@ plugins {
   id 'idea'
 }
 
+//noinspection GroovyUnusedAssignment
+sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8
+/*
+ CHANGE THESE VALUES
+    FOR YOUR OWN
+       ADDON
+
+ Addon ID must be unique.
+    Set it carefully!
+ */
 group 'de.griefed'
+version = "1.0.0"
+def pluginClass = group + '.exampleaddon.Example'
+def addon_id = 'example'
+def addon_name = 'Example Addon'
+def addon_description = 'An example addon for ServerPackCreator, demonstrating all extension points available.'
+def addon_author = 'Griefed'
 
-//noinspection GroovyUnusedAssignment
-sourceCompatibility = targetCompatibility = '1.8'
 
 sourceSets {
   //noinspection GroovyAssignabilityCheck
@@ -35,28 +49,15 @@ sourceSets {
 repositories {
   mavenCentral()
   maven { url "https://jitpack.io" }
-}
-
-configurations {
-  embed
-  implementation.extendsFrom(embed)
+  maven { url 'https://repo.spring.io/release' }
 }
 
 dependencies {
-  // Dependencies required for any plugin and/or extension to work
-  implementation 'org.pf4j:pf4j:3.7.0'
   annotationProcessor 'org.pf4j:pf4j:3.7.0'
-  implementation 'de.griefed:serverpackcreator:3.12.0'
-
-  // Required if you want to log to any of ServerPackCreators logs
-  implementation 'org.apache.logging.log4j:log4j-api:2.18.0'
-
-  // Dependencies your plugin or extension requires and need to be shipped within the plugin#s JAR-file
-  embed 'commons-io:commons-io:2.11.0'
-  embed 'org.apache.commons:commons-lang3:3.12.0'
+  implementation 'de.griefed:serverpackcreator:3.14.0'
 
   // Testing
-  testImplementation 'org.mockito:mockito-core:4.7.0'
+  testImplementation 'org.mockito:mockito-core:4.8.0'
   testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
   testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
 }
@@ -65,57 +66,58 @@ test {
   useJUnitPlatform()
   // Mention test result in logs
   testLogging {
-    events "passed",
+    events(
+        "passed",
         "skipped",
         "failed"
+    )
   }
 }
 
-// Include specific files in resources folder, like the license and readme.
-tasks.register('about', Copy) {
-  dependsOn tasks.named('clean')
-
-  from layout.projectDirectory.file("LICENSE") into layout.projectDirectory.dir("src/main/resources")
-  from layout.projectDirectory.file("README.md") into layout.projectDirectory.dir("src/main/resources")
-}
-
-tasks.withType(Javadoc) {
-  options.addStringOption('encoding', 'UTF-8')
-}
-
-javadoc {
-  options.memberLevel = JavadocMemberLevel.PRIVATE
-  classpath = sourceSets.main.runtimeClasspath
-}
-
-java {
-  withSourcesJar()
-  withJavadocJar()
+processResources {
+  filesMatching("addon.toml") {
+    expand(
+        "version": project.version,
+        "addon_id": addon_id,
+        "addon_name": addon_name,
+        "addon_description": addon_description,
+        "addon_author": addon_author,
+        "addon_class": pluginClass
+    )
+  }
+  copy {
+    from layout.projectDirectory.file("LICENSE")
+    into layout.projectDirectory.dir("src/main/resources")
+  }
+  copy {
+    from layout.projectDirectory.file("README.md")
+    into layout.projectDirectory.dir("src/main/resources")
+  }
+  copy {
+    from layout.projectDirectory.file("CHANGELOG.md")
+    into layout.projectDirectory.dir("src/main/resources")
+  }
 }
 
 jar {
   duplicatesStrategy = DuplicatesStrategy.EXCLUDE
 
-  from {
-    configurations.embed.findAll { it.name.endsWith('jar') }.collect { zipTree(it) }
-  }
-
   //noinspection GroovyAssignabilityCheck
   manifest {
     attributes(
-        "Main-Class": "de.griefed.serverpackcreatoraddonexample.ExamplePlugin",
-        "Class-Path": configurations.embed.findAll { it.name.endsWith('jar') }.collect { zipTree(it) },
-        "Description": "Example plugin for ServerPackCreator",
+        "Main-Class": pluginClass,
+        "Description": addon_description,
         "Built-By": System.getProperty("user.name"),
         "Build-Timestamp": new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(new Date()),
         "Created-By": "Gradle ${gradle.gradleVersion}",
         "Build-Jdk": "${System.getProperty('java.version')} (${System.getProperty('java.vendor')} ${System.getProperty('java.vm.version')})",
         "Build-OS": "${System.getProperty('os.name')} ${System.getProperty('os.arch')} ${System.getProperty('os.version')}",
-        "Plugin-Class": "de.griefed.serverpackcreatoraddonexample.ExamplePlugin",
-        "Plugin-Id": "example-plugin",
-        "Plugin-Provider": "Griefed",
-        "Plugin-Version": "0.0.1",
-        "Plugin-Description": "Example plugin for ServerPackCreator",
+        "Plugin-Class": pluginClass,
+        "Plugin-Id": addon_id,
+        "Plugin-Name": addon_name,
+        "Plugin-Provider": addon_author,
+        "Plugin-Version": project.version,
+        "Plugin-Description": addon_description
     )
   }
 }
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000000000000000000000000000000000000..f7aa37070694907bf1401aacf79e0e89950880ee
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.daemon=false
+#org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError
+org.gradle.configureondemand=true
+org.gradle.caching=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 249e5832f090a2944b7473328c07c9755baa3196..7454180f2ae8848c63b8b4dea2cb829da983f2fa 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ae04661ee733431762e7ccf8ab9b7409ed44960c..2e6e5897b5285c749d75662c65ac5d2904c37bc6 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index a69d9cb6c20655813e44515156e7253a2a239138..1b6c787337ffb79f0e3cf8b1e9f00f680a959de1 100644
--- a/gradlew
+++ b/gradlew
@@ -205,12 +205,6 @@ set -- \
         org.gradle.wrapper.GradleWrapperMain \
         "$@"
 
-# Stop when "xargs" is not available.
-if ! command -v xargs >/dev/null 2>&1
-then
-    die "xargs is not available"
-fi
-
 # Use "xargs" to parse quoted args.
 #
 # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
diff --git a/gradlew.bat b/gradlew.bat
index 53a6b238d414d91c30c5644c82393d27416fbbe6..ac1b06f93825db68fb0c0b5150917f340eaa5d02 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -14,7 +14,7 @@
 @rem limitations under the License.
 @rem
 
-@if "%DEBUG%"=="" @echo off
+@if "%DEBUG%" == "" @echo off
 @rem ##########################################################################
 @rem
 @rem  Gradle startup script for Windows
@@ -25,7 +25,7 @@
 if "%OS%"=="Windows_NT" setlocal
 
 set DIRNAME=%~dp0
-if "%DIRNAME%"=="" set DIRNAME=.
+if "%DIRNAME%" == "" set DIRNAME=.
 set APP_BASE_NAME=%~n0
 set APP_HOME=%DIRNAME%
 
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
 
 set JAVA_EXE=java.exe
 %JAVA_EXE% -version >NUL 2>&1
-if %ERRORLEVEL% equ 0 goto execute
+if "%ERRORLEVEL%" == "0" goto execute
 
 echo.
 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,15 +75,13 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
 
 :end
 @rem End local scope for the variables with windows NT shell
-if %ERRORLEVEL% equ 0 goto mainEnd
+if "%ERRORLEVEL%"=="0" goto mainEnd
 
 :fail
 rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
 rem the _cmd.exe /c_ return code!
-set EXIT_CODE=%ERRORLEVEL%
-if %EXIT_CODE% equ 0 set EXIT_CODE=1
-if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
-exit /b %EXIT_CODE%
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
 
 :mainEnd
 if "%OS%"=="Windows_NT" endlocal
diff --git a/img/afterGen.png b/img/afterGen.png
deleted file mode 100644
index 08155f2711ef22e9c4e25f2c3ef28e72567c5ddf..0000000000000000000000000000000000000000
Binary files a/img/afterGen.png and /dev/null differ
diff --git a/img/boot.png b/img/boot.png
deleted file mode 100644
index 0dbb3510d43d3ce4d931facd07a3970bff00e757..0000000000000000000000000000000000000000
Binary files a/img/boot.png and /dev/null differ
diff --git a/img/configcheck.png b/img/configcheck.png
new file mode 100644
index 0000000000000000000000000000000000000000..7acf1778af974700d9544571f1d64ea453f8476b
Binary files /dev/null and b/img/configcheck.png differ
diff --git a/img/configpanel.png b/img/configpanel.png
new file mode 100644
index 0000000000000000000000000000000000000000..710a12bcf1cfb32380cbdc7e033b9801878bb8c3
Binary files /dev/null and b/img/configpanel.png differ
diff --git a/img/exampleTab.png b/img/exampleTab.png
deleted file mode 100644
index d661c778f6d3b0e2359ec54cfcd1630661a7fb20..0000000000000000000000000000000000000000
Binary files a/img/exampleTab.png and /dev/null differ
diff --git a/img/icon.png b/img/icon.png
deleted file mode 100644
index 46b0a0fea97521757a8f905b6274aa70c2df2625..0000000000000000000000000000000000000000
Binary files a/img/icon.png and /dev/null differ
diff --git a/img/postgen.png b/img/postgen.png
new file mode 100644
index 0000000000000000000000000000000000000000..4bc2799918d97709dd124fcc9b9b4f2ee443e6d4
Binary files /dev/null and b/img/postgen.png differ
diff --git a/img/pregen.png b/img/pregen.png
new file mode 100644
index 0000000000000000000000000000000000000000..e0bafe29e7f3117e7e89a9cdc7ac2d34315b2368
Binary files /dev/null and b/img/pregen.png differ
diff --git a/img/prezip.png b/img/prezip.png
new file mode 100644
index 0000000000000000000000000000000000000000..744f661fb9eb3b4d614fc6bc9c9f9976b310ff38
Binary files /dev/null and b/img/prezip.png differ
diff --git a/img/tabextension.png b/img/tabextension.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd9d4ff612786ed36ebfd0de45d248e265e334ec
Binary files /dev/null and b/img/tabextension.png differ
diff --git a/settings.gradle b/settings.gradle
index 8247b072987e144b0dd05e5e2d5b2d172bf7f411..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +0,0 @@
-rootProject.name = 'ExampleAddon'
diff --git a/src/main/java/de/griefed/exampleaddon/Example.java b/src/main/java/de/griefed/exampleaddon/Example.java
new file mode 100644
index 0000000000000000000000000000000000000000..f4372ebf62dfe695ce79d9f9e1ec5b8016d2f225
--- /dev/null
+++ b/src/main/java/de/griefed/exampleaddon/Example.java
@@ -0,0 +1,36 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Griefed
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package de.griefed.exampleaddon;
+
+import de.griefed.serverpackcreator.addons.ServerPackCreatorAddon;
+import java.io.IOException;
+import org.pf4j.PluginWrapper;
+
+public class Example extends ServerPackCreatorAddon {
+
+  public Example(final PluginWrapper wrapper) throws IOException {
+    super(wrapper);
+  }
+}
diff --git a/src/main/java/de/griefed/exampleaddon/configcheck/ConfigurationCheck.java b/src/main/java/de/griefed/exampleaddon/configcheck/ConfigurationCheck.java
new file mode 100644
index 0000000000000000000000000000000000000000..e91f25ced459c7224753042fd758adb7c1a53a5f
--- /dev/null
+++ b/src/main/java/de/griefed/exampleaddon/configcheck/ConfigurationCheck.java
@@ -0,0 +1,180 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Griefed
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package de.griefed.exampleaddon.configcheck;
+
+import com.electronwill.nightconfig.core.CommentedConfig;
+import com.electronwill.nightconfig.toml.TomlWriter;
+import de.griefed.serverpackcreator.ApplicationProperties;
+import de.griefed.serverpackcreator.ConfigurationModel;
+import de.griefed.serverpackcreator.addons.configurationhandler.ConfigCheckExtension;
+import de.griefed.serverpackcreator.utilities.common.Utilities;
+import de.griefed.serverpackcreator.versionmeta.VersionMeta;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.pf4j.Extension;
+
+@Extension
+public class ConfigurationCheck implements ConfigCheckExtension {
+
+  private static final Logger LOG_ADDONS = LogManager.getLogger("AddonsLogger");
+  private TomlWriter tomlWriter = null;
+
+  /**
+   * @param versionMeta           Instance of {@link VersionMeta} so you can work with available
+   *                              Minecraft, Forge, Fabric, LegacyFabric and Quilt versions.
+   * @param applicationProperties Instance of {@link ApplicationProperties} The current
+   *                              configuration of ServerPackCreator, like the default list of
+   *                              clientside-only mods, the server pack directory etc.
+   * @param utilities             Instance of {@link Utilities} commonly used across
+   *                              ServerPackCreator.
+   * @param configurationModel    The configuration to check.
+   * @param encounteredErrors     A list of encountered errors during any and all checks. The list
+   *                              is displayed to the user if it contains any entries.
+   * @param addonConfig           Configuration for this addon, conveniently provided by
+   *                              ServerPackCreator.
+   * @param packSpecificConfigs   Modpack and server pack specific configurations for this addon,
+   *                              conveniently provided by ServerPackCreator.
+   * @return <code>true</code> if an error was encountered. <code>false</code> if the checks were
+   * successful.
+   * @throws Exception if any unexpected error is encountered during the execution of this method.
+   * @author Griefed
+   */
+  @Override
+  public boolean runCheck(
+      VersionMeta versionMeta,
+      ApplicationProperties applicationProperties,
+      Utilities utilities,
+      ConfigurationModel configurationModel,
+      List<String> encounteredErrors,
+      Optional<CommentedConfig> addonConfig,
+      ArrayList<CommentedConfig> packSpecificConfigs)
+      throws Exception {
+
+    boolean bool = false;
+
+    LOG_ADDONS.info("I am: " + getName() + "(" + getVersion() + ") by " + getAuthor() + ". "
+        + getDescription());
+    LOG_ADDONS.info("I would provide additional checks for a given server pack configuration!");
+
+    LOG_ADDONS.info("Maybe something based on this server packs Minecraft version "
+        + configurationModel.getMinecraftVersion() + "?");
+
+    if (addonConfig.isPresent()) {
+      LOG_ADDONS.info("I got passed the following configuration:");
+
+      StringWriter stringWriter = new StringWriter();
+
+      getTomlWriter().write(addonConfig.get(), stringWriter);
+      LOG_ADDONS.info(stringWriter);
+    }
+
+    LOG_ADDONS.info("I got passed the following pack specific configuration(s):");
+
+    for (int i = 0; i < packSpecificConfigs.size(); i++) {
+
+      LOG_ADDONS.info("Configuration " + i + " of " + packSpecificConfigs.size() + ":");
+
+      StringWriter stringWriter = new StringWriter();
+
+      getTomlWriter().write(packSpecificConfigs.get(i), stringWriter);
+      LOG_ADDONS.info(stringWriter);
+
+      if (!packSpecificConfigs.get(i).get("text").toString().isEmpty()) {
+        bool = true;
+        encounteredErrors.add("Extension " + packSpecificConfigs.get(i).get("extension").toString()
+            + " encountered an error.");
+      }
+    }
+
+    return bool;
+  }
+
+  private TomlWriter getTomlWriter() {
+    if (tomlWriter == null) {
+      tomlWriter = new TomlWriter();
+    }
+    return tomlWriter;
+  }
+
+  /**
+   * Get the if of this extension. Used by ServerPackCreator to determine which configuration, if
+   * any, to provide to any given extension being run.
+   *
+   * @return The ID of this extension.
+   * @author Griefed
+   */
+  @Override
+  public String getExtensionId() {
+    return "configcheckexample";
+  }
+
+  /**
+   * Get the name of this addon.
+   *
+   * @return The name of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getName() {
+    return "Example Configuration Check Extension";
+  }
+
+  /**
+   * Get the description of this addon.
+   *
+   * @return The description of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getDescription() {
+    return "An example for an extension which executes additional configuration checks.";
+  }
+
+  /**
+   * Get the author of this addon.
+   *
+   * @return The author of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getAuthor() {
+    return "Griefed";
+  }
+
+  /**
+   * Get the version of this addon.
+   *
+   * @return The version of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getVersion() {
+    return "0.0.1-SNAPSHOT";
+  }
+}
diff --git a/src/main/java/de/griefed/exampleaddon/gui/panel/ConfigurationPanel.java b/src/main/java/de/griefed/exampleaddon/gui/panel/ConfigurationPanel.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2bdec12b5520e1b6cf2af5465be1bdb1a863103
--- /dev/null
+++ b/src/main/java/de/griefed/exampleaddon/gui/panel/ConfigurationPanel.java
@@ -0,0 +1,258 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Griefed
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package de.griefed.exampleaddon.gui.panel;
+
+import com.electronwill.nightconfig.core.CommentedConfig;
+import com.electronwill.nightconfig.toml.TomlFormat;
+import de.griefed.serverpackcreator.ApplicationProperties;
+import de.griefed.serverpackcreator.addons.swinggui.ExtensionConfigPanel;
+import de.griefed.serverpackcreator.swing.TabCreateServerPack;
+import de.griefed.serverpackcreator.utilities.common.Utilities;
+import de.griefed.serverpackcreator.versionmeta.VersionMeta;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.util.ArrayList;
+import java.util.Optional;
+import javax.swing.JLabel;
+import javax.swing.JSeparator;
+import javax.swing.JTextField;
+
+public class ConfigurationPanel extends ExtensionConfigPanel {
+
+  private final String panelName;
+  private final JTextField pregen = new JTextField();
+  private final JTextField prezip = new JTextField();
+  private final JTextField postgen = new JTextField();
+  private final JTextField confcheck = new JTextField();
+
+  /**
+   * Construct a panel which allows users to further customize their ServerPackCreator experience.
+   *
+   * @param versionMeta           Instance of {@link VersionMeta} so you can work with available
+   *                              Minecraft, Forge, Fabric, LegacyFabric and Quilt versions.
+   * @param applicationProperties Instance of {@link ApplicationProperties} The current
+   *                              configuration of ServerPackCreator, like the default list of
+   *                              clientside-only mods, the server pack directory etc.
+   * @param utilities             Instance of {@link Utilities} commonly used across
+   *                              ServerPackCreator.
+   * @param tabCreateServerPack   Instance of {@link TabCreateServerPack} to give you access to the
+   *                              various fields inside it, like the modpack directory, selected
+   *                              Minecraft, modloader and modloader versions, etc.
+   * @param addonConfig           Addon specific configuration conveniently provided by
+   *                              ServerPackCreator. This is the global configuration of the addon
+   *                              which provides the ConfigPanelExtension to ServerPackCreator.
+   * @param extensionName         The name the titled border of this ConfigPanel will get.
+   * @param pluginID              The ID of the addon providing this extension implementation. The
+   *                              pluginID determines which extension specific configurations are
+   *                              provided to this panel, and how they are stored in a given
+   *                              serverpackcreator.conf.
+   * @author Griefed
+   */
+  protected ConfigurationPanel(VersionMeta versionMeta,
+      ApplicationProperties applicationProperties,
+      Utilities utilities,
+      TabCreateServerPack tabCreateServerPack,
+      Optional<CommentedConfig> addonConfig,
+      String extensionName, String pluginID) {
+
+    super(versionMeta, applicationProperties, utilities, tabCreateServerPack, addonConfig,
+        extensionName, pluginID);
+
+    panelName = extensionName + " (" + pluginID + ")";
+
+    Font extensionHeader = new Font("Noto Sans Display Regular", Font.BOLD, 18);
+
+    setLayout(new GridBagLayout());
+    GridBagConstraints gridBagConstraints = new GridBagConstraints();
+    gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
+    gridBagConstraints.anchor = GridBagConstraints.WEST;
+    gridBagConstraints.insets = new Insets(5, 5, 5, 5);
+    gridBagConstraints.gridwidth = 1;
+    gridBagConstraints.gridheight = 1;
+    gridBagConstraints.weightx = 1;
+    gridBagConstraints.weighty = 1;
+
+    JLabel pregenexample = new JLabel(
+        "Example Pre Server Pack Generation Extension (pregenexample)");
+    pregenexample.setFont(extensionHeader);
+    gridBagConstraints.gridx = 0;
+    gridBagConstraints.gridy = 0;
+    add(pregenexample, gridBagConstraints);
+    gridBagConstraints.gridy += 1;
+    JLabel pregenexampletext = new JLabel("text: ");
+    add(pregenexampletext, gridBagConstraints);
+    gridBagConstraints.gridy += 1;
+    add(pregen, gridBagConstraints);
+
+    add(new JSeparator());
+
+    JLabel prezipexample = new JLabel("Example Pre ZIP-Archive Creation Extension (prezipexample)");
+    prezipexample.setFont(extensionHeader);
+    gridBagConstraints.gridx = 0;
+    gridBagConstraints.gridy += 1;
+    add(prezipexample, gridBagConstraints);
+    gridBagConstraints.gridy += 1;
+    JLabel prezipexampletext = new JLabel("text: ");
+    add(prezipexampletext, gridBagConstraints);
+    gridBagConstraints.gridy += 1;
+    add(prezip, gridBagConstraints);
+
+    add(new JSeparator());
+
+    JLabel postgenexample = new JLabel(
+        "Example Post Server Pack Generation Extension (postgenexample)");
+    postgenexample.setFont(extensionHeader);
+    gridBagConstraints.gridx = 0;
+    gridBagConstraints.gridy += 1;
+    add(postgenexample, gridBagConstraints);
+    gridBagConstraints.gridy += 1;
+    JLabel postgenexampletext = new JLabel("text: ");
+    add(postgenexampletext, gridBagConstraints);
+    gridBagConstraints.gridy += 1;
+    add(postgen, gridBagConstraints);
+
+    add(new JSeparator());
+
+    JLabel configcheckexample = new JLabel(
+        "Example Configuration Check Extension (configcheckexample)");
+    configcheckexample.setFont(extensionHeader);
+    gridBagConstraints.gridx = 0;
+    gridBagConstraints.gridy += 1;
+    add(configcheckexample, gridBagConstraints);
+    gridBagConstraints.gridy += 1;
+    JLabel configcheckexampletext = new JLabel("text: ");
+    add(configcheckexampletext, gridBagConstraints);
+    gridBagConstraints.gridy += 1;
+    add(confcheck, gridBagConstraints);
+  }
+
+  /**
+   * Retrieve this extensions server pack specific configuration. When no configuration with configs
+   * for this extension has been loaded yet, the returned list is empty. Fill it with life!
+   *
+   * @return Config list to be used in subsequent server pack generation runs, by various other
+   * extensions.
+   * @author Griefed
+   */
+  @Override
+  public ArrayList<CommentedConfig> serverPackExtensionConfig() {
+
+    LOG_ADDONS.info("Retrieving " + panelName + " configuration.");
+
+    String comment = " The ID of the extension.\n This field is used to identify the configuration to use when\n running the extension during config checks or server pack generation.";
+
+    CommentedConfig pregenexample = TomlFormat.newConfig();
+    pregenexample.setComment("extension", comment);
+    pregenexample.set("extension", "pregenexample");
+    pregenexample.set("text", pregen.getText());
+
+    CommentedConfig prezipexample = TomlFormat.newConfig();
+    prezipexample.setComment("extension", comment);
+    prezipexample.set("extension", "prezipexample");
+    prezipexample.set("text", prezip.getText());
+
+    CommentedConfig postgenexample = TomlFormat.newConfig();
+    postgenexample.setComment("extension", comment);
+    postgenexample.set("extension", "postgenexample");
+    postgenexample.set("text", postgen.getText());
+
+    CommentedConfig configcheckexample = TomlFormat.newConfig();
+    configcheckexample.setComment("extension", comment);
+    configcheckexample.set("extension", "configcheckexample");
+    configcheckexample.set("text", confcheck.getText());
+
+    SERVERPACK_EXTENSION_CONFIG.clear();
+    SERVERPACK_EXTENSION_CONFIG.add(pregenexample);
+    SERVERPACK_EXTENSION_CONFIG.add(prezipexample);
+    SERVERPACK_EXTENSION_CONFIG.add(postgenexample);
+    SERVERPACK_EXTENSION_CONFIG.add(configcheckexample);
+
+    return SERVERPACK_EXTENSION_CONFIG;
+  }
+
+  /**
+   * Pass the extension configuration to the configuration panel so it can then, in turn, load the
+   * available configurations and make them editable, if so desired.
+   *
+   * @param serverPackExtensionConfig The list of extension configurations to pass to the
+   *                                  configuration panel.
+   * @author Griefed
+   */
+  @Override
+  public void setServerPackExtensionConfig(
+      ArrayList<CommentedConfig> serverPackExtensionConfig) {
+
+    LOG_ADDONS.info("Setting " + panelName + " configuration.");
+
+    serverPackExtensionConfig.forEach(
+        config -> {
+          if (config.getOptional("extension").isPresent()) {
+
+            String extension = config.getOptional("extension").get().toString();
+
+            switch (extension) {
+              case "pregenexample":
+
+                pregen.setText(config.get("text").toString());
+                break;
+
+              case "prezipexample":
+
+                prezip.setText(config.get("text").toString());
+                break;
+
+              case "postgenexample":
+
+                postgen.setText(config.get("text").toString());
+                break;
+
+              case "configcheckexample":
+
+                confcheck.setText(config.get("text").toString());
+                break;
+            }
+          }
+        }
+    );
+  }
+
+  /**
+   * Clear the interface, or in other words, reset this extensions config panel UI. If your Config
+   * Panel Extensions has no elements you wish to reset, then simply overwrite this method with an
+   * empty method body.<br><br> The <code>clear()</code>-method is called when the owning
+   * <code>TabCreateServerPack.clearInterface()</code>-method is called.
+   */
+  @Override
+  public void clear() {
+    LOG_ADDONS.info("Clearing " + panelName + " interface.");
+
+    pregen.setText("");
+    prezip.setText("");
+    postgen.setText("");
+    confcheck.setText("");
+  }
+}
diff --git a/src/main/java/de/griefed/exampleaddon/gui/panel/Panel.java b/src/main/java/de/griefed/exampleaddon/gui/panel/Panel.java
new file mode 100644
index 0000000000000000000000000000000000000000..b5384a4e32d2d1e64e0693be362e43f4951ea002
--- /dev/null
+++ b/src/main/java/de/griefed/exampleaddon/gui/panel/Panel.java
@@ -0,0 +1,134 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Griefed
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package de.griefed.exampleaddon.gui.panel;
+
+import com.electronwill.nightconfig.core.CommentedConfig;
+import de.griefed.serverpackcreator.ApplicationProperties;
+import de.griefed.serverpackcreator.ServerPackHandler;
+import de.griefed.serverpackcreator.addons.swinggui.ConfigPanelExtension;
+import de.griefed.serverpackcreator.addons.swinggui.ExtensionConfigPanel;
+import de.griefed.serverpackcreator.swing.TabCreateServerPack;
+import de.griefed.serverpackcreator.utilities.common.Utilities;
+import de.griefed.serverpackcreator.versionmeta.VersionMeta;
+import java.util.Optional;
+import org.pf4j.Extension;
+
+@Extension
+public class Panel implements ConfigPanelExtension {
+
+  /**
+   * This method gets called when an extension of this type is run. All parameters are provided by
+   * ServerPackCreator, so you do not have to take care of them. A ConfigPanel is intended to be
+   * used to change server pack-specific configurations which can then be used by other extensions
+   * in your plugin. A simple example would be downloading a specific version of some mod. You could
+   * add a panel which lets the user configure the version of the mod to use. When the user then
+   * runs the server pack generation, your setting will be stored in the subsequently generated
+   * serverpackcreator.conf, and could be used in any of the {@link ServerPackHandler}
+   * extension-points, which would then download the version you specified via this here panel.
+   *
+   * @param versionMeta           Instance of {@link VersionMeta} so you can work with available
+   *                              Minecraft, Forge, Fabric, LegacyFabric and Quilt versions.
+   * @param applicationProperties Instance of {@link ApplicationProperties} The current
+   *                              configuration of ServerPackCreator, like the default list of
+   *                              clientside-only mods, the server pack directory etc.
+   * @param utilities             Instance of {@link Utilities} commonly used across
+   *                              ServerPackCreator.
+   * @param tabCreateServerPack   Instance of {@link TabCreateServerPack} to give you access to the
+   *                              various fields inside it, like the modpack directory, selected
+   *                              Minecraft, modloader and modloader versions, etc.
+   * @param addonConfig           Addon specific configuration conveniently provided by
+   *                              ServerPackCreator. This is the global configuration of the addon
+   *                              which provides the ConfigPanelExtension to ServerPackCreator.
+   * @param extensionName         The name the titled border of this ConfigPanel will get.
+   * @param pluginID              The same as the PluginId.
+   * @return A ConfigPanel allowing users to further customize their ServerPackCreator experience
+   * when using an addon.
+   * @author Griefed
+   */
+  @Override
+  public ExtensionConfigPanel getPanel(VersionMeta versionMeta,
+      ApplicationProperties applicationProperties, Utilities utilities,
+      TabCreateServerPack tabCreateServerPack, Optional<CommentedConfig> addonConfig,
+      String extensionName, String pluginID) {
+    return new ConfigurationPanel(versionMeta, applicationProperties, utilities,
+        tabCreateServerPack, addonConfig, extensionName, pluginID);
+  }
+
+  /**
+   * Get the name of this addon.
+   *
+   * @return The name of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getName() {
+    return "Example Config Panel Extension";
+  }
+
+  /**
+   * Get the description of this addon.
+   *
+   * @return The description of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getDescription() {
+    return "An example for an extension which adds a server pack specific addon extension configuration panel.";
+  }
+
+  /**
+   * Get the author of this addon.
+   *
+   * @return The author of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getAuthor() {
+    return "Griefed";
+  }
+
+  /**
+   * Get the version of this addon.
+   *
+   * @return The version of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getVersion() {
+    return "0.0.1-SNAPSHOT";
+  }
+
+  /**
+   * Get the if of this extension. Used by ServerPackCreator to determine which configuration, if
+   * any, to provide to any given extension being run.
+   *
+   * @return The ID of this extension.
+   * @author Griefed
+   */
+  @Override
+  public String getExtensionId() {
+    return "configpanelexample";
+  }
+}
diff --git a/src/main/java/de/griefed/exampleaddon/gui/tab/Tab.java b/src/main/java/de/griefed/exampleaddon/gui/tab/Tab.java
new file mode 100644
index 0000000000000000000000000000000000000000..b61e79649b2836aba3c6148ba6fece7a84e4df91
--- /dev/null
+++ b/src/main/java/de/griefed/exampleaddon/gui/tab/Tab.java
@@ -0,0 +1,151 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Griefed
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package de.griefed.exampleaddon.gui.tab;
+
+import com.electronwill.nightconfig.core.CommentedConfig;
+import de.griefed.serverpackcreator.ApplicationProperties;
+import de.griefed.serverpackcreator.addons.swinggui.ExtensionTab;
+import de.griefed.serverpackcreator.addons.swinggui.TabExtension;
+import de.griefed.serverpackcreator.utilities.common.Utilities;
+import de.griefed.serverpackcreator.versionmeta.VersionMeta;
+import java.io.File;
+import java.util.Optional;
+import javax.swing.Icon;
+import org.pf4j.Extension;
+
+@Extension
+public class Tab implements TabExtension {
+
+  /**
+   * @param versionMeta           Instance of {@link VersionMeta} so you can work with available
+   *                              Minecraft, Forge, Fabric, LegacyFabric and Quilt versions.
+   * @param applicationProperties Instance of {@link ApplicationProperties} The current
+   *                              configuration of ServerPackCreator, like the default list of
+   *                              clientside-only mods, the server pack directory etc.
+   * @param utilities             Instance of {@link Utilities} commonly used across
+   *                              ServerPackCreator.
+   * @param addonConfig           Addon specific configuration conveniently provided by
+   *                              ServerPackCreator. This is the global configuration of the addon
+   *                              which provides the ConfigPanelExtension to ServerPackCreator.
+   * @param configFile            The config-file corresponding to the ID of the addon, wrapped in
+   *                              an Optional.
+   * @return Component to add to the ServerPackCreator GUI as a tab.
+   * @author Griefed
+   */
+  @Override
+  public ExtensionTab getTab(VersionMeta versionMeta, ApplicationProperties applicationProperties,
+      Utilities utilities, Optional<CommentedConfig> addonConfig, Optional<File> configFile) {
+    return new TetrisTab(versionMeta, applicationProperties, utilities, addonConfig, configFile);
+  }
+
+  /**
+   * Get the {@link Icon} for this tab to display to the ServerPackCreator GUI.
+   *
+   * @return Icon to be used by the added tab.
+   * @author Griefed
+   */
+  @Override
+  public Icon getTabIcon() {
+    return null;
+  }
+
+  /**
+   * Get the title of this tab to display in the ServerPackCreator GUI.
+   *
+   * @return The title of this addons tabbed pane.
+   * @author Griefed
+   */
+  @Override
+  public String getTabTitle() {
+    return "Tetris";
+  }
+
+  /**
+   * Get the tooltip for this tab to display in the ServerPackCreator GUI.
+   *
+   * @return The tooltip of this addons tabbed pane.
+   * @author Griefed
+   */
+  @Override
+  public String getTabTooltip() {
+    return "Click the button to play some Tetris.";
+  }
+
+  /**
+   * Get the name of this addon.
+   *
+   * @return The name of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getName() {
+    return "Example Tab Extension";
+  }
+
+  /**
+   * Get the description of this addon.
+   *
+   * @return The description of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getDescription() {
+    return "An example for an extension which adds an additional tab to the ServerPackCreator GUI which lets you play Tetris.";
+  }
+
+  /**
+   * Get the author of this addon.
+   *
+   * @return The author of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getAuthor() {
+    return "Griefed";
+  }
+
+  /**
+   * Get the version of this addon.
+   *
+   * @return The version of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getVersion() {
+    return "0.0.1-SNAPSHOT";
+  }
+
+  /**
+   * Get the if of this extension. Used by ServerPackCreator to determine which configuration, if
+   * any, to provide to any given extension being run.
+   *
+   * @return The ID of this extension.
+   * @author Griefed
+   */
+  @Override
+  public String getExtensionId() {
+    return "tabextensionexample";
+  }
+}
diff --git a/src/main/java/de/griefed/exampleaddon/gui/tab/Tetris.java b/src/main/java/de/griefed/exampleaddon/gui/tab/Tetris.java
new file mode 100644
index 0000000000000000000000000000000000000000..7574d33d59784fd9e9b709d40a3bff5f0b6c9262
--- /dev/null
+++ b/src/main/java/de/griefed/exampleaddon/gui/tab/Tetris.java
@@ -0,0 +1,2085 @@
+package de.griefed.exampleaddon.gui.tab;
+
+import java.awt.BorderLayout;
+import java.awt.Button;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.GridLayout;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.TextArea;
+import java.awt.TextField;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.Hashtable;
+import javax.swing.JComponent;
+
+
+public class Tetris {
+
+  /**
+   * The stand-alone main routine.
+   *
+   * @param args the command-line arguments
+   */
+  public static void main(String[] args) {
+    Frame frame = new Frame("Tetris");
+    final Game game = new Game();
+    final TextArea taHiScores = new TextArea("", 10, 10, TextArea.SCROLLBARS_NONE);
+
+    taHiScores.setBackground(Color.black);
+    taHiScores.setForeground(Color.white);
+    taHiScores.setFont(new Font("monospaced", Font.PLAIN, 11));
+    taHiScores.setText(
+        " High Scores                  \n"
+            + " ---------------------------\n\n"
+            + " PLAYER     LEVEL    SCORE    \n\n"
+            + " Lorenzo       12 1  50280     \n"
+            + " Lorenzo       12 1  50280     \n");
+    taHiScores.setEditable(false);
+    taHiScores.setFocusable(false);
+
+    final TextField txt = new TextField();
+    txt.setEnabled(false);
+
+    game.addPropertyChangeListener(
+        evt -> {
+          if (evt.getPropertyName().equals("state")) {
+            int state = (Integer) evt.getNewValue();
+            if (state == Game.STATE_GAMEOVER) {
+              txt.setEnabled(true);
+              txt.requestFocus();
+              txt.addActionListener(
+                  e -> {
+                    txt.setEnabled(false);
+                    game.init();
+                  });
+              // show score...
+            }
+          }
+        });
+
+    Button btnStart = new Button("Start");
+    btnStart.setFocusable(false);
+    btnStart.addActionListener(e -> game.start());
+
+    final Container c = new Container();
+    c.setLayout(new BorderLayout());
+    c.add(txt, BorderLayout.NORTH);
+    c.add(game.getSquareBoardComponent(), BorderLayout.CENTER);
+    c.add(btnStart, BorderLayout.SOUTH);
+
+    final Container c2 = new Container();
+    c2.setLayout(new GridLayout(1, 2));
+    c2.add(c);
+    c2.add(taHiScores);
+
+    frame.add(c2);
+    frame.pack();
+
+    // Add frame window listener
+    frame.addWindowListener(
+        new WindowAdapter() {
+          public void windowClosing(WindowEvent e) {
+            frame.dispose();
+            game.terminate();
+          }
+        });
+
+    frame.setLocationRelativeTo(null);
+
+    // Show frame (and start game)
+    frame.setVisible(true);
+  }
+}
+/*
+ * @(#)SquareBoard.java
+ *
+ * This work is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This work is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (c) 2003 Per Cederberg. All rights reserved.
+ */
+
+/**
+ * A Tetris square board. The board is rectangular and contains a grid of colored squares. The board
+ * is considered to be constrained to both sides (left and right), and to the bottom. There is no
+ * constraint to the top of the board, although colors assigned to positions above the board are not
+ * saved.
+ *
+ * @author Per Cederberg, per@percederberg.net
+ * @version 1.2
+ */
+@SuppressWarnings("unused")
+class SquareBoard {
+
+  /**
+   * The board width (in squares)
+   */
+  private final int width;
+
+  /**
+   * The board height (in squares).
+   */
+  private final int height;
+  /**
+   * The graphical sqare board component. This graphical representation is created upon the first
+   * call to getComponent().
+   */
+  private final SquareBoardComponent component;
+  /**
+   * The square board color matrix. This matrix (or grid) contains a color entry for each square in
+   * the board. The matrix is indexed by the vertical, and then the horizontal coordinate.
+   */
+  private final Color[][] matrix;
+  /**
+   * An optional board message. The board message can be set at any time, printing it on top of the
+   * board.
+   */
+  private String message = null;
+  /**
+   * The number of lines removed. This counter is increased each time a line is removed from the
+   * board.
+   */
+  private int removedLines = 0;
+
+  /**
+   * Creates a new square board with the specified size. The square board will initially be empty.
+   *
+   * @param width  the width of the board (in squares)
+   * @param height the height of the board (in squares)
+   */
+  public SquareBoard(int width, int height) {
+    this.width = width;
+    this.height = height;
+    this.matrix = new Color[height][width];
+    this.component = new SquareBoardComponent();
+    clear();
+  }
+
+  /**
+   * Checks if a specified square is empty, i.e. if it is not marked with a color. If the square is
+   * outside the board, false will be returned in all cases except when the square is directly above
+   * the board.
+   *
+   * @param x the horizontal position (0 &#60;= x &#60; width)
+   * @param y the vertical position (0 &#60;= y &#60; height)
+   * @return true if the square is emtpy, or false otherwise
+   */
+  public boolean isSquareEmpty(int x, int y) {
+    if (x < 0 || x >= width || y < 0 || y >= height) {
+      return x >= 0 && x < width && y < 0;
+    } else {
+      return matrix[y][x] == null;
+    }
+  }
+
+  /**
+   * Checks if a specified line is empty, i.e. only contains empty squares. If the line is outside
+   * the board, false will always be returned.
+   *
+   * @param y the vertical position (0 &#60;= y &#60; height)
+   * @return true if the whole line is empty, or false otherwise
+   */
+  public boolean isLineEmpty(int y) {
+    if (y < 0 || y >= height) {
+      return false;
+    }
+    for (int x = 0; x < width; x++) {
+      if (matrix[y][x] != null) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Checks if a specified line is full, i.e. only contains no empty squares. If the line is outside
+   * the board, true will always be returned.
+   *
+   * @param y the vertical position (0 &#60;= y &#60; height)
+   * @return true if the whole line is full, or false otherwise
+   */
+  public boolean isLineFull(int y) {
+    if (y < 0 || y >= height) {
+      return true;
+    }
+    for (int x = 0; x < width; x++) {
+      if (matrix[y][x] == null) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Checks if the board contains any full lines.
+   *
+   * @return true if there are full lines on the board, or false otherwise
+   */
+  public boolean hasFullLines() {
+    for (int y = height - 1; y >= 0; y--) {
+      if (isLineFull(y)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Returns a graphical component to draw the board. The component returned will automatically be
+   * updated when changes are made to this board. Multiple calls to this method will return the same
+   * component, as a square board can only have a single graphical representation.
+   *
+   * @return a graphical component that draws this board
+   */
+  public Component getComponent() {
+    return component;
+  }
+
+  /**
+   * Returns the board height (in squares). This method returns, i.e, the number of vertical squares
+   * that fit on the board.
+   *
+   * @return the board height in squares
+   */
+  public int getBoardHeight() {
+    return height;
+  }
+
+  /**
+   * Returns the board width (in squares). This method returns, i.e, the number of horizontal
+   * squares that fit on the board.
+   *
+   * @return the board width in squares
+   */
+  public int getBoardWidth() {
+    return width;
+  }
+
+  /**
+   * Returns the number of lines removed since the last clear().
+   *
+   * @return the number of lines removed since the last clear call
+   */
+  public int getRemovedLines() {
+    return removedLines;
+  }
+
+  /**
+   * Returns the color of an individual square on the board. If the square is empty or outside the
+   * board, null will be returned.
+   *
+   * @param x the horizontal position (0 &#60;= x &#60; width)
+   * @param y the vertical position (0 &#60;= y &#60; height)
+   * @return the square color, or null for none
+   */
+  public Color getSquareColor(int x, int y) {
+    if (x < 0 || x >= width || y < 0 || y >= height) {
+      return null;
+    } else {
+      return matrix[y][x];
+    }
+  }
+
+  /**
+   * Changes the color of an individual square on the board. The square will be marked as in need of
+   * a repaint, but the graphical component will NOT be repainted until the update() method is
+   * called.
+   *
+   * @param x     the horizontal position (0 &#60;= x &#60; width)
+   * @param y     the vertical position (0 &#60;= y &#60; height)
+   * @param color the new square color, or null for empty
+   */
+  public void setSquareColor(int x, int y, Color color) {
+    if (x < 0 || x >= width || y < 0 || y >= height) {
+      return;
+    }
+    matrix[y][x] = color;
+    if (component != null) {
+      component.invalidateSquare(x, y);
+    }
+  }
+
+  /**
+   * Sets a message to display on the square board. This is supposed to be used when the board is
+   * not being used for active drawing, as it slows down the drawing considerably.
+   *
+   * @param message a message to display, or null to remove a previous message
+   */
+  public void setMessage(String message) {
+    this.message = message;
+    if (component != null) {
+      component.redrawAll();
+    }
+  }
+
+  /**
+   * Clears the board, i.e. removes all the colored squares. As side-effects, the number of removed
+   * lines will be reset to zero, and the component will be repainted immediately.
+   */
+  public void clear() {
+    removedLines = 0;
+    for (int y = 0; y < height; y++) {
+      for (int x = 0; x < width; x++) {
+        this.matrix[y][x] = null;
+      }
+    }
+    if (component != null) {
+      component.redrawAll();
+    }
+  }
+
+  /**
+   * Removes all full lines. All lines above a removed line will be moved downward one step, and a
+   * new empty line will be added at the top. After removing all full lines, the component will be
+   * repainted.
+   *
+   * @see #hasFullLines
+   */
+  public void removeFullLines() {
+    boolean repaint = false;
+
+    // Remove full lines
+    for (int y = height - 1; y >= 0; y--) {
+      if (isLineFull(y)) {
+        removeLine(y);
+        removedLines++;
+        repaint = true;
+        y++;
+      }
+    }
+
+    // Repaint if necessary
+    if (repaint && component != null) {
+      component.redrawAll();
+    }
+  }
+
+  /**
+   * Removes a single line. All lines above are moved down one step, and a new empty line is added
+   * at the top. No repainting will be done after removing the line.
+   *
+   * @param y the vertical position (0 &#60;= y &#60; height)
+   */
+  private void removeLine(int y) {
+    if (y < 0 || y >= height) {
+      return;
+    }
+    for (; y > 0; y--) {
+      if (width >= 0) {
+        System.arraycopy(matrix[y - 1], 0, matrix[y], 0, width);
+      }
+    }
+    for (int x = 0; x < width; x++) {
+      matrix[0][x] = null;
+    }
+  }
+
+  /**
+   * Updates the graphical component. Any squares previously changed will be repainted by this
+   * method.
+   */
+  public void update() {
+    component.redraw();
+  }
+
+  /**
+   * The graphical component that paints the square board. This is implemented as an inner class in
+   * order to better abstract the detailed information that must be sent between the square board
+   * and its graphical representation.
+   */
+  private class SquareBoardComponent extends JComponent {
+
+    /**
+     * The component insets. The inset values are used to create a border around the board to
+     * compensate for a skewed aspect ratio. If the component has been resized, the insets values
+     * will be recalculated when the paint method executes.
+     */
+    private final Insets insets = new Insets(0, 0, 0, 0);
+    /**
+     * The square size in pixels. This value is updated when the component size is changed, i.e.
+     * when the <code>size</code> variable is modified.
+     */
+    private final Dimension squareSize = new Dimension(0, 0);
+    /**
+     * A clip boundary buffer rectangle. This rectangle is used when calculating the clip
+     * boundaries, in order to avoid allocating a new clip rectangle for each board square.
+     */
+    private final Rectangle bufferRect = new Rectangle();
+    /**
+     * The board message color.
+     */
+    private final Color messageColor;
+    /**
+     * A lookup table containing lighter versions of the colors. This table is used to avoid
+     * calculating the lighter versions of the colors for each and every square drawn.
+     */
+    private final Hashtable<Color, Color> lighterColors = new Hashtable<>();
+    /**
+     * A lookup table containing darker versions of the colors. This table is used to avoid
+     * calculating the darker versions of the colors for each and every square drawn.
+     */
+    private final Hashtable<Color, Color> darkerColors = new Hashtable<>();
+    /**
+     * A bounding box of the squares to update. The coordinates used in the rectangle refers to the
+     * square matrix.
+     */
+    private final Rectangle updateRect = new Rectangle();
+    /**
+     * The component size. If the component has been resized, that will be detected when the paint
+     * method executes. If this value is set to null, the component dimensions are unknown.
+     */
+    private Dimension size = null;
+    /**
+     * An image used for double buffering. The board is first painted onto this image, and that
+     * image is then painted onto the real surface in order to avoid making the drawing process
+     * visible to the user. This image is recreated each time the component size changes.
+     */
+    private Image bufferImage = null;
+    /**
+     * A flag set when the component has been updated.
+     */
+    private boolean updated = true;
+
+    /**
+     * Creates a new square board component.
+     */
+    public SquareBoardComponent() {
+      setBackground(Configuration.getColor("board.background", "#000000"));
+      messageColor = Configuration.getColor("board.message", "#ffffff");
+    }
+
+    /**
+     * Adds a square to the set of squares in need of redrawing.
+     *
+     * @param x the horizontal position (0 &#60;= x &#60; width)
+     * @param y the vertical position (0 &#60;= y &#60; height)
+     */
+    public void invalidateSquare(int x, int y) {
+      if (updated) {
+        updated = false;
+        updateRect.x = x;
+        updateRect.y = y;
+        updateRect.width = 0;
+        updateRect.height = 0;
+      } else {
+        if (x < updateRect.x) {
+          updateRect.width += updateRect.x - x;
+          updateRect.x = x;
+        } else if (x > updateRect.x + updateRect.width) {
+          updateRect.width = x - updateRect.x;
+        }
+        if (y < updateRect.y) {
+          updateRect.height += updateRect.y - y;
+          updateRect.y = y;
+        } else if (y > updateRect.y + updateRect.height) {
+          updateRect.height = y - updateRect.y;
+        }
+      }
+    }
+
+    /**
+     * Redraws all the invalidated squares. If no squares have been marked as in need of redrawing,
+     * no redrawing will occur.
+     */
+    public void redraw() {
+      Graphics g;
+
+      if (!updated) {
+        updated = true;
+        g = getGraphics();
+        if (g == null) {
+          return;
+        }
+        g.setClip(
+            insets.left + updateRect.x * squareSize.width,
+            insets.top + updateRect.y * squareSize.height,
+            (updateRect.width + 1) * squareSize.width,
+            (updateRect.height + 1) * squareSize.height);
+        paint(g);
+      }
+    }
+
+    /**
+     * Redraws the whole component.
+     */
+    public void redrawAll() {
+      Graphics g;
+
+      updated = true;
+      g = getGraphics();
+      if (g == null) {
+        return;
+      }
+      g.setClip(insets.left, insets.top, width * squareSize.width, height * squareSize.height);
+      paint(g);
+    }
+
+    /**
+     * Returns true as this component is double buffered.
+     *
+     * @return true as this component is double buffered
+     */
+    public boolean isDoubleBuffered() {
+      return true;
+    }
+
+    /**
+     * Returns the preferred size of this component.
+     *
+     * @return the preferred component size
+     */
+    public Dimension getPreferredSize() {
+      return new Dimension(width * 20, height * 20);
+    }
+
+    /**
+     * Returns the minimum size of this component.
+     *
+     * @return the minimum component size
+     */
+    public Dimension getMinimumSize() {
+      return getPreferredSize();
+    }
+
+    /**
+     * Returns the maximum size of this component.
+     *
+     * @return the maximum component size
+     */
+    public Dimension getMaximumSize() {
+      return getPreferredSize();
+    }
+
+    /**
+     * Returns a lighter version of the specified color. The lighter color will looked up in a
+     * hashtable, making this method fast. If the color is not found, the ligher color will be
+     * calculated and added to the lookup table for later reference.
+     *
+     * @param c the base color
+     * @return the lighter version of the color
+     */
+    private Color getLighterColor(Color c) {
+      Color lighter;
+
+      lighter = lighterColors.get(c);
+      if (lighter == null) {
+        lighter = c.brighter().brighter();
+        lighterColors.put(c, lighter);
+      }
+      return lighter;
+    }
+
+    /**
+     * Returns a darker version of the specified color. The darker color will looked up in a
+     * hashtable, making this method fast. If the color is not found, the darker color will be
+     * calculated and added to the lookup table for later reference.
+     *
+     * @param c the base color
+     * @return the darker version of the color
+     */
+    private Color getDarkerColor(Color c) {
+      Color darker;
+
+      darker = darkerColors.get(c);
+      if (darker == null) {
+        darker = c.darker().darker();
+        darkerColors.put(c, darker);
+      }
+      return darker;
+    }
+
+    /**
+     * Paints this component indirectly. The painting is first done to a buffer image, that is then
+     * painted directly to the specified graphics context.
+     *
+     * @param g the graphics context to use
+     */
+    public synchronized void paint(Graphics g) {
+      Graphics bufferGraphics;
+      Rectangle rect;
+
+      // Handle component size change
+      if (size == null || !size.equals(getSize())) {
+        size = getSize();
+        squareSize.width = size.width / width;
+        squareSize.height = size.height / height;
+
+        insets.left = (size.width - width * squareSize.width) / 2;
+        insets.right = insets.left;
+        insets.top = 0;
+        insets.bottom = size.height - height * squareSize.height;
+        bufferImage = createImage(width * squareSize.width, height * squareSize.height);
+      }
+
+      // Paint component in buffer image
+      rect = g.getClipBounds();
+      bufferGraphics = bufferImage.getGraphics();
+      bufferGraphics.setClip(rect.x - insets.left, rect.y - insets.top, rect.width, rect.height);
+      doPaintComponent(bufferGraphics);
+
+      // Paint image buffer
+      g.drawImage(bufferImage, insets.left, insets.top, getBackground(), null);
+    }
+
+    /**
+     * Paints this component directly. All the squares on the board will be painted directly to the
+     * specified graphics context.
+     *
+     * @param g the graphics context to use
+     */
+    private void doPaintComponent(Graphics g) {
+
+      // Paint background
+      g.setColor(getBackground());
+      g.fillRect(0, 0, width * squareSize.width, height * squareSize.height);
+
+      // Paint squares
+      for (int y = 0; y < height; y++) {
+        for (int x = 0; x < width; x++) {
+          if (matrix[y][x] != null) {
+            paintSquare(g, x, y);
+          }
+        }
+      }
+
+      // Paint message
+      if (message != null) {
+        paintMessage(g, message);
+      }
+    }
+
+    /**
+     * Paints a single board square. The specified position must contain a color object.
+     *
+     * @param g the graphics context to use
+     * @param x the horizontal position (0 &#60;= x &#60; width)
+     * @param y the vertical position (0 &#60; y &#60; height)
+     */
+    private void paintSquare(Graphics g, int x, int y) {
+      Color color = matrix[y][x];
+      int xMin = x * squareSize.width;
+      int yMin = y * squareSize.height;
+      int xMax = xMin + squareSize.width - 1;
+      int yMax = yMin + squareSize.height - 1;
+      int i;
+
+      // Skip drawing if not visible
+      bufferRect.x = xMin;
+      bufferRect.y = yMin;
+      bufferRect.width = squareSize.width;
+      bufferRect.height = squareSize.height;
+      if (!bufferRect.intersects(g.getClipBounds())) {
+        return;
+      }
+
+      // Fill with base color
+      g.setColor(color);
+      g.fillRect(xMin, yMin, squareSize.width, squareSize.height);
+
+      // Draw brighter lines
+      g.setColor(getLighterColor(color));
+      for (i = 0; i < squareSize.width / 10; i++) {
+        g.drawLine(xMin + i, yMin + i, xMax - i, yMin + i);
+        g.drawLine(xMin + i, yMin + i, xMin + i, yMax - i);
+      }
+
+      // Draw darker lines
+      g.setColor(getDarkerColor(color));
+      for (i = 0; i < squareSize.width / 10; i++) {
+        g.drawLine(xMax - i, yMin + i, xMax - i, yMax - i);
+        g.drawLine(xMin + i, yMax - i, xMax - i, yMax - i);
+      }
+    }
+
+    /**
+     * Paints a board message. The message will be drawn at the center of the component.
+     *
+     * @param g   the graphics context to use
+     * @param msg the string message
+     */
+    private void paintMessage(Graphics g, String msg) {
+      int fontWidth;
+      int offset;
+      int x;
+      int y;
+
+      // Find string font width
+      g.setFont(new Font("SansSerif", Font.BOLD, squareSize.width + 4));
+      fontWidth = g.getFontMetrics().stringWidth(msg);
+
+      // Find centered position
+      x = (width * squareSize.width - fontWidth) / 2;
+      y = height * squareSize.height / 2;
+
+      // Draw black version of the string
+      offset = squareSize.width / 10;
+      g.setColor(Color.black);
+      g.drawString(msg, x - offset, y - offset);
+      g.drawString(msg, x - offset, y);
+      g.drawString(msg, x - offset, y - offset);
+      g.drawString(msg, x, y - offset);
+      g.drawString(msg, x, y + offset);
+      g.drawString(msg, x + offset, y - offset);
+      g.drawString(msg, x + offset, y);
+      g.drawString(msg, x + offset, y + offset);
+
+      // Draw white version of the string
+      g.setColor(messageColor);
+      g.drawString(msg, x, y);
+    }
+  }
+}
+/*
+ * @(#)Game.java
+ *
+ * This work is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This work is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (c) 2003 Per Cederberg. All rights reserved.
+ */
+
+/**
+ * The Tetris game. This class controls all events in the game and handles all the game logics. The
+ * game is started through user interaction with the graphical game component provided by this
+ * class.
+ *
+ * @author Per Cederberg, per@percederberg.net
+ * @version 1.2
+ */
+@SuppressWarnings("unused")
+class Game {
+
+  public static final int STATE_GETREADY = 1;
+  public static final int STATE_PLAYING = 2;
+  public static final int STATE_PAUSED = 3;
+  public static final int STATE_GAMEOVER = 4;
+
+  /**
+   * The PropertyChangeSupport Object able to register listener and dispatch events to them.
+   */
+  private final PropertyChangeSupport PCS = new PropertyChangeSupport(this);
+
+  /**
+   * The main square board. This board is used for the game itself.
+   */
+  private final SquareBoard board;
+
+  /**
+   * The preview square board. This board is used to display a preview of the figures.
+   */
+  private final SquareBoard previewBoard = new SquareBoard(5, 5);
+  /**
+   * The thread that runs the game. When this variable is set to null, the game thread will
+   * terminate.
+   */
+  private final GameThread thread;
+  /**
+   * The figures used on both boards. All figures are reutilized in order to avoid creating new
+   * objects while the game is running. Special care has to be taken when the preview figure and the
+   * current figure refers to the same object.
+   */
+  private final Figure[] figures = {
+      new Figure(Figure.SQUARE_FIGURE),
+      new Figure(Figure.LINE_FIGURE),
+      new Figure(Figure.S_FIGURE),
+      new Figure(Figure.Z_FIGURE),
+      new Figure(Figure.RIGHT_ANGLE_FIGURE),
+      new Figure(Figure.LEFT_ANGLE_FIGURE),
+      new Figure(Figure.TRIANGLE_FIGURE)
+  };
+  /**
+   * The game level. The level will be increased for every 20 lines removed from the square board.
+   */
+  private int level = 1;
+
+  /**
+   * The current score. The score is increased for every figure that is possible to place on the
+   * main board.
+   */
+  private int score = 0;
+
+  /**
+   * The current figure. The figure will be updated when
+   */
+  private Figure figure = null;
+
+  /**
+   * The next figure.
+   */
+  private Figure nextFigure = null;
+
+  /**
+   * The rotation of the next figure.
+   */
+  private int nextRotation = 0;
+
+  /**
+   * The figure preview flag. If this flag is set, the figure will be shown in the figure preview
+   * board.
+   */
+  private boolean preview = true;
+
+  /**
+   * The move lock flag. If this flag is set, the current figure cannot be moved. This flag is set
+   * when a figure is moved all the way down, and reset when a new figure is displayed.
+   */
+  private boolean moveLock = false;
+
+  /**
+   *
+   */
+  private int state;
+
+  /**
+   * Creates a new Tetris game. The square board will be given the default size of 10x20.
+   */
+  public Game() {
+    this(10, 20);
+  }
+
+  /**
+   * Creates a new Tetris game. The square board will be given the specified size.
+   *
+   * @param width  the width of the square board (in positions)
+   * @param height the height of the square board (in positions)
+   */
+  public Game(int width, int height) {
+    board = new SquareBoard(width, height);
+    thread = new GameThread();
+    handleGetReady();
+    board.getComponent().setFocusable(true);
+    board
+        .getComponent()
+        .addKeyListener(
+            new KeyAdapter() {
+              public void keyPressed(KeyEvent e) {
+                handleKeyEvent(e);
+              }
+            });
+  }
+
+  /**
+   * Adds a PropertyChangeListener to this Game.
+   *
+   * <p>This is the list the Events that can be fired:
+   *
+   * <p>name: "state" value: new current state (int) one of those:
+   * STATE_OVER,STATE_PLAYING,STATE_PAUSED when: fired when the state changes.
+   *
+   * <p>name: "level" value: current level (int) when: fired when the player moves to the next
+   * level.
+   *
+   * <p>name: "score" value: current score (int) when: fired when the player increases his/her
+   * score.
+   *
+   * <p>name: "lines" value: number of 'removed' lines (int) when: fired when the player removes
+   * one
+   * or more lines.
+   *
+   * @param l the property change listener which is going to be notified.
+   */
+  public void addPropertyChangeListener(PropertyChangeListener l) {
+    PCS.addPropertyChangeListener(l);
+  }
+
+  /**
+   * Removes this propertyChangeListener
+   *
+   * @param l the PropertyChangeListener object to remove.
+   */
+  public void removePropertyChangeListener(PropertyChangeListener l) {
+    PCS.removePropertyChangeListener(l);
+  }
+
+  /**
+   * Gets the current 'state'. One of the following:
+   * STATE_GETREADY,STATE_PLAYING,STATE_PAUSED,STATE_GAMEOVER.
+   *
+   * @return the current state.
+   */
+  public int getState() {
+    return state;
+  }
+
+  /**
+   * Gets the current level.
+   *
+   * @return the current level.
+   */
+  public int getLevel() {
+    return level;
+  }
+
+  /**
+   * Gets the current score.
+   *
+   * @return the current score.
+   */
+  public int getScore() {
+    return score;
+  }
+
+  /**
+   * Gets the number of lines that have been removed since the game started.
+   *
+   * @return the number of removed lines.
+   */
+  public int getRemovedLines() {
+    return board.getRemovedLines();
+  }
+
+  /**
+   * Gets the java.awt.Component for the board.
+   *
+   * @return the gui component for the board.
+   */
+  public Component getSquareBoardComponent() {
+    return board.getComponent();
+  }
+
+  /**
+   * Gets the java.awt.Component for the preview board (5x5)
+   *
+   * @return the gui component for the board.
+   */
+  public Component getPreviewBoardComponent() {
+    return previewBoard.getComponent();
+  }
+
+  /**
+   * Initializes the game ready if the state is on STATE_GAMEOVER otherwise it does nothing.
+   */
+  public void init() {
+    if (state == STATE_GAMEOVER) {
+      handleGetReady();
+    }
+  }
+
+  /**
+   * Starts the game. (No matter what the current state is)
+   */
+  public void start() {
+    handleStart();
+  }
+
+  /**
+   * Pauses the game if the state is on STATE_PLAYING otherwise it does nothing.
+   */
+  public void pause() {
+    if (state == STATE_PLAYING) {
+      handlePause();
+    }
+  }
+
+  /**
+   * Resumes the game if the state is on STATE_PAUSED otherwise it does nothing.
+   */
+  public void resume() {
+    if (state == STATE_PAUSED) {
+      handleResume();
+    }
+  }
+
+  /**
+   * Terminates the game. (No matter what the current state is)
+   */
+  public void terminate() {
+    handleGameOver();
+  }
+
+  /**
+   * Handles a game start event. Both the main and preview square boards will be reset, and all
+   * other game parameters will be reset. Finally the game thread will be launched.
+   */
+  private void handleStart() {
+
+    // Reset score and figures
+    level = 1;
+    score = 0;
+    figure = null;
+    nextFigure = randomFigure();
+    nextFigure.rotateRandom();
+    nextRotation = nextFigure.getRotation();
+
+    // Reset components
+    state = STATE_PLAYING;
+    board.setMessage(null);
+    board.clear();
+    previewBoard.clear();
+    handleLevelModification();
+    handleScoreModification();
+
+    PCS.firePropertyChange("state", -1, STATE_PLAYING);
+
+    // Start game thread
+    thread.reset();
+  }
+
+  /**
+   * Handles a game over event. This will stop the game thread, reset all figures and print a game
+   * over message.
+   */
+  private void handleGameOver() {
+
+    // Stop game thred
+    thread.setPaused(true);
+
+    // Reset figures
+    if (figure != null) {
+      figure.detach();
+    }
+    figure = null;
+    if (nextFigure != null) {
+      nextFigure.detach();
+    }
+    nextFigure = null;
+
+    // Handle components
+    state = STATE_GAMEOVER;
+    board.setMessage("Game Over");
+    PCS.firePropertyChange("state", -1, STATE_GAMEOVER);
+  }
+
+  /**
+   * Handles a getReady event. This will print a 'get ready' message on the game board.
+   */
+  private void handleGetReady() {
+    board.setMessage("Get Ready");
+    board.clear();
+    previewBoard.clear();
+    state = STATE_GETREADY;
+    PCS.firePropertyChange("state", -1, STATE_GETREADY);
+  }
+
+  /**
+   * Handles a game pause event. This will pause the game thread and print a pause message on the
+   * game board.
+   */
+  private void handlePause() {
+    thread.setPaused(true);
+    state = STATE_PAUSED;
+    board.setMessage("Paused");
+    PCS.firePropertyChange("state", -1, STATE_PAUSED);
+  }
+
+  /**
+   * Handles a game resume event. This will resume the game thread and remove any messages on the
+   * game board.
+   */
+  private void handleResume() {
+    state = STATE_PLAYING;
+    board.setMessage(null);
+    thread.setPaused(false);
+    PCS.firePropertyChange("state", -1, STATE_PLAYING);
+  }
+
+  /**
+   * Handles a level modification event. This will modify the level label and adjust the thread
+   * speed.
+   */
+  private void handleLevelModification() {
+    PCS.firePropertyChange("level", -1, level);
+    thread.adjustSpeed();
+  }
+
+  /**
+   * Handle a score modification event. This will modify the score label.
+   */
+  private void handleScoreModification() {
+    PCS.firePropertyChange("score", -1, score);
+  }
+
+  /**
+   * Handles a figure start event. This will move the next figure to the current figure position,
+   * while also creating a new preview figure. If the figure cannot be introduced onto the game
+   * board, a game over event will be launched.
+   */
+  private void handleFigureStart() {
+    int rotation;
+
+    // Move next figure to current
+    figure = nextFigure;
+    moveLock = false;
+    rotation = nextRotation;
+    nextFigure = randomFigure();
+    nextFigure.rotateRandom();
+    nextRotation = nextFigure.getRotation();
+
+    // Handle figure preview
+    if (preview) {
+      previewBoard.clear();
+      nextFigure.attach(previewBoard, true);
+      nextFigure.detach();
+    }
+
+    // Attach figure to game board
+    figure.setRotation(rotation);
+    if (!figure.attach(board, false)) {
+      previewBoard.clear();
+      figure.attach(previewBoard, true);
+      figure.detach();
+      handleGameOver();
+    }
+  }
+
+  /**
+   * Handles a figure landed event. This will check that the figure is completely visible, or a game
+   * over event will be launched. After this control, any full lines will be removed. If no full
+   * lines could be removed, a figure start event is launched directly.
+   */
+  private void handleFigureLanded() {
+
+    // Check and detach figure
+    if (figure.isAllVisible()) {
+      score += 10;
+      handleScoreModification();
+    } else {
+      handleGameOver();
+      return;
+    }
+    figure.detach();
+    figure = null;
+
+    // Check for full lines or create new figure
+    if (board.hasFullLines()) {
+      board.removeFullLines();
+      PCS.firePropertyChange("lines", -1, board.getRemovedLines());
+      if (level < 9 && board.getRemovedLines() / 20 > level) {
+        level = board.getRemovedLines() / 20;
+        handleLevelModification();
+      }
+    } else {
+      handleFigureStart();
+    }
+  }
+
+  /**
+   * Handles a timer event. This will normally move the figure down one step, but when a figure has
+   * landed or isn't ready other events will be launched. This method is synchronized to avoid race
+   * conditions with other asynchronous events (keyboard and mouse).
+   */
+  private synchronized void handleTimer() {
+    if (figure == null) {
+      handleFigureStart();
+    } else if (figure.hasLanded()) {
+      handleFigureLanded();
+    } else {
+      figure.moveDown();
+    }
+  }
+
+  /**
+   * Handles a button press event. This will launch different events depending on the state of the
+   * game, as the button semantics change as the game changes. This method is synchronized to avoid
+   * race conditions with other asynchronous events (timer and keyboard).
+   */
+  private synchronized void handlePauseOnOff() {
+    if (nextFigure == null) {
+      handleStart();
+    } else if (thread.isPaused()) {
+      handleResume();
+    } else {
+      handlePause();
+    }
+  }
+
+  /**
+   * Handles a keyboard event. This will result in different actions being taken, depending on the
+   * key pressed. In some cases, other events will be launched. This method is synchronized to avoid
+   * race conditions with other asynchronous events (timer and mouse).
+   *
+   * @param e the key event
+   */
+  private synchronized void handleKeyEvent(KeyEvent e) {
+    // Handle start (any key to start !!!)
+    if (state == STATE_GETREADY) {
+      handleStart();
+      return;
+    }
+
+    // pause and resume
+    if (e.getKeyCode() == KeyEvent.VK_P) {
+      handlePauseOnOff();
+      return;
+    }
+
+    // Don't proceed if stopped or paused
+    if (figure == null || moveLock || thread.isPaused()) {
+      return;
+    }
+
+    // Handle remaining key events
+    switch (e.getKeyCode()) {
+      case KeyEvent.VK_LEFT:
+        figure.moveLeft();
+        break;
+
+      case KeyEvent.VK_RIGHT:
+        figure.moveRight();
+        break;
+
+      case KeyEvent.VK_DOWN:
+        figure.moveAllWayDown();
+        moveLock = true;
+        break;
+
+      case KeyEvent.VK_UP:
+      case KeyEvent.VK_SPACE:
+        if (e.isControlDown()) {
+          figure.rotateRandom();
+        } else if (e.isShiftDown()) {
+          figure.rotateClockwise();
+        } else {
+          figure.rotateCounterClockwise();
+        }
+        break;
+
+      case KeyEvent.VK_S:
+        if (level < 9) {
+          level++;
+          handleLevelModification();
+        }
+        break;
+
+      case KeyEvent.VK_N:
+        preview = !preview;
+        if (preview && figure != nextFigure) {
+          nextFigure.attach(previewBoard, true);
+          nextFigure.detach();
+        } else {
+          previewBoard.clear();
+        }
+        break;
+    }
+  }
+
+  /**
+   * Returns a random figure. The figures come from the figures array, and will not be initialized.
+   *
+   * @return a random figure
+   */
+  private Figure randomFigure() {
+    return figures[(int) (Math.random() * figures.length)];
+  }
+
+  /**
+   * The game time thread. This thread makes sure that the timer events are launched appropriately,
+   * making the current figure fall. This thread can be reused across games, but should be set to
+   * paused state when no game is running.
+   */
+  private class GameThread extends Thread {
+
+    /**
+     * The game pause flag. This flag is set to true while the game should pause.
+     */
+    private boolean paused = true;
+
+    /**
+     * The number of milliseconds to sleep before each automatic move. This number will be lowered
+     * as the game progresses.
+     */
+    private int sleepTime = 500;
+
+    /**
+     * Creates a new game thread with default values.
+     */
+    public GameThread() {
+    }
+
+    /**
+     * Resets the game thread. This will adjust the speed and start the game thread if not
+     * previously started.
+     */
+    public void reset() {
+      adjustSpeed();
+      setPaused(false);
+      if (!isAlive()) {
+        this.start();
+      }
+    }
+
+    /**
+     * Checks if the thread is paused.
+     *
+     * @return true if the thread is paused, or false otherwise
+     */
+    public boolean isPaused() {
+      return paused;
+    }
+
+    /**
+     * Sets the thread pause flag.
+     *
+     * @param paused the new paused flag value
+     */
+    public void setPaused(boolean paused) {
+      this.paused = paused;
+    }
+
+    /**
+     * Adjusts the game speed according to the current level. The sleeping time is calculated with a
+     * function making larger steps initially an smaller as the level increases. A level above ten
+     * (10) doesn't have any further effect.
+     */
+    public void adjustSpeed() {
+      sleepTime = 4500 / (level + 5) - 250;
+      if (sleepTime < 50) {
+        sleepTime = 50;
+      }
+    }
+
+    /**
+     * Runs the game.
+     */
+    public void run() {
+      while (true) {
+        // Make the time step
+        handleTimer();
+
+        // Sleep for some time
+        try {
+          Thread.sleep(sleepTime);
+        } catch (InterruptedException ignore) {
+          // Do nothing
+        }
+
+        // Sleep if paused
+        while (paused && thread == this) {
+          try {
+            Thread.sleep(1000);
+          } catch (InterruptedException ignore) {
+            // Do nothing
+          }
+        }
+      }
+    }
+  }
+}
+/*
+ * @(#)Configuration.java
+ *
+ * This work is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This work is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (c) 2003 Per Cederberg. All rights reserved.
+ */
+
+/**
+ * A program configuration. This class provides static methods for simplifying the reading of
+ * configuration parameters. It also provides some methods for transforming string values into more
+ * useful objects.
+ *
+ * @author Per Cederberg, per@percederberg.net
+ * @version 1.2
+ */
+@SuppressWarnings("unused")
+class Configuration {
+
+  /**
+   * The internal configuration property values. This lookup table is used to avoid setting
+   * configuration parameters in the system properties, as some programs (applets) do not have the
+   * security permissions to set system properties.
+   */
+  private static final Hashtable<String, String> config = new Hashtable<>();
+
+  /**
+   * Returns a configuration parameter value.
+   *
+   * @param key the configuration parameter key
+   * @return the configuration parameter value, or null if not set
+   */
+  public static String getValue(String key) {
+    if (config.containsKey(key)) {
+      return config.get(key);
+    } else {
+      try {
+        return System.getProperty(key);
+      } catch (SecurityException ignore) {
+        return null;
+      }
+    }
+  }
+
+  /**
+   * Returns a configuration parameter value. If the configuration parameter is not set, a default
+   * value will be returned instead.
+   *
+   * @param key the configuration parameter key
+   * @param def the default value to use
+   * @return the configuration parameter value, or the default value if not set
+   */
+  public static String getValue(String key, String def) {
+    String value = getValue(key);
+
+    return (value == null) ? def : value;
+  }
+
+  /**
+   * Sets a configuration parameter value.
+   *
+   * @param key   the configuration parameter key
+   * @param value the configuration parameter value
+   */
+  public static void setValue(String key, String value) {
+    config.put(key, value);
+  }
+
+  /**
+   * Returns the color configured for the specified key. The key will be prepended with
+   * "tetris.color." and the value will be read from the system properties. The color value must be
+   * specified in hexadecimal web format, i.e. in the "#RRGGBB" format. If the default color isn't
+   * in a valid format, white will be returned.
+   *
+   * @param key the configuration parameter key
+   * @param def the default value
+   * @return the color specified in the configuration, or a default color value
+   */
+  public static Color getColor(String key, String def) {
+    String value = getValue("tetris.color." + key, def);
+    Color color;
+
+    color = parseColor(value);
+    if (color != null) {
+      return color;
+    }
+    color = parseColor(def);
+    if (color != null) {
+      return color;
+    } else {
+      return Color.white;
+    }
+  }
+
+  /**
+   * Parses a web color string. If the color value couldn't be parsed correctly, null will be
+   * returned.
+   *
+   * @param value the color value to parse
+   * @return the color represented by the string, or null if the string was malformed
+   */
+  private static Color parseColor(String value) {
+    if (!value.startsWith("#")) {
+      return null;
+    }
+    try {
+      return new Color(Integer.parseInt(value.substring(1), 16));
+    } catch (NumberFormatException ignore) {
+      return null;
+    }
+  }
+}
+/*
+ * @(#)Figure.java
+ *
+ * This work is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This work is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (c) 2003 Per Cederberg. All rights reserved.
+ */
+
+/**
+ * A class representing a Tetris square figure. Each figure consists of four connected squares in
+ * one of seven possible constellations. The figures may be rotated in 90 degree steps and have
+ * sideways and downwards movability.
+ *
+ * <p>Each figure instance can have two states, either attached to a square board or not. When
+ * attached, all move and rotation operations are checked so that collisions do not occur with other
+ * squares on the board. When not attached, any rotation can be made (and will be kept when attached
+ * to a new board).
+ *
+ * @author Per Cederberg, per@percederberg.net
+ * @version 1.2
+ */
+class Figure {
+
+  /**
+   * A figure constant used to create a figure forming a square.
+   */
+  public static final int SQUARE_FIGURE = 1;
+
+  /**
+   * A figure constant used to create a figure forming a line.
+   */
+  public static final int LINE_FIGURE = 2;
+
+  /**
+   * A figure constant used to create a figure forming an "S".
+   */
+  public static final int S_FIGURE = 3;
+
+  /**
+   * A figure constant used to create a figure forming a "Z".
+   */
+  public static final int Z_FIGURE = 4;
+
+  /**
+   * A figure constant used to create a figure forming a right angle.
+   */
+  public static final int RIGHT_ANGLE_FIGURE = 5;
+
+  /**
+   * A figure constant used to create a figure forming a left angle.
+   */
+  public static final int LEFT_ANGLE_FIGURE = 6;
+
+  /**
+   * A figure constant used to create a figure forming a triangle.
+   */
+  public static final int TRIANGLE_FIGURE = 7;
+  /**
+   * The horizontal coordinates of the figure shape. The coordinates are relative to the current
+   * figure position and orientation.
+   */
+  private final int[] shapeX = new int[4];
+  /**
+   * The vertical coordinates of the figure shape. The coordinates are relative to the current
+   * figure position and orientation.
+   */
+  private final int[] shapeY = new int[4];
+  /**
+   * The square board to which the figure is attached. If this variable is set to null, the figure
+   * is not attached.
+   */
+  private SquareBoard board = null;
+  /**
+   * The horizontal figure position on the board. This value has no meaning when the figure is not
+   * attached to a square board.
+   */
+  private int xPos = 0;
+  /**
+   * The vertical figure position on the board. This value has no meaning when the figure is not
+   * attached to a square board.
+   */
+  private int yPos = 0;
+  /**
+   * The figure orientation (or rotation). This value is normally between 0 and 3, but must also be
+   * less than the maxOrientation value.
+   *
+   * @see #maxOrientation
+   */
+  private int orientation = 0;
+  /**
+   * The maximum allowed orientation number. This is used to reduce the number of possible rotations
+   * for some figures, such as the square figure. If this value is not used, the square figure will
+   * be possible to rotate around one of its squares, which gives an erroneous effect.
+   *
+   * @see #orientation
+   */
+  private int maxOrientation = 4;
+  /**
+   * The figure color.
+   */
+  private Color color = Color.white;
+
+  /**
+   * Creates a new figure of one of the seven predefined types. The figure will not be attached to
+   * any square board and default colors and orientations will be assigned.
+   *
+   * @param type the figure type (one of the figure constants)
+   * @throws IllegalArgumentException if the figure type specified is not recognized
+   * @see #SQUARE_FIGURE
+   * @see #LINE_FIGURE
+   * @see #S_FIGURE
+   * @see #Z_FIGURE
+   * @see #RIGHT_ANGLE_FIGURE
+   * @see #LEFT_ANGLE_FIGURE
+   * @see #TRIANGLE_FIGURE
+   */
+  public Figure(int type) throws IllegalArgumentException {
+    initialize(type);
+  }
+
+  /**
+   * Initializes the instance variables for a specified figure type.
+   *
+   * @param type the figure type (one of the figure constants)
+   * @throws IllegalArgumentException if the figure type specified is not recognized
+   * @see #SQUARE_FIGURE
+   * @see #LINE_FIGURE
+   * @see #S_FIGURE
+   * @see #Z_FIGURE
+   * @see #RIGHT_ANGLE_FIGURE
+   * @see #LEFT_ANGLE_FIGURE
+   * @see #TRIANGLE_FIGURE
+   */
+  private void initialize(int type) throws IllegalArgumentException {
+
+    // Initialize default variables
+    board = null;
+    xPos = 0;
+    yPos = 0;
+    orientation = 0;
+
+    // Initialize figure type variables
+    switch (type) {
+      case SQUARE_FIGURE:
+        maxOrientation = 1;
+        color = Configuration.getColor("figure.square", "#ffd8b1");
+        shapeX[0] = -1;
+        shapeY[0] = 0;
+        shapeX[1] = 0;
+        shapeY[1] = 0;
+        shapeX[2] = -1;
+        shapeY[2] = 1;
+        shapeX[3] = 0;
+        shapeY[3] = 1;
+        break;
+      case LINE_FIGURE:
+        maxOrientation = 2;
+        color = Configuration.getColor("figure.line", "#ffb4b4");
+        shapeX[0] = -2;
+        shapeY[0] = 0;
+        shapeX[1] = -1;
+        shapeY[1] = 0;
+        shapeX[2] = 0;
+        shapeY[2] = 0;
+        shapeX[3] = 1;
+        shapeY[3] = 0;
+        break;
+      case S_FIGURE:
+        maxOrientation = 2;
+        color = Configuration.getColor("figure.s", "#a3d5ee");
+        shapeX[0] = 0;
+        shapeY[0] = 0;
+        shapeX[1] = 1;
+        shapeY[1] = 0;
+        shapeX[2] = -1;
+        shapeY[2] = 1;
+        shapeX[3] = 0;
+        shapeY[3] = 1;
+        break;
+      case Z_FIGURE:
+        maxOrientation = 2;
+        color = Configuration.getColor("figure.z", "#f4adff");
+        shapeX[0] = -1;
+        shapeY[0] = 0;
+        shapeX[1] = 0;
+        shapeY[1] = 0;
+        shapeX[2] = 0;
+        shapeY[2] = 1;
+        shapeX[3] = 1;
+        shapeY[3] = 1;
+        break;
+      case RIGHT_ANGLE_FIGURE:
+        maxOrientation = 4;
+        color = Configuration.getColor("figure.right", "#c0b6fa");
+        shapeX[0] = -1;
+        shapeY[0] = 0;
+        shapeX[1] = 0;
+        shapeY[1] = 0;
+        shapeX[2] = 1;
+        shapeY[2] = 0;
+        shapeX[3] = 1;
+        shapeY[3] = 1;
+        break;
+      case LEFT_ANGLE_FIGURE:
+        maxOrientation = 4;
+        color = Configuration.getColor("figure.left", "#f5f4a7");
+        shapeX[0] = -1;
+        shapeY[0] = 0;
+        shapeX[1] = 0;
+        shapeY[1] = 0;
+        shapeX[2] = 1;
+        shapeY[2] = 0;
+        shapeX[3] = -1;
+        shapeY[3] = 1;
+        break;
+      case TRIANGLE_FIGURE:
+        maxOrientation = 4;
+        color = Configuration.getColor("figure.triangle", "#a4d9b6");
+        shapeX[0] = -1;
+        shapeY[0] = 0;
+        shapeX[1] = 0;
+        shapeY[1] = 0;
+        shapeX[2] = 1;
+        shapeY[2] = 0;
+        shapeX[3] = 0;
+        shapeY[3] = 1;
+        break;
+      default:
+        throw new IllegalArgumentException("No figure constant: " + type);
+    }
+  }
+
+  /**
+   * Checks if this figure is attached to a square board.
+   *
+   * @return true if the figure is already attached, or false otherwise
+   */
+  public boolean isAttached() {
+    return board != null;
+  }
+
+  /**
+   * Attaches the figure to a specified square board. The figure will be drawn either at the
+   * absolute top of the board, with only the bottom line visible, or centered onto the board. In
+   * both cases, the squares on the new board are checked for collisions. If the squares are already
+   * occupied, this method returns false and no attachment is made.
+   *
+   * <p>The horizontal and vertical coordinates will be reset for the figure, when centering the
+   * figure on the new board. The figure orientation (rotation) will be kept, however. If the figure
+   * was previously attached to another board, it will be detached from that board before attaching
+   * to the new board.
+   *
+   * @param board  the square board to attach to
+   * @param center the centered position flag
+   * @return true if the figure could be attached, or false otherwise
+   */
+  public boolean attach(SquareBoard board, boolean center) {
+    int newX;
+    int newY;
+    int i;
+
+    // Check for previous attachment
+    if (isAttached()) {
+      detach();
+    }
+
+    // Reset position (for correct controls)
+    xPos = 0;
+    yPos = 0;
+
+    // Calculate position
+    newX = board.getBoardWidth() / 2;
+    if (center) {
+      newY = board.getBoardHeight() / 2;
+    } else {
+      newY = 0;
+      for (i = 0; i < shapeX.length; i++) {
+        if (getRelativeY(i, orientation) - newY > 0) {
+          newY = -getRelativeY(i, orientation);
+        }
+      }
+    }
+
+    // Check position
+    this.board = board;
+    if (!canMoveTo(newX, newY, orientation)) {
+      this.board = null;
+      return false;
+    }
+
+    // Draw figure
+    xPos = newX;
+    yPos = newY;
+    paint(color);
+    board.update();
+
+    return true;
+  }
+
+  /**
+   * Detaches this figure from its square board. The figure will not be removed from the board by
+   * this operation, resulting in the figure being left intact.
+   */
+  public void detach() {
+    board = null;
+  }
+
+  /**
+   * Checks if the figure is fully visible on the square board. If the figure isn't attached to a
+   * board, false will be returned.
+   *
+   * @return true if the figure is fully visible, or false otherwise
+   */
+  public boolean isAllVisible() {
+    if (!isAttached()) {
+      return false;
+    }
+    for (int i = 0; i < shapeX.length; i++) {
+      if (yPos + getRelativeY(i, orientation) < 0) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Checks if the figure has landed. If this method returns true, the moveDown() or the
+   * moveAllWayDown() methods should have no effect. If no square board is attached, this method
+   * will return true.
+   *
+   * @return true if the figure has landed, or false otherwise
+   */
+  public boolean hasLanded() {
+    return !isAttached() || !canMoveTo(xPos, yPos + 1, orientation);
+  }
+
+  /**
+   * Moves the figure one step to the left. If such a move is not possible with respect to the
+   * square board, nothing is done. The square board will be changed as the figure moves, clearing
+   * the previous cells. If no square board is attached, nothing is done.
+   */
+  public void moveLeft() {
+    if (isAttached() && canMoveTo(xPos - 1, yPos, orientation)) {
+      paint(null);
+      xPos--;
+      paint(color);
+      board.update();
+    }
+  }
+
+  /**
+   * Moves the figure one step to the right. If such a move is not possible with respect to the
+   * square board, nothing is done. The square board will be changed as the figure moves, clearing
+   * the previous cells. If no square board is attached, nothing is done.
+   */
+  public void moveRight() {
+    if (isAttached() && canMoveTo(xPos + 1, yPos, orientation)) {
+      paint(null);
+      xPos++;
+      paint(color);
+      board.update();
+    }
+  }
+
+  /**
+   * Moves the figure one step down. If such a move is not possible with respect to the square
+   * board, nothing is done. The square board will be changed as the figure moves, clearing the
+   * previous cells. If no square board is attached, nothing is done.
+   */
+  public void moveDown() {
+    if (isAttached() && canMoveTo(xPos, yPos + 1, orientation)) {
+      paint(null);
+      yPos++;
+      paint(color);
+      board.update();
+    }
+  }
+
+  /**
+   * Moves the figure all the way down. The limits of the move are either the square board bottom,
+   * or squares not being empty. If no move is possible with respect to the square board, nothing is
+   * done. The square board will be changed as the figure moves, clearing the previous cells. If no
+   * square board is attached, nothing is done.
+   */
+  public void moveAllWayDown() {
+    int y = yPos;
+
+    // Check for board
+    if (!isAttached()) {
+      return;
+    }
+
+    // Find lowest position
+    while (canMoveTo(xPos, y + 1, orientation)) {
+      y++;
+    }
+
+    // Update
+    if (y != yPos) {
+      paint(null);
+      yPos = y;
+      paint(color);
+      board.update();
+    }
+  }
+
+  /**
+   * Returns the current figure rotation (orientation).
+   *
+   * @return the current figure rotation
+   */
+  public int getRotation() {
+    return orientation;
+  }
+
+  /**
+   * Sets the figure rotation (orientation). If the desired rotation is not possible with respect to
+   * the square board, nothing is done. The square board will be changed as the figure moves,
+   * clearing the previous cells. If no square board is attached, the rotation is performed
+   * directly.
+   *
+   * @param rotation the new figure orientation
+   */
+  public void setRotation(int rotation) {
+    int newOrientation;
+
+    // Set new orientation
+    newOrientation = rotation % maxOrientation;
+
+    // Check new position
+    if (!isAttached()) {
+      orientation = newOrientation;
+    } else if (canMoveTo(xPos, yPos, newOrientation)) {
+      paint(null);
+      orientation = newOrientation;
+      paint(color);
+      board.update();
+    }
+  }
+
+  /**
+   * Rotates the figure randomly. If such a rotation is not possible with respect to the square
+   * board, nothing is done. The square board will be changed as the figure moves, clearing the
+   * previous cells. If no square board is attached, the rotation is performed directly.
+   */
+  public void rotateRandom() {
+    setRotation((int) (Math.random() * 4.0) % maxOrientation);
+  }
+
+  /**
+   * Rotates the figure clockwise. If such a rotation is not possible with respect to the square
+   * board, nothing is done. The square board will be changed as the figure moves, clearing the
+   * previous cells. If no square board is attached, the rotation is performed directly.
+   */
+  public void rotateClockwise() {
+    if (maxOrientation != 1) {
+      setRotation((orientation + 1) % maxOrientation);
+    }
+  }
+
+  /**
+   * Rotates the figure counter-clockwise. If such a rotation is not possible with respect to the
+   * square board, nothing is done. The square board will be changed as the figure moves, clearing
+   * the previous cells. If no square board is attached, the rotation is performed directly.
+   */
+  public void rotateCounterClockwise() {
+    if (maxOrientation != 1) {
+      setRotation((orientation + 3) % 4);
+    }
+  }
+
+  /**
+   * Checks if a specified pair of (square) coordinates are inside the figure, or not.
+   *
+   * @param x the horizontal position
+   * @param y the vertical position
+   * @return true if the coordinates are inside the figure, or false otherwise
+   */
+  private boolean isInside(int x, int y) {
+    for (int i = 0; i < shapeX.length; i++) {
+      if (x == xPos + getRelativeX(i, orientation) && y == yPos + getRelativeY(i, orientation)) {
+
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Checks if the figure can move to a new position. The current figure position is taken into
+   * account when checking for collisions. If a collision is detected, this method will return
+   * false.
+   *
+   * @param newX           the new horizontal position
+   * @param newY           the new vertical position
+   * @param newOrientation the new orientation (rotation)
+   * @return true if the figure can be moved, or false otherwise
+   */
+  private boolean canMoveTo(int newX, int newY, int newOrientation) {
+    int x;
+    int y;
+
+    for (int i = 0; i < 4; i++) {
+      x = newX + getRelativeX(i, newOrientation);
+      y = newY + getRelativeY(i, newOrientation);
+      if (!isInside(x, y) && !board.isSquareEmpty(x, y)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Returns the relative horizontal position of a specified square. The square will be rotated
+   * according to the specified orientation.
+   *
+   * @param square      the square to rotate (0-3)
+   * @param orientation the orientation to use (0-3)
+   * @return the rotated relative horizontal position
+   */
+  private int getRelativeX(int square, int orientation) {
+    switch (orientation % 4) {
+      case 0:
+        return shapeX[square];
+      case 1:
+        return -shapeY[square];
+      case 2:
+        return -shapeX[square];
+      case 3:
+        return shapeY[square];
+      default:
+        return 0; // Should never occur
+    }
+  }
+
+  /**
+   * Rotates the relative vertical position of a specified square. The square will be rotated
+   * according to the specified orientation.
+   *
+   * @param square      the square to rotate (0-3)
+   * @param orientation the orientation to use (0-3)
+   * @return the rotated relative vertical position
+   */
+  private int getRelativeY(int square, int orientation) {
+    switch (orientation % 4) {
+      case 0:
+        return shapeY[square];
+      case 1:
+        return shapeX[square];
+      case 2:
+        return -shapeY[square];
+      case 3:
+        return -shapeX[square];
+      default:
+        return 0; // Should never occur
+    }
+  }
+
+  /**
+   * Paints the figure on the board with the specified color.
+   *
+   * @param color the color to paint with, or null for clearing
+   */
+  private void paint(Color color) {
+    int x, y;
+
+    for (int i = 0; i < shapeX.length; i++) {
+      x = xPos + getRelativeX(i, orientation);
+      y = yPos + getRelativeY(i, orientation);
+      board.setSquareColor(x, y, color);
+    }
+  }
+
+}
diff --git a/src/main/java/de/griefed/exampleaddon/gui/tab/TetrisTab.java b/src/main/java/de/griefed/exampleaddon/gui/tab/TetrisTab.java
new file mode 100644
index 0000000000000000000000000000000000000000..664bb5044e30bad138c9f29ad347d09da462270d
--- /dev/null
+++ b/src/main/java/de/griefed/exampleaddon/gui/tab/TetrisTab.java
@@ -0,0 +1,183 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Griefed
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package de.griefed.exampleaddon.gui.tab;
+
+import com.electronwill.nightconfig.core.CommentedConfig;
+import de.griefed.serverpackcreator.ApplicationProperties;
+import de.griefed.serverpackcreator.addons.swinggui.ExtensionTab;
+import de.griefed.serverpackcreator.utilities.common.Utilities;
+import de.griefed.serverpackcreator.versionmeta.VersionMeta;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.io.File;
+import java.util.Objects;
+import java.util.Optional;
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+public class TetrisTab extends ExtensionTab {
+
+  private JTextField ami;
+  private JTextField here;
+
+  /**
+   * Construct a new panel to add to the ServerPackCreator GUI as an additional tab.
+   *
+   * @param versionMeta           Instance of {@link VersionMeta} so you can work with available
+   *                              Minecraft, Forge, Fabric, LegacyFabric and Quilt versions.
+   * @param applicationProperties Instance of {@link ApplicationProperties} The current
+   *                              configuration of ServerPackCreator, like the default list of
+   *                              clientside-only mods, the server pack directory etc.
+   * @param utilities             Instance of {@link Utilities} commonly used across
+   *                              ServerPackCreator.
+   * @param addonConfig           Addon specific configuration conveniently provided by
+   *                              ServerPackCreator. This is the global configuration of the addon
+   *                              which provides the ConfigPanelExtension to ServerPackCreator.
+   * @param configFile            The config-file corresponding to the ID of the addon, wrapped in
+   *                              an Optional.
+   */
+  protected TetrisTab(
+      VersionMeta versionMeta,
+      ApplicationProperties applicationProperties,
+      Utilities utilities,
+      Optional<CommentedConfig> addonConfig,
+      Optional<File> configFile) {
+    super(versionMeta, applicationProperties, utilities, addonConfig, configFile);
+
+    setLayout(new GridBagLayout());
+    GridBagConstraints gridBagConstraints = new GridBagConstraints();
+    gridBagConstraints.fill = GridBagConstraints.NONE;
+    gridBagConstraints.anchor = GridBagConstraints.CENTER;
+    gridBagConstraints.gridwidth = 1;
+    gridBagConstraints.gridheight = 1;
+    gridBagConstraints.weightx = 1;
+    gridBagConstraints.weighty = 1;
+
+    JPanel miniGame = new JPanel();
+    miniGame.setBorder(BorderFactory.createTitledBorder("Tetris Minigame"));
+    miniGame.setLayout(new GridBagLayout());
+    GridBagConstraints miniGameConstraints = new GridBagConstraints();
+    miniGameConstraints.fill = GridBagConstraints.HORIZONTAL;
+    miniGameConstraints.anchor = GridBagConstraints.CENTER;
+
+    JLabel press = new JLabel("PRESS PLAY ON TAPE  ");
+    JButton play = new JButton();
+    play.setIcon(new ImageIcon(Objects.requireNonNull(TetrisTab.class.getResource("/play.png"))));
+    play.setToolTipText("Open Tetris in a new window");
+    play.addActionListener(e -> Tetris.main(null));
+
+    miniGameConstraints.gridx = 0;
+    miniGameConstraints.gridy = 0;
+    miniGame.add(press, miniGameConstraints);
+    miniGameConstraints.gridx = 0;
+    miniGameConstraints.gridy = 1;
+    miniGame.add(play, miniGameConstraints);
+
+    gridBagConstraints.gridx = 0;
+    gridBagConstraints.gridy = 0;
+    gridBagConstraints.gridwidth = 2;
+
+    miniGame.setPreferredSize(new Dimension(300, 135));
+    add(miniGame, gridBagConstraints);
+
+    gridBagConstraints.gridy += 1;
+    gridBagConstraints.anchor = GridBagConstraints.WEST;
+    gridBagConstraints.fill = GridBagConstraints.BOTH;
+    gridBagConstraints.insets = new Insets(10, 10, 10, 10);
+
+    if (addonConfig.isPresent() && configFile.isPresent()) {
+      JPanel config = new JPanel();
+      config.setBorder(BorderFactory.createTitledBorder("Global Example Addon Config"));
+      config.setLayout(new GridBagLayout());
+      GridBagConstraints configConstraints = new GridBagConstraints();
+
+      configConstraints.fill = GridBagConstraints.HORIZONTAL;
+      configConstraints.anchor = GridBagConstraints.NORTHWEST;
+      configConstraints.gridwidth = 1;
+      configConstraints.gridheight = 1;
+      configConstraints.weighty = 1;
+
+      JLabel who = new JLabel("Who am I?");
+      configConstraints.gridx = 0;
+      configConstraints.gridy = 0;
+      configConstraints.weightx = 0.1F;
+      config.add(who, configConstraints);
+
+      ami = new JTextField(addonConfig.get().get("whoami"));
+      configConstraints.gridx = 1;
+      configConstraints.weightx = 1;
+      config.add(ami, configConstraints);
+
+      JLabel why = new JLabel("Why am I here?");
+      configConstraints.gridx = 0;
+      configConstraints.gridy += 1;
+      configConstraints.weightx = 0.1F;
+      config.add(why, configConstraints);
+
+      here = new JTextField(addonConfig.get().get("whyamihere"));
+      configConstraints.gridx = 1;
+      configConstraints.weightx = 1;
+      config.add(here, configConstraints);
+
+      JButton set = new JButton("Set values");
+      configConstraints.gridx = 0;
+      configConstraints.gridy += 1;
+      configConstraints.gridwidth = 2;
+      set.addActionListener(e -> {
+        addonConfig.get().set("whoami", ami.getText());
+        addonConfig.get().set("whyamihere", here.getText());
+
+        if (JOptionPane.showConfirmDialog(
+            null,
+            "New values set. Save addon configuration?   ",
+            "Save?",
+            JOptionPane.YES_NO_OPTION,
+            JOptionPane.INFORMATION_MESSAGE) == 0) {
+
+          saveConfiguration();
+
+        } else {
+
+          JOptionPane.showMessageDialog(
+              null,
+              "Values not saved :-(   ",
+              "Sad face",
+              JOptionPane.WARNING_MESSAGE
+          );
+        }
+      });
+      config.add(set, configConstraints);
+
+      add(config, gridBagConstraints);
+    }
+  }
+}
diff --git a/src/main/java/de/griefed/exampleaddon/serverpack/PostGeneration.java b/src/main/java/de/griefed/exampleaddon/serverpack/PostGeneration.java
new file mode 100644
index 0000000000000000000000000000000000000000..857cfea15f791b6b809702a1ce62013eb782e3c4
--- /dev/null
+++ b/src/main/java/de/griefed/exampleaddon/serverpack/PostGeneration.java
@@ -0,0 +1,169 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Griefed
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package de.griefed.exampleaddon.serverpack;
+
+import com.electronwill.nightconfig.core.CommentedConfig;
+import com.electronwill.nightconfig.toml.TomlWriter;
+import de.griefed.serverpackcreator.ApplicationProperties;
+import de.griefed.serverpackcreator.ConfigurationModel;
+import de.griefed.serverpackcreator.addons.serverpackhandler.PostGenExtension;
+import de.griefed.serverpackcreator.utilities.common.Utilities;
+import de.griefed.serverpackcreator.versionmeta.VersionMeta;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.pf4j.Extension;
+
+@Extension
+public class PostGeneration implements PostGenExtension {
+
+  private static final Logger LOG_ADDONS = LogManager.getLogger("AddonsLogger");
+  private TomlWriter tomlWriter = null;
+
+  /**
+   * @param versionMeta           Instance of {@link VersionMeta} so you can work with available
+   *                              Minecraft, Forge, Fabric, LegacyFabric and Quilt versions.
+   * @param utilities             Instance of {@link Utilities} commonly used across
+   *                              ServerPackCreator.
+   * @param applicationProperties Instance of {@link ApplicationProperties} as ServerPackCreator
+   *                              itself uses it.
+   * @param configurationModel    Instance of {@link ConfigurationModel} for a given server pack.
+   * @param destination           String. The destination of the server pack.
+   * @param addonConfig           Configuration for this addon, conveniently provided by
+   *                              ServerPackCreator.
+   * @param packSpecificConfigs   Modpack and server pack specific configurations for this addon,
+   *                              conveniently provided by ServerPackCreator.
+   * @throws Exception {@link Exception} when an uncaught error occurs in the addon.
+   * @author Griefed
+   */
+  @Override
+  public void run(
+      VersionMeta versionMeta,
+      Utilities utilities,
+      ApplicationProperties applicationProperties,
+      ConfigurationModel configurationModel,
+      String destination,
+      Optional<CommentedConfig> addonConfig,
+      ArrayList<CommentedConfig> packSpecificConfigs
+  ) throws Exception {
+
+    LOG_ADDONS.info("I am: " + getName() + "(" + getVersion() + ") by " + getAuthor() + ". "
+        + getDescription());
+    LOG_ADDONS.info(
+        "Running with ServerPackCreator " + applicationProperties.SERVERPACKCREATOR_VERSION());
+    LOG_ADDONS.info("I would do stuff after the generation of the server pack has finished!");
+    LOG_ADDONS.info("I could make some magic happen in the " + destination + "-directory :-)");
+
+    LOG_ADDONS.info("Maybe something based on this server packs Minecraft version "
+        + configurationModel.getMinecraftVersion() + "?");
+
+    if (addonConfig.isPresent()) {
+      LOG_ADDONS.info("I got passed the following configuration:");
+
+      StringWriter stringWriter = new StringWriter();
+
+      getTomlWriter().write(addonConfig.get(), stringWriter);
+      LOG_ADDONS.info(stringWriter);
+    }
+
+    LOG_ADDONS.info("I got passed the following pack specific configuration(s):");
+
+    for (int i = 0; i < packSpecificConfigs.size(); i++) {
+
+      LOG_ADDONS.info("Configuration " + i + " of " + packSpecificConfigs.size() + ":");
+
+      StringWriter stringWriter = new StringWriter();
+
+      getTomlWriter().write(packSpecificConfigs.get(i), stringWriter);
+      LOG_ADDONS.info(stringWriter);
+    }
+  }
+
+  private TomlWriter getTomlWriter() {
+    if (tomlWriter == null) {
+      tomlWriter = new TomlWriter();
+    }
+    return tomlWriter;
+  }
+
+  /**
+   * Get the if of this extension. Used by ServerPackCreator to determine which configuration, if
+   * any, to provide to any given extension being run.
+   *
+   * @return The ID of this extension.
+   * @author Griefed
+   */
+  @Override
+  public String getExtensionId() {
+    return "postgenexample";
+  }
+
+
+  /**
+   * Get the name of this addon.
+   *
+   * @return The name of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getName() {
+    return "Example Post Server Pack Generation Extension";
+  }
+
+  /**
+   * Get the description of this addon.
+   *
+   * @return The description of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getDescription() {
+    return "An example for an extension which executes after the generation of a server pack has finished.";
+  }
+
+  /**
+   * Get the author of this addon.
+   *
+   * @return The author of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getAuthor() {
+    return "Griefed";
+  }
+
+  /**
+   * Get the version of this addon.
+   *
+   * @return The version of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getVersion() {
+    return "0.0.1-SNAPSHOT";
+  }
+}
diff --git a/src/main/java/de/griefed/exampleaddon/serverpack/PreGeneration.java b/src/main/java/de/griefed/exampleaddon/serverpack/PreGeneration.java
new file mode 100644
index 0000000000000000000000000000000000000000..f8cf768e43e35c909f4077d883463f6d447472d7
--- /dev/null
+++ b/src/main/java/de/griefed/exampleaddon/serverpack/PreGeneration.java
@@ -0,0 +1,169 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Griefed
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package de.griefed.exampleaddon.serverpack;
+
+import com.electronwill.nightconfig.core.CommentedConfig;
+import com.electronwill.nightconfig.toml.TomlWriter;
+import de.griefed.serverpackcreator.ApplicationProperties;
+import de.griefed.serverpackcreator.ConfigurationModel;
+import de.griefed.serverpackcreator.addons.serverpackhandler.PreGenExtension;
+import de.griefed.serverpackcreator.utilities.common.Utilities;
+import de.griefed.serverpackcreator.versionmeta.VersionMeta;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.pf4j.Extension;
+
+@Extension
+public class PreGeneration implements PreGenExtension {
+
+  private static final Logger LOG_ADDONS = LogManager.getLogger("AddonsLogger");
+  private TomlWriter tomlWriter = null;
+
+  /**
+   * @param versionMeta           Instance of {@link VersionMeta} so you can work with available
+   *                              Minecraft, Forge, Fabric, LegacyFabric and Quilt versions.
+   * @param utilities             Instance of {@link Utilities} commonly used across
+   *                              ServerPackCreator.
+   * @param applicationProperties Instance of {@link ApplicationProperties} as ServerPackCreator
+   *                              itself uses it.
+   * @param configurationModel    Instance of {@link ConfigurationModel} for a given server pack.
+   * @param destination           String. The destination of the server pack.
+   * @param addonConfig           Configuration for this addon, conveniently provided by
+   *                              ServerPackCreator.
+   * @param packSpecificConfigs   Modpack and server pack specific configurations for this addon,
+   *                              conveniently provided by ServerPackCreator.
+   * @throws Exception {@link Exception} when an uncaught error occurs in the addon.
+   * @author Griefed
+   */
+  @Override
+  public void run(
+      VersionMeta versionMeta,
+      Utilities utilities,
+      ApplicationProperties applicationProperties,
+      ConfigurationModel configurationModel,
+      String destination,
+      Optional<CommentedConfig> addonConfig,
+      ArrayList<CommentedConfig> packSpecificConfigs
+  ) throws Exception {
+
+    LOG_ADDONS.info("I am: " + getName() + "(" + getVersion() + ") by " + getAuthor() + ". "
+        + getDescription());
+    LOG_ADDONS.info(
+        "Running with ServerPackCreator " + applicationProperties.SERVERPACKCREATOR_VERSION());
+    LOG_ADDONS.info("I would do stuff before the generation of the server pack starts!");
+    LOG_ADDONS.info("I could make some magic happen in the " + destination + "-directory :-)");
+
+    LOG_ADDONS.info("Maybe something based on this server packs Minecraft version "
+        + configurationModel.getMinecraftVersion() + "?");
+
+    if (addonConfig.isPresent()) {
+      LOG_ADDONS.info("I got passed the following configuration:");
+
+      StringWriter stringWriter = new StringWriter();
+
+      getTomlWriter().write(addonConfig.get(), stringWriter);
+      LOG_ADDONS.info(stringWriter);
+    }
+
+    LOG_ADDONS.info("I got passed the following pack specific configuration(s):");
+
+    for (int i = 0; i < packSpecificConfigs.size(); i++) {
+
+      LOG_ADDONS.info("Configuration " + i + " of " + packSpecificConfigs.size() + ":");
+
+      StringWriter stringWriter = new StringWriter();
+
+      getTomlWriter().write(packSpecificConfigs.get(i), stringWriter);
+      LOG_ADDONS.info(stringWriter);
+    }
+  }
+
+  private TomlWriter getTomlWriter() {
+    if (tomlWriter == null) {
+      tomlWriter = new TomlWriter();
+    }
+    return tomlWriter;
+  }
+
+  /**
+   * Get the if of this extension. Used by ServerPackCreator to determine which configuration, if
+   * any, to provide to any given extension being run.
+   *
+   * @return The ID of this extension.
+   * @author Griefed
+   */
+  @Override
+  public String getExtensionId() {
+    return "pregenexample";
+  }
+
+
+  /**
+   * Get the name of this addon.
+   *
+   * @return The name of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getName() {
+    return "Example Pre Server Pack Generation Extension";
+  }
+
+  /**
+   * Get the description of this addon.
+   *
+   * @return The description of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getDescription() {
+    return "An example for an extension which executes right before the generation of a server pack starts.";
+  }
+
+  /**
+   * Get the author of this addon.
+   *
+   * @return The author of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getAuthor() {
+    return "Griefed";
+  }
+
+  /**
+   * Get the version of this addon.
+   *
+   * @return The version of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getVersion() {
+    return "0.0.1-SNAPSHOT";
+  }
+}
diff --git a/src/main/java/de/griefed/exampleaddon/serverpack/PreZipArchive.java b/src/main/java/de/griefed/exampleaddon/serverpack/PreZipArchive.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb3878f01199eb42df52fa0a594320310005f86b
--- /dev/null
+++ b/src/main/java/de/griefed/exampleaddon/serverpack/PreZipArchive.java
@@ -0,0 +1,169 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Griefed
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package de.griefed.exampleaddon.serverpack;
+
+import com.electronwill.nightconfig.core.CommentedConfig;
+import com.electronwill.nightconfig.toml.TomlWriter;
+import de.griefed.serverpackcreator.ApplicationProperties;
+import de.griefed.serverpackcreator.ConfigurationModel;
+import de.griefed.serverpackcreator.addons.serverpackhandler.PreZipExtension;
+import de.griefed.serverpackcreator.utilities.common.Utilities;
+import de.griefed.serverpackcreator.versionmeta.VersionMeta;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.pf4j.Extension;
+
+@Extension
+public class PreZipArchive implements PreZipExtension {
+
+  private static final Logger LOG_ADDONS = LogManager.getLogger("AddonsLogger");
+  private TomlWriter tomlWriter = null;
+
+  /**
+   * @param versionMeta           Instance of {@link VersionMeta} so you can work with available
+   *                              Minecraft, Forge, Fabric, LegacyFabric and Quilt versions.
+   * @param utilities             Instance of {@link Utilities} commonly used across
+   *                              ServerPackCreator.
+   * @param applicationProperties Instance of {@link ApplicationProperties} as ServerPackCreator
+   *                              itself uses it.
+   * @param configurationModel    Instance of {@link ConfigurationModel} for a given server pack.
+   * @param destination           String. The destination of the server pack.
+   * @param addonConfig           Configuration for this addon, conveniently provided by
+   *                              ServerPackCreator.
+   * @param packSpecificConfigs   Modpack and server pack specific configurations for this addon,
+   *                              conveniently provided by ServerPackCreator.
+   * @throws Exception {@link Exception} when an uncaught error occurs in the addon.
+   * @author Griefed
+   */
+  @Override
+  public void run(
+      VersionMeta versionMeta,
+      Utilities utilities,
+      ApplicationProperties applicationProperties,
+      ConfigurationModel configurationModel,
+      String destination,
+      Optional<CommentedConfig> addonConfig,
+      ArrayList<CommentedConfig> packSpecificConfigs
+  ) throws Exception {
+
+    LOG_ADDONS.info("I am: " + getName() + "(" + getVersion() + ") by " + getAuthor() + ". "
+        + getDescription());
+    LOG_ADDONS.info(
+        "Running with ServerPackCreator " + applicationProperties.SERVERPACKCREATOR_VERSION());
+    LOG_ADDONS.info("I would do stuff right before a ZIP-archive of the server pack is created!");
+    LOG_ADDONS.info("I could make some magic happen in the " + destination + "-directory :-)");
+
+    LOG_ADDONS.info("Maybe something based on this server packs Minecraft version "
+        + configurationModel.getMinecraftVersion() + "?");
+
+    if (addonConfig.isPresent()) {
+      LOG_ADDONS.info("I got passed the following configuration:");
+
+      StringWriter stringWriter = new StringWriter();
+
+      getTomlWriter().write(addonConfig.get(), stringWriter);
+      LOG_ADDONS.info(stringWriter);
+    }
+
+    LOG_ADDONS.info("I got passed the following pack specific configuration(s):");
+
+    for (int i = 0; i < packSpecificConfigs.size(); i++) {
+
+      LOG_ADDONS.info("Configuration " + i + " of " + packSpecificConfigs.size() + ":");
+
+      StringWriter stringWriter = new StringWriter();
+
+      getTomlWriter().write(packSpecificConfigs.get(i), stringWriter);
+      LOG_ADDONS.info(stringWriter);
+    }
+  }
+
+  private TomlWriter getTomlWriter() {
+    if (tomlWriter == null) {
+      tomlWriter = new TomlWriter();
+    }
+    return tomlWriter;
+  }
+
+  /**
+   * Get the if of this extension. Used by ServerPackCreator to determine which configuration, if
+   * any, to provide to any given extension being run.
+   *
+   * @return The ID of this extension.
+   * @author Griefed
+   */
+  @Override
+  public String getExtensionId() {
+    return "prezipexample";
+  }
+
+
+  /**
+   * Get the name of this addon.
+   *
+   * @return The name of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getName() {
+    return "Example Pre ZIP-Archive Creation Extension";
+  }
+
+  /**
+   * Get the description of this addon.
+   *
+   * @return The description of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getDescription() {
+    return "An example for an extension which executes right before the creation of a ZIP-archive of a server pack is started.";
+  }
+
+  /**
+   * Get the author of this addon.
+   *
+   * @return The author of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getAuthor() {
+    return "Griefed";
+  }
+
+  /**
+   * Get the version of this addon.
+   *
+   * @return The version of this addon.
+   * @author Griefed
+   */
+  @Override
+  public String getVersion() {
+    return "0.0.1-SNAPSHOT";
+  }
+}
diff --git a/src/main/java/de/griefed/serverpackcreatoraddonexample/ExamplePlugin.java b/src/main/java/de/griefed/serverpackcreatoraddonexample/ExamplePlugin.java
deleted file mode 100644
index 9fc2205cbc1550319862941122de71f7be958a60..0000000000000000000000000000000000000000
--- a/src/main/java/de/griefed/serverpackcreatoraddonexample/ExamplePlugin.java
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (C) 2021  Griefed
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- * the Software, and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-package de.griefed.serverpackcreatoraddonexample;
-
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import de.griefed.serverpackcreator.ApplicationProperties;
-import de.griefed.serverpackcreator.ConfigurationModel;
-import de.griefed.serverpackcreator.plugins.serverpackhandler.PostGenExtension;
-import de.griefed.serverpackcreator.plugins.serverpackhandler.PreGenExtension;
-import de.griefed.serverpackcreator.plugins.serverpackhandler.PreZipExtension;
-import de.griefed.serverpackcreator.plugins.swinggui.TabExtension;
-import de.griefed.serverpackcreator.utilities.ConfigUtilities;
-import de.griefed.serverpackcreator.utilities.common.Utilities;
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import javax.swing.Icon;
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.UIManager;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.pf4j.Extension;
-import org.pf4j.Plugin;
-import org.pf4j.PluginWrapper;
-
-@SuppressWarnings("unused")
-public class ExamplePlugin extends Plugin {
-
-  /*
-  You should provide and read all these from the manifest.
-                                                              */
-  public static final String NAME = "ServerPackCreatorExampleAddon";
-  public static final String DESCRIPTION =
-      "This is an example addon for ServerPackCreator showcasing all, currently 4, aspects of"
-          + "ServerPackCreators addon functionality. In this example you see code which gets executed before a server pack is generated,"
-          + "code that gets executed after a server pack was generated but before the ZIP-archive is created, code that gets executed"
-          + "after this ZIP-archive was created, as well as code adding a new tabbed pane to the GUI.";
-  public static final String AUTHOR = "Griefed";
-  public static final String VERSION = "0.0.1";
-  private static final Logger LOG_ADDONS = LogManager.getLogger("AddonsLogger");
-
-  public ExamplePlugin(PluginWrapper wrapper) {
-    super(wrapper);
-  }
-
-  @Override
-  public void start() {
-    LOG_ADDONS.info("Starting ExamplePlugin...");
-    LOG_ADDONS.info(
-        "This methods should prepare the environment for anything you want to do with it.");
-    LOG_ADDONS.info(
-        "You could download some files. Create or replace some files. Basically you can do whatever you want.");
-    /*
-    Write all your preparation code here..
-                                            */
-  }
-
-  @Extension(ordinal = 1)
-  public static class ExampleStartExtension implements PreGenExtension {
-
-    @Override
-    public void run(
-        ApplicationProperties applicationProperties,
-        ConfigurationModel configurationModel,
-        String destination)
-        throws Exception {
-      LOG_ADDONS.info("This would run before a server pack generation.");
-      LOG_ADDONS.info("Received destination: " + destination);
-      LOG_ADDONS.info(
-          "We recieved the following configurationModel: " + configurationModel.toString());
-      LOG_ADDONS.info(
-          "We received the following applicationProperties: " + applicationProperties.toString());
-      StringUtils.upperCase("some text in lower case");
-      // Create example file in server pack
-      try {
-        Files.createFile(
-            Paths.get(String.format("%s/%s", destination, this.getClass().getSimpleName())));
-      } catch (Exception ignored) {
-      }
-      /*
-      Write all your pre-gen stuff here...
-                                              */
-    }
-
-    @Override
-    public String getName() {
-      return NAME;
-    }
-
-    @Override
-    public String getDescription() {
-      return DESCRIPTION;
-    }
-
-    @Override
-    public String getAuthor() {
-      return AUTHOR;
-    }
-
-    @Override
-    public String getVersion() {
-      return VERSION;
-    }
-  }
-
-  @Extension(ordinal = 1)
-  public static class ExampleCreatedExtension implements PreZipExtension {
-
-    @Override
-    public void run(
-        ApplicationProperties applicationProperties,
-        ConfigurationModel configurationModel,
-        String destination)
-        throws Exception {
-      LOG_ADDONS.info(
-          "This would run after a server pack was generated, but BEFORE the ZIP-archive would be generated.");
-      LOG_ADDONS.info("Received destination: " + destination);
-      LOG_ADDONS.info(
-          "We recieved the following configurationModel: " + configurationModel.toString());
-      LOG_ADDONS.info(
-          "We received the following applicationProperties: " + applicationProperties.toString());
-      StringUtils.upperCase("some text in lower case");
-      // Create example file in server pack
-      try {
-        FileUtils.createParentDirectories(new File(destination + "/some/folder/with/a/name"));
-      } catch (Exception ex) {
-        LOG_ADDONS.info("Error occurred creating parent directories.", ex);
-      }
-
-      new ConfigUtilities(
-          new Utilities(applicationProperties), applicationProperties,
-          new ObjectMapper()
-              .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
-              .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY))
-          .writeConfigToFile(configurationModel, new File(destination + "/serverpackcreator.conf"));
-      /*
-      Write all your post-gen-pre-zip stuff here...
-                                                       */
-    }
-
-    @Override
-    public String getName() {
-      return NAME;
-    }
-
-    @Override
-    public String getDescription() {
-      return DESCRIPTION;
-    }
-
-    @Override
-    public String getAuthor() {
-      return AUTHOR;
-    }
-
-    @Override
-    public String getVersion() {
-      return VERSION;
-    }
-  }
-
-  @Extension(ordinal = 1)
-  public static class ExampleArchiveExtension implements PostGenExtension {
-
-    @Override
-    public void run(
-        ApplicationProperties applicationProperties,
-        ConfigurationModel configurationModel,
-        String destination)
-        throws Exception {
-      LOG_ADDONS.info("This would run after the server pack ZIP-archive was created.");
-      LOG_ADDONS.info("Received destination: " + destination);
-      LOG_ADDONS.info(
-          "We recieved the following configurationModel: " + configurationModel.toString());
-      LOG_ADDONS.info(
-          "We received the following applicationProperties: " + applicationProperties.toString());
-      // Create example file in server pack
-      try {
-        Files.createFile(
-            Paths.get(String.format("%s/%s", destination, this.getClass().getSimpleName())));
-      } catch (Exception ignored) {
-      }
-      
-      /*
-      Write all your post-archive stuff here...
-                                              */
-    }
-
-    @Override
-    public String getName() {
-      return NAME;
-    }
-
-    @Override
-    public String getDescription() {
-      return DESCRIPTION;
-    }
-
-    @Override
-    public String getAuthor() {
-      return AUTHOR;
-    }
-
-    @Override
-    public String getVersion() {
-      return VERSION;
-    }
-  }
-
-  @Extension(ordinal = 1)
-  public static class ExampleAddTabExtension extends JComponent implements TabExtension {
-
-    @Override
-    public JComponent getTab() {
-      JComponent jComponent = new JPanel(false);
-      jComponent.setLayout(new GridBagLayout());
-
-      GridBagConstraints gridBagConstraints = new GridBagConstraints();
-      gridBagConstraints.fill = GridBagConstraints.NONE;
-      gridBagConstraints.anchor = GridBagConstraints.CENTER;
-      gridBagConstraints.gridwidth = 1;
-      gridBagConstraints.gridheight = 1;
-      gridBagConstraints.weightx = 1;
-      gridBagConstraints.weighty = 1;
-
-      JLabel jLabel = new JLabel("This is a label");
-      jLabel.setToolTipText("Some label tooltip");
-      gridBagConstraints.gridx = 1;
-      gridBagConstraints.gridy = 1;
-      jComponent.add(jLabel, gridBagConstraints);
-
-      JButton jButton = new JButton("Some button");
-      jButton.setToolTipText("This could do anything");
-      gridBagConstraints.gridx = 1;
-      gridBagConstraints.gridy = 2;
-      jComponent.add(jButton, gridBagConstraints);
-
-      return jComponent;
-    }
-
-    @Override
-    public Icon getTabIcon() {
-      /*
-       * other default icons can be:
-       *   OptionPane.errorIcon
-       *   OptionPane.warningIcon
-       *   OptionPane.questionIcon
-       */
-      return UIManager.getIcon("OptionPane.informationIcon");
-    }
-
-    @Override
-    public String getTabTitle() {
-      return "Some title text";
-    }
-
-    @Override
-    public String getTabTooltip() {
-      return "Some tooltip text";
-    }
-
-    @Override
-    public void run(
-        ApplicationProperties applicationProperties,
-        ConfigurationModel configurationModel,
-        String destination)
-        throws Exception {
-      /*
-      So far, the run(...) method for AddTab extensions never gets called.
-      There is no point in writing code here with the intention of
-      ServerPackCreator executing it.
-                                              */
-      System.out.println("Flitzmdeför!");
-    }
-
-    @Override
-    public String getName() {
-      return NAME;
-    }
-
-    @Override
-    public String getDescription() {
-      return DESCRIPTION;
-    }
-
-    @Override
-    public String getAuthor() {
-      return AUTHOR;
-    }
-
-    @Override
-    public String getVersion() {
-      return VERSION;
-    }
-  }
-}
diff --git a/src/main/resources/CHANGELOG.md b/src/main/resources/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..69c4df38821b0f7c4884698c7d0190c43dc0bb12
--- /dev/null
+++ b/src/main/resources/CHANGELOG.md
@@ -0,0 +1,157 @@
+### [3.0.2](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.1...3.0.2) (2022-01-31)
+
+
+### 🦊 CI/CD
+
+* Update dependency ServerPackCreator to 3.0.0-alpha.19 ([d6a1cfe](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/d6a1cfe2fccb665e06aebf0501b29bb838ee05dc))
+
+### [3.0.1](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/3.0.0...3.0.1) (2022-01-28)
+
+
+### 📔 Docs
+
+* Add info regarding Pf4j documentation and request pages, as well as add minigame example to list of addons ([480e6f8](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/480e6f8b837dc25d03252e1537d81311a4c8ca1f))
+
+
+### 🦊 CI/CD
+
+* **deps:** bump JamesIves/github-pages-deploy-action ([ac94f9f](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/ac94f9fbae9a44e08535550b0dac43c9f5fb6529))
+* **deps:** bump mockito-core from 4.2.0 to 4.3.1 ([99f557b](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/99f557b532b74fe70bc1d8c1f8f49c04461dbdd5))
+
+
+### Other
+
+* **deps:** update griefed/baseimage-ubuntu-jdk-8 docker tag to v2.0.2 ([10233dc](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/10233dc53983c35d887acde71eb5727b3ad6d170))
+* **deps:** update griefed/gitlab-ci-cd docker tag to v2 ([5e8e8c3](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/5e8e8c330bc6e5cc84472c58b918566083db510b))
+
+## [3.0.0](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/2.0.2...3.0.0) (2022-01-26)
+
+
+### 🧨 Breaking changes!
+
+* Implement ServerPackCreator from GitLab Maven packages and provide basic example of a plugin ([f211a3c](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/f211a3caee31e25270c00f4faea1317fbc7b5239))
+
+### [2.0.2](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/2.0.1...2.0.2) (2022-01-23)
+
+
+### 🦊 CI/CD
+
+* **deps:** bump JamesIves/github-pages-deploy-action ([e4c593f](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/e4c593f7d9b18d8689b04f4dc50e4142147c3fd3))
+
+
+### Other
+
+* **deps:** update dependency gradle to v7.3.3 ([6431489](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/643148920effd12d04077cfe6a7a5d3c89546d40))
+* **deps:** update griefed/baseimage-ubuntu-jdk-8 docker tag to v2.0.1 ([265a053](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/265a053875d18e249f4fc39feee6e910ecfce163))
+* **deps:** update griefed/gitlab-ci-cd docker tag to v1.1.0 ([ceded41](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/ceded418603f317de2ce86fd0a1896fb8829382c))
+* **deps:** update jamesives/github-pages-deploy-action action to v4.2.0 ([f06731b](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/f06731b091af1c7eaef83fc71a1b695f59b99ed9))
+* **deps:** update jamesives/github-pages-deploy-action action to v4.2.2 ([6ff96c9](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/6ff96c9ea7615d7fcb26a242ccad2fe5001f61cc))
+
+### [2.0.1](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/2.0.0...2.0.1) (2021-12-20)
+
+
+### 🦊 CI/CD
+
+* **deps:** bump JamesIves/github-pages-deploy-action ([4b8b864](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/4b8b8642b7a1e7df07fbf259de305a7f15640aca))
+* **deps:** bump mockito-core from 4.1.0 to 4.2.0 ([f2f0f2e](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/f2f0f2e74120abaaef0fd9a2aa01d3d86cfa4bcc))
+* Change dependabot branch prefix. Upload artifacts from gradle builds ([242ce4b](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/242ce4bbe0fe7322b13192bae6443555a6c7074a))
+* Improve GitHub related workflows and add tests ([d080074](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/d080074e89110654321115e01bf8c7175f529b3f))
+* Move to gitlab-ci-cd:1.0.4 which deprecates armv7 images. Staying up-to-date is more important than supporting old platforms. Sorry, you may need to consider getting a Raspberry Pi 4. ([b8a8461](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/b8a84611c0d50e443c88ce29bb21579d9fd1c118))
+* Update ci-cd image to 1.0.2 ([5c9edeb](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/5c9edeb8b36e634c9ea884a4864f666490d9040f))
+
+
+### Other
+
+* **deps:** update dependency gradle to v7.3 ([342504f](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/342504f32e9989f6a8ef03a8eb8703a1c370583d))
+* **deps:** update dependency gradle to v7.3.1 ([b2bd802](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/b2bd8027e6fc733a7386d38176b321af1f2ef6ce))
+* **deps:** update dependency gradle to v7.3.2 ([da75096](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/da75096d9268d7ffd111e9d57dad2a36fc718e3a))
+* **deps:** update dependency org.mockito:mockito-core to v4.1.0 ([c00dcad](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/c00dcad1fa34bfbad4cbcdbbf1ea5d09727abed3))
+* **deps:** update dependency org.mockito:mockito-core to v4.2.0 ([b9b9870](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/b9b98707215486922a3cb91c1a04eca7ee16e5c6))
+* **deps:** update griefed/baseimage-ubuntu-jdk-8 docker tag to v2 ([9085fec](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/9085fec0b0e1c78dbe11fc7ae30a71530f3c81da))
+* **deps:** update jamesives/github-pages-deploy-action action to v4.1.6 ([9527eff](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/9527eff8e2f78ec60e6072fbdee002b7e6ce4130))
+* **deps:** update jamesives/github-pages-deploy-action action to v4.1.7 ([bb616a2](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/bb616a2a4ca2dde49d833b534a0641201da0e419))
+* **deps:** update jamesives/github-pages-deploy-action action to v4.1.8 ([5b546c0](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/5b546c011958167bbc61c4acb1d279bd4e2945d4))
+* **deps:** update junit5 monorepo to v5.8.2 ([531b080](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/531b0800678deb0e6668b17a575d5bdeb2349389))
+
+## [2.0.0](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/1.1.0...2.0.0) (2021-10-11)
+
+
+### 🦊 CI/CD
+
+* Add breaking type ([02eda08](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/02eda08d519522cae305f8fe54bfbe0876f3718a))
+
+
+### 🧨 Breaking changes!
+
+* Remove start-script related code as this option is no longer available in ServerPackCreator ([150070c](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/150070c4359813b7cc6f33c16bd38410e7d753cf))
+
+
+### Other
+
+* Update info regarding paths to reflect recent changes to ServerPackCreators AddonHandler. ([891fc94](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/891fc94b85402893f096ad5d71e8f6784bbc924b))
+* **deps:** update dependency gradle to v7.2 ([cb4cb13](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/cb4cb131c6f16f3fdbb4db0dc987dedcfe9aed26))
+* **deps:** update dependency org.mockito:mockito-core to v3.12.1 ([484494b](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/484494b6417415eafed83fcd2c7f1302a1c9fb62))
+* **deps:** update dependency org.mockito:mockito-core to v3.12.4 ([69ab366](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/69ab36665876777499bf6903e3c3e744d6852cd6))
+* **deps:** update dependency org.mockito:mockito-core to v4 ([fed2f66](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/fed2f66532fe7695c28ab7dc6ab95ff52086860e))
+* **deps:** update jamesives/github-pages-deploy-action action to v4.1.5 ([8141900](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/814190011af369a4ac88dd2cbfafb64f5b4d6c52))
+* **deps:** update junit5 monorepo to v5.8.0 ([a3cc8fd](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/a3cc8fdf58bd880e8468042fb0cdd9a7ff51892b))
+* **deps:** update junit5 monorepo to v5.8.1 ([a838135](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/a838135a909965ed03c5338fdf5034a7f72380c5))
+
+## [1.1.0](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/1.0.1...1.1.0) (2021-08-02)
+
+
+### :scissors: Refactor
+
+* Rewrite configparser in AddonConfiguration ([dade3aa](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/dade3aae909967f55c858878cd8fbddc903af59c))
+
+
+### 🦊 CI/CD
+
+* Explicitly specify source sets ([fdb4099](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/fdb40991cb8ed3bec997201904b966ebbf62fb54))
+* Deploy to GitHub pages on push ([e074834](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/e0748344f98fb238d4231af818c665e6eca089ee))
+
+
+### 🚀 Features
+
+* Add example of ServerPackCreator path passed by the very same. ([b1aafea](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/b1aafea9c0e9b913625015b5966dc6df74c44805))
+
+
+### Other
+
+* Add example output produces by this example addon when using it with a dev build of ServerPackCreator 3.x.x ([6c7d3a5](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/6c7d3a583517b7063865cf59c0005f8e425c6aad))
+* Remove some unnecessary whitespace ([e803410](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/e803410c812b0a32046507fb2ae1dc266c054969))
+* **deps:** update dependency gradle to v7.1.1 ([2ec8fdc](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/2ec8fdce4ecb96be2556c3f5642182cee06cbbe7))
+
+### [1.0.1](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/1.0.0...1.0.1) (2021-08-01)
+
+
+### 🛠 Fixes
+
+* Fix release assets on GitLab ([6e7d6fb](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/6e7d6fbedc6b50cc62d3410c3e691da9b72500a8))
+* Fix release on GitHub ([3c8d25d](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/3c8d25dd4958db107ac6f6424c7340cb5f8c96d1))
+
+## [1.0.0](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/compare/...1.0.0) (2021-08-01)
+
+
+### 📔 Docs
+
+* Add issue templates ([360e4f4](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/360e4f4cf62c80ed8377c56fc04933b58dbbd8b8))
+
+
+### 🦊 CI/CD
+
+* Add CI/CD configuration files and dependency checker configuration ([3ec18fb](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/3ec18fb46e7ae99b2c58776a9cdcee7bb06f5daf))
+* GitHub workflows for documentation, release mirroring from GitLab upon tag push, greetings and sponsor labels for issues ([b2d98f4](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/b2d98f49049133aa2a850f95e438a7bdc3cda60f))
+* Remove folder from artifacts which is not generated by this repo anyway ([5d8cf86](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/5d8cf86558c0ab8976dfdb2a6b14eae7634b1e84))
+
+
+### 🛠 Fixes
+
+* Add gradlew script files. Whoops. ([37eaea9](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/37eaea9c2ea8f74c809519bc4883a9d3cd42ffb4))
+
+
+### Other
+
+* Add funding info ([ad0a1a6](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/ad0a1a62332e6785504e5b0c100bce60f173a316))
+* Add Gradle plugin maven publish for versioning and publishing to GitHub package registry ([97f13ba](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/97f13ba22a19bc80f7282a33b638df56d5c54f3a))
+* Update table of addons with link to GitHub project of example ([c3410ba](https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon/commit/c3410ba8373a5b1f264501c79b8233a51fd3b92c))
diff --git a/src/main/resources/README.md b/src/main/resources/README.md
index 83a6c2538bc5a255813b9b64f446f1564b283001..7a2e3c60d43c4a98d14e598e57bb2441133f5df8 100644
--- a/src/main/resources/README.md
+++ b/src/main/resources/README.md
@@ -1,210 +1,305 @@
-# Example Addon for ServerPackCreator
+# 1. Example Addon for ServerPackCreator
 
-[![Homepage](https://img.shields.io/badge/Griefed.de-Homepage-c0ffee?style=for-the-badge&labelColor=325358&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAACylBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v6OsnIvAAAA7XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiQlJicoKSorLC0uLzAxMjU2Nzg5Ojs8Pj9AQUJERUZHSElLTE9QUVJTVFVWV1hZXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5/gIGCg4SGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6Cio6WmqKmqq6ytrrCxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jKy8zNzs/Q0dLT1NXW2Nrb3N3e3+Dh4uPk5ebn6Onq6+zt7u/w8fLz9PX29/j5+vv8/f4O/wLaAAAGUklEQVR42o3WBVsb2xoF4G9PQqgXubktUnd3d3fFqjjHQt2Vg9Xd3TXU3d1b3KEugczM+g8nY5VAmrxYZGaxtj1A9jgfcoS1SdKTUx73gg2kYZzOTcdIYQjKeVWZnPJ7a91bh2zc6/SJWbNj16F9G+aNa12VkXGjBYU+5FTTz0DqmPphB9MsIjRCgXnRXRH42pSc6lQCgLdCIYoifsL3IqcG8FB8e34sISpkUnjUjKRj2VpeIDmjmweFdYeRkcr/mtYjK9CNfstnt1pAyFxmJA3XIi5VUDucqEeOsV5p6u2v5tbl6Cecb9RtC2T5Qxk5wE35rN5u8uPKhHuMPPZB/FgClEzXUbl0s3jg6S3+Ubgnlati1w1XB74ExPhyJ4KbzQNHasQHVyeHdH56nxQpoZwOLIyHuLkC6ciJSrtEiNMZ2ev3BdjgTi6ouAsoGUJ2amUCh9zJJZVSgHy71XQ7CDzwJhf5vQRO/DqR4wR87qYn1+gS7gHi0gGdmvp5cKSv4tOsd1A68PaaH7loAg8bvuTz23s+lPS66BsvAhDX66rWlDlt4vEYitIgojbZULzvwqSH/OOPy8iZCBGyPQYiFmSB7GgFBpuNNPrtWfOZW2bzlcvmlFtnzGdvms3XLppTbqeYz18/15YkXq8heVObbAwb5DjLCJID4qjfmTBDyNTuxpi/vHtOCTGEhrarE/FX1cGTR1eMiuxxsTVJYgWp7GiSGe/A5rYHMVy68eGU35wddXc3o/EJ7u4rxlKLXXVYxHx91Y0DqNv2/7E5C5QOflIFoSkpZktPIhkxPMzNB7AzpHdYjcjh3XsOifl/aO+QOlPHtRs84E/vqL6jmwZP6HFO6sDNlyr8STJ2GECqLxHDLTmgcINpxZNZplPHTbOeJps23ZlhOn/AtOD5UtOua7GxZxMfMCKql8UDpxhJKmdCFOI4Iia+KCoGYO5rZ/qGn591Ihtu+V4euVVJ0qwUqZdakM3UE6mb41Lj2pOdfn9TGcYa6bC2JMkwEfv8dSQJmBDtYSZyJYDYGWAUSf4BlpAiIMJ/4R0XA2g1MJMkiUCUFrCs34bnrgaYgLVyle0QA7SAwMAGZlcDJgC75YC9EMdqAXcXBDxyNWA8cJBsdCeBUC1gZijnuIFnTVVFkgQAR8iGOwxEawHjfUPPkL1mJ+/0JKKW36C6TpIQYA9JtgLztYB1AcnvyE717LeFX5sT9YMmlyR/qZNIC4Et3xs0DCjToGeSu25KZNmARO0XBwKXOTVgaQAzk53aNYn07ewDdLWOAQEkac8jx9+oBFyOL2cVmPxJjd6LqtPSjFzOBt9emdwi8IeXccoQRraxazDYpAqqvnqnagoRFy8ARZ4k4S4AQmY9ZRlbL3hFP2Ol0ERDk0PklwbgPEeymQCEeZwUMKNt/INfA/DdPz/NAYsW8OMItRYBvPKTAoKm+JpdCfC4A5s7RpKNldKEWCng2Sy7SWQiNFHQZNAoC2zE9QayqZMKyStPooA/ovVyA6Yh0w5VfOWl2sNhFY9DZglkRIZ9kAnh0ip4RF8gonioSgc9hKpggADVo24foMhuQxRiheJxdWkrrywmohS7gds9zN8oAhD5r4WvEsnn3tvPJcofyxBbg7bDU5wHfLr2EcgO7NnUp7KeuOq+TTr2TxCB+/G6gFUjmAsBeUNKIQTSz9wvAGm1KeDE2vHSKqyHih/5GqqPnZ/nqi6mAUfc6BdN3gKXqgSMH9DNTETBO1RrqoZrDxdzpKpyGciuTXbGWYGDocv7xWcQUS40fQRompOi0gHg2wCyxy0EkBXhP/cWEeVD0w/ftSIbHVXeBwjRjMrQrxOBq9OMV3SOAzwmJPifBwR1OHbc1wJIH5q2sWvFcofQwivyCX/3NSAu0VO59AutgNUifji2f+O/cbL5VYLiFPHJM16LkFj+5sgBbtxbyCx3on79f52rP/+12iVvMCPHGp8TIRNS/22hI40xLku9XTxel37LMDUPCvGKP6k4n708FEvdyJlgEYqis2tmhE+dGDZr/dl0KxR8f3KqN49yCTyAkg7kVLNvgHh72YViAd9Z81JMLcakAp+bkFM+RbCsMxLn1Wnism1Hjh/YsSp2ZMtqjIjq7LW+9SWnqrzODjCQijFGPxiC71Ynp/RJrZnjehzZ+Q9fNggLmTcf8AAAAABJRU5ErkJggg==)](https://www.griefed.de)
-[![Blog](https://img.shields.io/badge/Griefed.de-Blog-c0ffee?style=for-the-badge&labelColor=325358&logo=wordpress)](https://blog.griefed.de)
-[![Fleet](https://img.shields.io/badge/Griefed.de-Fleet-c0ffee?style=for-the-badge&labelColor=325358&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABiVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8XJWL///8gNCTuAAAAgXRSTlMAAgMEBQYHCAkLDA4PEBESFhgZGh0eISIlKC8zNDU3OT0+P0BBQkhKS01QU1RVVltcXWJjZGlrbG1wcnN0eHl6e4CBhYyOkJWWmZ6foaKkqaqsrrKztLe8vsHExcbHy8zNz9DT1NXX2Nna293e3+Tm5+nq7O3u7/Hy9fb4+fr7/f4zgtRAAAABWklEQVR42mKgHhCFMbg0dHW1xNGlBcw5wTS7tkcJoNV5UJItBoMA3GPjcm3btm3btq1+8s1JxijuV0p+X5NzVcfNRoT4O26ClEZykdydI5ddCHDt+EfqthiwYoXfRH++yb9r8IZ+o/CpeB1hE/zMtXf0KYKkP9p8ZSaCfq1S2ddDU84zrsuXYXZjYy0VcBxQKYVmgQ9slS8HhUbxSP+kNAPB+kIyO1ggr5mn9GoX70wKydA4/RNQQSVPvGspZEHQ5ZKHK5lu8UymUi/enRQuxYjcN5LPH+RTQfcVlQ4AbfJ1n4JKRusG0ErpzCn3vjFMe8g9/eUU2himHEAilc+Vq6XJ4Zyx6eXTk3f6/IOww7i2oOliXNXQ/H5kHBcWSO2MowyKJc4VU/BLOGcMm04EJF3FyHsQImkvMj/tQBhrz0to+rwSUf70XvjT23VWxKLLaBkYGuqr+Y8f9Q3q0fzzGED8cgAAAABJRU5ErkJggg==)](https://fleet.griefed.de)
-[![GitHub](https://img.shields.io/badge/Griefed.de-Github-c0ffee?style=for-the-badge&labelColor=325358&logo=github)](https://github.com/Griefed)
-[![DockerHub](https://img.shields.io/badge/Griefed.de-DockerHub-c0ffee?style=for-the-badge&labelColor=325358&logo=docker&logoColor=white)](https://hub.docker.com/u/griefed)
-[![Discord](https://img.shields.io/badge/Griefed.de-Discord-c0ffee?style=for-the-badge&labelColor=325358&logo=discord&logoColor=white)](https://discord.griefed.de)
+This is an example server pack addon for [ServerPackCreator](https://github.com/Griefed/ServerPackCreator)
 
----
+ServerPackCreator provides several extension endpoints for [pf4j plugins](https://github.com/pf4j/pf4j), from hereon out called **addons**, to add
+additional functionality. This example addon demonstrates an implementation for all available extension endpoints of ServerPackCreator.
 
-# Sources, GitHub, GitLab and Mirroring and all that good stuff
+This repository demonstrates how extension for ServerPackCreator are implemented, one small example for every extension
+point available in ServerPackCreator.
 
-Repositories on GitHub are now for issues only. I've set up my own installation of GitLab and moved all my repositories over to [Git.Griefed.de](https://git.griefed.de/users/Griefed/projects). Make sure to check there first for the latest code before opening an issue on GitHub.
+## 1.1 Addon details
 
-For questions, you can always join my [Discord server](https://discord.griefed.de) and talk to me there.
+Take care to edit this section in the [build.gradle](build.gradle)-file if you forked, or intent on forking, this repository.
 
-###### This repository is available at:
+```groovy
+/*
+ CHANGE THESE VALUES
+    FOR YOUR OWN
+       ADDON
 
-- Source: https://git.griefed.de/Griefed/ServerPackCreatorExampleAddon
-- Mirror: https://gitlab.com/Griefed/ServerPackCreatorExampleAddon
-- Mirror: https://github.com/Griefed/ServerPackCreatorExampleAddon
-- Mirror: https://gitea.com/Griefed/ServerPackCreatorExampleAddon
+ Addon ID must be unique.
+    Set it carefully!
+ */
+def pluginClass = 'de.griefed.exampleaddon.Example'
+def addon_id = 'example'
+def addon_name = 'Example Addon'
+def addon_description = 'An example addon for ServerPackCreator'
+def addon_author = 'Griefed'
+group 'de.griefed'
+version = "1.0.0"
+```
 
----
+`pluginClass` must point at the Addon/Plugin class of your addon. Think of it as the Main-Class-attribute from a regular JARs manifest.
 
-[![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/Griefed/ServerPackCreatorExampleAddon?include_prereleases&label=Latest%20Release&logo=Github&style=for-the-badge&color=c0ffee&labelColor=325358)](https://github.com/Griefed/ServerPackCreatorExampleAddon/releases/latest)
-[![GitHub](https://img.shields.io/github/license/Griefed/ServerPackCreatorExampleAddon?logo=GitHub&style=for-the-badge&color=c0ffee&labelColor=325358)](https://github.com/Griefed/ServerPackCreatorExampleAddon/blob/main/LICENSE)
+`addon_id` Is the ID used by ServerPackCreator to identify your addon, extensions in your addon, the global configuration-file of your addon (if you provide one) and
+for identifying extension-configurations from or given to a serverpackcreator.conf. Make it as **unique** as possible.
+[pf4j](https://pf4j.org/doc/plugins.html) does **not** allow for two addons with the same ID to co-exist in a given environment. The more unique
+your addon-ID is, the more likely it will be able to co-exist with any other addon in a given users ServerPackCreator environment.
 
-[![GitHub Repo stars](https://img.shields.io/github/stars/Griefed/ServerPackCreatorExampleAddon?label=GitHub%20Stars&style=for-the-badge&logo=Github&labelColor=325358&color=c0ffee)](https://github.com/Griefed/ServerPackCreatorExampleAddon)
-[![GitHub forks](https://img.shields.io/github/forks/Griefed/ServerPackCreatorExampleAddon?label=GitHub%20Forks&style=for-the-badge&logo=Github&labelColor=325358&color=c0ffee)](https://github.com/Griefed/ServerPackCreatorExampleAddon)
-[![GitHub contributors](https://img.shields.io/github/contributors/Griefed/ServerPackCreatorExampleAddon?color=c0ffee&label=Contributors&logo=GitHub&logoColor=white&style=for-the-badge&labelColor=325358)](https://github.com/Griefed/ServerPackCreatorExampleAddon/graphs/contributors)
-[![GitHub all releases](https://img.shields.io/github/downloads/Griefed/ServerPackCreatorExampleAddon/total?color=c0ffee&logo=GitHub&logoColor=white&labelColor=325358&style=for-the-badge)](https://github.com/Griefed/ServerPackCreatorExampleAddon/releases)
+`addon_name` Is good for identifying a troublesome addon in the logs.
 
----
+`addon_description`, `addon_author` and `version` are fancy to have and should contain a value, but they are not used by ServerPackCreator for any vital or sensitive operations.
 
+`group` well yeah, this should obviously be changed to **your** group as you're **not** me 😅
 
-This is an example server pack addon for [ServerPackCreator](https://github.com/Griefed/ServerPackCreator)
+### 1.1.1 Updating the implemented version of ServerPackCreator
 
-The following endpoints are available in ServerPackCreator to add you extra functionality with a plugin. This example plugin provides examples for all four of them:
+It's as simple as changing the version specified in the `dependencies`-section of the [build.gradle](build.gradle):
 
-- TabExtension: Allows you to add your own JComponent in the form of an additional tab to the GUI. You can run whatever code you want in that tab.
+```groovy
+dependencies {
+    ...
+    implementation 'de.griefed:serverpackcreator:3.14.0'
+    ...
+}
+```
 
-- PreGenExtension: Will allow you to run your code before generation of a server pack starts
+## 1.2 Configuration Panel Extension
 
-- PreZipExtension: Will allow you to run your code after a server pack was generated, but before the ZIP-archive is created
+The configuration panel is intended to let you add a panel in which you, or the user of your addon, may
+configure something for any of the extensions added by your addon.
 
-- PostGenExtension: Will allow you to run your code after the server pack ZIP-archive was generated, so right at the on of the SPC process, basically.
+![configpanel](img/configpanel.png)
 
-Using this example plugin would result in the following or similar output in ServerPackCreator itself:
+The above example lets you configure four textfields, one for each extension point used during server pack 
+configuration checking and server pack generation. More on this in **Configuration Check Extension**.
 
-serverpackcreator.log:
-```
-INFO [main] (AbstractPluginManager.java:814) - Plugin 'example-plugin@0.0.1' resolved
-INFO [main] (AbstractPluginManager.java:357) - Start plugin 'example-plugin@0.0.1'
-INFO [main] (ApplicationPlugins.java:74) - Available PreGenExtension plugins:
-INFO [main] (ApplicationPlugins.java:76) - Name:       ServerPackCreatorExampleAddon
-INFO [main] (ApplicationPlugins.java:77) - Description:This is an example addon for ServerPackCreator showcasing all, currently 4, aspects ofServerPackCreators addon functionality. In this example you see code which gets executed before a server pack is generated,code that gets executed after a server pack was generated but before the ZIP-archive is created, code that gets executedafter this ZIP-archive was created, as well as code adding a new tabbed pane to the GUI.
-INFO [main] (ApplicationPlugins.java:78) - Version:    0.0.1
-INFO [main] (ApplicationPlugins.java:79) - Author:     Griefed
-INFO [main] (ApplicationPlugins.java:83) - Available PreZipExtension plugins:
-INFO [main] (ApplicationPlugins.java:85) - Name:       ServerPackCreatorExampleAddon
-INFO [main] (ApplicationPlugins.java:86) - Description:This is an example addon for ServerPackCreator showcasing all, currently 4, aspects ofServerPackCreators addon functionality. In this example you see code which gets executed before a server pack is generated,code that gets executed after a server pack was generated but before the ZIP-archive is created, code that gets executedafter this ZIP-archive was created, as well as code adding a new tabbed pane to the GUI.
-INFO [main] (ApplicationPlugins.java:87) - Version:    0.0.1
-INFO [main] (ApplicationPlugins.java:88) - Author:     Griefed
-INFO [main] (ApplicationPlugins.java:92) - Available PostGenExtension plugins:
-INFO [main] (ApplicationPlugins.java:94) - Name:       ServerPackCreatorExampleAddon
-INFO [main] (ApplicationPlugins.java:95) - Description:This is an example addon for ServerPackCreator showcasing all, currently 4, aspects ofServerPackCreators addon functionality. In this example you see code which gets executed before a server pack is generated,code that gets executed after a server pack was generated but before the ZIP-archive is created, code that gets executedafter this ZIP-archive was created, as well as code adding a new tabbed pane to the GUI.
-INFO [main] (ApplicationPlugins.java:96) - Version:    0.0.1
-INFO [main] (ApplicationPlugins.java:97) - Author:     Griefed
-INFO [main] (ApplicationPlugins.java:101) - Available TabExtension plugins:
-INFO [main] (ApplicationPlugins.java:103) - Name:       ServerPackCreatorExampleAddon
-INFO [main] (ApplicationPlugins.java:104) - Description:This is an example addon for ServerPackCreator showcasing all, currently 4, aspects ofServerPackCreators addon functionality. In this example you see code which gets executed before a server pack is generated,code that gets executed after a server pack was generated but before the ZIP-archive is created, code that gets executedafter this ZIP-archive was created, as well as code adding a new tabbed pane to the GUI.
-INFO [main] (ApplicationPlugins.java:105) - Version:    0.0.1
-INFO [main] (ApplicationPlugins.java:106) - Author:     Griefed
-```
+Extension configurations are saved to the serverpackcreator.conf of the server pack and re-loaded along
+with everything else, like the Minecraft version, modloader and modloader version etc.
 
-addons.log:
-```
-2022-01-26T18:20:45,896  INFO [main] (ExamplePlugin.java:45) - Starting ExamplePlugin...
-2022-01-26T18:20:45,897  INFO [main] (ExamplePlugin.java:46) - This methods should prepare the environment for anything you want to do with it.
-2022-01-26T18:20:45,897  INFO [main] (ExamplePlugin.java:47) - You could download some files. Create or replace some files. Basically you can do whatever you want.
-2022-01-26T18:21:55,874  INFO [pool-2-thread-1] (ServerPackHandler.java:253) - Executing PreGenExtension addons
-2022-01-26T18:21:55,874  INFO [pool-2-thread-1] (ServerPackHandler.java:255) - Executing plugin ServerPackCreatorExampleAddon
-2022-01-26T18:21:55,874  INFO [pool-2-thread-1] (ExamplePlugin.java:58) - This would run before a server pack generation.
-2022-01-26T18:21:55,875  INFO [pool-2-thread-1] (ExamplePlugin.java:59) - Received destination: G:/GitLab/ServerPackCreator/testruns/server-packs/Deathdusk
-2022-01-26T18:21:55,875  INFO [pool-2-thread-1] (ExamplePlugin.java:60) - We recieved the following configurationModel: ConfigurationModel{clientMods=[AdvancementPlaques-, AmbientSounds_, backtools-, BetterAdvancements-, BetterAnimationsCollection-, BetterDarkMode-, betterf3-, BetterF3-, BetterFoliage-, BetterPingDisplay-, BetterPlacement-, Blur-, catalogue-, cherishedworlds-, classicbar-, clickadv-, ClientTweaks_, configured-, Controlling-, CraftPresence-, CTM-, customdiscordrpc-, CustomMainMenu-, defaultoptions-, DefaultOptions_, desiredservers-, Ding-, drippyloadingscreen-, drippyloadingscreen_, Durability101-, dynmus-, dynamic-music-, DynamicSurroundings-, DynamicSurroundingsHuds-, eiramoticons-, EiraMoticons_, EnchantmentDescriptions-, EquipmentCompare-, extremesoundmuffler-, extremeSoundMuffler-, Fallingleaves-, fallingleaves-, fancymenu_, findme-, flickerfix-, FpsReducer-, FullscreenWindowed-, WindowedFullscreen-, InventoryEssentials_, InventorySpam-, invtweaks-, InventoryTweaks-, ItemBorders-, itemzoom, itlt-, jeed-, jeiintegration_, JustEnoughProfessions-, JEITweaker-, justenoughbeacons-, JustEnoughCalculation-, jehc-, just-enough-harvestcraft-, JustEnoughProfessions-, JustEnoughResources-, keywizard-, konkrete_, lazydfu-, LegendaryTooltips-, LightOverlay-, light-overlay-, LLOverlayReloaded-, loadmyresources_, lootbeams-, mcbindtype-, modnametooltip_, modnametooltip-, moreoverlays-, MouseTweaks-, multihotbar-, MyServerIsCompatible-, Neat, NotifMod-, OldJavaWarning-, ornaments-, overloadedarmorbar-, PackMenu-, PickUpNotifier-, Ping-, preciseblockplacing-, presencefootsteps-, PresenceFootsteps-, ReAuth-, ResourceLoader-, shutupexperimentalsettings-, SimpleDiscordRichPresence-, smoothboot-, sounddeviceoptions-, SpawnerFix-, spoticraft-, tconplanner-, timestamps-, Tips-, TipTheScales-, Toast Control-, Toast-Control-, torohealth-, toughnessbar-, TravelersTitles-, WorldNameRandomizer-], copyDirs=[config, mods], modpackDir='C:/Minecraft/Game/Instances/Deathdusk', javaPath='C:/Program Files/Java/jdk1.8.0_291/jre/bin/java.exe', minecraftVersion='1.18.1', modLoader='Fabric', modLoaderVersion='0.12.12', javaArgs='empty', serverPackSuffix='', serverIconPath='', serverPropertiesPath='', includeServerInstallation=true, includeServerIcon=true, includeServerProperties=true, includeZipCreation=true, curseModpack=null, projectName='null', fileName='null', fileDiskName='null', projectID=0, fileID=0}
-2022-01-26T18:21:55,875  INFO [pool-2-thread-1] (ExamplePlugin.java:61) - We received the following applicationProperties: {de.griefed.serverpackcreator.serverpack.autodiscoverenabled=true, de.griefed.serverpackcreator.spring.schedules.database.cleanup=0 0 24 * *, de.griefed.serverpackcreator.configuration.hastebinserver=https://haste.zneix.eu/documents, de.griefed.serverpackcreator.spring.artemis.queue.max_disk_usage=90, de.griefed.serverpackcreator.serverpack.overwrite.enabled=true, de.griefed.serverpackcreator.configuration.saveloadedconfig=false, de.griefed.serverpackcreator.gui.darkmode=true, de.griefed.serverpackcreator.configuration.directories.shouldexclude=overrides,packmenu,resourcepacks,server_pack,fancymenu, de.griefed.serverpackcreator.language=en_us, de.griefed.serverpackcreator.configuration.directories.serverpacks=server-packs, homeDir=G:/GitLab/ServerPackCreator/build/classes/java, de.griefed.serverpackcreator.curseforge.cleanup.enabled=true, de.griefed.serverpackcreator.plugins.directory=./plugins, de.griefed.serverpackcreator.versioncheck.prerelease=true, de.griefed.serverpackcreator.configuration.directories.mustinclude=mods,config,defaultconfigs,scripts,saves,seeds,libraries, de.griefed.serverpackcreator.serverpack.cleanup.enabled=true, de.griefed.serverpackcreator.spring.cursecontroller.regenerate.enabled=false, de.griefed.serverpackcreator.configuration.fallbackmodslist=AdvancementPlaques-,AmbientSounds_,backtools-,BetterAdvancements-,BetterAnimationsCollection-,BetterDarkMode-,betterf3-,BetterF3-,BetterFoliage-,BetterPingDisplay-,BetterPlacement-,Blur-,catalogue-,cherishedworlds-,classicbar-,clickadv-,ClientTweaks_,configured-,Controlling-,CraftPresence-,CTM-,customdiscordrpc-,CustomMainMenu-,defaultoptions-,DefaultOptions_,desiredservers-,Ding-,drippyloadingscreen-,drippyloadingscreen_,Durability101-,dynmus-,dynamic-music-,DynamicSurroundings-,DynamicSurroundingsHuds-,eiramoticons-,EiraMoticons_,EnchantmentDescriptions-,EquipmentCompare-,extremesoundmuffler-,extremeSoundMuffler-,Fallingleaves-,fallingleaves-,fancymenu_,findme-,flickerfix-,FpsReducer-,FullscreenWindowed-,WindowedFullscreen-,InventoryEssentials_,InventorySpam-,invtweaks-,InventoryTweaks-,ItemBorders-,itemzoom,itlt-,jeed-,jeiintegration_,JustEnoughProfessions-,JEITweaker-,justenoughbeacons-,JustEnoughCalculation-,jehc-,just-enough-harvestcraft-,JustEnoughProfessions-,JustEnoughResources-,keywizard-,konkrete_,lazydfu-,LegendaryTooltips-,LightOverlay-,light-overlay-,LLOverlayReloaded-,loadmyresources_,lootbeams-,mcbindtype-,modnametooltip_,modnametooltip-,moreoverlays-,MouseTweaks-,multihotbar-,MyServerIsCompatible-,Neat,NotifMod-,OldJavaWarning-,ornaments-,overloadedarmorbar-,PackMenu-,PickUpNotifier-,Ping-,preciseblockplacing-,presencefootsteps-,PresenceFootsteps-,ReAuth-,ResourceLoader-,shutupexperimentalsettings-,SimpleDiscordRichPresence-,smoothboot-,sounddeviceoptions-,SpawnerFix-,spoticraft-,tconplanner-,timestamps-,Tips-,TipTheScales-,Toast Control-,Toast-Control-,torohealth-,toughnessbar-,TravelersTitles-,WorldNameRandomizer-}
-2022-01-26T18:22:06,414  INFO [pool-2-thread-1] (ServerPackHandler.java:303) - Executing PreZipExtension addons
-2022-01-26T18:22:06,414  INFO [pool-2-thread-1] (ServerPackHandler.java:305) - Executing plugin ServerPackCreatorExampleAddon
-2022-01-26T18:22:06,414  INFO [pool-2-thread-1] (ExamplePlugin.java:98) - This would run after a server pack was generated, but BEFORE the ZIP-archive would be generated.
-2022-01-26T18:22:06,414  INFO [pool-2-thread-1] (ExamplePlugin.java:99) - Received destination: G:/GitLab/ServerPackCreator/testruns/server-packs/Deathdusk
-2022-01-26T18:22:06,414  INFO [pool-2-thread-1] (ExamplePlugin.java:100) - We recieved the following configurationModel: ConfigurationModel{clientMods=[AdvancementPlaques-, AmbientSounds_, backtools-, BetterAdvancements-, BetterAnimationsCollection-, BetterDarkMode-, betterf3-, BetterF3-, BetterFoliage-, BetterPingDisplay-, BetterPlacement-, Blur-, catalogue-, cherishedworlds-, classicbar-, clickadv-, ClientTweaks_, configured-, Controlling-, CraftPresence-, CTM-, customdiscordrpc-, CustomMainMenu-, defaultoptions-, DefaultOptions_, desiredservers-, Ding-, drippyloadingscreen-, drippyloadingscreen_, Durability101-, dynmus-, dynamic-music-, DynamicSurroundings-, DynamicSurroundingsHuds-, eiramoticons-, EiraMoticons_, EnchantmentDescriptions-, EquipmentCompare-, extremesoundmuffler-, extremeSoundMuffler-, Fallingleaves-, fallingleaves-, fancymenu_, findme-, flickerfix-, FpsReducer-, FullscreenWindowed-, WindowedFullscreen-, InventoryEssentials_, InventorySpam-, invtweaks-, InventoryTweaks-, ItemBorders-, itemzoom, itlt-, jeed-, jeiintegration_, JustEnoughProfessions-, JEITweaker-, justenoughbeacons-, JustEnoughCalculation-, jehc-, just-enough-harvestcraft-, JustEnoughProfessions-, JustEnoughResources-, keywizard-, konkrete_, lazydfu-, LegendaryTooltips-, LightOverlay-, light-overlay-, LLOverlayReloaded-, loadmyresources_, lootbeams-, mcbindtype-, modnametooltip_, modnametooltip-, moreoverlays-, MouseTweaks-, multihotbar-, MyServerIsCompatible-, Neat, NotifMod-, OldJavaWarning-, ornaments-, overloadedarmorbar-, PackMenu-, PickUpNotifier-, Ping-, preciseblockplacing-, presencefootsteps-, PresenceFootsteps-, ReAuth-, ResourceLoader-, shutupexperimentalsettings-, SimpleDiscordRichPresence-, smoothboot-, sounddeviceoptions-, SpawnerFix-, spoticraft-, tconplanner-, timestamps-, Tips-, TipTheScales-, Toast Control-, Toast-Control-, torohealth-, toughnessbar-, TravelersTitles-, WorldNameRandomizer-], copyDirs=[config, mods], modpackDir='C:/Minecraft/Game/Instances/Deathdusk', javaPath='C:/Program Files/Java/jdk1.8.0_291/jre/bin/java.exe', minecraftVersion='1.18.1', modLoader='Fabric', modLoaderVersion='0.12.12', javaArgs='empty', serverPackSuffix='', serverIconPath='', serverPropertiesPath='', includeServerInstallation=true, includeServerIcon=true, includeServerProperties=true, includeZipCreation=true, curseModpack=null, projectName='null', fileName='null', fileDiskName='null', projectID=0, fileID=0}
-2022-01-26T18:22:06,414  INFO [pool-2-thread-1] (ExamplePlugin.java:101) - We received the following applicationProperties: {de.griefed.serverpackcreator.serverpack.autodiscoverenabled=true, de.griefed.serverpackcreator.spring.schedules.database.cleanup=0 0 24 * *, de.griefed.serverpackcreator.configuration.hastebinserver=https://haste.zneix.eu/documents, de.griefed.serverpackcreator.spring.artemis.queue.max_disk_usage=90, de.griefed.serverpackcreator.serverpack.overwrite.enabled=true, de.griefed.serverpackcreator.configuration.saveloadedconfig=false, de.griefed.serverpackcreator.gui.darkmode=true, de.griefed.serverpackcreator.configuration.directories.shouldexclude=overrides,packmenu,resourcepacks,server_pack,fancymenu, de.griefed.serverpackcreator.language=en_us, de.griefed.serverpackcreator.configuration.directories.serverpacks=server-packs, homeDir=G:/GitLab/ServerPackCreator/build/classes/java, de.griefed.serverpackcreator.curseforge.cleanup.enabled=true, de.griefed.serverpackcreator.plugins.directory=./plugins, de.griefed.serverpackcreator.versioncheck.prerelease=true, de.griefed.serverpackcreator.configuration.directories.mustinclude=mods,config,defaultconfigs,scripts,saves,seeds,libraries, de.griefed.serverpackcreator.serverpack.cleanup.enabled=true, de.griefed.serverpackcreator.spring.cursecontroller.regenerate.enabled=false, de.griefed.serverpackcreator.configuration.fallbackmodslist=AdvancementPlaques-,AmbientSounds_,backtools-,BetterAdvancements-,BetterAnimationsCollection-,BetterDarkMode-,betterf3-,BetterF3-,BetterFoliage-,BetterPingDisplay-,BetterPlacement-,Blur-,catalogue-,cherishedworlds-,classicbar-,clickadv-,ClientTweaks_,configured-,Controlling-,CraftPresence-,CTM-,customdiscordrpc-,CustomMainMenu-,defaultoptions-,DefaultOptions_,desiredservers-,Ding-,drippyloadingscreen-,drippyloadingscreen_,Durability101-,dynmus-,dynamic-music-,DynamicSurroundings-,DynamicSurroundingsHuds-,eiramoticons-,EiraMoticons_,EnchantmentDescriptions-,EquipmentCompare-,extremesoundmuffler-,extremeSoundMuffler-,Fallingleaves-,fallingleaves-,fancymenu_,findme-,flickerfix-,FpsReducer-,FullscreenWindowed-,WindowedFullscreen-,InventoryEssentials_,InventorySpam-,invtweaks-,InventoryTweaks-,ItemBorders-,itemzoom,itlt-,jeed-,jeiintegration_,JustEnoughProfessions-,JEITweaker-,justenoughbeacons-,JustEnoughCalculation-,jehc-,just-enough-harvestcraft-,JustEnoughProfessions-,JustEnoughResources-,keywizard-,konkrete_,lazydfu-,LegendaryTooltips-,LightOverlay-,light-overlay-,LLOverlayReloaded-,loadmyresources_,lootbeams-,mcbindtype-,modnametooltip_,modnametooltip-,moreoverlays-,MouseTweaks-,multihotbar-,MyServerIsCompatible-,Neat,NotifMod-,OldJavaWarning-,ornaments-,overloadedarmorbar-,PackMenu-,PickUpNotifier-,Ping-,preciseblockplacing-,presencefootsteps-,PresenceFootsteps-,ReAuth-,ResourceLoader-,shutupexperimentalsettings-,SimpleDiscordRichPresence-,smoothboot-,sounddeviceoptions-,SpawnerFix-,spoticraft-,tconplanner-,timestamps-,Tips-,TipTheScales-,Toast Control-,Toast-Control-,torohealth-,toughnessbar-,TravelersTitles-,WorldNameRandomizer-}
-2022-01-26T18:22:45,053  INFO [pool-2-thread-1] (ServerPackHandler.java:333) - Executing PostGenExtension addons
-2022-01-26T18:22:45,053  INFO [pool-2-thread-1] (ServerPackHandler.java:335) - Executing plugin ServerPackCreatorExampleAddon
-2022-01-26T18:22:45,053  INFO [pool-2-thread-1] (ExamplePlugin.java:143) - This would run after the server pack ZIP-archive was created.
-2022-01-26T18:22:45,053  INFO [pool-2-thread-1] (ExamplePlugin.java:144) - Received destination: G:/GitLab/ServerPackCreator/testruns/server-packs/Deathdusk
-2022-01-26T18:22:45,053  INFO [pool-2-thread-1] (ExamplePlugin.java:145) - We recieved the following configurationModel: ConfigurationModel{clientMods=[AdvancementPlaques-, AmbientSounds_, backtools-, BetterAdvancements-, BetterAnimationsCollection-, BetterDarkMode-, betterf3-, BetterF3-, BetterFoliage-, BetterPingDisplay-, BetterPlacement-, Blur-, catalogue-, cherishedworlds-, classicbar-, clickadv-, ClientTweaks_, configured-, Controlling-, CraftPresence-, CTM-, customdiscordrpc-, CustomMainMenu-, defaultoptions-, DefaultOptions_, desiredservers-, Ding-, drippyloadingscreen-, drippyloadingscreen_, Durability101-, dynmus-, dynamic-music-, DynamicSurroundings-, DynamicSurroundingsHuds-, eiramoticons-, EiraMoticons_, EnchantmentDescriptions-, EquipmentCompare-, extremesoundmuffler-, extremeSoundMuffler-, Fallingleaves-, fallingleaves-, fancymenu_, findme-, flickerfix-, FpsReducer-, FullscreenWindowed-, WindowedFullscreen-, InventoryEssentials_, InventorySpam-, invtweaks-, InventoryTweaks-, ItemBorders-, itemzoom, itlt-, jeed-, jeiintegration_, JustEnoughProfessions-, JEITweaker-, justenoughbeacons-, JustEnoughCalculation-, jehc-, just-enough-harvestcraft-, JustEnoughProfessions-, JustEnoughResources-, keywizard-, konkrete_, lazydfu-, LegendaryTooltips-, LightOverlay-, light-overlay-, LLOverlayReloaded-, loadmyresources_, lootbeams-, mcbindtype-, modnametooltip_, modnametooltip-, moreoverlays-, MouseTweaks-, multihotbar-, MyServerIsCompatible-, Neat, NotifMod-, OldJavaWarning-, ornaments-, overloadedarmorbar-, PackMenu-, PickUpNotifier-, Ping-, preciseblockplacing-, presencefootsteps-, PresenceFootsteps-, ReAuth-, ResourceLoader-, shutupexperimentalsettings-, SimpleDiscordRichPresence-, smoothboot-, sounddeviceoptions-, SpawnerFix-, spoticraft-, tconplanner-, timestamps-, Tips-, TipTheScales-, Toast Control-, Toast-Control-, torohealth-, toughnessbar-, TravelersTitles-, WorldNameRandomizer-], copyDirs=[config, mods], modpackDir='C:/Minecraft/Game/Instances/Deathdusk', javaPath='C:/Program Files/Java/jdk1.8.0_291/jre/bin/java.exe', minecraftVersion='1.18.1', modLoader='Fabric', modLoaderVersion='0.12.12', javaArgs='empty', serverPackSuffix='', serverIconPath='', serverPropertiesPath='', includeServerInstallation=true, includeServerIcon=true, includeServerProperties=true, includeZipCreation=true, curseModpack=null, projectName='null', fileName='null', fileDiskName='null', projectID=0, fileID=0}
-2022-01-26T18:22:45,053  INFO [pool-2-thread-1] (ExamplePlugin.java:146) - We received the following applicationProperties: {de.griefed.serverpackcreator.serverpack.autodiscoverenabled=true, de.griefed.serverpackcreator.spring.schedules.database.cleanup=0 0 24 * *, de.griefed.serverpackcreator.configuration.hastebinserver=https://haste.zneix.eu/documents, de.griefed.serverpackcreator.spring.artemis.queue.max_disk_usage=90, de.griefed.serverpackcreator.serverpack.overwrite.enabled=true, de.griefed.serverpackcreator.configuration.saveloadedconfig=false, de.griefed.serverpackcreator.gui.darkmode=true, de.griefed.serverpackcreator.configuration.directories.shouldexclude=overrides,packmenu,resourcepacks,server_pack,fancymenu, de.griefed.serverpackcreator.language=en_us, de.griefed.serverpackcreator.configuration.directories.serverpacks=server-packs, homeDir=G:/GitLab/ServerPackCreator/build/classes/java, de.griefed.serverpackcreator.curseforge.cleanup.enabled=true, de.griefed.serverpackcreator.plugins.directory=./plugins, de.griefed.serverpackcreator.versioncheck.prerelease=true, de.griefed.serverpackcreator.configuration.directories.mustinclude=mods,config,defaultconfigs,scripts,saves,seeds,libraries, de.griefed.serverpackcreator.serverpack.cleanup.enabled=true, de.griefed.serverpackcreator.spring.cursecontroller.regenerate.enabled=false, de.griefed.serverpackcreator.configuration.fallbackmodslist=AdvancementPlaques-,AmbientSounds_,backtools-,BetterAdvancements-,BetterAnimationsCollection-,BetterDarkMode-,betterf3-,BetterF3-,BetterFoliage-,BetterPingDisplay-,BetterPlacement-,Blur-,catalogue-,cherishedworlds-,classicbar-,clickadv-,ClientTweaks_,configured-,Controlling-,CraftPresence-,CTM-,customdiscordrpc-,CustomMainMenu-,defaultoptions-,DefaultOptions_,desiredservers-,Ding-,drippyloadingscreen-,drippyloadingscreen_,Durability101-,dynmus-,dynamic-music-,DynamicSurroundings-,DynamicSurroundingsHuds-,eiramoticons-,EiraMoticons_,EnchantmentDescriptions-,EquipmentCompare-,extremesoundmuffler-,extremeSoundMuffler-,Fallingleaves-,fallingleaves-,fancymenu_,findme-,flickerfix-,FpsReducer-,FullscreenWindowed-,WindowedFullscreen-,InventoryEssentials_,InventorySpam-,invtweaks-,InventoryTweaks-,ItemBorders-,itemzoom,itlt-,jeed-,jeiintegration_,JustEnoughProfessions-,JEITweaker-,justenoughbeacons-,JustEnoughCalculation-,jehc-,just-enough-harvestcraft-,JustEnoughProfessions-,JustEnoughResources-,keywizard-,konkrete_,lazydfu-,LegendaryTooltips-,LightOverlay-,light-overlay-,LLOverlayReloaded-,loadmyresources_,lootbeams-,mcbindtype-,modnametooltip_,modnametooltip-,moreoverlays-,MouseTweaks-,multihotbar-,MyServerIsCompatible-,Neat,NotifMod-,OldJavaWarning-,ornaments-,overloadedarmorbar-,PackMenu-,PickUpNotifier-,Ping-,preciseblockplacing-,presencefootsteps-,PresenceFootsteps-,ReAuth-,ResourceLoader-,shutupexperimentalsettings-,SimpleDiscordRichPresence-,smoothboot-,sounddeviceoptions-,SpawnerFix-,spoticraft-,tconplanner-,timestamps-,Tips-,TipTheScales-,Toast Control-,Toast-Control-,torohealth-,toughnessbar-,TravelersTitles-,WorldNameRandomizer-}
+To see how this is achieved, please see
+
+```java
+@Override
+public ArrayList<CommentedConfig> serverPackExtensionConfig() {...}
+``` 
+
+and 
+
+```java
+@Override
+public void setServerPackExtensionConfig(ArrayList<CommentedConfig> serverPackExtensionConfig) {...}
 ```
 
-Additionally, the following files/directories would be created in the server pack-directory:
+in the [ConfigurationPanel](src/main/java/de/griefed/exampleaddon/gui/panel/ConfigurationPanel.java)-class.
+
+## 1.3 Tab Extension
+
+Tab extensions allow you to add whole tabs to the GUI of ServerPackCreator. These additional tabs are intended
+to let you add textfields and such, which allow you to configure your global addon configuration.
+You may add anything you want to it. The sky is the limit!
+
+![tab](img/tabextension.png)
+
+The above example adds a button which, when pressed, opens a minimalistic Tetris game in a new window.
+It's not supposed to be actually that entertaining, but rather to demonstrate that you can do what you want inside
+your tab.
+
+Below the big button are some textfields which allow you to change some values of the global addon-wide configuration.
+Global addon-configurations are handed to you by ServerPackCreator when the tab is instantiated. The only thing
+you need to take care of is to call `saveConfiguration()` from within your tab to save the configuration.
+See the example in the [TetrisTab](src/main/java/de/griefed/exampleaddon/gui/tab/TetrisTab.java)-class.
+The example above simply adds a button `Set values` which does just that. 
+
+Global addon-configurations are passed to every extension, along with any available extension-specific configuration,
+automatically, so you don't have to worry about anything other than actually saving changes you made in the tab.
+
+Maybe have a timer auto-save every few seconds? Your tab, your choice! 😁
+
+## 1.4 Configuration Check Extension
+
+The configuration check extension point allows you to run your own config checks, be that on any of the
+already available data from the server pack config tab, or your own data from the configuration panel, or your
+own tab, or whatever else you may want to check.
+
+![check](img/configcheck.png)
+
+The above example simply checks whether the string in `text` of the passed `CommentedConfig` in a list
+of passed configs contains text. If it does, then we add a custom error message to the list of errors encountered
+during configuration checks.
+That list is then displayed to the user after the configurations checks have all run.
+
+For details see `runCheck(...) {...}` in the [ConfigurationCheck](src/main/java/de/griefed/exampleaddon/configcheck/ConfigurationCheck.java)-class.
+
+Keep in mind that the method must return `true` in order to trigger a config check failure on ServerPackCreators part.
+Only if any one configuration check, be that ServerPackCreator native or from addons, returns `true` will the
+error messages be displayed to the user.
+
+Make use of this extension point in combination with the **Configuration Panel Extension** and/or **Tab Extension** in order to
+check user input for any errors!
+
+## 1.5 Pre Server Pack Generation Extension
+
+The Pre Server Pack Generation extensions run, as the name implies, *right before* the generation of a server pack really begins.
+You may use this to prepare the environment for any of the tailing extensions.
+
+![pregen](img/pregen.png)
+
+The above example shows the run of a PreGen extension, with the global addon configuration as well as the extension-specific
+extension passed to it by ServerPackCreator.
+
+See the [PreGeneration](src/main/java/de/griefed/exampleaddon/serverpack/PreGeneration.java)-class for details on how the example above was achieved.
+
+## 1.6 Pre Server Pack ZIP-archive Creation Extension
+
+The Pre Server Pack ZIP-archive Creation extensions run, as the name implies, *right before* the creation of the server packs ZIP-archive is, or would be,
+started. Want to add any files to the ZIP-archive? Or make sure some file doesn't make it into the ZIP-archive?
+
+![prezip](img/prezip.png)
+
+The above example shows the run of a PreZip extension, with the global addon configuration as well as the extension-specific
+extension passed to it by ServerPackCreator.
+
+See the [PreZipArchive](src/main/java/de/griefed/exampleaddon/serverpack/PreZipArchive.java)-class for details on how the example above was achieved.
+
+## 1.7 Post Server Pack Generation Extension
+
+The Post Server Pack Generation extensions run, as the name implies, *after* the generation of a server pack has finished.
+Want to add any files to the server pack, but don't want them to end up in the ZIP-archive? Maybe download,
+install and configure DynMap with some renderdata? This would be the place to do that!
+
+![postgen](img/postgen.png)
+
+The above example shows the run of a PreGen extension, with the global addon configuration as well as the extension-specific
+extension passed to it by ServerPackCreator.
 
-- `./ExampleStartExtension`
-- `./ExampleArchiveExtension`
-- `serverpackcreator.conf` as it was used to generate the server pack
-- `./some/folder/with/a/name`
+See the [PostGeneration](src/main/java/de/griefed/exampleaddon/serverpack/PostGeneration.java)-class for details on how the example above was achieved.
 
-| Example at boot time       | Example after generation finished |
-|----------------------------|-----------------------------------|
-| ![boot](img/boot.png)      | ![preGen](img/afterGen.png)       |
+See now why the ConfigPanel, ConfigCheck and Tab extensions are so nice to have?
+The possibilities are (almost) **endless**!😁 
 
-**Example tab**
+# 2. The reason for allowing ServerPackCreator to run addons:
 
-![tab](img/exampleTab.png)
+Some people need additional functionality for their server packs, or have some additional wishes for
+them. Some of those
+things may not fit into the core functionality of ServerPackCreator itself.
 
-# 1. Addons
+It may also be that it is such a niche feature, that I either don't have the time to code it in, or
+simply don't want to.
+Maybe it doesn't fit into the overall design of ServerPackCreator, too. Who knows, it could be any
+of those reasons or another.
 
-## 1.1 Why
+**Hence, the addon functionality.**
 
-There are things which people want to do with their server packs which could most certainly be automated. Some of those
-things so special, or maybe out of place, that they would not warrant a separate feature for ServerPackCreator itself.
+This allows people to write their own addons to expand the functionality of ServerPackCreator with
+their own features as
+they see fit.
 
-It may also be that it is such a niche feature, that I either don't have the time to code it in, or simply don't want to.
-Maybe it doesn't fit into the overall design of ServerPackCreator, too. Who knows, it could be any of those reasons or another.
+# 3. How
 
-**Hence, the addon functionality!**
+During the start of ServerPackCreator, all addons are loaded and then started. As per the pf4j
+plugin lifecycle, you may use the `start()` method of your addon-class to run any operations needed
+to ensure your environment is set up.
+ServerPackCreator provides a nice `ServerPackCreatorExampleAddon`-class which you can extend if you so
+desire. 
+What it does is it prints some information about your addon during the start of ServerPackCreator,
+which in turn helps you, any other addon-dev and me with debugging things. You may, of course,
+choose not to extend the aforementioned class, and simply extend pf4j's `Plugin`-class, setting up
+your very own addon from scratch.
 
-This allows people to write their own addons to expand the functionality of ServerPackCreator with their own features as
-they see fit or want.
+For detailed documentation about Pf4j, visit https://pf4j.org/
 
-For documentation about Pf4j, visit the [Pf4j documentation](https://pf4j.org/)
+After addons have been loaded an started by ServerPackCreator, it will list all available extensions. Depending on the mode in which ServerPackCreator is run
 
-## 1.2 Adding your own
+- additional tabs may get added the GUI
+- additional panels may get added to the server pack configuration tab
+- additional extensions for configuration checking and server pack generation may be added
 
-How to get your own addon into this list:
+## 3.1 Extension Endpoints
 
-If you have written your own addon or plugin for ServerPackCreator and you would like to see it added here, please open an issue over at ServerPackCreatoron GitHub, using the Documentation template.
+One plugin can have multiple extensions. You may add and provide as many as you like.
+Currently existing endpoint and their intended purpose:
 
-For an addon to be accepted, you must at least provide:
-- The name of the repository, and therefore the addon.
-- The owner of the repository, and therefore the addon.
-- The branch of the repository where the main code resides in.
-- A description of the plugin or addon.
+| **Extension Endpoint**                 | **Description**                                                                                                                                                                                                                                                                                                                  |
+|----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Configuration Check                    | Allows you to run your own, additional, configuration checks. Useful if you provide any of the other extensions and want to make them configurable.<br>The Configuration Check Extension of your addon receives the global configuration for your addon, as well as any server pack specific extension configurations available. |
+| Tab Extension                          | Allows you to add an entirely new tab to the GUI of ServerPackCreator. A usage example for this: Provide configuration possibilities in case you have a global configuration for your addon.                                                                                                                                     |
+| Configuration Panel                    | Allows you to add additional panels to the server pack configuration tab. A usage example for this: Your extensions have configuration you would like to be configurable on a server pack - by - server pack basis.                                                                                                              |
+| Pre Server Pack Generation             | Allows you to run your own operations before the generation of a server pack starts. You may provide configurations via the previously mentioned Tab- and Configuration Panel extensions.                                                                                                                                        |
+| Pre Server Pack ZIP-archive Generation | Allows you to run your own operations after the server pack was generated, but before the ZIP-archive creation would start. You may provide configurations via the previously mentioned Tab- and Configuration Panel extensions.                                                                                                 |
+| Post Server Pack Generation            | Allows you to run your own operations after the generation of a server pack has finished. You may provide configurations via the previously mentioned Tab- and Configuration Panel extensions.                                                                                                                                   |
 
-A curated list of officially acknowledged addons/plugins can be found at [addons.griefed.de](https://addons.griefed.de) (redirects to [GitHub Pages](https://griefed.github.io/ServerPackCreator-Addons-Overview/#/))
+## 3.2 Data provided to extensions
 
-## 1.3 Examples for potential addons
+Depending on the extension, different data will be supplied by ServerPackCreator, to the extension, automatically.
 
-Some examples for potential addons can be found [in this discussion thread](https://github.com/Griefed/ServerPackCreator/discussions/201).
+| **Extension Endpoint**                                                                                | **Description**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
+|-------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Configuration Check                                                                                   | 1. The Version Meta ServerpackCreator uses for Minecraft, Forge, Fabric, LegacyFabric and Quilt related operations.<br>2. The configuration of ServerPackCreator, ApplicationProperties.<br>3. Commonly used utilities across ServerPackCreator<br>4. Receives the configuration model which was just checked by ServerPackCreator.<br>5. A list of encountered errors during previous configuration checks to which you may add your own encountered errors, if any.<br>6. A global, addon-specific, configuration, if you've provided one.<br>6. A list of server pack-specific configurations. You may provide as many as you like, one for each of your extensions for example. |
+| Tab Extension                                                                                         | 1. The Version Meta ServerpackCreator uses for Minecraft, Forge, Fabric, LegacyFabric and Quilt related operations.<br>2. The configuration of ServerPackCreator, ApplicationProperties.<br>3. Commonly used utilities across ServerPackCreator<br>4. A global, addon-specific, configuration, if you've provided one.<br>4.1. The configuration-file for your addon-wide configuration, used to load and save.                                                                                                                                                                                                                                                                     |
+| Configuration Panel                                                                                   | 1. The Version Meta ServerpackCreator uses for Minecraft, Forge, Fabric, LegacyFabric and Quilt related operations.<br>2. The configuration of ServerPackCreator, ApplicationProperties.<br>3. Commonly used utilities across ServerPackCreator<br>4. The tab in which the panel(s) reside in, giving you access to its data.<br>5. A global, addon-specific, configuration, if you've provided one.<br>6. The name of your extension, used by the `ExtensionConfigPanel`-class to add a title to a titled border around your panel.<br>7. The ID of the addon which holds the extension. This is the, unique to a given environment, unique identifier of your addon.              |
+| Pre Server Pack Generation,<br>Pre Server Pack ZIP-archive Generation,<br>Post Server Pack Generation | 1. The Version Meta ServerpackCreator uses for Minecraft, Forge, Fabric, LegacyFabric and Quilt related operations.<br>2. Commonly used utilities across ServerPackCreator<br>3. The configuration of ServerPackCreator, ApplicationProperties.<br>4. Receives the configuration model which was just checked by ServerPackCreator.<br>5. The destination at which the server pack was/is/will be/being generated.<br>6. A global, addon-specific, configuration, if you've provided one.<br>7. A list of server pack-specific configurations. You may provide as many as you like, one for each of your extensions for example.                                                    |
 
-Some excerpts:
-1. Changelog generator, by @TheButterbrotMan at [Feature request]: Changelog generator #198
-   - A changelog generator that checks the differences to the previous version and generates a changelog.
+As you can see, a lot of data and information is provided by ServerPackCreator for your convenience.
+If your addon provides a global configuration, you do not need to take care of it, manage it, ensure
+it exists or load it. ServerPackCreator will extract it and provide any and all of your extensions
+with it, so you may use, change, overwrite and save it as you so desire.
 
-2. Bundle Adoptium Java with server packs, by @kreezxil at [Feature request]: Bundle Adoptium Java #199
-   - Because modpacks need one of the either Java 8, 16, or 17, it would be nice to have the corresponding https://adoptium.net java prebundled with the server pack.
+Furthermore, any server pack specific configuration provided by you via a Configuration Panel will
+be stored in the server pack related configuration file, along with any other default fields of
+ServerPackCreator. When said configuration file is then loaded, your Configuration Panel extension
+is again, automatically, provided with your extension configurations. No need to load it yourself,
+all you have to make sure is to provide the changed data back to ServerPackCreator for when a given
+user saves their configuration.
 
-3. Automatic setup of a server pack for [BlueMap](https://www.curseforge.com/minecraft/mc-mods/bluemap)
-   - Check all mods in the specified modpacks mods-directory for textures, and if any are found, add the mod to
-     BlueMap's resourcepack folder `config/bluemap/resourcepacks`, install BlueMap for the specified Minecraft and Forge/Fabric
-     version and voilà!
+See [pf4j documentation](https://pf4j.org/doc/extensions) on extensions.
 
-# 2. How
+## 3.3 Global addon configuration
 
-During the start of ServerPackCreator, all plugins are loaded and started. If you have anything you need to run then and there,
-use `public void start() {...}` and do your thing.
+This example contains a `config.toml`-file which serves as a global configuration for your addon and
+any extensions it provides. As mentioned previously, every one of your extensions will receive this
+global configuration and in order for it to be changeable, you need to provide a suitable TabExtension.
 
-For documentation about Pf4j, visit https://pf4j.org/
+If you intend on using the global configuration, bear in mind:
+1. to **not** rename the file yourself. ServerPackCreator extracts it from your addon and stores it under a different filename, matching your addons ID
+2. that your addon ID should be as unique as possible. Duplicate IDs will cause conflicts with other addons which have the same ID as yours
+3. pf4j relies on each addon to have a unique ID, otherwise pf4j will encounter issues if multiple addons with the same ID are provided
+4. Change the ID in `build.grdle`, in the `def addon_id`-section. Change the other values accordingly, too
 
-If you have ideas and/or suggestions for improvements to the addon-system in ServerPackCreator, open an improvement-issue over at the ServerPackCreator [issues page](https://github.com/Griefed/ServerPackCreator/issues/new?assignees=Griefed&labels=enhancement&template=improvement.yml&title=%5BImprovement+request%5D%3A+)
+ServerPackCreator will manage and provide the global configuration itself. You simply have to take
+care to provide, change and use the values therein.
 
-## 2.1 Extensions
+## 3.4 Addon identification
 
-One plugin can have multiple extensions.
+The `addon.toml`-file contains values by which to identify your addon in the logs. Do not edit this
+file manually, as the `processResources`-task will automatically replace the values with the ones
+you set in the `def <key>`-section.
 
-ServerPackCreator passes the `ConfigurationModel` and `ApplicationProperties`, used to create a server pack, to all `PreGenExtension`, `PreZipExtension` and `PostGenExtension` extensions.
+As well as with the `addon.toml`-file, your addon#s manifest must contain specific information. This
+information is also automatically replace.
 
-If you have a plugin with multiple extensions of the same type, you can set the priority in which they are executed by specifiying and ordinal for your extension: `@Extension(ordinal = 1)`
-The higher the ordinal, the lower the priority. See [pf4j documentation](https://pf4j.org/doc/extensions) on extensions.
+Again, make sure to change the values in the `def <key>`-section!
 
-## 2.2 Logs
+## 3.5 Logs
 
-If you want to print log messages, then use the `LOG_ADDONS` logger as shown in this example. This will result in ServerPackCreator writing any and all log entries from this logger to the `addons.log`-file.
+If you want to print log messages, then use the `LOG_ADDONS` logger as shown in this example. This
+will result in ServerPackCreator writing any and all log entries from this logger to
+the `addons.log`-file.
 
-# 3. Building
+# 4. Building
 
 1. Fork and clone this repository
 2. Make your changes and additions.
-3. Build with `gradlew about build --info`.
+3. Build with `gradlew clean build --info --stacktrace`.
 4. Copy the JAR-file from `build/libs` to the plugins-directory created by ServerPackCreator.
 5. Run ServerPackCreator!
 
-## 4. Contributing
+# 5. Contributing
 
-If you have written an addon, let me know by creating an issue in this repository. Provide a short description of what your
-addon does and a link to the GitHub repository as well. I will add it to a list in the README of ServerPackCreator.
+If you have written an addon, let me know by creating an issue in this repository. Provide a short
+description of what your
+addon does and a link to the GitHub repository as well. I will add it to a list in the README of
+ServerPackCreator.
 
-**NOTE: I only add addons which are open source. I will NOT add any direct download links to any file. People must be able
-to check your code before they download and install your addon, and as such, I will only add a link to your addon-respository
+**NOTE: I only add addons which are open source. I will NOT add any direct download links to any
+file. People must be able
+to check your code before they download and install your addon, and as such, I will only add a link
+to your addon-respository
 along with a small description, if you provided one.
 
 Example:
 
-| Addon                                                                                                                                           | Creator | Description                                                                                                                                                                                         |
-|:------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| [ExampleAddon](https://github.com/Griefed/ServerPackCreatorExampleAddon)                                                                        | Griefed | An example addon providing a starting point for addon development.                                                                                                                                  |
-| [ExampleAddon Alternative Extension Declaration](https://github.com/Griefed/ServerPackCreatorExampleAddon/tree/alternativeExtensionDeclaration) | Griefed | An example addon providing a starting point for addon development. This addon provides an example for a different way of declaring extensions as well as reading entries from the plugins manifest. |
-| [Example MiniGame](https://github.com/Griefed/ServerPackCreatorExampleAddon/tree/tetris)                                                        | Griefed | Play Tetris in a new window whilst your server packs generate!                                                                                                                                      |
+| Addon                                                                                    | Creator | Description                                                                                             |
+|:-----------------------------------------------------------------------------------------|:--------|:--------------------------------------------------------------------------------------------------------|
+| [ExampleAddon](https://github.com/Griefed/ServerPackCreatorExampleAddon)                 | Griefed | An example addon providing a starting point for addon development with one example for every extension. |
diff --git a/src/main/resources/addon.toml b/src/main/resources/addon.toml
new file mode 100644
index 0000000000000000000000000000000000000000..53491209c7d193b33f3a712c797d15960a1b6ba6
--- /dev/null
+++ b/src/main/resources/addon.toml
@@ -0,0 +1,5 @@
+id = "${addon_id}"
+name = "${addon_name}"
+description = "${addon_description}"
+author = "${addon_author}"
+version = "${version}"
diff --git a/src/main/resources/config.toml b/src/main/resources/config.toml
new file mode 100644
index 0000000000000000000000000000000000000000..adb738b33e084ace7949b687bc74bb25ca999cf1
--- /dev/null
+++ b/src/main/resources/config.toml
@@ -0,0 +1,2 @@
+whoami = "example"
+whyamihere = "Because"
\ No newline at end of file
diff --git a/src/main/resources/play.png b/src/main/resources/play.png
new file mode 100644
index 0000000000000000000000000000000000000000..d639ad7711c13988f84d88e613de95c250ad3229
Binary files /dev/null and b/src/main/resources/play.png differ
diff --git a/src/test/java/de/griefed/exampleaddon/AddonTests.java b/src/test/java/de/griefed/exampleaddon/AddonTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..2daccb839be5946639413701762d5e1227716129
--- /dev/null
+++ b/src/test/java/de/griefed/exampleaddon/AddonTests.java
@@ -0,0 +1,7 @@
+package de.griefed.exampleaddon;
+
+public class AddonTests {
+
+
+
+}