From c9dbc1631a25c0550e8b124a26eeebac1e8b7a91 Mon Sep 17 00:00:00 2001 From: thedarkcolour <30441001+thedarkcolour@users.noreply.github.com> Date: Tue, 2 Jul 2024 15:43:25 -0700 Subject: [PATCH] Switch to Java coremods --- build.gradle | 3 + coremod/build.gradle | 28 +++ .../exdeorum/coremod/ASMTransformer.java | 162 ++++++++++++++++++ .../exdeorum/coremod/package-info.java | 21 +++ ...net.neoforged.neoforgespi.coremod.ICoreMod | 1 + settings.gradle | 2 + src/main/resources/META-INF/coremods.json | 3 - src/main/resources/coremods.js | 110 ------------ 8 files changed, 217 insertions(+), 113 deletions(-) create mode 100644 coremod/build.gradle create mode 100644 coremod/src/main/java/thedarkcolour/exdeorum/coremod/ASMTransformer.java create mode 100644 coremod/src/main/java/thedarkcolour/exdeorum/coremod/package-info.java create mode 100644 coremod/src/main/resources/META-INF/services/net.neoforged.neoforgespi.coremod.ICoreMod delete mode 100644 src/main/resources/META-INF/coremods.json delete mode 100644 src/main/resources/coremods.js diff --git a/build.gradle b/build.gradle index 60151858..0a774af7 100644 --- a/build.gradle +++ b/build.gradle @@ -135,6 +135,9 @@ dependencies { // ModKit DEV ONLY implementation('com.github.thedarkcolour:Modkit:e7c1881681') + // Core mod + jarJar(project(':coremod')) + // Oculus + Embeddium OPTIONAL compileOnly('maven.modrinth:oculus:1.20.1-1.6.9') compileOnly('maven.modrinth:embeddium:0.3.9+mc1.20.4') diff --git a/coremod/build.gradle b/coremod/build.gradle new file mode 100644 index 00000000..c10a99c3 --- /dev/null +++ b/coremod/build.gradle @@ -0,0 +1,28 @@ +plugins { + id 'java' +} + +java.toolchain.languageVersion = JavaLanguageVersion.of(21) + +jar { + manifest { + attributes(["FMLModType": "LIBRARY"]) + } +} + +repositories { + maven { + url = "https://libraries.minecraft.net" + metadataSources{ + mavenPom() + } + } + maven { + url = "https://maven.neoforged.net/releases" + } +} + +dependencies { + compileOnly 'net.neoforged.fancymodloader:loader:4.0.6' + compileOnly 'org.jetbrains:annotations:24.1.0' +} \ No newline at end of file diff --git a/coremod/src/main/java/thedarkcolour/exdeorum/coremod/ASMTransformer.java b/coremod/src/main/java/thedarkcolour/exdeorum/coremod/ASMTransformer.java new file mode 100644 index 00000000..4f15702e --- /dev/null +++ b/coremod/src/main/java/thedarkcolour/exdeorum/coremod/ASMTransformer.java @@ -0,0 +1,162 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.coremod; + +import cpw.mods.modlauncher.api.ITransformer; +import cpw.mods.modlauncher.api.ITransformerVotingContext; +import cpw.mods.modlauncher.api.TargetType; +import cpw.mods.modlauncher.api.TransformerVoteResult; +import net.neoforged.coremod.api.ASMAPI; +import net.neoforged.neoforgespi.coremod.ICoreMod; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.VarInsnNode; + +import java.util.List; +import java.util.Set; + +public class ASMTransformer implements ICoreMod { + @Override + public Iterable> getTransformers() { + return List.of( + new EndCityStructureTransformer(), + new DedicatedServerPropertiesTransformer(), + new EndDragonFightTransformer() + ); + } + + private interface MethodTransformer extends ITransformer { + @Override + default TransformerVoteResult castVote(ITransformerVotingContext context) { + return TransformerVoteResult.YES; + } + + @Override + default TargetType getTargetType() { + return TargetType.METHOD; + } + } + + // inserts a hook into EndCityStructure#findGenerationPoint to fix the position of the city if it is in a void world + private static class EndCityStructureTransformer implements MethodTransformer { + @Override + public Set> targets() { + return Set.of(ITransformer.Target.targetMethod( + "net.minecraft.world.level.levelgen.structure.structures.EndCityStructure", + "findGenerationPoint", + "(Lnet/minecraft/world/level/levelgen/structure/Structure$GenerationContext;)Ljava/util/Optional;" + )); + } + + // + @Override + public MethodNode transform(MethodNode input, ITransformerVotingContext context) { + var insnList = input.instructions; + + for (var i = 0; i < insnList.size(); ++i) { + var insn = insnList.get(i); + + // patch before ASTORE 3 + if (insn.getOpcode() == Opcodes.ASTORE && ((VarInsnNode) insn).var == 3) { + insnList.insertBefore(insn, ASMAPI.listOf( + new VarInsnNode(Opcodes.ALOAD, 1), + new MethodInsnNode(Opcodes.INVOKESTATIC, "thedarkcolour/exdeorum/asm/ASMHooks", "adjustPos", "(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/levelgen/structure/Structure$GenerationContext;)Lnet/minecraft/core/BlockPos;", false) + )); + ASMAPI.log("INFO", "Successfully patched End City generation for void worlds"); + return input; + } + } + + ASMAPI.log("ERROR", "Unable to patch End City generation, void worlds will have no end cities!!!"); + return input; + } + } + + // Redirects a field access in the constructor of DedicatedServerProperties from WorldPresets.NORMAL to ASMHooks.overrideDefaultWorldPreset() + private static class DedicatedServerPropertiesTransformer implements MethodTransformer { + @Override + public Set> targets() { + return Set.of(ITransformer.Target.targetMethod( + "net.minecraft.server.dedicated.DedicatedServerProperties", + "", + "(Ljava/util/Properties;)V" + )); + } + + @Override + public MethodNode transform(MethodNode input, ITransformerVotingContext context) { + var insnList = input.instructions; + + for (var i = 0; i < insnList.size(); ++i) { + var insn = insnList.get(i); + + if (insn.getOpcode() == Opcodes.GETSTATIC && (((MethodInsnNode) insn).name.equals("f_226437_") || ((MethodInsnNode) insn).name.equals("NORMAL"))) { + var newInsn = new MethodInsnNode(Opcodes.INVOKESTATIC, "thedarkcolour/exdeorum/asm/ASMHooks", "overrideDefaultWorldPreset", "()Lnet/minecraft/resources/ResourceKey;", false); + insnList.set(insn, newInsn); + + ASMAPI.log("INFO", "Successfully patched server.properties to use void world type by default"); + return input; + } + } + + ASMAPI.log("ERROR", "Unable to patch server.properties, you will have to set \"level-type\" to \"exdeorum:void_world\" manually."); + return input; + } + } + + // Fixes heightmap issues when placing the end portal podium that would only spawn half of the portal + // What this patch looks like in code: EndIslandPodium.place(..., this.portalLocation = ASMHooks.prePlaceEndPodium(this.portalLocation)) + private static class EndDragonFightTransformer implements MethodTransformer { + @Override + public Set> targets() { + return Set.of(ITransformer.Target.targetMethod( + "net.minecraft.world.level.dimension.end.EndDragonFight", + "spawnExitPortal", + "(Z)V" + )); + } + + @Override + public MethodNode transform(MethodNode input, ITransformerVotingContext context) { + var insnList = input.instructions; + + // Start at 2 to avoid null getPrevious().getPrevious() + for (var i = 2; i < insnList.size(); ++i) { + var insn = insnList.get(i); + + if (insn.getOpcode() == Opcodes.ALOAD && ((VarInsnNode) insn).var == 2) { + insnList.insertBefore(insn, ASMAPI.listOf( + new VarInsnNode(Opcodes.ALOAD, 0), + new VarInsnNode(Opcodes.ALOAD, 0), + new FieldInsnNode(Opcodes.GETFIELD, "net/minecraft/world/level/dimension/end/EndDragonFight", "portalLocation", "Lnet/minecraft/core/BlockPos;"), + new MethodInsnNode(Opcodes.INVOKESTATIC, "thedarkcolour/exdeorum/asm/ASMHooks", "prePlaceEndPodium", "(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/core/BlockPos;", false), + new FieldInsnNode(Opcodes.PUTFIELD, "net/minecraft/world/level/dimension/end/EndDragonFight", "portalLocation", "Lnet/minecraft/core/BlockPos;") + )); + ASMAPI.log("INFO", "Successfully patched end portal."); + return input; + } + } + + ASMAPI.log("ERROR", "Unable to patch End Portal, it will not spawn properly and you will be unable to return to the overworld without cheats."); + return input; + } + } +} diff --git a/coremod/src/main/java/thedarkcolour/exdeorum/coremod/package-info.java b/coremod/src/main/java/thedarkcolour/exdeorum/coremod/package-info.java new file mode 100644 index 00000000..1bcb754f --- /dev/null +++ b/coremod/src/main/java/thedarkcolour/exdeorum/coremod/package-info.java @@ -0,0 +1,21 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +@com.mojang.logging.annotations.MethodsReturnNonnullByDefault +@javax.annotation.ParametersAreNonnullByDefault +package thedarkcolour.exdeorum.coremod; diff --git a/coremod/src/main/resources/META-INF/services/net.neoforged.neoforgespi.coremod.ICoreMod b/coremod/src/main/resources/META-INF/services/net.neoforged.neoforgespi.coremod.ICoreMod new file mode 100644 index 00000000..8951e244 --- /dev/null +++ b/coremod/src/main/resources/META-INF/services/net.neoforged.neoforgespi.coremod.ICoreMod @@ -0,0 +1 @@ +thedarkcolour.exdeorum.coremod.ASMTransformer \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 09ead32f..ea06bbfc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,3 +8,5 @@ pluginManagement { plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0' } + +include('coremod') diff --git a/src/main/resources/META-INF/coremods.json b/src/main/resources/META-INF/coremods.json deleted file mode 100644 index 7da1c31b..00000000 --- a/src/main/resources/META-INF/coremods.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "exdeorum": "coremods.js" -} \ No newline at end of file diff --git a/src/main/resources/coremods.js b/src/main/resources/coremods.js deleted file mode 100644 index 5498e98f..00000000 --- a/src/main/resources/coremods.js +++ /dev/null @@ -1,110 +0,0 @@ -// Type definitions - -var Opcodes = Java.type('org.objectweb.asm.Opcodes'); -var InsnList = Java.type('org.objectweb.asm.tree.InsnList'); -var VarInsnNode = Java.type('org.objectweb.asm.tree.VarInsnNode'); -var FieldInsnNode = Java.type('org.objectweb.asm.tree.FieldInsnNode'); -var MethodInsnNode = Java.type('org.objectweb.asm.tree.MethodInsnNode'); -var LdcInsnNode = Java.type('org.objectweb.asm.tree.LdcInsnNode'); -var TypeInsnNode = Java.type('org.objectweb.asm.tree.TypeInsnNode'); -var InsnNode = Java.type('org.objectweb.asm.tree.InsnNode'); - -var ASMAPI = Java.type('net.neoforged.coremod.api.ASMAPI'); - -function initializeCoreMod() { - return { - // inserts a hook into EndCityStructure#findGenerationPoint to fix the position of the city if it is in a void world - 'EndCityPatch': { - 'target': { - 'type': 'METHOD', - 'class': 'net.minecraft.world.level.levelgen.structure.structures.EndCityStructure', - 'methodName': 'findGenerationPoint', - 'methodDesc': '(Lnet/minecraft/world/level/levelgen/structure/Structure$GenerationContext;)Ljava/util/Optional;' - }, - 'transformer': function (method) { - var insnList = method.instructions; - - for (var i = 0; i < insnList.size(); ++i) { - var insn = insnList.get(i); - - // patch before ASTORE 3 - if (insn.getOpcode() === Opcodes.ASTORE && insn.var === 3) { - insnList.insertBefore(insn, ASMAPI.listOf( - new VarInsnNode(Opcodes.ALOAD, 1), - new MethodInsnNode(Opcodes.INVOKESTATIC, 'thedarkcolour/exdeorum/asm/ASMHooks', 'adjustPos', '(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/levelgen/structure/Structure$GenerationContext;)Lnet/minecraft/core/BlockPos;', false) - )); - ASMAPI.log('INFO', 'Successfully patched End City generation for void worlds'); - return method; - } - } - - ASMAPI.log('ERROR', 'Unable to patch End City generation, void worlds will have no end cities!!!'); - return method; - } - }, - // Redirects a field access in the constructor of DedicatedServerProperties from WorldPresets.NORMAL to ASMHooks.overrideDefaultWorldPreset() - 'DedicatedServerPropertiesPatch': { - 'target': { - 'type': 'METHOD', - 'class': 'net.minecraft.server.dedicated.DedicatedServerProperties', - 'methodName': '', - 'methodDesc': '(Ljava/util/Properties;)V' - }, - 'transformer': function (method) { - var insnList = method.instructions; - - for (var i = 0; i < insnList.size(); ++i) { - var insn = insnList.get(i); - - if (insn.getOpcode() === Opcodes.GETSTATIC && (insn.name.equals('f_226437_') || insn.name.equals('NORMAL'))) { - var newInsn = new MethodInsnNode(Opcodes.INVOKESTATIC, 'thedarkcolour/exdeorum/asm/ASMHooks', 'overrideDefaultWorldPreset', '()Lnet/minecraft/resources/ResourceKey;', false) - insnList.set(insn, newInsn); - - ASMAPI.log('INFO', 'Successfully patched server.properties to use void world type by default'); - return method; - } - } - - ASMAPI.log('ERROR', 'Unable to patch server.properties, you will have to set "level-type" to "exdeorum:void_world" manually.'); - return method; - } - }, - // Fixes heightmap issues when placing the end portal podium that would only spawn half of the portal - // What this patch looks like in code: EndIslandPodium.place(..., this.portalLocation = ASMHooks.prePlaceEndPodium(this.portalLocation)) - 'EndPortalPatch': { - 'target': { - 'type': 'METHOD', - 'class': 'net.minecraft.world.level.dimension.end.EndDragonFight', - 'methodName': 'spawnExitPortal', // spawnExitPortal - 'methodDesc': '(Z)V' - }, - 'transformer': function(method) { - var insnList = method.instructions; - // Cache the mapped method name - var randomSourceCreate = ASMAPI.mapMethod('m_216327_'); - - // Start at 2 to avoid null getPrevious().getPrevious() - for (var i = 2; i < insnList.size(); ++i) { - var insn = insnList.get(i); - - if (insn.getOpcode() == Opcodes.ALOAD && insn.var == 2) { - // this.portalLocation = ASMHooks.prePlaceEndPodium(this.portalLocation) - // f_64072_ maps to portalLocation - insnList.insertBefore(insn, ASMAPI.listOf( - new VarInsnNode(Opcodes.ALOAD, 0), - new VarInsnNode(Opcodes.ALOAD, 0), - new FieldInsnNode(Opcodes.GETFIELD, 'net/minecraft/world/level/dimension/end/EndDragonFight', 'portalLocation', 'Lnet/minecraft/core/BlockPos;'), - new MethodInsnNode(Opcodes.INVOKESTATIC, 'thedarkcolour/exdeorum/asm/ASMHooks', 'prePlaceEndPodium', '(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/core/BlockPos;', false), - new FieldInsnNode(Opcodes.PUTFIELD, 'net/minecraft/world/level/dimension/end/EndDragonFight', 'portalLocation', 'Lnet/minecraft/core/BlockPos;') - )); - ASMAPI.log('INFO', 'Successfully patched end portal.'); - return method; - } - } - - ASMAPI.log('ERROR', 'Unable to patch End Portal, it will not spawn properly and you will be unable to return to the overworld without cheats.'); - return method; - } - } - }; -} \ No newline at end of file