Browse Source

added recipe chosen event, recipes can be changed, cancelable

Kajetan Johannes Hammerle 6 years ago
parent
commit
60dd71b506

+ 2 - 2
build.gradle

@@ -20,7 +20,7 @@ compileJava {
 }
 
 minecraft {
-    version = "1.12.1-14.22.0.2475"
+    version = "1.12.2-14.23.0.2537"
     runDir = "run"
     
     // the mappings can be changed at any time, and must be in the following format.
@@ -28,7 +28,7 @@ minecraft {
     // stable_#            stables are built at the discretion of the MCP team.
     // Use non-default mappings at your own risk. they may not always work.
     // simply re-run your setup task after changing the mappings to update your workspace.
-    mappings = "snapshot_20170624"
+    mappings = "snapshot_20171003"
     // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
 }
 

+ 2 - 5
src/main/java/me/kcm/KajetansCoreMod.java

@@ -1,8 +1,5 @@
 package me.kcm;
 
-import me.kcm.events.FarmlandTrampleEvent;
-import me.kcm.events.PlayerTabListNameEvent;
-import net.minecraft.util.text.TextComponentString;
 import net.minecraftforge.fml.common.Mod;
 import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
 
@@ -17,7 +14,7 @@ public class KajetansCoreMod
     public void preInit(FMLPreInitializationEvent e) 
     {
         System.out.println(NAME + " is loading!");
-        net.minecraftforge.common.MinecraftForge.EVENT_BUS.register(new Object()
+        /*net.minecraftforge.common.MinecraftForge.EVENT_BUS.register(new Object()
             {
                 @net.minecraftforge.fml.common.eventhandler.SubscribeEvent
                 public void wusi2(FarmlandTrampleEvent e) 
@@ -31,6 +28,6 @@ public class KajetansCoreMod
                 {
                     e.setName(new TextComponentString("§6[]§r" + e.getEntityPlayer().getName()));
                 }
-            });
+            });*/
     }
 }

+ 55 - 2
src/main/java/me/kcm/KajetansTransformer.java

@@ -1,5 +1,6 @@
 package me.kcm;
 
+import java.util.ListIterator;
 import me.kcm.events.*;
 import net.minecraft.launchwrapper.IClassTransformer;
 import net.minecraft.launchwrapper.Launch;
@@ -49,9 +50,9 @@ public class KajetansTransformer implements IClassTransformer
     {
         switch(searge)
         {
-            case "net.minecraft.entity.EntityLivingBase":
+            /*case "net.minecraft.entity.EntityLivingBase":
                 printPatch(LivingDamageCalculationEvent.class.getSimpleName());
-                return patchDamageHook(old, bytes);
+                return patchDamageHook(old, bytes);*/
             case "net.minecraft.entity.player.EntityPlayerMP":
                 printPatch(PlayerTabListNameEvent.class.getSimpleName());
                 return patchTabList(old, bytes);
@@ -67,6 +68,9 @@ public class KajetansTransformer implements IClassTransformer
             case "net.minecraft.network.NetHandlerPlayServer":
                 printPatch("NetHandlerPlayServer");
                 return patchNetHandlerPlayServer(old, bytes);
+            case "net.minecraft.item.crafting.CraftingManager":
+                printPatch("CraftingManager");
+                return patchCraftingManager(old, bytes);
         }
         return bytes;
     }
@@ -522,6 +526,55 @@ public class KajetansTransformer implements IClassTransformer
         return bytes;
     }
     
