diff --git a/build.gradle b/build.gradle
index 17c4683b..e8878d32 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,10 +6,8 @@ plugins {
     id("me.modmuss50.mod-publish-plugin") version "0.8.1" apply false
 }
 
-/*
- * Gets the version name from the latest Git tag
- */
-// https://stackoverflow.com/questions/28498688/gradle-script-to-autoversion-and-include-the-commit-hash-in-android
+extensions.create("runConfigCommon", RunConfigCommon.class)
+
 def getGitCommit = providers.exec {
     commandLine 'git', 'rev-parse', '--short', 'HEAD'
 }.standardOutput.getAsText().map { it.trim() }
@@ -21,29 +19,22 @@ def aw2at = Aw2AtTask.configureDefault(
 )
 
 neoForge {
-    neoFormVersion = "1.21.3-20241023.131943"
+    neoFormVersion = neoform_version
     validateAccessTransformers = true
     accessTransformers.files.setFrom(aw2at.flatMap { t -> t.getOutputFile() })
 }
 
-class FixAsmRule implements ComponentMetadataRule {
-    @Override
-    void execute(ComponentMetadataContext ctx) {
-        ctx.details.allVariants {
-            it.withDependencies {
-                it.removeIf {
-                    it.group == "org.ow2.asm"
-                }
-            }
-        }
-    }
+runConfigCommon {
+    systemProperties.put "mixin.debug", "true"
+    systemProperties.put "Moonrise.MaxViewDistance", "128"
 }
 
 dependencies {
     // todo: check versions
     compileOnly "net.fabricmc:sponge-mixin:0.15.4+mixin.0.8.7"
     compileOnly "io.github.llamalad7:mixinextras-common:0.4.1"
-    components.withModule("net.neoforged:minecraft-dependencies", FixAsmRule.class)
+    // work around minecraft (MDG) forcing ASM 9.3 which is incompatible with the above deps...
+    components.withModule("net.neoforged:minecraft-dependencies", RemoveAsmDependency.class)
 
     api("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
     api("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}")
@@ -136,4 +127,16 @@ subprojects {
             minecraftVersions = supportedMcVersions
         }
     }
+
+    // Setup a run with lithium for compatibility testing
+    configurations.create("lithium")
+    dependencies {
+        String coordinates =  "maven.modrinth:lithium:"
+        if (getProject().name == "Moonrise-NeoForge") {
+            coordinates += rootProject.neo_lithium_version
+        } else {
+            coordinates += rootProject.fabric_lithium_version
+        }
+        lithium coordinates
+    }
 }
diff --git a/buildSrc/src/main/java/RemoveAsmDependency.java b/buildSrc/src/main/java/RemoveAsmDependency.java
new file mode 100644
index 00000000..34a2346a
--- /dev/null
+++ b/buildSrc/src/main/java/RemoveAsmDependency.java
@@ -0,0 +1,14 @@
+import java.util.Objects;
+import org.gradle.api.artifacts.ComponentMetadataContext;
+import org.gradle.api.artifacts.ComponentMetadataRule;
+
+public abstract class RemoveAsmDependency implements ComponentMetadataRule {
+    @Override
+    public void execute(final ComponentMetadataContext ctx) {
+        ctx.getDetails().allVariants(variant -> {
+            variant.withDependencies(deps -> {
+                deps.removeIf(dep -> Objects.equals(dep.getGroup(), "org.ow2.asm"));
+            });
+        });
+    }
+}
diff --git a/buildSrc/src/main/java/RunConfigCommon.java b/buildSrc/src/main/java/RunConfigCommon.java
new file mode 100644
index 00000000..5fab6d17
--- /dev/null
+++ b/buildSrc/src/main/java/RunConfigCommon.java
@@ -0,0 +1,5 @@
+import org.gradle.api.provider.MapProperty;
+
+public abstract class RunConfigCommon {
+    public abstract MapProperty<String, String> getSystemProperties();
+}
diff --git a/fabric/build.gradle b/fabric/build.gradle
index 7c796da0..b3762486 100644
--- a/fabric/build.gradle
+++ b/fabric/build.gradle
@@ -26,15 +26,20 @@ dependencies {
     include fabricApiLibs.base
 }
 
-processResources {
-    inputs.property "version", project.version
-
+tasks.processResources {
+    def properties = [
+        "version": project.version,
+        "minecraft_version": minecraft_version,
+        "loader_version": loader_version,
+        "mod_version": mod_version
+    ]
+    inputs.properties(properties)
     filesMatching("fabric.mod.json") {
-        expand "version": project.version, "minecraft_version": minecraft_version, "loader_version": loader_version, "mod_version": mod_version
+        expand properties
     }
 }
 
-shadowJar {
+tasks.shadowJar {
     archiveClassifier = "dev-all"
     destinationDirectory = layout.buildDirectory.dir("libs")
     configurations = [project.configurations.shadow]
@@ -68,10 +73,8 @@ loom {
     mixin {
         useLegacyMixinAp = false
     }
-    runs.all {
+    runs.configureEach {
         ideConfigGenerated true
-        property "mixin.debug", "true"
-        property "Moonrise.MaxViewDistance", "128"
     }
     mods {
         main {
@@ -81,27 +84,27 @@ loom {
     }
 }
 
+afterEvaluate {
+    loom.runs.configureEach { cfg ->
+        runConfigCommon.systemProperties.get().each {
+            cfg.property it.key, it.value
+        }
+    }
+}
+
 // Setup a run with lithium for compatibility testing
 sourceSets.create("lithium")
-configurations.create("lithium")
 loom {
     createRemapConfigurations(sourceSets.lithium)
     runs {
         register("lithiumClient") {
             client()
-            property "mixin.debug", "true"
         }
     }
 }
+configurations.modLithiumRuntimeOnly {
+    extendsFrom configurations.lithium
+}
 tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) {
     getClasspath().from(configurations.modRuntimeClasspathLithiumMapped)
 }
-dependencies {
-    String coordinates =  "maven.modrinth:lithium:"
-    if (getProject().name == "Moonrise-NeoForge") {
-        coordinates += rootProject.neo_lithium_version
-    } else {
-        coordinates += rootProject.fabric_lithium_version
-    }
-    modLithiumRuntimeOnly coordinates
-}
diff --git a/gradle.properties b/gradle.properties
index 729541df..3ae9a10f 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,12 +1,14 @@
 # Done to increase the memory available to gradle.
 org.gradle.jvmargs=-Xmx2G
-org.gradle.daemon=false
+org.gradle.parallel=true
+org.gradle.caching=true
 # Fabric Properties
 # check these on https://modmuss50.me/fabric.html
 minecraft_version=1.21.3
 loader_version=0.16.9
 supported_minecraft_versions=1.21.3
-neoforge_version=21.3.31-beta
+neoforge_version=21.3.51-beta
+neoform_version=1.21.3-20241023.131943
 fabric_api_version=0.107.0+1.21.3
 snakeyaml_version=2.3
 concurrentutil_version=0.0.2-SNAPSHOT
diff --git a/neoforge/build.gradle b/neoforge/build.gradle
index 1a6db22e..bc602139 100644
--- a/neoforge/build.gradle
+++ b/neoforge/build.gradle
@@ -1,5 +1,6 @@
-import net.neoforged.moddevgradle.internal.RunGameTask
 import java.nio.file.Files
+import java.nio.file.StandardCopyOption
+import net.neoforged.moddevgradle.internal.RunGameTask
 
 plugins {
     id("net.neoforged.moddev")
@@ -30,17 +31,11 @@ neoForge {
         }
     }
     runs {
-        configureEach {
-            additionalRuntimeClasspathConfiguration.extendsFrom configurations.libs
-            systemProperties.put('mixin.debug', 'true')
-        }
         client {
             client()
-            // loadedMods.set([]) // Work around module issues by using the production jar for dev runs
         }
         server {
             server()
-            // loadedMods.set([]) // Work around module issues by using the production jar for dev runs
         }
     }
 }
@@ -52,38 +47,29 @@ dependencies {
 
     libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
     libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}")
-    libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
+    additionalRuntimeClasspath libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
 
     implementation "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
     jarJar "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
 }
 
-// Work around module issues by using the production jar for dev runs
-/*
-tasks.withType(RunGameTask).configureEach {
-    dependsOn(tasks.shadowJar)
-    doFirst {
-        def jar = file("run/mods/main.jar")
-        jar.parentFile.mkdirs()
-        jar.delete()
-        Files.copy(tasks.shadowJar.archiveFile.get().asFile.toPath(), jar.toPath())
-    }
-}
- */
-
-processResources {
-    inputs.property "version", project.version
-
+tasks.processResources {
+    def properties = [
+        "version": project.version,
+        "minecraft_version": minecraft_version,
+        "mod_version": mod_version
+    ]
+    inputs.properties(properties)
     filesMatching("META-INF/neoforge.mods.toml") {
-        expand "version": project.version, "minecraft_version": minecraft_version, "loader_version": loader_version, "mod_version": mod_version
+        expand properties
     }
 }
 
-jar {
-    archiveClassifier = ""
+tasks.jar {
+    archiveClassifier = "slim"
 }
 
-shadowJar {
+tasks.shadowJar {
     archiveClassifier = ""
     destinationDirectory = layout.buildDirectory.dir("libs")
     configurations = [project.configurations.shadow]
@@ -92,6 +78,10 @@ shadowJar {
     relocate 'org.yaml.snakeyaml', 'ca.spottedleaf.moonrise.libs.org.yaml.snakeyaml'
 }
 
+tasks.assemble {
+    dependsOn tasks.shadowJar
+}
+
 publishMods {
     file = shadowJar.archiveFile
     modLoaders = ["neoforge"]
@@ -111,3 +101,42 @@ publishMods {
         )
     }
 }
+
+afterEvaluate {
+    neoForge.runs.configureEach { cfg ->
+        runConfigCommon.systemProperties.get().each {
+            cfg.systemProperties.put it.key, it.value
+        }
+    }
+}
+
+// Setup a run with lithium for compatibility testing
+neoForge {
+    runs {
+        lithiumClient {
+            client()
+            disableIdeRun()
+        }
+    }
+}
+tasks.withType(RunGameTask).configureEach {
+    if (name == "runLithiumClient") {
+        return
+    }
+    def out = gameDirectory.get().getAsFile().toPath().resolve("mods/lithium-tmp.jar")
+    doFirst {
+        Files.deleteIfExists(out)
+    }
+}
+def lithium = configurations.lithium
+tasks.runLithiumClient {
+    def out = gameDirectory.get().getAsFile().toPath().resolve("mods/lithium-tmp.jar")
+    doFirst {
+        for (File file in lithium) {
+            Files.copy(file.toPath(), out, StandardCopyOption.REPLACE_EXISTING)
+        }
+    }
+    doLast {
+        Files.deleteIfExists(out)
+    }
+}
diff --git a/settings.gradle b/settings.gradle
index 634c0b9c..0b50d854 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -23,7 +23,7 @@ pluginManagement {
 
 plugins {
     id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
-    id("quiet-fabric-loom") version "1.8.306" apply false
+    id("quiet-fabric-loom") version "1.8.309" apply false
     id("net.neoforged.moddev") version "2.0.49-beta" apply false
     id 'com.gradleup.shadow' version '8.3.5' apply false
 }