diff --git a/.github/workflows/build-project.yaml b/.github/workflows/build-project.yaml
index a93ec38f5a5..e4db1c2ea43 100644
--- a/.github/workflows/build-project.yaml
+++ b/.github/workflows/build-project.yaml
@@ -1,113 +1,17 @@
-# This workflow will build a Java project with Gradle
-# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
-
 name: Java CI with Gradle
 
 on:
   push:
     branches-ignore: [ 'api-*' ]
-    tags-ignore: ["*"]
+    tags-ignore: ["**"]
   pull_request:
     branches-ignore: [ stable-7 ]
 
-env:
-  CACHE_REV: "1"
-
 jobs:
   build:
-    # Only run on PRs if the source branch is on someone else's repo
-    if: "${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}"
-    runs-on: ubuntu-22.04
-    steps:
-      - uses: actions/checkout@v3
-        with:
-          submodules: recursive
-      - uses: actions/cache@v3
-        with:
-          path: |
-            ~/.gradle/caches
-            ~/.gradle/wrapper
-            ${{ github.workspace}}/buildSrc/.gradle/
-            ${{ github.workspace}}/forge/build/fg_cache
-            ${{ github.workspace}}/.gradle/
-          key: "${{ runner.os }}-minecraft-${{ env.CACHE_REV }}-${{ hashFiles('**/*.gradle*') }}"
-          restore-keys: |
-            ${{ runner.os }}-minecraft-${{ env.CACHE_REV }}-
-      - name: Set up JDK 17
-        uses: actions/setup-java@v3
-        with:
-          distribution: temurin
-          java-version: 17
-      - name: Validate Gradle Wrapper
-        uses: gradle/wrapper-validation-action@v1
-      - name: Setup workspace
-        run: |
-          echo "GIT_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
-          echo "GIT_BRANCH=${GITHUB_REF##*/}" >> $GITHUB_ENV
-      - name: Build with Gradle
-        run: ./gradlew -PenableTestPlugins=true -PenableSpongeForge=true build --stacktrace
-        env:
-          CI_SYSTEM: Github Actions
-      - name: Archive artifacts for build
-        uses: actions/upload-artifact@v3
-        with:
-          name: Sponge Jars
-          path: |
-            ${{ github.workspace }}/SpongeAPI/build/libs/*.jar
-            ${{ github.workspace }}/build/libs/*.jar
-            ${{ github.workspace }}/vanilla/build/libs/*.jar
-            ${{ github.workspace }}/forge/build/libs/*.jar
-      - name: SpongeVanilla Production Jar
-        uses: actions/upload-artifact@v3
-        with:
-          name: SpongeVanilla Production Jar
-          path: "${{ github.workspace }}/vanilla/build/libs/*-universal.jar"
-      - name: SpongeForge Production Jar
-        uses: actions/upload-artifact@v3
-        with:
-          name: SpongeForge Production Jar
-          path: "${{ github.workspace }}/forge/build/libs/*-universal.jar"
-      - name: SpongeVanilla libraries
-        uses: actions/upload-artifact@v3
-        with:
-          name: SpongeVanilla installer libraries
-          path: "${{ github.workspace }}/vanilla/build/resources/installer/libraries.json"
+    uses: ./.github/workflows/common-run-build.yaml
+    secrets: inherit
   integrationTest:
     needs: build
-    timeout-minutes: 10
-    strategy:
-      fail-fast: false
-      matrix:
-        os: [ubuntu-22.04, windows-latest, macos-latest]
-        java: [17]
-    runs-on: "${{ matrix.os }}"
-    steps:
-      - name: Check out repository to use the build.gradle.kts as a hash file
-        uses: actions/checkout@v2
-        with:
-          path: code
-      - name: Download SpongeVanilla libraries as an additional hash file
-        uses: actions/download-artifact@v3
-        with:
-          name: SpongeVanilla installer libraries
-          path: "${{ github.workspace }}/code/libraries.json"
-      - name: "Setup JDK ${{ matrix.java }}"
-        uses: actions/setup-java@v3
-        with:
-          distribution: temurin
-          java-version: "${{ matrix.java }}"
-      - uses: actions/cache@v3
-        with:
-          path: "${{ github.workspace}}/libraries"
-          key: "${{runner.os}}-${{matrix.java}}-it-libraries-${{ hashFiles('code/build.gradle.kts') }}-${{ github.workspace }}/code/libraries.json"
-          restore-keys: "${{runner.os}}-${{matrix.java}}-it-libraries-"
-      - name: Download SpongeVanilla server
-        uses: actions/download-artifact@v3
-        with:
-          name: SpongeVanilla Production Jar
-      - name: Run SpongeVanilla Test (windows)
-        if: "runner.os == 'Windows'"
-        run: java "-Dmixin.debug.verbose=true" -jar $(gci | Where-Object NameString -Match "-universal.jar") --launchTarget sponge_server_it
-      - name: Run SpongeVanilla Test (other)
-        if: "runner.os != 'Windows'"
-        run: java -Dmixin.debug.verbose=true -jar *-universal.jar --launchTarget sponge_server_it
\ No newline at end of file
+    uses: ./.github/workflows/common-integration-test.yaml
+    secrets: inherit
\ No newline at end of file
diff --git a/.github/workflows/common-integration-test.yaml b/.github/workflows/common-integration-test.yaml
new file mode 100644
index 00000000000..c5a2c5edc58
--- /dev/null
+++ b/.github/workflows/common-integration-test.yaml
@@ -0,0 +1,49 @@
+name: "common / integration test"
+
+on:
+  workflow_call:
+    inputs:
+      distribution:
+        type: string
+        required: false
+        default: "SpongeVanilla"
+
+jobs:
+  integrationTest:
+    timeout-minutes: 10
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-22.04, windows-latest, macos-latest]
+        java: [17, 21]
+    runs-on: "${{ matrix.os }}"
+    steps:
+      - name: Check out repository to use the build.gradle.kts as a hash file
+        uses: actions/checkout@v4.1.1
+        with:
+          path: code
+      - name: Download ${{ inputs.distribution }} libraries as an additional hash file
+        uses: actions/download-artifact@v4.1.1
+        with:
+          name: ${{ inputs.distribution }} installer libraries
+          path: "${{ github.workspace }}/code/libraries.json"
+      - name: "Setup JDK ${{ matrix.java }}"
+        uses: actions/setup-java@v4.0.0
+        with:
+          distribution: temurin
+          java-version: "${{ matrix.java }}"
+      - uses: actions/cache@v4.0.0
+        with:
+          path: "${{ github.workspace}}/libraries"
+          key: "${{runner.os}}-${{matrix.java}}-it-libraries-${{ hashFiles('code/build.gradle.kts') }}-${{ github.workspace }}/code/libraries.json"
+          restore-keys: "${{runner.os}}-${{matrix.java}}-it-libraries-"
+      - name: Download ${{ inputs.distribution }} server
+        uses: actions/download-artifact@v4.1.1
+        with:
+          name: ${{ inputs.distribution }} Production Jar
+      - name: Run ${{ inputs.distribution }} Test (windows)
+        if: "runner.os == 'Windows'"
+        run: java "-Dmixin.debug.verbose=true" -jar $(gci | Where-Object NameString -Match "-universal.jar") --launchTarget sponge_server_it
+      - name: Run ${{ inputs.distribution }} Test (other)
+        if: "runner.os != 'Windows'"
+        run: java -Dmixin.debug.verbose=true -jar *-universal.jar --launchTarget sponge_server_it
diff --git a/.github/workflows/common-run-build.yaml b/.github/workflows/common-run-build.yaml
new file mode 100644
index 00000000000..659cb6cb7bf
--- /dev/null
+++ b/.github/workflows/common-run-build.yaml
@@ -0,0 +1,62 @@
+name: "common / build"
+
+on:
+  workflow_call:
+
+env:
+  CACHE_REV: "1"
+
+jobs:
+  build:
+    # Only run on PRs if the source branch is on someone else's repo
+    if: "${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}"
+    runs-on: ubuntu-22.04
+    steps:
+      - name: setup
+        id: setup
+        uses: SpongePowered/.github/.github/actions/setup-java-env@master
+        with:
+          runtime_version: 17
+          publishing_branch_regex: 'api-\d+'
+      - name: setup / cache minecraft dependencies
+        uses: actions/cache@v4.0.0
+        with:
+          path: |
+            ${{ github.workspace}}/buildSrc/.gradle/
+            ${{ github.workspace}}/.gradle/
+          key: "${{ runner.os }}-minecraft-${{ env.CACHE_REV }}-${{ hashFiles('**/*.gradle*') }}"
+          restore-keys: |
+            ${{ runner.os }}-minecraft-${{ env.CACHE_REV }}-
+      - name: setup / environment
+        run: |
+          echo "GIT_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
+          echo "GIT_BRANCH=${GITHUB_REF##*/}" >> $GITHUB_ENV
+          echo "BUILD_NUMBER=${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
+      - name: build with Gradle
+        run: ./gradlew -PenableTestPlugins=true -PenableSpongeForge=true build --stacktrace
+        env:
+          CI_SYSTEM: Github Actions
+      - name: Archive artifacts for build
+        uses: actions/upload-artifact@v4.2.0
+        with:
+          name: Sponge Jars
+          path: |
+            ${{ github.workspace }}/SpongeAPI/build/libs/*.jar
+            ${{ github.workspace }}/build/libs/*.jar
+            ${{ github.workspace }}/vanilla/build/libs/*.jar
+            ${{ github.workspace }}/forge/build/libs/*.jar
+      - name: SpongeVanilla Production Jar
+        uses: actions/upload-artifact@v4.2.0
+        with:
+          name: SpongeVanilla Production Jar
+          path: "${{ github.workspace }}/vanilla/build/libs/*-universal.jar"
+      - name: SpongeForge Production Jar
+        uses: actions/upload-artifact@v4.2.0
+        with:
+          name: SpongeForge Production Jar
+          path: "${{ github.workspace }}/forge/build/libs/*-universal.jar"
+      - name: SpongeVanilla libraries
+        uses: actions/upload-artifact@v4.2.0
+        with:
+          name: SpongeVanilla installer libraries
+          path: "${{ github.workspace }}/vanilla/build/resources/installer/libraries.json"
diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml
index ab042a286ab..8ed49df7711 100644
--- a/.github/workflows/deploy.yaml
+++ b/.github/workflows/deploy.yaml
@@ -6,132 +6,39 @@ name: Build and Deploy
 on:
   push:
     branches: [ 'api-*' ]
+    tags-ignore: [ '**' ]
 
 env:
-  CACHE_REV: "1"
+  CACHE_REV: 1
 
 jobs:
   build:
-    runs-on: ubuntu-22.04
-    steps:
-      - uses: actions/checkout@v3
-        with:
-          submodules: recursive
-      - uses: actions/cache@v3
-        with:
-          path: |
-            ~/.gradle/caches
-            ~/.gradle/wrapper
-            ${{ github.workspace}}/buildSrc/.gradle/
-            ${{ github.workspace}}/forge/build/fg_cache
-            ${{ github.workspace}}/.gradle/
-          key: "${{ runner.os }}-minecraft-${{ env.CACHE_REV }}-${{ hashFiles('**/*.gradle*') }}"
-          restore-keys: |
-            ${{ runner.os }}-minecraft-${{ env.CACHE_REV }}-
-      - name: Set up JDK 17
-        uses: actions/setup-java@v3
-        with:
-          distribution: temurin
-          java-version: 17
-      - name: Validate Gradle Wrapper
-        uses: gradle/wrapper-validation-action@v1
-      - name: Setup workspace
-        run: |
-          echo "GIT_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
-          echo "GIT_BRANCH=${GITHUB_REF##*/}" >> $GITHUB_ENV
-          echo "BUILD_NUMBER=${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
-      - name: Build with Gradle
-        run: ./gradlew -PenableTestPlugins=true -PenableSpongeForge=true build --stacktrace
-        env:
-          CI_SYSTEM: Github Actions
-      - name: Archive artifacts for build
-        uses: actions/upload-artifact@v3
-        with:
-          name: Sponge Jars
-          path: |
-            ${{ github.workspace }}/SpongeAPI/build/libs/*.jar
-            ${{ github.workspace }}/build/libs/*.jar
-            ${{ github.workspace }}/vanilla/build/libs/*.jar
-            ${{ github.workspace }}/forge/build/libs/*.jar
-      - name: SpongeVanilla Production Jar
-        uses: actions/upload-artifact@v3
-        with:
-          name: SpongeVanilla Production Jar
-          path: "${{ github.workspace }}/vanilla/build/libs/*-universal.jar"
-      - name: SpongeForge Production Jar
-        uses: actions/upload-artifact@v3
-        with:
-          name: SpongeForge Production Jar
-          path: "${{ github.workspace }}/forge/build/libs/*-universal.jar"
-      - name: SpongeVanilla libraries
-        uses: actions/upload-artifact@v3
-        with:
-          name: SpongeVanilla installer libraries
-          path: "${{ github.workspace }}/vanilla/build/resources/installer/libraries.json"
+    uses: ./.github/workflows/common-run-build.yaml
+    secrets: inherit
   integrationTest:
     needs: build
-    timeout-minutes: 10
-    strategy:
-      fail-fast: false
-      matrix:
-        os: [ubuntu-22.04, windows-latest, macos-latest]
-        java: [17]
-    runs-on: "${{ matrix.os }}"
-    steps:
-      - name: Check out repository to use the build.gradle.kts as a hash file
-        uses: actions/checkout@v3
-        with:
-          path: code
-      - name: Download SpongeVanilla libraries as an additional hash file
-        uses: actions/download-artifact@v3
-        with:
-          name: SpongeVanilla installer libraries
-          path: "${{ github.workspace }}/code/libraries.json"
-      - name: "Setup JDK ${{ matrix.java }}"
-        uses: actions/setup-java@v3
-        with:
-          distribution: temurin
-          java-version: "${{ matrix.java }}"
-      - uses: actions/cache@v3
-        with:
-          path: "${{ github.workspace}}/libraries"
-          key: "${{runner.os}}-${{matrix.java}}-it-libraries-${{ hashFiles('code/build.gradle.kts') }}-${{ github.workspace }}/code/libraries.json"
-          restore-keys: "${{runner.os}}-${{matrix.java}}-it-libraries-"
-      - name: Download SpongeVanilla server
-        uses: actions/download-artifact@v3
-        with:
-          name: SpongeVanilla Production Jar
-      - name: Run SpongeVanilla Test (windows)
-        if: "runner.os == 'Windows'"
-        run: java "-Dmixin.debug.verbose=true" -jar $(gci | Where-Object NameString -Match "-universal.jar") --launchTarget sponge_server_it
-      - name: Run SpongeVanilla Test (other)
-        if: "runner.os != 'Windows'"
-        run: java -Dmixin.debug.verbose=true -jar *-universal.jar --launchTarget sponge_server_it
+    uses: ./.github/workflows/common-integration-test.yaml
+    secrets: inherit
   publish:
     needs: integrationTest
     runs-on: ubuntu-22.04
     steps:
-      - uses: actions/checkout@v3
+      - name: setup
+        id: setup
+        uses: SpongePowered/.github/.github/actions/setup-java-env@master
         with:
-          submodules: recursive
-      - uses: actions/cache@v3
+          runtime_version: 17
+      - name: setup / minecraft cache
+        uses: actions/cache@v3
         with:
           path: |
-            ~/.gradle/caches
-            ~/.gradle/wrapper
             ${{ github.workspace}}/buildSrc/.gradle/
-            ${{ github.workspace}}/forge/build/fg_cache
             ${{ github.workspace}}/.gradle/
           key: "${{ runner.os }}-minecraft-${{ env.CACHE_REV }}-${{ hashFiles('**/*.gradle*') }}"
           restore-keys: |
             ${{ runner.os }}-minecraft-${{ env.CACHE_REV }}-
