diff --git a/README.md b/README.md index 169910d..750354f 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,9 @@ Patches Galacticraft Core. In the method BlockBasic#getPickBlock, Galacticraft r rather than using the state passed in to the method. This leaves it incompatible with Scannable which does not pass in the BlockPos when calling Block#getPickBlock. This patch simply removes the call to World#getBlockState, replacing it with the state from the argument. + +#### Just A Few Fish Augmentation (JAFFA) +A set of three patches to JAFF which together serve to implement vanilla spawning mechanics. + * Removes JAFF's own spawn loop + * Register vanilla spawns and placement types + * Implement EntityFish#isNotColliding, overriding EntityAnimal's implementation which prevents spawning in water. diff --git a/build.gradle b/build.gradle index b554056..f989342 100644 --- a/build.gradle +++ b/build.gradle @@ -81,6 +81,7 @@ dependencies { } compile fg.deobf('curse.maven:astralsorcery-1.0.23:2920254') + compile fg.deobf('curse.maven:jaff-1.7:2448283') } mixin { diff --git a/gradle.properties b/gradle.properties index c32dd31..20ce9c6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version_base = 1.2 +version_base = 1.3 # Sets default memory used for gradle commands. Can be overridden by user or command line properties. # This is required to provide enough memory for the Minecraft decompilation process. diff --git a/src/main/java/tv/darkosto/sevpatches/core/FishHook.java b/src/main/java/tv/darkosto/sevpatches/core/FishHook.java new file mode 100644 index 0000000..2969a8b --- /dev/null +++ b/src/main/java/tv/darkosto/sevpatches/core/FishHook.java @@ -0,0 +1,43 @@ +package tv.darkosto.sevpatches.core; + +import com.tmtravlr.jaff.entities.*; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntitySpawnPlacementRegistry; +import net.minecraft.entity.EnumCreatureType; +import net.minecraft.world.biome.Biome; +import net.minecraftforge.common.BiomeDictionary; +import net.minecraftforge.fml.common.registry.EntityRegistry; + +import java.util.HashSet; +import java.util.Set; + +public class FishHook { + public static void registerPlacement() { + SevPatchesLoadingPlugin.LOGGER.info("JAFFA Phase 1"); + EntitySpawnPlacementRegistry.setPlacementType(EntityFishCod.class, EntityLiving.SpawnPlacementType.IN_WATER); + EntitySpawnPlacementRegistry.setPlacementType(EntityFishClownfish.class, EntityLiving.SpawnPlacementType.IN_WATER); + EntitySpawnPlacementRegistry.setPlacementType(EntityFishPufferfish.class, EntityLiving.SpawnPlacementType.IN_WATER); + EntitySpawnPlacementRegistry.setPlacementType(EntityFishSalmon.class, EntityLiving.SpawnPlacementType.IN_WATER); + } + + public static void registerSpawns() { + SevPatchesLoadingPlugin.LOGGER.info("JAFFA Phase 2"); + + Set waterBiomes = new HashSet<>(); + waterBiomes.addAll(BiomeDictionary.getBiomes(BiomeDictionary.Type.RIVER)); + waterBiomes.addAll(BiomeDictionary.getBiomes(BiomeDictionary.Type.SWAMP)); + waterBiomes.addAll(BiomeDictionary.getBiomes(BiomeDictionary.Type.OCEAN)); + + Set landWaterBiomes = new HashSet<>(); + landWaterBiomes.addAll(BiomeDictionary.getBiomes(BiomeDictionary.Type.RIVER)); + landWaterBiomes.addAll(BiomeDictionary.getBiomes(BiomeDictionary.Type.SWAMP)); + + EntityRegistry.addSpawn(EntityFishCod.class, 40, 4, 4, EnumCreatureType.WATER_CREATURE, waterBiomes.toArray(new Biome[0])); + EntityRegistry.addSpawn(EntityFishClownfish.class, 12, 4, 4, EnumCreatureType.WATER_CREATURE, BiomeDictionary.getBiomes(BiomeDictionary.Type.OCEAN).toArray(new Biome[0])); + EntityRegistry.addSpawn(EntityFishPufferfish.class, 8, 1, 1, EnumCreatureType.WATER_CREATURE, BiomeDictionary.getBiomes(BiomeDictionary.Type.OCEAN).toArray(new Biome[0])); + EntityRegistry.addSpawn(EntityFishSalmon.class, 4, 4, 4, EnumCreatureType.WATER_CREATURE, BiomeDictionary.getBiomes(BiomeDictionary.Type.OCEAN).toArray(new Biome[0])); + EntityRegistry.addSpawn(EntityFishSalmon.class, 40, 4, 4, EnumCreatureType.WATER_CREATURE, landWaterBiomes.toArray(new Biome[0])); + + SevPatchesLoadingPlugin.LOGGER.info(EntitySpawnPlacementRegistry.getPlacementForEntity(EntityFishCod.class)); + } +} diff --git a/src/main/java/tv/darkosto/sevpatches/core/SevPatchesLoadingPlugin.java b/src/main/java/tv/darkosto/sevpatches/core/SevPatchesLoadingPlugin.java index a7713cd..cd1377b 100644 --- a/src/main/java/tv/darkosto/sevpatches/core/SevPatchesLoadingPlugin.java +++ b/src/main/java/tv/darkosto/sevpatches/core/SevPatchesLoadingPlugin.java @@ -22,6 +22,12 @@ public class SevPatchesLoadingPlugin implements IFMLLoadingPlugin { public static String GET_BLOCK_STATE; + public static String ENTITY_WORLD; + public static String ENTITY_IS_NOT_COLLIDING; + public static String ENTITY_GET_CAN_SPAWN_HERE; + public static String GET_ENTITY_BOUNDING_BOX; + public static String CHECK_NO_ENTITY_COLLISION; + public SevPatchesLoadingPlugin() { LOGGER.info("setting up mixin environment"); MixinBootstrap.init(); @@ -54,6 +60,12 @@ public void injectData(Map data) { SevPatchesLoadingPlugin.SET_INT = dev ? "setInteger" : "func_74768_a"; SevPatchesLoadingPlugin.GET_BLOCK_STATE = dev ? "getBlockState" : "func_180495_p"; + + SevPatchesLoadingPlugin.ENTITY_WORLD = dev ? "world" : "field_70170_p"; + SevPatchesLoadingPlugin.ENTITY_IS_NOT_COLLIDING = dev ? "isNotColliding" : "func_70058_J"; + SevPatchesLoadingPlugin.ENTITY_GET_CAN_SPAWN_HERE = dev ? "getCanSpawnHere" : "func_70601_bi"; + SevPatchesLoadingPlugin.GET_ENTITY_BOUNDING_BOX = dev ? "getEntityBoundingBox" : "func_174813_aQ"; + SevPatchesLoadingPlugin.CHECK_NO_ENTITY_COLLISION = dev ? "checkNoEntityCollision" : "func_72917_a"; } @Override diff --git a/src/main/java/tv/darkosto/sevpatches/core/SevPatchesTransformer.java b/src/main/java/tv/darkosto/sevpatches/core/SevPatchesTransformer.java index 535a657..cc73a4c 100644 --- a/src/main/java/tv/darkosto/sevpatches/core/SevPatchesTransformer.java +++ b/src/main/java/tv/darkosto/sevpatches/core/SevPatchesTransformer.java @@ -24,6 +24,12 @@ public byte[] transform(String name, String transformedName, byte[] basicClass) return this.amuletTransform(basicClass); case "micdoodle8.mods.galacticraft.core.blocks.BlockBasic": return this.galacticraftBlockTransform(basicClass); + case "com.tmtravlr.jaff.JAFFEventHandler": + return this.unnecessaryLagRemover(basicClass); + case "com.tmtravlr.jaff.JAFFMod": + return this.fishLiveInWater(basicClass); + case "com.tmtravlr.jaff.entities.EntityFish": + return this.fishAreFish(basicClass); default: return basicClass; } @@ -48,6 +54,152 @@ private void setEventSubPriority(ClassNode input, String targetMethod, String pr } } + /** + * Make JAFF fish behave like fish + * The JAFFA patch + */ + private byte[] unnecessaryLagRemover(byte[] basicClass) { + ClassReader classReader = new ClassReader(basicClass); + + ClassNode classNode = new ClassNode(); + classReader.accept(classNode, 0); + + for (MethodNode methodNode : classNode.methods) { + if (!methodNode.name.equals("onWorldTick")) continue; + for (AnnotationNode annotationNode : methodNode.visibleAnnotations) { + if (!annotationNode.desc.equals("Lnet/minecraftforge/fml/common/eventhandler/SubscribeEvent;")) + continue; + + methodNode.visibleAnnotations.remove(annotationNode); + SevPatchesLoadingPlugin.LOGGER.info("Disabling custom spawn logic in JAFF"); + break; + } + } + + ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + classNode.accept(classWriter); + return classWriter.toByteArray(); + } + + private byte[] fishLiveInWater(byte[] basicClass) { + ClassReader classReader = new ClassReader(basicClass); + + ClassNode classNode = new ClassNode(); + classReader.accept(classNode, 0); + + MethodNode preInit = null; + MethodNode postInit = null; + + for (MethodNode methodNode : classNode.methods) { + if (methodNode.name.equals("preInit")) preInit = methodNode; + if (methodNode.name.equals("postInit")) postInit = methodNode; + } + + if (preInit == null || postInit == null) { + SevPatchesLoadingPlugin.LOGGER.warn("Failed to find JAFF init methods"); + return basicClass; + } + + InsnList callRegisterPlacement = new InsnList(); + callRegisterPlacement.add(new MethodInsnNode( + Opcodes.INVOKESTATIC, + "tv/darkosto/sevpatches/core/FishHook", + "registerPlacement", + "()V", + false + )); + + InsnNode preInitReturn = null; + for (ListIterator it = preInit.instructions.iterator(); it.hasNext(); ) { + AbstractInsnNode insnNode = it.next(); + + if (insnNode.getOpcode() == Opcodes.RETURN) preInitReturn = (InsnNode) insnNode; + } + + if (preInitReturn != null) + preInit.instructions.insertBefore(preInitReturn, callRegisterPlacement); + + InsnList callRegisterSpawns = new InsnList(); + callRegisterSpawns.add(new MethodInsnNode( + Opcodes.INVOKESTATIC, + "tv/darkosto/sevpatches/core/FishHook", + "registerSpawns", + "()V", + false + )); + + InsnNode postInitReturn = null; + for (ListIterator it = postInit.instructions.iterator(); it.hasNext(); ) { + AbstractInsnNode insnNode = it.next(); + + if (insnNode.getOpcode() == Opcodes.RETURN) postInitReturn = (InsnNode) insnNode; + } + + if (postInitReturn != null) + postInit.instructions.insertBefore(postInitReturn, callRegisterSpawns); + + ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + classNode.accept(classWriter); + return classWriter.toByteArray(); + } + + private byte[] fishAreFish(byte[] basicClass) { + SevPatchesLoadingPlugin.LOGGER.info("JAFFA patch in progress"); + + ClassReader classReader = new ClassReader(basicClass); + + ClassNode classNode = new ClassNode(); + classReader.accept(classNode, 0); + + MethodNode isFishNotColliding = new MethodNode( + Opcodes.ACC_PUBLIC, + SevPatchesLoadingPlugin.ENTITY_IS_NOT_COLLIDING, + "()Z", + null, + null + ); + InsnList fishyInsns = isFishNotColliding.instructions; + fishyInsns.add(new VarInsnNode(Opcodes.ALOAD, 0)); + fishyInsns.add(new FieldInsnNode( + Opcodes.GETFIELD, + "com/tmtravlr/jaff/entities/EntityFish", + SevPatchesLoadingPlugin.ENTITY_WORLD, + "Lnet/minecraft/world/World;" + )); + fishyInsns.add(new VarInsnNode(Opcodes.ALOAD, 0)); + fishyInsns.add(new MethodInsnNode( + Opcodes.INVOKEVIRTUAL, + "com/tmtravlr/jaff/entities/EntityFish", + SevPatchesLoadingPlugin.GET_ENTITY_BOUNDING_BOX, + "()Lnet/minecraft/util/math/AxisAlignedBB;", + false + )); + fishyInsns.add(new VarInsnNode(Opcodes.ALOAD, 0)); + fishyInsns.add(new MethodInsnNode( + Opcodes.INVOKEVIRTUAL, + "net/minecraft/world/World", + SevPatchesLoadingPlugin.CHECK_NO_ENTITY_COLLISION, + "(Lnet/minecraft/util/math/AxisAlignedBB;Lnet/minecraft/entity/Entity;)Z", + false + )); + fishyInsns.add(new InsnNode(Opcodes.IRETURN)); + classNode.methods.add(isFishNotColliding); + SevPatchesLoadingPlugin.LOGGER.info("JAFFA patch: implemented isNotColliding"); + + for (MethodNode methodNode : classNode.methods) { + if (!methodNode.name.equals(SevPatchesLoadingPlugin.ENTITY_GET_CAN_SPAWN_HERE)) continue; + InsnList replacement = new InsnList(); + replacement.add(new InsnNode(Opcodes.ICONST_1)); + replacement.add(new InsnNode(Opcodes.IRETURN)); + methodNode.instructions = replacement; + SevPatchesLoadingPlugin.LOGGER.info("JAFFA patch: getCanSpawnHere now always true"); + } + + ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + classNode.accept(classWriter); + return classWriter.toByteArray(); + } + /** * DarkPacks/SevTech-Ages#3522 */ diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index 742ec45..1514d55 100644 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -3,7 +3,7 @@ "modid": "sevpatches", "name": "SevPatches", "description": "Consolidated patches for mods that are EOL used in SevTech: Ages", - "version": "1.1", + "version": "1.3", "mcversion": "1.12.2", "url": "https://www.curseforge.com/minecraft/mc-mods/sevpatches", "updateUrl": "",