@@ -2,6 +2,16 @@ import com.gtnewhorizons.retrofuturagradle.mcp.InjectTagsTask
22import org.jetbrains.changelog.Changelog
33import org.jetbrains.gradle.ext.Gradle
44
5+ import com.gtnewhorizons.retrofuturagradle.MinecraftExtension
6+ import com.gtnewhorizons.retrofuturagradle.mcp.MCPTasks
7+ import com.gtnewhorizons.retrofuturagradle.minecraft.MinecraftTasks
8+ import com.gtnewhorizons.retrofuturagradle.minecraft.RunMinecraftTask
9+ import com.gtnewhorizons.retrofuturagradle.util.Distribution
10+ import de.undercouch.gradle.tasks.download.DownloadExtension
11+ import org.apache.commons.io.FileUtils
12+
13+ import javax.inject.Inject
14+
515plugins {
616 id ' java'
717 id ' java-library'
@@ -11,6 +21,7 @@ plugins {
1121 id ' com.matthewprenger.cursegradle' version ' 1.4.0' apply false
1222 id ' com.modrinth.minotaur' version ' 2.+' apply false
1323 id ' org.jetbrains.changelog' version ' 2.5.0'
24+ id ' de.undercouch.download' version ' 5.6.0' apply false
1425}
1526
1627apply from : ' gradle/scripts/helpers.gradle'
@@ -63,6 +74,22 @@ java {
6374
6475configurations {
6576 embed
77+ create(" runtimeOnlyNonPublishable" ) {
78+ description = " Runtime only dependencies that are not published alongside the jar"
79+ canBeConsumed = false
80+ canBeResolved = false
81+ }
82+ create(" devOnlyNonPublishable" ) {
83+ description = " Runtime and compiletime dependencies that are not published alongside the jar (compileOnly + runtimeOnlyNonPublishable)"
84+ canBeConsumed = false
85+ canBeResolved = false
86+ }
87+
88+ compileOnly. extendsFrom(devOnlyNonPublishable)
89+ runtimeOnlyNonPublishable. extendsFrom(devOnlyNonPublishable)
90+ runtimeClasspath. extendsFrom(runtimeOnlyNonPublishable)
91+ testRuntimeClasspath. extendsFrom(runtimeOnlyNonPublishable)
92+
6693 implementation. extendsFrom(embed)
6794}
6895
@@ -230,9 +257,29 @@ idea {
230257 " 1. Run Client" (Gradle ) {
231258 taskNames = [" runClient" ]
232259 }
260+ if (propertyBool(' enable_java17_run_tasks' )) {
261+ ' 1a. Run Hotswap Client (Java 17)' (Gradle ) {
262+ taskNames = [' runClient17' ]
263+ envs= [HOTSWAP :" true" ]
264+ }
265+ ' 1b. Run Hotswap Client (Java 21)' (Gradle ) {
266+ taskNames = [' runClient21' ]
267+ envs= [HOTSWAP :" true" ]
268+ }
269+ }
233270 " 2. Run Server" (Gradle ) {
234271 taskNames = [" runServer" ]
235272 }
273+ if (propertyBool(' enable_java17_run_tasks' )) {
274+ ' 2a. Run Hotswap Server (Java 17)' (Gradle ) {
275+ taskNames = [' runServer17' ]
276+ envs= [HOTSWAP :" true" ]
277+ }
278+ ' 2b. Run Hotswap Server (Java 21)' (Gradle ) {
279+ taskNames = [' runServer21' ]
280+ envs= [HOTSWAP :" true" ]
281+ }
282+ }
236283 " 3. Run Obfuscated Client" (Gradle ) {
237284 taskNames = [" runObfClient" ]
238285 }
@@ -354,3 +401,215 @@ idea.project.settings {
354401
355402apply from : ' gradle/scripts/publishing.gradle'
356403apply from : ' gradle/scripts/extra.gradle'
404+
405+
406+ if (propertyBool(' enable_java17_run_tasks' )) {
407+
408+ apply plugin : ' de.undercouch.download'
409+
410+ ext. java17Toolchain = (JavaToolchainSpec spec) -> {
411+ spec. languageVersion. set(JavaLanguageVersion . of(17 ))
412+ spec. vendor. set(JvmVendorSpec . matching(" jetbrains" ))
413+ }
414+ ext. java21Toolchain = (JavaToolchainSpec spec) -> {
415+ spec. languageVersion. set(JavaLanguageVersion . of(21 ))
416+ spec. vendor. set(JvmVendorSpec . matching(" jetbrains" ))
417+ }
418+
419+ ext. java17DependenciesCfg = configurations. create(" java17Dependencies" ) {
420+ extendsFrom(configurations. getByName(" runtimeClasspath" )) // Ensure consistent transitive dependency resolution
421+ canBeConsumed = false
422+ }
423+ ext. java17PatchDependenciesCfg = configurations. create(" java17PatchDependencies" ) {
424+ canBeConsumed = false
425+ }
426+
427+ dependencies {
428+ if (propertyString(' mod_id' ) != ' lwjgl3ify' ) {
429+ java17Dependencies(" io.github.twilightflower:lwjgl3ify:1.0.1" )
430+ java17PatchDependencies(" io.github.twilightflower:lwjgl3ify:1.0.1:forgePatches" ) {
431+ transitive = false
432+ }
433+ }
434+ }
435+
436+ ext. java17JvmArgs = [
437+ " -Dfile.encoding=UTF-8" ,
438+ " -Djava.system.class.loader=com.gtnewhorizons.retrofuturabootstrap.RfbSystemClassLoader" ,
439+ " -Djava.security.manager=allow" ,
440+ " --add-opens" , " java.base/jdk.internal.loader=ALL-UNNAMED" ,
441+ " --add-opens" , " java.base/java.net=ALL-UNNAMED" ,
442+ " --add-opens" , " java.base/java.nio=ALL-UNNAMED" ,
443+ " --add-opens" , " java.base/java.io=ALL-UNNAMED" ,
444+ " --add-opens" , " java.base/java.lang=ALL-UNNAMED" ,
445+ " --add-opens" , " java.base/java.lang.reflect=ALL-UNNAMED" ,
446+ " --add-opens" , " java.base/java.text=ALL-UNNAMED" ,
447+ " --add-opens" , " java.base/java.util=ALL-UNNAMED" ,
448+ " --add-opens" , " java.base/jdk.internal.reflect=ALL-UNNAMED" ,
449+ " --add-opens" , " java.base/sun.nio.ch=ALL-UNNAMED" ,
450+ " --add-opens" , " jdk.naming.dns/com.sun.jndi.dns=ALL-UNNAMED,java.naming" ,
451+ " --add-opens" , " java.desktop/sun.awt=ALL-UNNAMED" ,
452+ " --add-opens" , " java.desktop/sun.awt.image=ALL-UNNAMED" ,
453+ " --add-opens" , " java.desktop/com.sun.imageio.plugins.png=ALL-UNNAMED" ,
454+ " --add-opens" , " jdk.dynalink/jdk.dynalink.beans=ALL-UNNAMED" ,
455+ " --add-opens" , " java.sql.rowset/javax.sql.rowset.serial=ALL-UNNAMED"
456+ ]
457+
458+ ext. hotswapJvmArgs = [
459+ // DCEVM advanced hot reload
460+ " -XX:+AllowEnhancedClassRedefinition" ,
461+ " -XX:HotswapAgent=fatjar"
462+ ]
463+
464+ ext. setupHotswapAgent17 = tasks. register(" setupHotswapAgent17" , SetupHotswapAgentTask , t -> {
465+ t. setTargetForToolchain(java17Toolchain)
466+ })
467+
468+ ext. setupHotswapAgent21 = tasks. register(" setupHotswapAgent21" , SetupHotswapAgentTask , t -> {
469+ t. setTargetForToolchain(java21Toolchain)
470+ })
471+
472+ def runClient17Task = tasks. register(" runClient17" , RunHotswappableMinecraftTask , Distribution . CLIENT , " runClient" )
473+ runClient17Task. configure {
474+ dependsOn(setupHotswapAgent17)
475+ setup(project)
476+ javaLauncher = project. javaToolchains. launcherFor(project. java17Toolchain)
477+ }
478+
479+ def runServer17Task = tasks. register(" runServer17" , RunHotswappableMinecraftTask , Distribution . DEDICATED_SERVER , " runServer" )
480+ runServer17Task. configure {
481+ dependsOn(setupHotswapAgent17)
482+ setup(project)
483+ javaLauncher = project. javaToolchains. launcherFor(project. java17Toolchain)
484+ }
485+
486+ def runClient21Task = tasks. register(" runClient21" , RunHotswappableMinecraftTask , Distribution . CLIENT , " runClient" )
487+ runClient21Task. configure {
488+ dependsOn(setupHotswapAgent21)
489+ setup(project)
490+ javaLauncher = project. javaToolchains. launcherFor(project. java21Toolchain)
491+ }
492+
493+ def runServer21Task = tasks. register(" runServer21" , RunHotswappableMinecraftTask , Distribution . DEDICATED_SERVER , " runServer" )
494+ runServer21Task. configure {
495+ dependsOn(setupHotswapAgent21)
496+ setup(project)
497+ javaLauncher = project. javaToolchains. launcherFor(project. java21Toolchain)
498+ }
499+ }
500+
501+ abstract class RunHotswappableMinecraftTask extends RunMinecraftTask {
502+
503+ // IntelliJ doesn't seem to allow pre-set commandline arguments, so we also support an env variable
504+ private boolean enableHotswap = Boolean . valueOf(System . getenv(" HOTSWAP" ))
505+
506+ public final Distribution side
507+ public final String superTask
508+
509+ @Input
510+ boolean getEnableHotswap () {
511+ return enableHotswap
512+ }
513+
514+ @Option (option = " hotswap" , description = " Enables HotSwapAgent for enhanced class reloading under a debugger" )
515+ boolean setEnableHotswap (boolean enable ) {
516+ enableHotswap = enable
517+ }
518+
519+ @Inject
520+ RunHotswappableMinecraftTask (Distribution side , String superTask , org.gradle.api.invocation.Gradle gradle ) {
521+ super (side, gradle)
522+
523+ this . side = side
524+ this . superTask = superTask
525+ setGroup(" Modded Minecraft" )
526+ setDescription(" Runs the modded " + side. name(). toLowerCase(Locale . ROOT ) + " using modern Java and lwjgl3ify" )
527+ this . getLwjglVersion(). set(3 )
528+ }
529+
530+ void setup (Project project ) {
531+ final MinecraftExtension minecraft = project. getExtensions(). getByType(MinecraftExtension . class)
532+ final MCPTasks mcpTasks = project. getExtensions(). getByType(MCPTasks . class)
533+ final MinecraftTasks mcTasks = project. getExtensions(). getByType(MinecraftTasks . class)
534+
535+ this . getExtraJvmArgs(). addAll((List<String > ) project. property(" java17JvmArgs" ))
536+ if (getEnableHotswap()) {
537+ this . getExtraJvmArgs(). addAll((List<String > ) project. property(" hotswapJvmArgs" ))
538+ }
539+
540+ this . classpath(project. property(" java17PatchDependenciesCfg" ))
541+ this . classpath(mcpTasks. getTaskPackageMcLauncher())
542+ this . classpath(mcpTasks. getTaskPackagePatchedMc())
543+ this . classpath(mcpTasks. getPatchedConfiguration())
544+ this . classpath(project. getTasks(). named(" jar" ))
545+ this . classpath(project. property(" java17DependenciesCfg" ))
546+
547+ super . setup(project)
548+
549+ dependsOn(
550+ mcpTasks. getLauncherSources(). getClassesTaskName(),
551+ mcTasks. getTaskDownloadVanillaAssets(),
552+ mcpTasks. getTaskPackagePatchedMc(),
553+ " jar"
554+ )
555+
556+ getMainClass(). set((side == Distribution . CLIENT ) ? " GradleStart" : " GradleStartServer" )
557+ getUsername(). set(minecraft. getUsername())
558+ getUserUUID(). set(minecraft. getUserUUID())
559+ if (side == Distribution . DEDICATED_SERVER ) {
560+ getExtraArgs(). add(" nogui" )
561+ }
562+
563+ systemProperty(" gradlestart.bouncerClient" , " com.gtnewhorizons.retrofuturabootstrap.Main" )
564+ systemProperty(" gradlestart.bouncerServer" , " com.gtnewhorizons.retrofuturabootstrap.Main" )
565+
566+ if (project. use_mixins. toBoolean()) {
567+ this . extraJvmArgs. addAll(project. provider(() -> {
568+ def mixinCfg = project. configurations. detachedConfiguration(project. dependencies. create(project. mixinProviderSpec))
569+ mixinCfg. canBeConsumed = false
570+ mixinCfg. canBeResolved = true
571+ mixinCfg. transitive = false
572+ enableHotswap ? [" -javaagent:" + mixinCfg. singleFile. absolutePath] : []
573+ }))
574+ }
575+ }
576+ }
577+
578+ abstract class SetupHotswapAgentTask extends DefaultTask {
579+
580+ @OutputFile
581+ abstract RegularFileProperty getTargetFile ()
582+
583+ void setTargetForToolchain (Action<JavaToolchainSpec > spec ) {
584+ getTargetFile(). set(project. javaToolchains. launcherFor(spec). map {
585+ it. metadata. installationPath. file(" lib/hotswap/hotswap-agent.jar" )
586+ })
587+ }
588+
589+ @Inject
590+ SetupHotswapAgentTask () {
591+ setGroup(" GT Buildscript" )
592+ setDescription(" Installs a recent version of HotSwapAgent into the Java runtime directory" )
593+ onlyIf(" Run only if not already installed" , t -> ! ((SetupHotswapAgentTask ) t). getTargetFile(). getAsFile(). get(). exists())
594+ }
595+
596+ @TaskAction
597+ void installHSA () {
598+ final String url = ' https://github.com/HotswapProjects/HotswapAgent/releases/download/RELEASE-2.0.3/hotswap-agent-2.0.3.jar'
599+ final File target = getTargetFile(). getAsFile(). get()
600+ final File parent = target. getParentFile()
601+ FileUtils . forceMkdir(parent)
602+ final DownloadExtension download = getProject(). getExtensions(). findByType(DownloadExtension . class)
603+ download. run(ds -> {
604+ try {
605+ ds. src(url)
606+ } catch (MalformedURLException e) {
607+ throw new RuntimeException (e)
608+ }
609+ ds. dest(target)
610+ ds. overwrite(false )
611+ ds. tempAndMove(true )
612+ })
613+ }
614+ }
615+
0 commit comments