-      - name: Set up JDK 17
-        uses: actions/setup-java@v3
-        with:
-          distribution: temurin
-          java-version: 17
         # We don't need to run tests again, so we just publish
-      - name: Setup workspace
+      - name: setup / workspace
         run: |
           echo "GIT_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
           echo "GIT_BRANCH=${GITHUB_REF##*/}" >> $GITHUB_ENV
diff --git a/SpongeAPI b/SpongeAPI
index ac8aa2bce2b..92619ec8919 160000
--- a/SpongeAPI
+++ b/SpongeAPI
@@ -1 +1 @@
-Subproject commit ac8aa2bce2b5594af257f0a239368ec525ce0d86
+Subproject commit 92619ec8919f18bfd083c295a972c4cdeceb0e43
diff --git a/build-logic/build.gradle b/build-logic/build.gradle
index 62501a681f3..85d251347c1 100644
--- a/build-logic/build.gradle
+++ b/build-logic/build.gradle
@@ -1,6 +1,6 @@
 plugins {
     id "java-gradle-plugin"
-    id "org.spongepowered.gradle.sponge.dev" version "2.1.1"
+    alias apiLibs.plugins.spongeGradle.convention
 }
 
 indra {
@@ -10,8 +10,8 @@ indra {
 }
 
 dependencies {
-    api("com.google.code.gson:gson:2.9.1")
-    implementation("gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:1.1.7")
+    api(apiLibs.gson)
+    implementation("gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:${apiLibs.versions.ideaExt.get()}")
 }
 
 indraSpotlessLicenser {
@@ -30,9 +30,5 @@ gradlePlugin {
             id = "implementation-structure"
             implementationClass = "org.spongepowered.gradle.impl.SpongeImplementationPlugin"
         }
-        templateResources {
-            id = "templated-resources"
-            implementationClass = "org.spongepowered.gradle.impl.TemplatedResourcesPlugin"
-        }
     }
 }
