Browse Source

more hooks, command manager, interface for the command manager

Kajetan Johannes Hammerle 6 years ago
parent
commit
4318902d3a

+ 0 - 3
src/main/java/me/kcm/KajetansCoreMod.java

@@ -2,7 +2,6 @@ package me.kcm;
 
 import me.kcm.events.FarmlandTrampleEvent;
 import me.kcm.events.PlayerTabListNameEvent;
-import net.minecraft.client.Minecraft;
 import net.minecraft.util.text.TextComponentString;
 import net.minecraftforge.fml.common.Mod;
 import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
@@ -17,9 +16,7 @@ public class KajetansCoreMod
     @Mod.EventHandler
     public void preInit(FMLPreInitializationEvent e) 
     {
-        Minecraft m;
         System.out.println(NAME + " is loading!");
-        
         net.minecraftforge.common.MinecraftForge.EVENT_BUS.register(new Object()
             {
                 @net.minecraftforge.fml.common.eventhandler.SubscribeEvent

+ 205 - 29
src/main/java/me/kcm/KajetansTransformer.java

@@ -1,10 +1,8 @@
 package me.kcm;
 
-import java.util.ListIterator;
 import me.kcm.events.*;
 import net.minecraft.launchwrapper.IClassTransformer;
-import net.minecraftforge.fml.common.asm.transformers.DeobfuscationTransformer;
-import net.minecraftforge.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
+import net.minecraft.launchwrapper.Launch;
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassWriter;
 import org.objectweb.asm.Opcodes;
@@ -23,10 +21,26 @@ import org.objectweb.asm.tree.VarInsnNode;
 
 public class KajetansTransformer implements IClassTransformer
 {
-    private void printPatch(Class c)
+    private boolean obfuscated;
+    
+    public KajetansTransformer() 
+    {
+        Boolean ldev = (Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment");
+        if(ldev == null)
+        {
+            obfuscated = true;
+        }
+        else
+        {
+            obfuscated = !ldev;
+        }
+        System.out.println(obfuscated);
+    }
+    
+    private void printPatch(String s)
     {
         System.out.println("|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾");
-        System.out.println("| Patching " + c.getSimpleName());
+        System.out.println("| Patching " + s);
         System.out.println("|___________________________________________________");
     }
     
@@ -36,17 +50,23 @@ public class KajetansTransformer implements IClassTransformer
         switch(searge)
         {
             case "net.minecraft.entity.EntityLivingBase":
-                printPatch(LivingDamageCalculationEvent.class);
-                return patchDamageHook(old, bytes, !old.equals(searge));
+                printPatch(LivingDamageCalculationEvent.class.getSimpleName());
+                return patchDamageHook(old, bytes);
             case "net.minecraft.entity.player.EntityPlayerMP":
-                printPatch(PlayerTabListNameEvent.class);
-                return patchTabList(old, bytes, !old.equals(searge));
+                printPatch(PlayerTabListNameEvent.class.getSimpleName());
+                return patchTabList(old, bytes);
             case "net.minecraft.entity.Entity":
-                printPatch(FarmlandTrampleEvent.class);
-                return patchEntityCanTrample(old, bytes, !old.equals(searge));
+                printPatch(FarmlandTrampleEvent.class.getSimpleName());
+                return patchEntityCanTrample(old, bytes);
             case "net.minecraft.server.management.PlayerList":
-                printPatch(PlayerConnectionEvent.class);
-                return patchPlayerList(old, bytes, !old.equals(searge));
+                printPatch("PlayerList");
+                return patchPlayerList(old, bytes);
+            case "net.minecraft.server.MinecraftServer":
+                printPatch("MinecraftServer");
+                return patchMinecraftServer(old, bytes);
+            case "net.minecraft.network.NetHandlerPlayServer":
+                printPatch("NetHandlerPlayServer");
+                return patchNetHandlerPlayServer(old, bytes);
         }
         return bytes;
     }
@@ -58,7 +78,7 @@ public class KajetansTransformer implements IClassTransformer
         System.out.println(getString(next));
     }*/
     
-    public byte[] patchDamageHook(String c, byte[] bytes, boolean obfuscated)
+    public byte[] patchDamageHook(String c, byte[] bytes)
     {
         try
         {
@@ -106,7 +126,7 @@ public class KajetansTransformer implements IClassTransformer
         return bytes;
     }
     
-    public byte[] patchTabList(String c, byte[] bytes, boolean obfuscated)
+    public byte[] patchTabList(String c, byte[] bytes)
     {
         try
         {
@@ -151,7 +171,7 @@ public class KajetansTransformer implements IClassTransformer
         return bytes;
     }
     
-    public byte[] patchEntityCanTrample(String c, byte[] bytes, boolean obfuscated)
+    public byte[] patchEntityCanTrample(String c, byte[] bytes)
     {
         try
         {
@@ -217,7 +237,7 @@ public class KajetansTransformer implements IClassTransformer
         return bytes;
     }
     
-    public byte[] patchPlayerList(String c, byte[] bytes, boolean obfuscated)
+    public byte[] patchPlayerList(String c, byte[] bytes)
     {
         try
         {
@@ -225,6 +245,11 @@ public class KajetansTransformer implements IClassTransformer
             ClassReader classReader = new ClassReader(bytes);
             classReader.accept(classNode, 0);
             
+            int index = 0;
+            AbstractInsnNode node;
+            // -----------------------------------------------------------------
+            // first part - inserting PlayerConnectionEvent hook
+            // -----------------------------------------------------------------
             // EntityPlayerMP <--> oq
             // initializeConnectionToPlayer <--> func_72355_a <--> a
             //MD: pl/a (Lgw;Loq;)V net/minecraft/server/management/PlayerList/func_72355_a 
@@ -234,13 +259,10 @@ public class KajetansTransformer implements IClassTransformer
             // its something else: (Lgw;Loq;Lpa;)V
             // it seems forge is already overwriting this method
             MethodNode mn = classNode.methods.stream().filter(me -> me.name.equals("initializeConnectionToPlayer")).findAny().get();
-            
             InsnList ins = mn.instructions;
-            int index = 0;
-            AbstractInsnNode node;
-            // first part - inserting PlayerConnectionEvent hook
             // connection <--> field_71135_a <--> a
             // FD: oq/a net/minecraft/entity/player/EntityPlayerMP/field_71135_a
+            // searching for line: p.connection = nethandlerplayserver;
             String search = obfuscated ? "a" : "connection";
             String playerOwner = obfuscated ? "oq" : "net/minecraft/entity/player/EntityPlayerMP";
             boolean found = false;
@@ -265,20 +287,24 @@ public class KajetansTransformer implements IClassTransformer
                 return bytes;
             }
             
+            node = ins.get(index);
             if(obfuscated)
             {
-                ins.insert(ins.get(index), new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onPlayerConnection", "(Loq;)V", false));
+                ins.insert(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onPlayerConnection", "(Loq;)V", false));
             }
             else
             {
-                ins.insert(ins.get(index), new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", 
+                ins.insert(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", 
                     "onPlayerConnection", "(Lnet/minecraft/entity/player/EntityPlayerMP;)V", false));
             }
-            ins.insert(ins.get(index), new VarInsnNode(Opcodes.ALOAD, 2));
+            ins.insert(node, new VarInsnNode(Opcodes.ALOAD, 2));
+            // -----------------------------------------------------------------
             // end of first part - inserting PlayerConnectionEvent hook
             // second part - removing join message
+            // -----------------------------------------------------------------
             // func_70005_c_
             // MD: aed/h_ ()Ljava/lang/String; net/minecraft/entity/player/EntityPlayer/func_70005_c_ ()Ljava/lang/String;
+            // Searching for: if (playerIn.getName().equalsIgnoreCase(s))
             String start = "equalsIgnoreCase";
             String startDesc = "(Ljava/lang/String;)Z";
             
@@ -326,13 +352,163 @@ public class KajetansTransformer implements IClassTransformer
                 ins.remove(node);
             }
             
-            // End of second Part - Removing join message
+            // -----------------------------------------------------------------
+            // end of second part - removing join message
+            // third part - inserting PlayerPreRespawnEvent hook
+            // -----------------------------------------------------------------
+            // recreatePlayerEntity <--> func_72368_a <--> a
+            // MD: pl/a (Loq;IZ)Loq; net/minecraft/server/management/PlayerList/func_72368_a 
+            // (Lnet/minecraft/entity/player/EntityPlayerMP;IZ)Lnet/minecraft/entity/player/EntityPlayerMP;
+            if(obfuscated)
+            {
+                mn = classNode.methods.stream().filter(me -> me.name.equals("a")).filter(me -> "(Loq;IZ)Loq;".equals(me.desc)).findAny().get();
+            }
+            else
+            {
+                mn = classNode.methods.stream().filter(me -> me.name.equals("recreatePlayerEntity")).findAny().get();
+            }
+            ins = mn.instructions;
+            node = ins.get(1);
+            if(obfuscated)
+            {
+                ins.insert(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onPlayerPreRespawn", "(Loq;IZ)V", false));
+            }
+            else
+            {
+                ins.insert(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", 
+                    "onPlayerPreRespawn", "(Lnet/minecraft/entity/player/EntityPlayerMP;IZ)V", false));
+            }
+            ins.insert(node, new VarInsnNode(Opcodes.ILOAD, 3));
+            ins.insert(node, new VarInsnNode(Opcodes.ILOAD, 2));
+            ins.insert(node, new VarInsnNode(Opcodes.ALOAD, 1));
+            
+            // -----------------------------------------------------------------
+            // end of third part - inserting PlayerPreRespawnEvent hook
+            // -----------------------------------------------------------------
+            
+            ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+            classNode.accept(writer);
+            return writer.toByteArray();
+        }
+        catch(Exception ex)
+        {
+            ex.printStackTrace();
+        }       
+        return bytes;
+    }
+    
+    public byte[] patchMinecraftServer(String c, byte[] bytes)
+    {
+        try
+        {
+            ClassNode classNode = new ClassNode();
+            ClassReader classReader = new ClassReader(bytes);
+            classReader.accept(classNode, 0);
+            
+            // createCommandManager <--> func_175582_h <--> i
+            // MD: net/minecraft/server/MinecraftServer/i ()Ldh; 
+            // MD: chd/i ()Ldh; net/minecraft/server/integrated/IntegratedServer/func_175582_h
+            // ()Lnet/minecraft/command/ServerCommandManager;
+            MethodNode mn;
+            if(obfuscated)
+            {
+                mn = classNode.methods.stream().filter(me -> me.name.equals("i")).filter(me -> "()Ldh;".equals(me.desc)).findAny().get();
+            }
+            else
+            {
+                mn = classNode.methods.stream().filter(me -> me.name.equals("createCommandManager")).findAny().get();
+            }
+            
+            InsnList ins = mn.instructions;
+            
+            ins.remove(ins.get(2));
+            ins.remove(ins.get(2));
+            ins.remove(ins.get(3));
+            /*if(obfuscated)
+            {
+                ins.insert(ins.get(2), new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", 
+                    "onCreateCommandManager", "(Ldh;)Lme/kcm/command/ModServerCommandManager;", false));        
+            }
+            else
+            {*/
+                ins.insert(ins.get(2), new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", 
+                    "onCreateCommandManager", "(Lnet/minecraft/server/MinecraftServer;)Lme/kcm/command/ModServerCommandManager;", false));     
+            //}
+            
+            ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+            classNode.accept(writer);
+            return writer.toByteArray();
+        }
+        catch(Exception ex)
+        {
+            ex.printStackTrace();
+        }       
+        return bytes;
+    }
+    
+    public byte[] patchNetHandlerPlayServer(String c, byte[] bytes)
+    {
+        try
+        {
+            ClassNode classNode = new ClassNode();
+            ClassReader classReader = new ClassReader(bytes);
+            classReader.accept(classNode, 0);
+            
+            // public void onDisconnect(ITextComponent reason)
+            // onDisconnect <--> func_147231_a <--> a
+            // MD: pa/a (Lhh;)V net/minecraft/network/NetHandlerPlayServer/func_147231_a (Lnet/minecraft/util/text/ITextComponent;)V
+            MethodNode mn;
+            if(obfuscated)
+            {
+                mn = classNode.methods.stream().filter(me -> me.name.equals("a")).filter(me -> "(Lhh;)V".equals(me.desc)).findAny().get();
+            }
+            else
+            {
+                mn = classNode.methods.stream().filter(me -> me.name.equals("onDisconnect")).findAny().get();
+            }
+            
+            InsnList ins = mn.instructions;
+            
+            // VarInsnNode  25  2  0
+            // FieldInsnNode  180  4  Lnet/minecraft/server/MinecraftServer; serverController net/minecraft/network/NetHandlerPlayServer
+            // MethodInsnNode  182  5  ()Lnet/minecraft/server/management/PlayerList; false getPlayerList net/minecraft/server/MinecraftServer
+            // VarInsnNode  25  2  2
+            // MethodInsnNode  182  5  (Lnet/minecraft/util/text/ITextComponent;)V false sendMessage net/minecraft/server/management/PlayerList
+            
+            // sendMessage <--> func_148539_a <--> a
+            // MD: pl/a (Lhh;)V net/minecraft/server/management/PlayerList/func_148539_a (Lnet/minecraft/util/text/ITextComponent;)V
+            
+            int index = 0;
+            AbstractInsnNode node;
+            String search = obfuscated ? "a" : "sendMessage";
+            String owner = obfuscated ? "pl" : "net/minecraft/server/management/PlayerList";
+            boolean found = false;
+            while(index < ins.size())
+            {
+                node = ins.get(index);
+                if(node instanceof MethodInsnNode)
+                {
+                    MethodInsnNode field = (MethodInsnNode) node;
+                    if(field.name.equals(search) && field.owner.equals(owner))
+                    {
+                        found = true;
+                        break;
+                    }
+                }
+                index++;
+            }
+            
+            if(!found)
+            {
+                System.out.println("Start of sendMessage was not found");
+                return bytes;
+            }
+            
+            index -= 4;
             
-            ListIterator<AbstractInsnNode> list = ins.iterator();
-            while(list.hasNext())
+            for(int i = 0; i < 5; i++)
             {
-                AbstractInsnNode next = list.next();
-                System.out.println(getString(next));
+                ins.remove(ins.get(index));
             }
             
             ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);

+ 40 - 0
src/main/java/me/kcm/command/ICommandManager.java

@@ -0,0 +1,40 @@
+package me.kcm.command;
+
+import net.minecraft.command.ICommand;
+import net.minecraft.command.ICommandSender;
+
+public interface ICommandManager 
+{
+    /** 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
+     * @return true to allow the command, false to call {@link ICommandManager#printMissingPermission(ICommandSender, ICommand)}
+     */
+    public boolean hasPermission(ICommandSender cs, String perm);
+
+    /** This method is called, if there is no vanilla command available for the
+     * given name
+     * 
+     * @param cs the sender of the command
+     * @param command the command
+     * @param args arguments of the command, splitted at spaces
+     * @return true on success, false to call {@link ICommandManager#printMissingCommand(ICommandSender, String)}
+     */
+    public boolean executeCustomCommand(ICommandSender cs, String command, String[] args);
+    
+    /** Called if there is no normal or custom command available
+     *
+     * @param cs the sender of the command
+     * @param command the command
+     */
+    public void printMissingCommand(ICommandSender cs, String command);
+    
+    /** Called if there is a normal command available but {@link ICommandManager#hasPermission(ICommandSender, String)}
+     * returned false
+     * 
+     * @param cs the sender of the command
+     * @param command the command
+     */
+    public void printMissingPermission(ICommandSender cs, ICommand command);
+}

+ 157 - 0
src/main/java/me/kcm/command/ModServerCommandManager.java

@@ -0,0 +1,157 @@
+package me.kcm.command;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import net.minecraft.command.CommandBase;
+import net.minecraft.command.ICommand;
+import net.minecraft.command.ICommandSender;
+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;
+
+public class ModServerCommandManager extends ServerCommandManager
+{
+    private ICommandManager manager;
+    
+    public ModServerCommandManager(MinecraftServer serverIn) 
+    {
+        super(serverIn);
+        this.manager = new ICommandManager() 
+        {
+            @Override
+            public boolean hasPermission(ICommandSender cs, String perm) 
+            {
+                return true;
+            }
+
+            @Override
+            public boolean executeCustomCommand(ICommandSender cs, String command, String[] args) 
+            {
+                return false;
+            }
+
+            @Override
+            public void printMissingCommand(ICommandSender cs, String command) 
+            {
+            }
+
+            @Override
+            public void printMissingPermission(ICommandSender cs, ICommand command) 
+            {
+            }
+        };
+    }
+    
+    public void setPermissionManager(ICommandManager manager)
+    {
+        if(manager == null)
+        {
+            throw new NullPointerException();
+        }
+        this.manager = manager;
+    }
+    
+    @Override
+    public void notifyListener(ICommandSender cs, ICommand command, int flags, String key, Object... args)
+    {
+        ITextComponent text = new TextComponentTranslation("chat.type.admin", new Object[] {cs.getName(), new TextComponentTranslation(key, args)});
+        text.getStyle().setColor(TextFormatting.GRAY);
+        text.getStyle().setItalic(true);
+        cs.sendMessage(text);
+    }
+    
+    private String[] dropFirstString(String[] input)
+    {
+        String[] astring = new String[input.length - 1];
+        System.arraycopy(input, 1, astring, 0, input.length - 1);
+        return astring;
+    }
+    
+    @Override
+    public int executeCommand(ICommandSender sender, String rawCommand)
+    {
+        rawCommand = rawCommand.trim();
+        if(rawCommand.startsWith("/"))
+        {
+            rawCommand = rawCommand.substring(1);
+        }
+
+        String[] args = rawCommand.split(" ");
+        String command = args[0];
+        args = dropFirstString(args);
+        
+        ICommand icommand = this.getCommands().get(command);
+        if(icommand == null)
+        {
+            if(manager.executeCustomCommand(sender, command, args))
+            {
+                return 1;
+            }
+            manager.printMissingCommand(sender, command);
+            return 0;
+        }
+        
+        if(manager.hasPermission(sender, icommand.getName()))
+        {
+            CommandEvent e = new CommandEvent(icommand, sender, args);
+            if(net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(e))
+            {
+                if(e.getException() != null)
+                {
+                    com.google.common.base.Throwables.propagateIfPossible(e.getException());
+                }
+                return 1;
+            }
+            if(e.getParameters() != null)
+            {
+                args = e.getParameters();
+            }
+            this.tryExecute(sender, args, icommand, rawCommand);
+        }
+        else
+        {
+            manager.printMissingPermission(sender, icommand);
+        }
+        return 1;
+    }
+
+    @Override
+    public List<String> getTabCompletions(ICommandSender sender, String input, @Nullable BlockPos pos)
+    {
+        String[] astring = input.split(" ", -1);
+        String command = astring[0];
+        if(astring.length == 1)
+        {
+            return this.getCommands().entrySet().stream()
+                    .filter(e -> (CommandBase.doesStringStartWith(command, e.getKey()) && 
+                            manager.hasPermission(sender, e.getKey())))
+                    .map(e -> e.getKey())
+                    .collect(Collectors.toList());
+        }
+        else if(astring.length > 1)
+        {
+            ICommand icommand = this.getCommands().get(command);
+            if(icommand != null && manager.hasPermission(sender, icommand.getName()))
+            {
+                return icommand.getTabCompletions(this.getServer(), sender, dropFirstString(astring), pos);
+            }
+        }
+        return Collections.<String>emptyList();
+    }
+
+    @Override
+    public List<ICommand> getPossibleCommands(ICommandSender sender)
+    {
+        return this.getCommands().entrySet().stream()
+                    .filter(e -> manager.hasPermission(sender, e.getKey()))
+                    .map(e -> e.getValue())
+                    .collect(Collectors.toList());
+    }
+}

+ 13 - 1
src/main/java/me/kcm/events/Hooks.java

@@ -1,9 +1,12 @@
 package me.kcm.events;
 
+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.EntityPlayerMP;
+import net.minecraft.server.MinecraftServer;
 import net.minecraft.util.DamageSource;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.util.text.ITextComponent;
@@ -34,7 +37,16 @@ public class Hooks
     
     public static void onPlayerConnection(EntityPlayerMP p)
     {
-        System.out.println("CONNECT " + p.getName());
         MinecraftForge.EVENT_BUS.post(new PlayerConnectionEvent(p));
     }
+    
+    public static void onPlayerPreRespawn(EntityPlayerMP p, int dimension, boolean conqueredEnd)
+    {
+        MinecraftForge.EVENT_BUS.post(new PlayerPreRespawnEvent(p));
+    }
+    
+    public static ModServerCommandManager onCreateCommandManager(MinecraftServer server)
+    {
+        return new ModServerCommandManager(server);
+    }
 }

+ 12 - 0
src/main/java/me/kcm/events/PlayerPreRespawnEvent.java

@@ -0,0 +1,12 @@
+package me.kcm.events;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraftforge.event.entity.player.PlayerEvent;
+
+public class PlayerPreRespawnEvent extends PlayerEvent
+{
+    public PlayerPreRespawnEvent(EntityPlayer player) 
+    {
+        super(player);
+    }
+}