diff --git a/coremod/build.gradle b/coremod/build.gradle index 8c68115b..e14e729e 100644 --- a/coremod/build.gradle +++ b/coremod/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java' } -java.toolchain.languageVersion = JavaLanguageVersion.of(21) +java.toolchain.languageVersion = JavaLanguageVersion.of(25) java.toolchain.vendor = JvmVendorSpec.JETBRAINS jar { @@ -27,6 +27,5 @@ repositories { } dependencies { - compileOnly 'net.neoforged.fancymodloader:loader:4.0.15' - compileOnly 'org.jetbrains:annotations:24.1.0' + compileOnly 'net.neoforged.fancymodloader:loader:11.0.13' } \ 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 deleted file mode 100644 index 0e16f73e..00000000 --- a/coremod/src/main/java/thedarkcolour/exdeorum/coremod/ASMTransformer.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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 && (((FieldInsnNode) insn).name.equals("f_226437_") || ((FieldInsnNode) 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/DedicatedServerPropertiesTransformer.java b/coremod/src/main/java/thedarkcolour/exdeorum/coremod/DedicatedServerPropertiesTransformer.java new file mode 100644 index 00000000..ca5c9f25 --- /dev/null +++ b/coremod/src/main/java/thedarkcolour/exdeorum/coremod/DedicatedServerPropertiesTransformer.java @@ -0,0 +1,49 @@ +package thedarkcolour.exdeorum.coremod; + +import net.neoforged.neoforgespi.transformation.ProcessorName; +import net.neoforged.neoforgespi.transformation.SimpleMethodProcessor; +import net.neoforged.neoforgespi.transformation.SimpleTransformationContext; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +import java.util.Set; + +// Redirects a field access in the constructor of DedicatedServerProperties from WorldPresets.NORMAL to ASMHooks.overrideDefaultWorldPreset() +public class DedicatedServerPropertiesTransformer extends SimpleMethodProcessor { + private static final ProcessorName NAME = new ProcessorName("exdeorum", "dedicated_server_properties_transformer"); + + @Override + public ProcessorName name() { + return NAME; + } + + @Override + public Set targets() { + return Set.of(new Target( + "net.minecraft.server.dedicated.DedicatedServerProperties", + "", + "(Ljava/util/Properties;)V" + )); + } + + @Override + public void transform(MethodNode input, SimpleTransformationContext context) { + var insnList = input.instructions; + + for (var i = 0; i < insnList.size(); ++i) { + var insn = insnList.get(i); + + if (insn.getOpcode() == Opcodes.GETSTATIC && (((FieldInsnNode) insn).name.equals("f_226437_") || ((FieldInsnNode) insn).name.equals("NORMAL"))) { + var newInsn = new MethodInsnNode(Opcodes.INVOKESTATIC, "thedarkcolour/exdeorum/asm/ASMHooks", "overrideDefaultWorldPreset", "()Lnet/minecraft/resources/ResourceKey;", false); + insnList.set(insn, newInsn); + + ExDeorumASM.LOGGER.info("Successfully patched server.properties to use void world type by default"); + return; + } + } + + ExDeorumASM.LOGGER.error("Unable to patch server.properties, you will have to set \"level-type\" to \"exdeorum:void_world\" manually."); + } +} diff --git a/coremod/src/main/java/thedarkcolour/exdeorum/coremod/EndCityStructureTransformer.java b/coremod/src/main/java/thedarkcolour/exdeorum/coremod/EndCityStructureTransformer.java new file mode 100644 index 00000000..9a7353f6 --- /dev/null +++ b/coremod/src/main/java/thedarkcolour/exdeorum/coremod/EndCityStructureTransformer.java @@ -0,0 +1,51 @@ +package thedarkcolour.exdeorum.coremod; + +import net.neoforged.neoforgespi.transformation.ProcessorName; +import net.neoforged.neoforgespi.transformation.SimpleMethodProcessor; +import net.neoforged.neoforgespi.transformation.SimpleTransformationContext; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.VarInsnNode; + +import java.util.Set; + +// inserts a hook into EndCityStructure#findGenerationPoint to fix the position of the city if it is in a void world +public class EndCityStructureTransformer extends SimpleMethodProcessor { + private static final ProcessorName NAME = new ProcessorName("exdeorum", "end_city_structure_void_transformer"); + + @Override + public ProcessorName name() { + return NAME; + } + + @Override + public Set targets() { + return Set.of(new Target( + "net.minecraft.world.level.levelgen.structure.structures.EndCityStructure", + "findGenerationPoint", + "(Lnet/minecraft/world/level/levelgen/structure/Structure$GenerationContext;)Ljava/util/Optional;" + )); + } + + @Override + public void transform(MethodNode input, SimpleTransformationContext 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, ExDeorumASM.insnList( + 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) + )); + ExDeorumASM.LOGGER.info("Successfully patched End City generation for void worlds"); + return; + } + } + + ExDeorumASM.LOGGER.error("Unable to patch End City generation, void worlds will have no end cities!!!"); + } +} diff --git a/coremod/src/main/java/thedarkcolour/exdeorum/coremod/ExDeorumASM.java b/coremod/src/main/java/thedarkcolour/exdeorum/coremod/ExDeorumASM.java new file mode 100644 index 00000000..b88208d7 --- /dev/null +++ b/coremod/src/main/java/thedarkcolour/exdeorum/coremod/ExDeorumASM.java @@ -0,0 +1,35 @@ +/* + * 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 org.objectweb.asm.tree.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class ExDeorumASM { + static final Logger LOGGER = LoggerFactory.getLogger(ExDeorumASM.class); + + public static InsnList insnList(AbstractInsnNode... nodes) { + InsnList list = new InsnList(); + for (var node : nodes) { + list.add(node); + } + return list; + } +} 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 deleted file mode 100644 index 8951e244..00000000 --- a/coremod/src/main/resources/META-INF/services/net.neoforged.neoforgespi.coremod.ICoreMod +++ /dev/null @@ -1 +0,0 @@ -thedarkcolour.exdeorum.coremod.ASMTransformer \ No newline at end of file diff --git a/coremod/src/main/resources/META-INF/services/net.neoforged.neoforgespi.transformation.ClassProcessor b/coremod/src/main/resources/META-INF/services/net.neoforged.neoforgespi.transformation.ClassProcessor new file mode 100644 index 00000000..124c1630 --- /dev/null +++ b/coremod/src/main/resources/META-INF/services/net.neoforged.neoforgespi.transformation.ClassProcessor @@ -0,0 +1,2 @@ +thedarkcolour.exdeorum.coremod.DedicatedServerPropertiesTransformer +thedarkcolour.exdeorum.coremod.EndCityStructureTransformer diff --git a/src/main/java/thedarkcolour/exdeorum/asm/ASMHooks.java b/src/main/java/thedarkcolour/exdeorum/asm/ASMHooks.java index 3e977720..2a28c45e 100644 --- a/src/main/java/thedarkcolour/exdeorum/asm/ASMHooks.java +++ b/src/main/java/thedarkcolour/exdeorum/asm/ASMHooks.java @@ -33,11 +33,11 @@ import thedarkcolour.exdeorum.voidworld.VoidChunkGenerator; import java.util.Properties; -// todo test that all of these patches still work properly @SuppressWarnings("unused") public final class ASMHooks { /** * Called in {@link net.minecraft.world.level.levelgen.structure.structures.EndCityStructure#findGenerationPoint(Structure.GenerationContext)} + * by {@link thedarkcolour.exdeorum.coremod.EndCityStructureTransformer} * to fix End Cities not generating in void worlds. */ public static BlockPos adjustPos(BlockPos pos, Structure.GenerationContext context) { @@ -48,21 +48,9 @@ public final class ASMHooks { } } - /** - * Called in {@link net.minecraft.world.level.dimension.end.EndDragonFight#spawnExitPortal(boolean)} - * right before {@code EndPodiumFeature.place} is called to fix End Portal not spawning fully, - * with part of it being generated outside the world in the void. - */ - public static BlockPos prePlaceEndPodium(BlockPos pos) { - if (pos.getY() < 4) { - return pos.above(32); - } else { - return pos.immutable(); - } - } - /** * Called in {@link net.minecraft.server.dedicated.DedicatedServerProperties#DedicatedServerProperties(Properties)} + * by {@link thedarkcolour.exdeorum.coremod.DedicatedServerPropertiesTransformer} * where {@code WorldPresets.NORMAL} is used in the line that looks like {@code WorldPresets.NORMAL.location().toString()} */ public static ResourceKey overrideDefaultWorldPreset() {