\ No newline at end of file
diff --git a/build-logic/settings.gradle b/build-logic/settings.gradle
index 48cb0515985..9e7a64e236c 100644
--- a/build-logic/settings.gradle
+++ b/build-logic/settings.gradle
@@ -10,6 +10,14 @@ pluginManagement {
 dependencyResolutionManagement {
     repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
     repositories { pluginManagement.repositories.each { add(it) } }
+    versionCatalogs {
+        libs {
+            from files("../gradle/libs.versions.toml")
+        }
+        apiLibs {
+            from files("../SpongeAPI/gradle/libs.versions.toml")
+        }
+    }
 }
 
 rootProject.name = "build-logic"
\ No newline at end of file
diff --git a/build-logic/src/main/java/org/spongepowered/gradle/impl/GenerateResourceTemplates.java b/build-logic/src/main/java/org/spongepowered/gradle/impl/GenerateResourceTemplates.java
deleted file mode 100644
index 5de750d5e5e..00000000000
--- a/build-logic/src/main/java/org/spongepowered/gradle/impl/GenerateResourceTemplates.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * This file is part of Sponge, licensed under the MIT License (MIT).
- *
- * Copyright (c) SpongePowered <https://www.spongepowered.org>
- * Copyright (c) contributors
- *
- * 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 org.spongepowered.gradle.impl;
-
-import org.gradle.api.tasks.Copy;
-
-public class GenerateResourceTemplates extends Copy {
-
-}
diff --git a/build-logic/src/main/java/org/spongepowered/gradle/impl/TemplatedResourcesPlugin.java b/build-logic/src/main/java/org/spongepowered/gradle/impl/TemplatedResourcesPlugin.java
deleted file mode 100644
index 468e58ae491..00000000000
--- a/build-logic/src/main/java/org/spongepowered/gradle/impl/TemplatedResourcesPlugin.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * This file is part of Sponge, licensed under the MIT License (MIT).
- *
- * Copyright (c) SpongePowered <https://www.spongepowered.org>
- * Copyright (c) contributors
- *
- * 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 org.spongepowered.gradle.impl;
-
-import org.gradle.api.Plugin;
-import org.gradle.api.Project;
-import org.gradle.api.Task;
-import org.gradle.api.file.Directory;
-import org.gradle.api.file.ProjectLayout;
-import org.gradle.api.plugins.ExtensionAware;
-import org.gradle.api.plugins.JavaBasePlugin;
-import org.gradle.api.provider.Provider;
-import org.gradle.api.tasks.SourceSetContainer;
-import org.gradle.api.tasks.TaskContainer;
-import org.gradle.api.tasks.TaskProvider;
-import org.gradle.plugins.ide.eclipse.EclipsePlugin;
-import org.gradle.plugins.ide.eclipse.model.EclipseModel;
-import org.gradle.plugins.ide.idea.model.IdeaModel;
-import org.jetbrains.gradle.ext.IdeaExtPlugin;
-import org.jetbrains.gradle.ext.ProjectSettings;
-import org.jetbrains.gradle.ext.TaskTriggersConfig;
-
-/**
- * Do templated resources, but in a way that's compatible with IDE run configs.
- */
-public class TemplatedResourcesPlugin implements Plugin<Project> {
-
-    @Override
-    public void apply(final Project target) {
-        // for every source set, register a templateResources task
-        // that task's output gets registered as a project task
-
-        target.getPlugins().withType(JavaBasePlugin.class, pl -> this.withJavaBase(target));
-    }
-
-    private void withJavaBase(final Project target) {
-        final SourceSetContainer sourceSets = target.getExtensions().getByType(SourceSetContainer.class);
-        final TaskContainer tasks = target.getTasks();
-        final ProjectLayout layout = target.getLayout();
-
-        sourceSets.all(set -> {
-            final Provider<Directory> destination = layout.getBuildDirectory().dir("generated/templateResources/" + set.getName());
-            final Directory srcDir = layout.getProjectDirectory().dir("src/" + set.getName() + "/resourceTemplates");
-            final TaskProvider<?> generateTask = tasks.register(set.getTaskName("template", "resources"), GenerateResourceTemplates.class, copy -> {
-                copy.into(destination);
-                copy.from(srcDir);
-            });
-            set.getResources().srcDir(generateTask.map(Task::getOutputs));
-        });
-
-        final TaskProvider<?> generateAllTask = tasks.register("allTemplateResource", t -> {
-            t.dependsOn(tasks.withType(GenerateResourceTemplates.class));
-        });
-
-        this.configureIdeIntegrations(target, generateAllTask);
-    }
-
-    private void configureIdeIntegrations(final Project target, final TaskProvider<?> generateAllTask) {
-        target.getPlugins().withType(EclipsePlugin.class, plug -> this.withEclipse(target.getExtensions().getByType(EclipseModel.class), generateAllTask));
-        target.getPlugins().withType(IdeaExtPlugin.class, plug -> this.withIdea(target, generateAllTask));
-    }
-
-    private void withEclipse(final EclipseModel eclipse, final TaskProvider<?> generateAllTask) {
-        eclipse.synchronizationTasks(generateAllTask);
-    }
-
-    private void withIdea(final Project target, final TaskProvider<?> generateAllTask) {
-        if (!Boolean.getBoolean("idea.active")) {
-            return;
-        }
-
-        // Apply the IDE plugin to the root project
-        final Project rootProject = target.getRootProject();
-        if (target != rootProject) {
-            rootProject.getPlugins().apply(IdeaExtPlugin.class);
-        }
-        final IdeaModel model = rootProject.getExtensions().findByType(IdeaModel.class);
-        if (model == null || model.getProject() == null) {
-            return;
-        }
-        final ProjectSettings ideaExt = ((ExtensionAware) model.getProject()).getExtensions().getByType(ProjectSettings.class);
-
-        // But actually perform the configuration with the subproject context
-        final TaskTriggersConfig taskTriggers = ((ExtensionAware) ideaExt).getExtensions().getByType(TaskTriggersConfig.class);
-        taskTriggers.afterSync(generateAllTask);
-    }
-}
diff --git a/build.gradle.kts b/build.gradle.kts
index 662e780a9e6..873074f4b03 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -6,32 +6,20 @@ plugins {
     `java-library`
     eclipse
     id("org.spongepowered.gradle.vanilla")
-    id("com.github.johnrengelman.shadow")
-    id("org.spongepowered.gradle.sponge.dev") apply false // for version json generation
-    id("net.kyori.indra.licenser.spotless")
+    alias(libs.plugins.shadow)
+    alias(apiLibs.plugins.spongeGradle.convention) apply false // for version json generation
+    alias(libs.plugins.indra.licenserSpotless) version apiLibs.versions.indra.get()
     id("implementation-structure")
-    id("org.jetbrains.gradle.plugin.idea-ext")
-    id("com.github.ben-manes.versions")
+    id(apiLibs.plugins.ideaExt.get().pluginId)
+    alias(libs.plugins.versions)
 }
 
 val commonProject = project
 val apiVersion: String by project
+val apiJavaTarget: String by project
 val minecraftVersion: String by project
 val recommendedVersion: String by project
 
-val apiAdventureVersion: String by project
-val apiConfigurateVersion: String by project
-val apiGsonVersion: String by project
-val apiCheckerVersion: String by project
-val guavaVersion: String by project
-val apiPluginSpiVersion: String by project
-val asmVersion: String by project
-val log4jVersion: String by project
-val modlauncherVersion: String by project
-val mixinVersion: String by project
-val junitVersion: String by project
-val mockitoVersion: String by project
-
 val commonManifest = java.manifest {
     attributes(
         "Specification-Title" to "Sponge",
@@ -163,80 +151,82 @@ dependencies {
     api("org.spongepowered:spongeapi:$apiVersion")
 
     // Database stuffs... likely needs to be looked at
-    implementation("com.zaxxer:HikariCP:2.6.3") {
+    implementation(libs.db.hikariCp) {
         exclude(group = "org.slf4j", module = "slf4j-api")
     }
-    implementation("org.mariadb.jdbc:mariadb-java-client:2.0.3")
-    implementation("com.h2database:h2:1.4.196")
-    implementation("org.xerial:sqlite-jdbc:3.20.0")
-    implementation("javax.inject:javax.inject:1")
+    implementation(libs.db.maria)
+    implementation(libs.db.h2)
+    implementation(libs.db.sqlite)
+    implementation(libs.javaxInject)
 
     // ASM - required for generating event listeners
-    implementation("org.ow2.asm:asm-util:$asmVersion")
-    implementation("org.ow2.asm:asm-tree:$asmVersion")
+    implementation(libs.asm.util)
+    implementation(libs.asm.tree)
 
     // Implementation-only Adventure
-    implementation(platform("net.kyori:adventure-bom:$apiAdventureVersion"))
-    implementation("net.kyori:adventure-serializer-configurate4") {
+    implementation(platform(apiLibs.adventure.bom))
+    implementation(libs.adventure.serializerConfigurate4) {
         exclude(group = "org.jetbrains", module = "annotations")
         exclude(group = "org.checkerframework", module = "checker-qual")
     }
 
     // Launch Dependencies - Needed to bootstrap the engine(s)
     launchConfig("org.spongepowered:spongeapi:$apiVersion")
-    launchConfig("org.spongepowered:plugin-spi:$apiPluginSpiVersion") {
+    launchConfig(apiLibs.pluginSpi) {
         exclude(group = "org.checkerframework", module = "checker-qual")
         exclude(group = "com.google.code.gson", module = "gson")
         exclude(group = "org.apache.logging.log4j", module = "log4j-api")
     }
-    launchConfig("org.spongepowered:mixin:$mixinVersion")
-    launchConfig("org.checkerframework:checker-qual:$apiCheckerVersion")
-    launchConfig("com.google.guava:guava:$guavaVersion") {
+    launchConfig(libs.mixin)
+    launchConfig(apiLibs.checkerQual)
+    launchConfig(libs.guava) {
         exclude(group = "com.google.code.findbugs", module = "jsr305") // We don't want to use jsr305, use checkerframework
         exclude(group = "org.checkerframework", module = "checker-qual") // We use our own version
         exclude(group = "com.google.j2objc", module = "j2objc-annotations")
         exclude(group = "org.codehaus.mojo", module = "animal-sniffer-annotations")
         exclude(group = "com.google.errorprone", module = "error_prone_annotations")
     }
-    launchConfig("com.google.code.gson:gson:$apiGsonVersion")
-    launchConfig("org.ow2.asm:asm-tree:$asmVersion")
-    launchConfig("org.ow2.asm:asm-util:$asmVersion")
+    launchConfig(apiLibs.gson)
+    launchConfig(libs.asm.tree)
+    launchConfig(libs.asm.util)
 
     // Applaunch -- initialization that needs to occur without game access
-    applaunchConfig("org.checkerframework:checker-qual:$apiCheckerVersion")
-    applaunchConfig("org.apache.logging.log4j:log4j-api:$log4jVersion")
-    applaunchConfig("com.google.guava:guava:$guavaVersion"){
+    applaunchConfig(apiLibs.checkerQual)
+    applaunchConfig(libs.log4j.api)
+    applaunchConfig(libs.guava){
         exclude(group = "com.google.errorprone", module = "error_prone_annotations")
         exclude(group = "org.checkerframework", module = "checker-qual")
     }
-    applaunchConfig(platform("org.spongepowered:configurate-bom:$apiConfigurateVersion"))
-    applaunchConfig("org.spongepowered:configurate-core") {
+    applaunchConfig(platform(apiLibs.configurate.bom))
+    applaunchConfig(apiLibs.configurate.core) {
         exclude(group = "org.checkerframework", module = "checker-qual") // We use our own version
     }
-    applaunchConfig("org.spongepowered:configurate-hocon") {
+    applaunchConfig(apiLibs.configurate.hocon) {
         exclude(group = "org.spongepowered", module = "configurate-core")
         exclude(group = "org.checkerframework", module = "checker-qual") // We use our own version
     }
-    applaunchConfig("org.spongepowered:configurate-jackson") {
+    applaunchConfig(libs.configurate.jackson) {
         exclude(group = "org.spongepowered", module = "configurate-core")
         exclude(group = "org.checkerframework", module = "checker-qual") // We use our own version
     }
-    applaunchConfig("org.apache.logging.log4j:log4j-core:$log4jVersion")
-    applaunchConfig("org.apache.logging.log4j:log4j-jpl:$log4jVersion")
+    applaunchConfig(libs.log4j.core)
+    applaunchConfig(libs.log4j.jpl)
 
-    mixinsConfig(sourceSets.named("main").map { it.output })
+    mixinsConfig(sourceSets.main.map { it.output })
     add(mixins.get().implementationConfigurationName, "org.spongepowered:spongeapi:$apiVersion")
 
     // Tests
-    testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion")
-    testImplementation("org.junit.jupiter:junit-jupiter-params:$junitVersion")
-    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitVersion")
-
-    testImplementation("org.mockito:mockito-core:$mockitoVersion")
-    testImplementation("org.mockito:mockito-junit-jupiter:$mockitoVersion") {
+    testImplementation(platform(apiLibs.junit.bom))
+    testImplementation(apiLibs.junit.api)
+    testImplementation(apiLibs.junit.params)
+    testRuntimeOnly(apiLibs.junit.engine)
+    testRuntimeOnly(apiLibs.junit.launcher)
+
+    testImplementation(libs.mockito.core)
+    testImplementation(libs.mockito.junitJupiter) {
         exclude(group = "org.junit.jupiter", module = "junit-jupiter-api")
     }
-    testImplementation("org.mockito:mockito-inline:$mockitoVersion")
+    testImplementation(libs.mockito.inline)
 }
 
 val organization: String by project
@@ -280,7 +270,6 @@ allprojects {
     }
 
     plugins.withId("org.spongepowered.gradle.vanilla") {
-        val vineflowerVersion: String by project
         minecraft {
             version(minecraftVersion)
             injectRepositories(false)
@@ -294,7 +283,7 @@ allprojects {
         }
 
         dependencies {
-            decompiler("org.vineflower:vineflower:$vineflowerVersion")
+            decompiler(libs.vineflower)
         }
 
         tasks.named("decompile", DecompileJarTask::class) {
@@ -309,7 +298,7 @@ allprojects {
                     delegateBuildRunToGradle = false
                     testRunner = org.jetbrains.gradle.ext.ActionDelegationConfig.TestRunner.PLATFORM
                 }
-                (this as ExtensionAware).extensions.getByType(org.jetbrains.gradle.ext.IdeaCompilerConfiguration::class).run {
+                extensions.getByType(org.jetbrains.gradle.ext.IdeaCompilerConfiguration::class).run {
                     addNotNullAssertions = false
                     useReleaseOption = JavaVersion.current().isJava10Compatible
                     parallelCompilation = true
@@ -319,11 +308,12 @@ allprojects {
     }
 
     java {
-        sourceCompatibility = JavaVersion.VERSION_17
-        targetCompatibility = JavaVersion.VERSION_17
-        if (JavaVersion.current() < JavaVersion.VERSION_17) {
+        val targetJavaVersion = JavaVersion.toVersion(apiJavaTarget.toInt())
+        sourceCompatibility = targetJavaVersion
+        targetCompatibility = targetJavaVersion
+        if (JavaVersion.current() < targetJavaVersion) {
             toolchain {
-                languageVersion.set(JavaLanguageVersion.of(17))
+                languageVersion.set(JavaLanguageVersion.of(apiJavaTarget.toInt()))
             }
         }
     }
@@ -357,7 +347,7 @@ allprojects {
         withType(JavaCompile::class).configureEach {
             options.compilerArgs.addAll(listOf("-Xmaxerrs", "1000"))
             options.encoding = "UTF-8"
-            options.release.set(17)
+            options.release.set(apiJavaTarget.toInt())
             if (project.name != "testplugins" && System.getProperty("idea.sync.active") != null) {
                 options.annotationProcessorPath = emptyAnnotationProcessors // hack so IntelliJ doesn't try to run Mixin AP
             }
diff --git a/forge/build.gradle.kts b/forge/build.gradle.kts
index 8e585890acc..e5f0e1a5a29 100644
--- a/forge/build.gradle.kts
+++ b/forge/build.gradle.kts
@@ -14,9 +14,9 @@ buildscript {
 }
 
 plugins {
-    id("com.github.johnrengelman.shadow")
+    alias(libs.plugins.shadow)
     id("implementation-structure")
-    id("templated-resources")
+    alias(libs.plugins.blossom)
     id("net.smoofyuniverse.loom") version "1.1-SNAPSHOT"
 }
 
@@ -210,11 +210,6 @@ dependencies {
         }
     })
 
-    val apiAdventureVersion: String by project
-    val apiConfigurateVersion: String by project
-    val apiPluginSpiVersion: String by project
-    val log4jVersion: String by project
-
     api(project(":", configuration = "launch")) {
         exclude(group = "org.spongepowered", module = "mixin")
     }
@@ -228,27 +223,27 @@ dependencies {
     forgeMixins.implementationConfigurationName(project(commonProject.path))
 
     val serviceLibraries = serviceLibrariesConfig.name
-    serviceLibraries("org.spongepowered:plugin-spi:$apiPluginSpiVersion")
+    serviceLibraries(apiLibs.pluginSpi)
     serviceLibraries(project(transformersProject.path))
-    serviceLibraries(platform("org.spongepowered:configurate-bom:$apiConfigurateVersion"))
-    serviceLibraries("org.spongepowered:configurate-core") {
+    serviceLibraries(platform(apiLibs.configurate.bom))
+    serviceLibraries(apiLibs.configurate.core) {
         exclude(group = "org.checkerframework", module = "checker-qual")
     }
-    serviceLibraries("org.spongepowered:configurate-hocon") {
+    serviceLibraries(apiLibs.configurate.hocon) {
         exclude(group = "org.spongepowered", module = "configurate-core")
         exclude(group = "org.checkerframework", module = "checker-qual")
     }
-    serviceLibraries("org.spongepowered:configurate-jackson") {
+    serviceLibraries(libs.configurate.jackson) {
         exclude(group = "org.spongepowered", module = "configurate-core")
         exclude(group = "org.checkerframework", module = "checker-qual")
     }
 
     val gameLibraries = gameLibrariesConfig.name
     gameLibraries("org.spongepowered:spongeapi:$apiVersion")
-    gameLibraries("javax.inject:javax.inject:1")
-    gameLibraries("com.zaxxer:HikariCP:2.7.8")
-    gameLibraries(platform("net.kyori:adventure-bom:$apiAdventureVersion"))
-    gameLibraries("net.kyori:adventure-serializer-configurate4")
+    gameLibraries(libs.javaxInject)
+    gameLibraries(libs.db.hikariCp)
+    gameLibraries(platform(apiLibs.adventure.bom))
+    gameLibraries(libs.adventure.serializerConfigurate4)
 
     val serviceShadedLibraries = serviceShadedLibrariesConfig.name
     serviceShadedLibraries(project(transformersProject.path)) { isTransitive = false }
@@ -434,14 +429,14 @@ tasks {
     assemble {
         dependsOn(universalJar)
     }
+}
 
-    templateResources {
-        val props = mutableMapOf(
-                "version" to project.version,
-                "description" to project.description
-        )
-        inputs.properties(props)
-        expand(props)
+sourceSets {
+    main {
+        blossom.resources {
+            property("version", project.provider { project.version.toString() })
+            property("description", project.description.toString())
+        }
     }
 }
 
diff --git a/forge/src/main/resourceTemplates/META-INF/mods.toml b/forge/src/main/resource-templates/META-INF/mods.toml
similarity index 90%
rename from forge/src/main/resourceTemplates/META-INF/mods.toml
rename to forge/src/main/resource-templates/META-INF/mods.toml
index 1e4efaa50e1..061565ab0eb 100644
--- a/forge/src/main/resourceTemplates/META-INF/mods.toml
+++ b/forge/src/main/resource-templates/META-INF/mods.toml
@@ -7,12 +7,12 @@ issueTrackerURL="https://github.com/SpongePowered/Sponge/issues"
 
 [[mods]]
 modId="spongeforge"
-version="${version}"
+version="{{ version }}"
 displayName="SpongeForge"
 displayTest="IGNORE_SERVER_VERSION"
 credits="SpongePowered and Contributors"
 authors="SpongePowered"
-description = "${description}"
+description = "{{ description }}"
 
 [[dependencies.spongeforge]]
 modId="forge"
@@ -24,14 +24,14 @@ side="BOTH"
 [[dependencies.spongeforge]]
 modId="sponge"
 mandatory=true
-versionRange="[${version}]"
+versionRange="[{{ version }}]"
 ordering="AFTER"
 side="BOTH"
 
 
 [[mods]]
 modId="sponge"
-version="${version}"
+version="{{ version }}"
 displayName="Sponge"
 displayTest="IGNORE_SERVER_VERSION"
 credits="SpongePowered and Contributors"
diff --git a/generator/build.gradle.kts b/generator/build.gradle.kts
index e45a2fcaff2..d2a16fbe78f 100644
--- a/generator/build.gradle.kts
+++ b/generator/build.gradle.kts
@@ -18,16 +18,16 @@ minecraft {
 }
 
 configurations.configureEach {
-    exclude(group = "org.apache.logging.log4j", module = "log4j-slf4j2-impl")
+    val dep = libs.log4j.slf4j2.get()
+    exclude(group = dep.group, module = dep.name)
 }
 
 dependencies {
-    val tinyLogVersion: String by project
-    implementation("com.squareup:javapoet:1.13.0")
-    implementation("com.github.javaparser:javaparser-core:3.24.4")
-    implementation("org.tinylog:tinylog-api:$tinyLogVersion")
-    runtimeOnly("org.tinylog:tinylog-impl:$tinyLogVersion")
-    runtimeOnly("org.tinylog:slf4j-tinylog:$tinyLogVersion")
+    implementation(libs.javapoet)
+    implementation(libs.javaparser)
+    implementation(libs.tinylog.api)
+    runtimeOnly(libs.tinylog.impl)
+    runtimeOnly(libs.tinylog.slf4j)
 }
 
 indraSpotlessLicenser {
diff --git a/gradle.properties b/gradle.properties
index 3e351310c9e..a7d2afed7c5 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -14,18 +14,6 @@ superClassChanges=common.superclasschange
 
 minecraftVersion=1.20.2
 recommendedVersion=0-SNAPSHOT
-asmVersion=9.4
-log4jVersion=2.19.0
-forgeAutoRenamingToolVersion=0.1.24
-mixinVersion=0.8.5
-modlauncherVersion=8.1.3
-guavaVersion=32.1.2-jre
-junitVersion=5.9.1
-mockitoVersion=4.8.0
-jlineVersion=3.21.0
-tinyLogVersion=2.5.0
-vineflowerVersion=1.9.1
-checkerVersion=3.26.0
 
 org.gradle.jvmargs=-Xss4m
 org.gradle.parallel=true
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 00000000000..e73a2ec0cd9
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,73 @@
+[metadata]
+format = { version = "1.1" }
+
+# Implementation-only dependencies, or dependencies that use a different version than in API
+# See SpongeAPI's gradle/libs.versions.toml for many dependency declarations that Sponge also uses
+[versions]
+asm = "9.6"
+log4j = "2.19.0"
+forgeAutoRenamingTool = "1.0.6"
+mixin = "0.8.5"
+modlauncher = "8.1.3"
+guava = "32.1.2-jre"
+mockito = "4.8.0"
+jline = "3.25.1"
+tinylog = "2.6.2"
+vineflower = "1.9.3"
+
+[libraries]
+# common
+asm = { module = "org.ow2.asm:asm", version.ref = "asm" }
+asm-analysis = { module = "org.ow2.asm:asm-analysis", version.ref = "asm" }
+asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" }
+asm-util = { module = "org.ow2.asm:asm-util", version.ref = "asm" }
+asm-tree = { module = "org.ow2.asm:asm-tree", version.ref = "asm" }
+grossJava9Hacks = { module = "cpw.mods:grossjava9hacks", version = "1.3.3" }
+joptSimple = { module = "net.sf.jopt-simple:jopt-simple", version = "5.0.4" }
+log4j-api = { module = "org.apache.logging.log4j:log4j-api", version.ref = "log4j" }
+log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" }
+log4j-jpl = { module = "org.apache.logging.log4j:log4j-jpl", version.ref = "log4j" }
+log4j-slf4j2 = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version.ref = "log4j" }
+modlauncher = { module = "cpw.mods:modlauncher", version.ref = "modlauncher" }
+tinylog-api = { module = "org.tinylog:tinylog-api", version.ref = "tinylog" }
+tinylog-impl = { module = "org.tinylog:tinylog-impl", version.ref = "tinylog" }
+tinylog-slf4j = { module = "org.tinylog:slf4j-tinylog", version.ref = "tinylog" }
+
+# generator
+javaparser =  { module = "com.github.javaparser:javaparser-core", version = "3.24.4" }
+javapoet = { module = "com.squareup:javapoet", version = "1.13.0" }
+
+# modlauncher-transformers
+accessWidener = { module = "net.fabricmc:access-widener", version = "2.1.0" }
+
+# Sponge
+adventure-serializerConfigurate4 = { module = "net.kyori:adventure-serializer-configurate4" } # version inherited from bom in API catalog
+configurate-jackson = { module = "org.spongepowered:configurate-jackson" } # version inherited from bom in API catalog
+db-h2 = { module = "com.h2database:h2", version = "1.4.196" }
+db-hikariCp = { module = "com.zaxxer:HikariCP", version = "2.6.3" }
+db-maria = { module = "org.mariadb.jdbc:mariadb-java-client",  version = "2.0.3" }
+db-sqlite = { module = "org.xerial:sqlite-jdbc", version = "3.20.0" }
+guava = { module = "com.google.guava:guava", version.ref = "guava" }
+javaxInject = { module = "javax.inject:javax.inject", version = "1" }
+mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
+mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" }
+mockito-inline = { module = "org.mockito:mockito-inline", version.ref = "mockito" }
+mockito-junitJupiter = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockito" }
+
+# vanilla
+forgeAutoRenamingTool = { module = "net.minecraftforge:ForgeAutoRenamingTool", version.ref = "forgeAutoRenamingTool" }
+jline-reader = { module = "org.jline:jline-reader", version.ref = "jline" }
+jline-terminal = { module = "org.jline:jline-terminal", version.ref = "jline" }
+jline-terminalJansi = { module = "org.jline:jline-terminal-jansi", version.ref = "jline" }
+lmaxDisruptor = { module = "com.lmax:disruptor", version = "3.4.4" }
+terminalConsoleAppender = { module = "net.minecrell:terminalconsoleappender", version = "1.3.0" }
+
+# buildtime-only
+vineflower = { module = "org.vineflower:vineflower", version.ref = "vineflower" }
+
+[plugins]
+blossom = { id = "net.kyori.blossom", version = "2.1.0" }
+indra-licenserSpotless = { id = "net.kyori.indra.licenser.spotless", version = "3.1.3" }
+shadow = { id = "com.github.johnrengelman.shadow", version = "8.1.1" }
+vanillaGradle = { id = "org.spongepowered.gradle.vanilla" }
+versions = { id = "com.github.ben-manes.versions", version = "0.51.0" }
diff --git a/modlauncher-patcher/build.gradle b/modlauncher-patcher/build.gradle
index ec82448c24a..03e64a2a5fe 100644
--- a/modlauncher-patcher/build.gradle
+++ b/modlauncher-patcher/build.gradle
@@ -1,5 +1,5 @@
 dependencies {
-    implementation "org.ow2.asm:asm:$asmVersion"
+    implementation libs.asm
 }
 
 // Make sure jar is present for other projects
diff --git a/modlauncher-transformers/build.gradle.kts b/modlauncher-transformers/build.gradle.kts
index aece085afae..4fd5d54ec83 100644
--- a/modlauncher-transformers/build.gradle.kts
+++ b/modlauncher-transformers/build.gradle.kts
@@ -19,41 +19,36 @@ indraSpotlessLicenser {
     property("url", projectUrl)
 }
 
-val apiConfigurateVersion: String by project
-val asmVersion: String by project
-val apiCheckerVersion: String by project
-val log4jVersion: String by project
-val modlauncherVersion: String by project
 
 dependencies {
     // AccessWidener transformer
-    implementation("net.fabricmc:access-widener:2.1.0") {
+    implementation(libs.accessWidener) {
         exclude(group="org.apache.logging.log4j")
     }
     // ModLauncher inherited dependencies - strictly should be provided by
     //  the platform making use of this project
-    compileOnly("org.apache.logging.log4j:log4j-api:$log4jVersion")
-    compileOnly("cpw.mods:modlauncher:$modlauncherVersion") {
+    compileOnly(libs.log4j.api)
+    compileOnly(libs.modlauncher) {
         exclude(group = "org.apache.logging.log4j")
         exclude(group = "net.sf.jopt-simple") // uses a newer version than MC
     }
 
-    compileOnly("net.sf.jopt-simple:jopt-simple:5.0.4")
-    compileOnly("org.ow2.asm:asm-commons:$asmVersion")
-    compileOnly("cpw.mods:grossjava9hacks:1.3.3") {
+    compileOnly(libs.joptSimple)
+    compileOnly(libs.asm.commons)
+    compileOnly(libs.grossJava9Hacks) {
         exclude(group="org.apache.logging.log4j")
     }
     // Configurate dependencies, also to be provided by the platform
     //  making use of this project
-    compileOnly(platform("org.spongepowered:configurate-bom:$apiConfigurateVersion"))
-    compileOnly("org.spongepowered:configurate-core") {
+    compileOnly(platform(apiLibs.configurate.bom))
+    compileOnly(apiLibs.configurate.core) {
         exclude(group = "org.checkerframework", module="checker-qual") // We use our own version
     }
-    compileOnly("org.spongepowered:configurate-jackson") {
+    compileOnly(libs.configurate.jackson) {
         exclude(group="org.spongepowered", module="configurate-core")
         exclude(group="org.checkerframework", module="checker-qual") // We use our own version
     }
 
     // And finally, compile only annotations
-    compileOnly("org.checkerframework:checker-qual:$apiCheckerVersion")
+    compileOnly(apiLibs.checkerQual)
 }
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 49ca7ea7539..5cd1420116c 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -8,20 +8,14 @@ pluginManagement {
     }
 
     plugins {
-        // Default plugin versions
         id("org.spongepowered.gradle.vanilla") version "0.2.1-SNAPSHOT"
-        id("com.github.johnrengelman.shadow") version "8.1.0"
-        id("org.spongepowered.gradle.sponge.dev") version "2.1.1"
-        id("net.kyori.indra.licenser.spotless") version "3.1.3"
         id("implementation-structure")
-        id("org.jetbrains.gradle.plugin.idea-ext") version "1.1.7"
-        id("com.github.ben-manes.versions") version "0.49.0"
     }
 }
 
 plugins {
     id("org.spongepowered.gradle.vanilla")
-    id("org.gradle.toolchains.foojay-resolver-convention") version("0.7.0")
+    id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
 }
 
 dependencyResolutionManagement {
@@ -31,6 +25,11 @@ dependencyResolutionManagement {
             name = "sponge"
         }
     }
+    versionCatalogs {
+        register("apiLibs") {
+            from(files("SpongeAPI/gradle/libs.versions.toml"))
+        }
+    }
 }
 
 rootProject.name = "Sponge"
diff --git a/vanilla/build.gradle.kts b/vanilla/build.gradle.kts
index a0b92406d22..2cb6d073e1c 100644
--- a/vanilla/build.gradle.kts
+++ b/vanilla/build.gradle.kts
@@ -1,16 +1,14 @@
-import org.jetbrains.gradle.ext.TaskTriggersConfig
-import org.spongepowered.gradle.impl.GenerateResourceTemplates
-
 plugins {
     id("org.spongepowered.gradle.vanilla")
-    id("com.github.johnrengelman.shadow")
+    alias(libs.plugins.shadow)
     id("implementation-structure")
-    id("templated-resources")
+    alias(libs.plugins.blossom)
     eclipse
 }
 
 val commonProject = parent!!
 val apiVersion: String by project
+val apiJavaTarget: String by project
 val minecraftVersion: String by project
 val recommendedVersion: String by project
 val organization: String by project
@@ -66,9 +64,11 @@ val vanillaInstallerJava9 by sourceSets.register("installerJava9") {
         options.release.set(9)
     }
 
-    configurations.configureEach {
-        attributes {
-            attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
+    sequenceOf(runtimeClasspathConfigurationName, compileClasspathConfigurationName).map(configurations::named).forEach {
+        it.configure {
+            attributes {
+                attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, apiJavaTarget.toInt())
+            }
         }
     }
 
@@ -181,7 +181,7 @@ minecraft {
         }
 
         configureEach {
-            targetVersion(17)
+            targetVersion(apiJavaTarget.toInt())
             workingDirectory(project.file("run/"))
             if (org.spongepowered.gradle.vanilla.internal.util.IdeConfigurer.isIdeaImport()) { // todo(zml): promote to API... eventually
                 // IntelliJ does not properly report its compatibility
@@ -247,20 +247,7 @@ minecraft {
             .forEach { accessWideners(it) }
 }
 
-val asmVersion: String by project
 dependencies {
-    val apiAdventureVersion: String by project
-    val apiConfigurateVersion: String by project
-    val apiGsonVersion: String by project
-    val guavaVersion: String by project
-    val apiPluginSpiVersion: String by project
-    val forgeAutoRenamingToolVersion: String by project
-    val jlineVersion: String by project
-    val log4jVersion: String by project
-    val mixinVersion: String by project
-    val modlauncherVersion: String by project
-    val tinyLogVersion: String by project
-
     api(project(":", configuration = "launch"))
     implementation(project(":", configuration = "accessors"))
     implementation(project(commonProject.path))
@@ -269,22 +256,22 @@ dependencies {
     vanillaMixinsImplementation(project(commonProject.path))
 
     val installer = vanillaInstallerConfig.name
-    installer("com.google.code.gson:gson:$apiGsonVersion")
-    installer("org.spongepowered:configurate-hocon:$apiConfigurateVersion")
-    installer("org.spongepowered:configurate-core:$apiConfigurateVersion")
-    installer("org.spongepowered:configurate-jackson:$apiConfigurateVersion")
-    installer("net.sf.jopt-simple:jopt-simple:5.0.4")
-    installer("org.tinylog:tinylog-api:$tinyLogVersion")
-    installer("org.tinylog:tinylog-impl:$tinyLogVersion")
+    installer(apiLibs.gson)
+    installer(platform(apiLibs.configurate.bom))
+    installer(apiLibs.configurate.hocon)
+    installer(apiLibs.configurate.core)
+    installer(libs.configurate.jackson)
+    installer(libs.joptSimple)
+    installer(libs.tinylog.api)
+    installer(libs.tinylog.impl)
     // Override ASM versions, and explicitly declare dependencies so ASM is excluded from the manifest.
-    val asmExclusions = sequenceOf("-commons", "-tree", "-analysis", "")
-            .map { "asm$it" }
+    val asmExclusions = sequenceOf(libs.asm.asProvider(), libs.asm.commons, libs.asm.tree, libs.asm.analysis)
             .onEach {
-                installer("org.ow2.asm:$it:$asmVersion")
+                installer(it)
             }.toSet()
-    installer("net.minecraftforge:ForgeAutoRenamingTool:$forgeAutoRenamingToolVersion") {
+    installer(libs.forgeAutoRenamingTool) {
         exclude(group = "net.sf.jopt-simple")
-        asmExclusions.forEach { exclude(group = "org.ow2.asm", module = it) } // Use our own ASM version
+        asmExclusions.forEach { exclude(group = it.get().group, module = it.get().name) } // Use our own ASM version
     }
     mlTransformersConfig.name(rootProject.project(":modlauncher-transformers"))
 
@@ -301,62 +288,62 @@ dependencies {
     // Libraries only needed on the TCL (during main game lifecycle)
 
     libraries("org.spongepowered:spongeapi:$apiVersion")
-    libraries(platform("net.kyori:adventure-bom:$apiAdventureVersion"))
-    libraries("net.kyori:adventure-serializer-configurate4") {
+    libraries(platform(apiLibs.adventure.bom))
+    libraries(libs.adventure.serializerConfigurate4) {
         exclude(group = "org.checkerframework", module = "checker-qual")
     }
-    libraries("javax.inject:javax.inject:1")
-    libraries("org.spongepowered:configurate-jackson") {
+    libraries(libs.javaxInject)
+    libraries(libs.configurate.jackson) {
         exclude(group = "org.spongepowered", module = "configurate-core")
         exclude(group = "org.checkerframework", module = "checker-qual")
     }
 
     // Databases
-    libraries("com.zaxxer:HikariCP:2.6.3")
+    libraries(libs.db.hikariCp)
 
     // Libraries needed during applaunch phase and runtime
-    bootstrapLibraries("net.minecrell:terminalconsoleappender:1.3.0")
-    bootstrapLibraries("org.jline:jline-terminal:$jlineVersion")
-    bootstrapLibraries("org.jline:jline-reader:$jlineVersion")
-    bootstrapLibraries("org.jline:jline-terminal-jansi:$jlineVersion")
+    bootstrapLibraries(libs.terminalConsoleAppender)
+    bootstrapLibraries(libs.jline.terminal)
+    bootstrapLibraries(libs.jline.reader)
+    bootstrapLibraries(libs.jline.terminalJansi)
     // Must be on the base ClassLoader since ModLauncher has a dependency on log4j
-    bootstrapLibraries("org.apache.logging.log4j:log4j-jpl:$log4jVersion")
+    bootstrapLibraries(libs.log4j.jpl)
 
-    bootstrapLibraries(platform("org.spongepowered:configurate-bom:$apiConfigurateVersion"))
-    bootstrapLibraries("org.spongepowered:configurate-core") {
+    bootstrapLibraries(platform(apiLibs.configurate.bom))
+    bootstrapLibraries(apiLibs.configurate.core) {
         exclude(group = "org.checkerframework", module = "checker-qual")
     }
-    bootstrapLibraries("org.spongepowered:configurate-hocon") {
+    bootstrapLibraries(apiLibs.configurate.hocon) {
         exclude(group = "org.spongepowered", module = "configurate-core")
         exclude(group = "org.checkerframework", module = "checker-qual")
     }
-    bootstrapLibraries("org.apache.logging.log4j:log4j-api:$log4jVersion")
-    bootstrapLibraries("org.apache.logging.log4j:log4j-core:$log4jVersion")
-    bootstrapLibraries("org.apache.logging.log4j:log4j-slf4j2-impl:$log4jVersion")
+    bootstrapLibraries(libs.log4j.api)
+    bootstrapLibraries(libs.log4j.core)
+    bootstrapLibraries(libs.log4j.slf4j2)
 
     // Mixin and dependencies
-    bootstrapLibraries("org.spongepowered:mixin:$mixinVersion")
-    bootstrapLibraries("org.ow2.asm:asm-util:$asmVersion")
-    bootstrapLibraries("org.ow2.asm:asm-tree:$asmVersion")
-    bootstrapLibraries("com.google.guava:guava:$guavaVersion")
+    bootstrapLibraries(libs.mixin)
+    bootstrapLibraries(libs.asm.util)
+    bootstrapLibraries(libs.asm.tree)
+    bootstrapLibraries(libs.guava)
 
     // Launch Dependencies - Needed to bootstrap the engine(s)
     // Not needing to be source-visible past the init phase
     // The ModLauncher compatibility launch layer
-    appLaunch("cpw.mods:modlauncher:$modlauncherVersion") {
+    appLaunch(libs.modlauncher) {
         exclude(group = "org.apache.logging.log4j")
         exclude(group = "net.sf.jopt-simple") // uses a newer version than MC
     }
-    appLaunch("org.ow2.asm:asm-commons:$asmVersion")
-    appLaunch("cpw.mods:grossjava9hacks:1.3.3") {
+    appLaunch(libs.asm.commons)
+    appLaunch(libs.grossJava9Hacks) {
         exclude(group = "org.apache.logging.log4j")
     }
-    appLaunch("org.spongepowered:plugin-spi:$apiPluginSpiVersion") {
+    appLaunch(apiLibs.pluginSpi) {
         exclude(group = "org.checkerframework", module = "checker-qual")
         exclude(group = "com.google.code.gson", module = "gson")
         exclude(group = "org.apache.logging.log4j", module = "log4j-api")
     }
-    appLaunch("com.lmax:disruptor:3.4.4")
+    appLaunch(libs.lmaxDisruptor)
 
     testplugins?.also {
         vanillaAppLaunchRuntime(project(it.path)) {
@@ -379,23 +366,24 @@ val vanillaManifest = java.manifest {
     System.getenv()["GIT_BRANCH"]?.apply { attributes("Git-Branch" to this) }
 }
 
+vanillaLaunch.apply {
+    blossom.resources {
+        property("apiVersion", apiVersion)
+        property("minecraftVersion", minecraftVersion)
+        property("version", provider { project.version.toString() })
+    }
+}
+vanillaInstaller.apply {
+    blossom.javaSources {
+        property("minecraftVersion", minecraftVersion)
+    }
+}
+
 tasks {
     jar {
         manifest.from(vanillaManifest)
     }
 
-    named("templateLaunchResources", GenerateResourceTemplates::class) {
-        inputs.property("version.api", apiVersion)
-        inputs.property("version.minecraft", minecraftVersion)
-        inputs.property("version.vanilla", project.version)
-
-        expand(
-            "apiVersion" to apiVersion,
-            "minecraftVersion" to minecraftVersion,
-            "version" to project.version
-        )
-    }
-
     val vanillaInstallerJar by registering(Jar::class) {
         archiveClassifier.set("installer")
         manifest{
@@ -433,31 +421,6 @@ tasks {
         dependsOn("integrationTestServer", "integrationTestClient")
     }
 
-    val installerTemplateSource = project.file("src/installer/templates")
-    val installerTemplateDest = project.layout.buildDirectory.dir("generated/sources/installerTemplates")
-    val generateInstallerTemplates by registering(Copy::class) {
-        group = "sponge"
-        description = "Generate classes from templates for the SpongeVanilla installer"
-        val properties = mutableMapOf(
-                "minecraftVersion" to minecraftVersion
-        )
-        inputs.properties(properties)
-
-        // Copy template
-        from(installerTemplateSource)
-        into(installerTemplateDest)
-        expand(properties)
-    }
-    vanillaInstaller.java.srcDir(generateInstallerTemplates.map { it.outputs })
-
-    // Generate templates on IDE import as well
-    (rootProject.idea.project as? ExtensionAware)?.also {
-        (it.extensions["settings"] as ExtensionAware).extensions.getByType(TaskTriggersConfig::class).afterSync(generateInstallerTemplates)
-    }
-    project.eclipse {
-        synchronizationTasks(generateInstallerTemplates)
-    }
-
     val installerResources = project.layout.buildDirectory.dir("generated/resources/installer")
     vanillaInstaller.resources.srcDir(installerResources)
 
@@ -499,7 +462,7 @@ tasks {
                     "Launcher-Agent-Class" to "org.spongepowered.vanilla.installer.Agent"
             ))
             attributes(
-                mapOf("Implementation-Version" to asmVersion),
+                mapOf("Implementation-Version" to libs.versions.asm.get()),
                 "org/objectweb/asm/"
             )
             from(vanillaManifest)
diff --git a/vanilla/src/installer/templates/org/spongepowered/vanilla/installer/Constants.java b/vanilla/src/installer/java-templates/org/spongepowered/vanilla/installer/Constants.java
similarity index 96%
rename from vanilla/src/installer/templates/org/spongepowered/vanilla/installer/Constants.java
rename to vanilla/src/installer/java-templates/org/spongepowered/vanilla/installer/Constants.java
index a62c9b3e77c..2da830df3cd 100644
--- a/vanilla/src/installer/templates/org/spongepowered/vanilla/installer/Constants.java
+++ b/vanilla/src/installer/java-templates/org/spongepowered/vanilla/installer/Constants.java
@@ -32,7 +32,7 @@ public final class Constants {
 
     public static final class Libraries {
 
-        public static final String MINECRAFT_VERSION_TARGET = "${minecraftVersion}";
+        public static final String MINECRAFT_VERSION_TARGET = "{{ minecraftVersion }}";
         public static final String MINECRAFT_MANIFEST_URL = "https://launchermeta.mojang.com/mc/game/version_manifest.json";
         public static final String MINECRAFT_PATH_PREFIX = "net/minecraft";
         public static final String MINECRAFT_SERVER_JAR_NAME = "minecraft_server";
diff --git a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/InstallerMain.java b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/InstallerMain.java
index 6a186853953..3e6f96caa00 100644
--- a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/InstallerMain.java
+++ b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/InstallerMain.java
@@ -388,12 +388,10 @@ private ServerAndLibraries remapMinecraft(final ServerAndLibraries minecraft, fi
         Logger.info("Remapping Minecraft. This may take a while...");
         final IMappingFile mappings = IMappingFile.load(serverMappings.toFile()).reverse();
 
-        Renamer.builder()
-            .input(minecraft.server().toFile())
-            .output(tempOutput.toFile())
+        final Renamer.Builder renamerBuilder = Renamer.builder()
             .add(Transformer.parameterAnnotationFixerFactory())
             .add(ctx -> {
-              final Transformer backing = Transformer.renamerFactory(mappings).create(ctx);
+              final Transformer backing = Transformer.renamerFactory(mappings, false).create(ctx);
               return new Transformer() {
 
                 @Override
@@ -430,9 +428,10 @@ public Collection<? extends Entry> getExtras() {
             .add(Transformer.parameterAnnotationFixerFactory())
             .add(Transformer.sourceFixerFactory(SourceFixerConfig.JAVA))
             .add(Transformer.signatureStripperFactory(SignatureStripperConfig.ALL))
-            .logger(Logger::debug) // quiet
-            .build()
-            .run();
+            .logger(Logger::debug); // quiet
+            try (final Renamer ren = renamerBuilder.build()) {
+                ren.run(minecraft.server.toFile(), tempOutput.toFile());
+            }
 
         // Restore file
         try {
diff --git a/vanilla/src/launch/resourceTemplates/META-INF/sponge_plugins.json b/vanilla/src/launch/resource-templates/META-INF/sponge_plugins.json
similarity index 87%
rename from vanilla/src/launch/resourceTemplates/META-INF/sponge_plugins.json
rename to vanilla/src/launch/resource-templates/META-INF/sponge_plugins.json
index 17fc2b6d2d6..de066322672 100644
--- a/vanilla/src/launch/resourceTemplates/META-INF/sponge_plugins.json
+++ b/vanilla/src/launch/resource-templates/META-INF/sponge_plugins.json
@@ -8,7 +8,7 @@
         {
             "id": "minecraft",
             "name": "Minecraft",
-            "version": "${minecraftVersion}",
+            "version": "{{ minecraftVersion }}",
             "entrypoint": "org.spongepowered.common.launcher.Launcher",
             "links": {
                 "homepage": "https://www.minecraft.net"
@@ -24,7 +24,7 @@
             "loader": "java_plain",
             "id": "spongeapi",
             "name": "SpongeAPI",
-            "version": "${apiVersion}",
+            "version": "{{ apiVersion }}",
             "entrypoint": "org.spongepowered.api.Sponge",
             "description": "The Minecraft API specification",
             "links": {
@@ -42,7 +42,7 @@
         {
             "id": "sponge",
             "name": "Sponge",
-            "version": "${minecraftVersion}-${apiVersion}",
+            "version": "{{ minecraftVersion }}-{{ apiVersion }}",
             "entrypoint": "org.spongepowered.common.SpongeCommon",
             "description": "The common Sponge implementation",
             "links": {
@@ -59,18 +59,18 @@
             "dependencies": [
                 {
                     "id": "minecraft",
-                    "version": "${minecraftVersion}"
+                    "version": "{{ minecraftVersion }}"
                 },
                 {
                     "id": "spongeapi",
-                    "version": "${apiVersion}"
+                    "version": "{{ apiVersion }}"
                 }
             ]
         },
         {
             "id": "spongevanilla",
             "name": "SpongeVanilla",
-            "version": "${version}",
+            "version": "{{ version }}",
             "entrypoint": "org.spongepowered.vanilla.SpongeVanilla",
             "description": "Vanilla Minecraft with Sponge",
             "links": {
@@ -87,7 +87,7 @@
             "dependencies": [
                 {
                     "id": "sponge",
-                    "version": "${minecraftVersion}-${apiVersion}"
+                    "version": "{{ minecraftVersion }}-{{ apiVersion }}"
                 }
             ]
         }