Filled SleepBuffs, Add a reason for sleeping

Discussion in 'Plugin Requests' started by Dark_Serpent, Aug 2, 2016.

Thread Status:
Not open for further replies.
  1. Offline

    I Al Istannen

    @frej4189
    Change it to return String. Then move the cursor over the warning in the return statement (the first) and left click. Then press CTRL+F1 for a quick fix and let it change the return type. Maybe you can select that option even earlier, but I think that should work.
     
  2. Offline

    frej4189

    @I Al Istannen
    That changes it to Optional<?>, meaning that it is not a valid parameter for instantiate
     
  3. Offline

    I Al Istannen

    @frej4189
    My IntelliJ is quite pleased about "Optional<Constructor>". What does yours say?
     
  4. Offline

    frej4189

  5. Offline

    I Al Istannen

    @frej4189
    Then remove the whole Optional and just let it return "Constructor" or maybe "Constructor<?>" or "Constructor<Object>". And replace the Optional.empty() with null.
     
  6. Offline

    frej4189

    @I Al Istannen
    That seems to work, and just quickly, when and how am I supposed to add the parameters (EntityHuman arg0, BlockPosition arg1), in instantiate or getConstructor?
     
  7. Offline

    I Al Istannen

    @frej4189
    In getConstructor you need to get the constructor for the class. Each constructor (and method) is defined by it's parameters. So you need to pass the CLASSES of the parameters to the getConstructor method.

    To invoke it, you need to pass in the objects as the parameter, as you normally would.
     
  8. Offline

    frej4189

    @I Al Istannen
    So I do getNMSClass("EntityHuman") and getNMSClass("BlockPosition"), right?
     
  9. Offline

    I Al Istannen

    @frej4189
    Correct :)
    And then you have the joy of getting the EntityHuman (class getHandle on a Player object)
    and creating a BlockPosition.
     
  10. Offline

    frej4189

    @I Al Istannen
    Great :)
    I might be able to release the first version today :D
     
  11. Offline

    I Al Istannen

    @frej4189
    Good luck! I am looking forward to it :)

    Just shout if you have any problems!
     
  12. Offline

    frej4189

    @I Al Istannen
    More problems :(

    How can I get the playerConnection field?
    getField(getNMSClass("EntityHuman"), "playerConnection") wouldn't do the job right?
     
  13. Offline

    I Al Istannen

    @frej4189
    Not quite. I have a method for that somewhere. Wrote it now without testing. Should work though.
    Code:java
    1. /**
    2.   * Gets the value of an instance field
    3.   *
    4.   * @param handle The handle to invoke it on
    5.   * @param name The name of the field
    6.   *
    7.   * @return The value
    8.   */
    9. public static Object getInstanceField(Object handle, String name) {
    10. // get the class
    11. Class<?> clazz = handle.getClass();
    12. // get the field with the given name
    13. Optional<Field> fieldOptional = getField(clazz, name);
    14. if (!fieldOptional.isPresent()) {
    15. return null;
    16. }
    17. // now we have the field
    18. Field field = fieldOptional.get();
    19. // it's a private one? Too bad...
    20. if (!field.isAccessible()) {
    21. // I don't care about that. Let me edit/retrieve it.
    22. field.setAccessible(true);
    23. }
    24. try {
    25. // return the value of the field "field" for the Object "handle". Is the same as writing "handle.field", just
    26. // the reflection version.
    27. return field.get(handle);
    28. } catch (IllegalAccessException e) {
    29. e.printStackTrace();
    30. }
    31. return null;
    32. }
     
  14. Offline

    frej4189

    @I Al Istannen
    Soo how do I get a method from an Object, getMethod takes a Class, not an object.

    Sorry for so many questions, but I really suck at reflecting :p
     
  15. Offline

    I Al Istannen

    @frej4189
    Well, I should probably explain reflection a bit more. Or you read the link again :p

    Reflection only operates on classes. Every reflection method is defined in the "Class" class. You can only get the method of a class. To do so you use the getMethod or getDeclaredMethod methods. GetDeclaredXXX contains private ones as well, but NO inherited ones. Only ones declared directly in this class.

    So you get the method from a class, lets use Player. Now you have a "Method" object. THIS can be invoked for an object, using the "Method#invoke" method. I have encapsulated most of that somewhat in the ReflectionUtil. There the method is called "invokeMethod".

    My current reflection util. Many parts sloppy done. (open)

    Code:
    package me.ialistannen.mystlinkingbooks.util;
    
    import me.ialistannen.mystlinkingbooks.MystLinkingBooks;
    import org.bukkit.Bukkit;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.Optional;
    import java.util.logging.Level;
    
    /**
    * A small help with reflection
    */
    public class ReflectionUtil {
    
        private static final String SERVER_VERSION;
    
        static {
            String name = Bukkit.getServer().getClass().getName();
            name = name.substring(name.indexOf("craftbukkit.") + "craftbukkit.".length());
            name = name.substring(0, name.indexOf("."));
    
            SERVER_VERSION = name;
        }
    
        /**
         * Returns the NMS class.
         *
         * @param name The name of the class
         *
         * @return The NMS class or null if an error occurred
         */
        public static Class<?> getNMSClass(String name) {
            try {
                return Class.forName("net.minecraft.server." + SERVER_VERSION + "." + name);
            } catch (ClassNotFoundException e) {
                return null;
            }
        }
    
        /**
         * Returns the CraftBukkit class.
         *
         * @param name The name of the class
         *
         * @return The CraftBukkit class or null if an error occurred
         */
    
        public static Class<?> getCraftbukkitClass(String name, String packageName) {
            try {
                return Class.forName("org.bukkit.craftbukkit." + SERVER_VERSION + "." + packageName + "." + name);
            } catch (ClassNotFoundException e) {
                return null;
            }
        }
    
        /**
         * Invokes the method
         *
         * @param handle           The handle to invoke it on
         * @param methodName       The name of the method
         * @param parameterClasses The parameter types
         * @param args             The arguments
         *
         * @return The resulting object or null if an error occurred / the method didn't return a thing
         */
        public static Object invokeMethod(Object handle, String methodName, Class[] parameterClasses, Object... args) {
            return invokeMethod(handle.getClass(), handle, methodName, parameterClasses, args);
        }
    
        /**
         * Invokes the method
         *
         * @param clazz            The class to invoke it from
         * @param handle           The handle to invoke it on
         * @param methodName       The name of the method
         * @param parameterClasses The parameter types
         * @param args             The arguments
         *
         * @return The resulting object or null if an error occurred / the method didn't return a thing
         */
        public static Object invokeMethod(Class<?> clazz, Object handle, String methodName, Class[] parameterClasses,
                                          Object... args) {
            Optional<Method> methodOptional = getMethod(clazz, methodName, parameterClasses);
    
            if (!methodOptional.isPresent()) {
                return null;
            }
    
            Method method = methodOptional.get();
    
            try {
                return method.invoke(handle, args);
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * Sets the value of an instance field
         *
         * @param handle The handle to invoke it on
         * @param name   The name of the field
         * @param value  The new value of the field
         */
        @SuppressWarnings("SameParameterValue")
        public static void setInstanceField(Object handle, String name, Object value) {
            Class<?> clazz = handle.getClass();
            Optional<Field> fieldOptional = getField(clazz, name);
            if (!fieldOptional.isPresent()) {
                return;
            }
    
            Field field = fieldOptional.get();
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            try {
                field.set(handle, value);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * Gets the value of an instance field
         *
         * @param handle The handle to invoke it on
         * @param name   The name of the field
         *
         * @return The value
         */
        public static Object getInstanceField(Object handle, String name) {
            Class<?> clazz = handle.getClass();
            Optional<Field> fieldOptional = getField(clazz, name);
            if (!fieldOptional.isPresent()) {
                return null;
            }
    
            Field field = fieldOptional.get();
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            try {
                return field.get(handle);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * Returns the constructor
         *
         * @param clazz  The class
         * @param params The Constructor parameters
         *
         * @return The Constructor or an empty Optional if there is none with these parameters
         */
        public static Optional<Constructor> getConstructor(Class<?> clazz, Class<?>... params) {
            try {
                return Optional.of(clazz.getConstructor(params));
            } catch (NoSuchMethodException e) {
                return Optional.empty();
            }
        }
    
        /**
         * Instantiates the class. Will print the errors it gets
         *
         * @param constructor The constructor
         * @param arguments   The initial arguments
         *
         * @return The resulting object, or null if an error occurred.
         */
        public static Object instantiate(Constructor<?> constructor, Object... arguments) {
            try {
                return constructor.newInstance(arguments);
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
                MystLinkingBooks.getInstance().getLogger()
                        .log(Level.WARNING, "Can't instantiate class " + constructor.getDeclaringClass(), e);
            }
            return null;
        }
    
        private static Optional<Method> getMethod(Class<?> clazz, String name, Class<?>... params) {
            try {
                return Optional.of(clazz.getMethod(name, params));
            } catch (NoSuchMethodException ignored) {
            }
    
            try {
                return Optional.of(clazz.getDeclaredMethod(name, params));
            } catch (NoSuchMethodException ignored) {
            }
    
            return Optional.empty();
        }
    
        private static Optional<Field> getField(Class<?> clazz, String name) {
            try {
                return Optional.of(clazz.getField(name));
            } catch (NoSuchFieldException ignored) {
            }
    
            try {
                return Optional.of(clazz.getDeclaredField(name));
            } catch (NoSuchFieldException ignored) {
            }
    
            return Optional.empty();
        }
    
    }
    


    I mean this one:
    "public static Object invokeMethod(Object handle, String methodName, Class[] parameterClasses, Object... args) {"
    You pass it the object you want to execute the method on, then the method name and then the classes of the parameters. Finally the parameters itself.

    Let's say you want to call getLocation() on a player "player". You use these parameters:
    (player, "getLocation", new Class[0])

    If you want to invoke setDisplayName, use:
    (player, "setDisplayName", new Class[] {String.class}, newName)

    The Reflection trail, linked somehwere before (or google it), explains that more in-depth. It is really worth a read.
     
  16. Offline

    frej4189

    @I Al Istannen
    Pretty much had the plugin finished but then I realised that PacketPlayOutAnimation doesn't work with reflection, I'm pretty sure I've done something wrong, but I can't see what it should be.

    Code:
           Class<?> packetClass = ReflectionUtils.getNMSClass("PacketPlayOutAnimation");
           Constructor<?> constructor = ReflectionUtils.getConstructor(packetClass, ReflectionUtils.getNMSClass("Entity"), int.class);
           Object packet = ReflectionUtils.instantiate(constructor, nmsPlayer, 2);
        
           for(Player p : Bukkit.getOnlinePlayers()) {
             Object nmsP = ReflectionUtils.invokeMethod(p, "getHandle", new Class[0], new Object[0]);
             Object playerConnection = ReflectionUtils.getInstanceField(nmsP, "playerConnection");
             ReflectionUtils.invokeMethod(playerConnection, "sendPacket", new Class[] {ReflectionUtils.getNMSClass("Packet")}, packet);
           }
    
    The problem is that it kicks the player out of bed, but then he just stays there, can only move client side and the server still thinks he's in bed.
     
    Last edited: Aug 22, 2016
  17. Offline

    I Al Istannen

    @frej4189
    How do you do it without reflection? And are you sure it works differently without reflection?
     
  18. Offline

    frej4189

    @I Al Istannen
    This is how I do it without reflection:
    Code:
    PacketPlayOutAnimation aniPacket = new PacketPlayOutAnimation(((CraftPlayer)this.p).getHandle(), 2);
      for (Player p : Bukkit.getOnlinePlayers()) {
      ((CraftPlayer)p).getHandle().playerConnection.sendPacket(aniPacket);
      }
    And I just tested it, works perfectly without reflection.
     
    Last edited: Aug 22, 2016
  19. Offline

    I Al Istannen

    @frej4189
    You can leave that out:
    ", new Object[0]". Varargs are cool :p

    But reflection either works the same as the normal way or throws some error. If it doesn't, it means everything went according to plan.

    Did you really only change that bit of code?
     
  20. Offline

    frej4189

    @I Al Istannen
    Yes, I only changed that bit of code, and also I just found out that all animations are send, but not the movement.

    Just did some debugging with PlayerMoveEvent, showing that even if the player stands still, the server is convinced that he moves from X to X+1 to X constantly.
     
    Last edited: Aug 22, 2016
  21. Offline

    I Al Istannen

    @frej4189
    Then I sadly don't know. Reflection shouldn't change the outcome of a method :confused:
     
  22. Offline

    frej4189

    @I Al Istannen
    It just came to my mind that I forgot to cancel the event after putting the player to bed, this would probably mean that if it's night, the player will be put to bed as normal aswell.
    I can't change it before later today though, but I hope that that's the error, so that the plugin is done.
     
  23. Offline

    I Al Istannen

  24. Offline

    frej4189

  25. Offline

    I Al Istannen

    @frej4189
    Nice :)

    I would suggest that we somehow merge your version and mine. I believe some things were solved a bit nicer in my plugin. May be subjective though.

    I would propose using my:
    • DurationParser (it is much more flexible and allows for mixtures like "1h 20m 20s" or spaces. Just catch the RuntimeException for an explanation of the error, should any occur.)
    • RewardParser (it checks for a few more user errors. You can quite easily add the %REALNAME% too, if you want.)
    • And the structure. With the way I built it, additions and changes should be quite easy to implement, as every action is (hopefully) defined in their own part, speeking through some kind of API.
    To the things in your plugin:
    • Which IDE are you using?
    • Make the Utils class abstract, if you never instantiate it and only have it as a base class
      • "static Main main;"
        First, this should probably be private. Second, if you pass it through the constructor, why is it static. Doesn't make much sense to me.
    • The KeepAsleep class. Every variable should be private. Always reduce the visibility as much as possible.
    • Same for SleepListener
      • What is this: "(block.getData() & 0x8) == 0x8"?
        This might be the stranges decompiler artifact I have seen. If not:
        Either delete that and make it easier to understand (What does it even do??) or comment what it does. I can't see comments, so I don't know if you did.
      • Same for this:
        "switch (block.getData()) {" and the following
      • "while (Bukkit.getScheduler().isCurrentlyRunning(task.getTaskId())) {}"
        What? Why??
    • Main class
      • These variables should not be public. Make getters, but do not make them public. Encapsulate and define way to communicate (and if it is just a getter), so that later changes can be done without blowing everything up.
    • ReflectionUtils
      • No real need to make it an instance. Why do you do this, if you only access static methods?
    • Cooldown:
      • Why is the cooldown map static? And why is it defined as HashMap? Always try to code against an interface or abstract class, so that all methods you need are barely defined. This allows you to easily switch the implementation (e.g. to a LinkedHashMap) without needing to change anything else.
    • ConfigUtils
      • All fields private.
      • Just out of curiosity, but do you need this class? Seems to be as difficult as directly accessing the config.
      • "logger.info("Fatal: "..)". There is a level for it. Use .log(Level.SEVERE, "message", optional exception)
    • There might be more I haven't seen.
     
  26. Offline

    frej4189

    @I Al Istannen
    Here's some quick answers and explanations for your questions and suggestions.

    First off, implementing your methods would be no problem, and I'll probably do so tomorrow.
    To your questions about my plugin:

    I'm using Eclipse Neon
    Making my Utils class abstract is not something I though about, but I'll make sure to do so.
    Making the variables private in KeepAsleep is something I'll do aswell, I didn't really think about that either.
    Since I didn't think about the fact that someone was gonna read it, and since I know what it does myself, I didn't comment it, here's what it means:
    (block.getData() & 0x8) == 8 returns whether the block is the head of the bed or not.
    I'm using switch(block.getData()) because 1-4 tells me which direction the bed is facing.
    My while loop was used for debugging, I just forgot to remove it again.
    I will make getters and setters for the Main class variables
    I made ReflectionUtils an instance because I didn't think I was gonna be coding against static methods.
    Because CooldownUtils wasn't that advanced, I didn't think it would be necesarry to do that much about it.
    I'm used to my ConfigUtils, so I really don't want to get rid of it, and logging with the SEVERE Level is something I'm gonna be adding too.

    I wrote this from my iPad, so if anything is hard to read, it's most likely because of autocorrect, sorry about that.
     
  27. Offline

    I Al Istannen

    @frej4189
    You are writing the comments for yourself :p In less than half a year you have clue what that means. Trust me, I've been there xD

    I think the Bukkit way of doing this is using these methods:
    Bed#isHeadOfBed() and Bed#getFacing().

    I was asking about the IDE, as IntelliJ (maybe some others too) would be already screaming at you for having too high access levels. The code inspection is really nice. I think "FindBugs" does something similiar with Eclipse, or there is a setting for it. Don't know right now.

    Good luck with the rest ;)
    Glad you took it as the advice it was meant to be and not an offence :)

    Have a nice day!
     
  28. Offline

    frej4189

    @I Al Istannen
    I don't really like using those methods, I honestly don't know why.
    But overall, they just return exactly what I am doing, so it doesn't really matter that much, I guess that's just personal preference, I'll try out FindBugs and see if it helps, thanks for the advice.

    And also, I just realised that having main to static is required as we're trying to make a static reference to it.

    And DurationParser does not have the field val$input, but you're using it either way.
     
    Last edited: Aug 24, 2016
Thread Status:
Not open for further replies.

Share This Page