+    public byte[] patchCraftingManager(String c, byte[] bytes)
+    {
+        try
+        {
+            ClassNode classNode = new ClassNode();
+            ClassReader classReader = new ClassReader(bytes);
+            classReader.accept(classNode, 0);
+            
+            MethodNode mn;
+            if(obfuscated)
+            {
+                mn = classNode.methods.stream().filter(me -> me.name.equals("b")).filter(me -> "(Lafy;Lamu;)Lakt;".equals(me.desc)).findAny().get();
+            }
+            else
+            {
+                mn = classNode.methods.stream().filter(me -> me.name.equals("findMatchingRecipe")).findAny().get();
+            }
+            
+            InsnList ins = mn.instructions;
+
+            // sendMessage <--> func_148539_a <--> a
+            // MD: aku/b (Lafy;Lamu;)Lakt; net/minecraft/item/crafting/CraftingManager/func_192413_b
+            // (Lnet/minecraft/inventory/InventoryCrafting;Lnet/minecraft/world/World;)Lnet/minecraft/item/crafting/IRecipe;
+            
+            AbstractInsnNode node = ins.get(1);
+            ins.insert(node, new InsnNode(Opcodes.ARETURN));
+            if(obfuscated)
+            {
+                ins.insert(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onfindMatchingRecipe", "(Lafy;Lamu;)Lakt;", false));
+            }
+            else
+            {
+                ins.insert(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onfindMatchingRecipe", 
+                    "(Lnet/minecraft/inventory/InventoryCrafting;Lnet/minecraft/world/World;)Lnet/minecraft/item/crafting/IRecipe;", false));
+            }
+            ins.insert(node, new VarInsnNode(Opcodes.ALOAD, 1));
+            ins.insert(node, new VarInsnNode(Opcodes.ALOAD, 0));
+            
+            ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+            classNode.accept(writer);
+            return writer.toByteArray();
+        }
+        catch(Exception ex)
+        {
+            ex.printStackTrace();
+        }       
+        return bytes;
+    }
+    
     public String getString(AbstractInsnNode node)
     {
         StringBuilder sb = new StringBuilder();

+ 2 - 2
src/main/java/me/kcm/command/ICommandManager.java

@@ -5,7 +5,7 @@ import net.minecraft.command.ICommandSender;
 
 public interface ICommandManager 
 {
-    /** Called to check if a command sender should be allowed to use a normal command
+    /** Called to check, if a command sender should be allowed to use a normal command
      *
      * @param cs the sender of the command
      * @param perm the name of the permission, for commands this is their name
@@ -23,7 +23,7 @@ public interface ICommandManager
      */
     public boolean executeCustomCommand(ICommandSender cs, String command, String[] args);
     
-    /** Called if there is no normal or custom command available
+    /** Called, if there is no normal or custom command available
      *
      * @param cs the sender of the command
      * @param command the command

+ 2 - 1
src/main/java/me/kcm/command/ModServerCommandManager.java

@@ -11,7 +11,6 @@ import net.minecraft.command.ServerCommandManager;
 import net.minecraft.server.MinecraftServer;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.util.text.ITextComponent;
-import net.minecraft.util.text.TextComponentString;
 import net.minecraft.util.text.TextComponentTranslation;
 import net.minecraft.util.text.TextFormatting;
 import net.minecraftforge.event.CommandEvent;
@@ -23,6 +22,7 @@ public class ModServerCommandManager extends ServerCommandManager
     public ModServerCommandManager(MinecraftServer serverIn) 
     {
         super(serverIn);
+        // the manager must not be null
         this.manager = new ICommandManager() 
         {
             @Override
@@ -51,6 +51,7 @@ public class ModServerCommandManager extends ServerCommandManager
     
     public void setPermissionManager(ICommandManager manager)
     {
+        // the manager must not be null
         if(manager == null)
         {
             throw new NullPointerException();

+ 97 - 5
src/main/java/me/kcm/events/Hooks.java

@@ -1,27 +1,36 @@
 package me.kcm.events;
 
+import java.lang.reflect.Field;
+import java.util.Map;
+import java.util.UUID;
 import me.kcm.command.ModServerCommandManager;
 import net.minecraft.block.Block;
-import net.minecraft.command.ServerCommandManager;
 import net.minecraft.entity.Entity;
-import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.inventory.Container;
+import net.minecraft.inventory.ContainerPlayer;
+import net.minecraft.inventory.ContainerWorkbench;
+import net.minecraft.inventory.InventoryCrafting;
+import net.minecraft.item.crafting.CraftingManager;
+import net.minecraft.item.crafting.IRecipe;
 import net.minecraft.server.MinecraftServer;
-import net.minecraft.util.DamageSource;
+import net.minecraft.server.management.PlayerList;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.util.text.ITextComponent;
 import net.minecraft.world.World;
 import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.fml.relauncher.ReflectionHelper;
 
 public class Hooks 
 {
-    public static void onDamageCalculation(EntityLivingBase liv, DamageSource ds, float damage)
+    /*public static void onDamageCalculation(EntityLivingBase liv, DamageSource ds, float damage)
     {
         if(!liv.isEntityInvulnerable(ds))
         {
             MinecraftForge.EVENT_BUS.post(new LivingDamageCalculationEvent(liv, ds, damage));
         }
-    }
+    }*/
     
     public static ITextComponent onGetTabListDisplayName(EntityPlayerMP p)
     {
@@ -49,4 +58,87 @@ public class Hooks
     {
         return new ModServerCommandManager(server);
     }
+    
+    public static IRecipe onfindMatchingRecipe(InventoryCrafting craftMatrix, World w)
+    {
+        for(IRecipe irecipe : CraftingManager.REGISTRY)
+        {
+            if(irecipe.matches(craftMatrix, w))
+            {
+                Container c = getContainerEventHandler(craftMatrix);
+                EntityPlayer p;
+                if(c == null)
+                {
+                    return null;
+                }
+                else if(c instanceof ContainerPlayer)
+                {
+                    p = getContainerPlayer((ContainerPlayer) c);
+                }
+                else if(c instanceof ContainerWorkbench)
+                {
+                    p = getContainerEventHandler((ContainerWorkbench) c);
+                }
+                else
+                {
+                    return null;
+                }
+                System.out.println(p);
+                RecipeChosenEvent e = new RecipeChosenEvent(p, irecipe);
+                if(MinecraftForge.EVENT_BUS.post(e))
+                {
+                    return null;
+                }
+                return e.getRecipe();
+            }
+        }
+        return null;
+    }
+    
+    private static Field getField(Class c, String... field)
+    {
+        try
+        {
+            return ReflectionHelper.findField(c, field);
+        }
+        catch(SecurityException | ReflectionHelper.UnableToFindFieldException ex)
+        {
+            System.out.println(String.join(", ", field) + " - " + ex);
+        }
+        return null;
+    }
+    
+    private static <T> T getFieldValue(Class<T> cast, Object o, Field f)
+    {
+        try
+        {
+            return (T) f.get(o);
+        }
+        catch(SecurityException | IllegalAccessException | IllegalArgumentException ex)
+        {
+            System.out.println(f + " - " + ex);
+            return null;
+        }
+    }
+    
+    private final static Field CONTAINER_EVENT_HANDLER = getField(InventoryCrafting.class, "field_70465_c", "eventHandler");
+    
+    public static Container getContainerEventHandler(InventoryCrafting craftMatrix)
+    {
+        return getFieldValue(Container.class, craftMatrix, CONTAINER_EVENT_HANDLER);
+    }
+    
+    private final static Field CONTAINER_PLAYER = getField(ContainerPlayer.class, "field_82862_h", "player");
+    
+    public static EntityPlayer getContainerPlayer(ContainerPlayer cp)
+    {
+        return getFieldValue(EntityPlayer.class, cp, CONTAINER_PLAYER);
+    }
+    
+    private final static Field CONTAINER_WORKBENCH = getField(ContainerWorkbench.class, "field_192390_i", "player");
+    
+    public static EntityPlayer getContainerEventHandler(ContainerWorkbench cw)
+    {
+        return getFieldValue(EntityPlayer.class, cw, CONTAINER_WORKBENCH);
+    }
 }

+ 28 - 0
src/main/java/me/kcm/events/RecipeChosenEvent.java

@@ -0,0 +1,28 @@
+package me.kcm.events;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.crafting.IRecipe;
+import net.minecraftforge.event.entity.player.PlayerEvent;
+import net.minecraftforge.fml.common.eventhandler.Cancelable;
+
+@Cancelable
+public class RecipeChosenEvent extends PlayerEvent
+{
+    private IRecipe recipe;
+    
+    public RecipeChosenEvent(EntityPlayer player, IRecipe recipe) 
+    {
+        super(player);
+        this.recipe = recipe;
+    }
+
+    public void setRecipe(IRecipe recipe) 
+    {
+        this.recipe = recipe;
+    }
+    
+    public IRecipe getRecipe() 
+    {
+        return recipe;
+    }
+}