[Util/LIB] ClassTrap an polite reflection library - Interface classes in a completely new way!

Discussion in 'Resources' started by MylesIsCool, Jul 25, 2014.

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

    MylesIsCool

    ClassTrap - A polite reflection library which allows the uses of interfaces.

    So we've all been there, you want to implement a new fancy feature, or maybe something out of the box.

    But then... It dawns on you having to use reflection!

    So reflection, what's horrible about it?
    It's not easy just to reuse a class and isn't as simple as calling a method like without.

    What does ClassTrap do?
    It allows you to turn an interface into a 'proxy' for the class your using reflection on.

    I'm still confused :S
    Have an example:
    Code:java
    1. CraftPlayer cp = Trap.create(CraftPlayer.class, sender);
    2. EntityPlayer ep = Trap.create(EntityPlayer.class, cp.getHandle());
    3. PlayerConnection pc = Trap.create(PlayerConnection.class, ep.getPlayerConnection());
    4. Object packet = MCUtils.makeNMS("PacketPlayOutUpdateHealth", 10f, 5, 1f);
    5. pc.sendPacket(packet);


    You will have to make CraftPlayer, EntityPlayer, PlayerConnection interfaces though.
    Note: CraftPlayer, EntityPlayer & PlayerConnection are NOT the minecraft classes!

    What does this achieve?
    Allows you to create re-creatable interfaces to control NMS/CB methods with.

    Download:
    ClassTrap - Core library for creating traps for objects, < 200 lines of code.
    MCUtils - Small class for when using with bukkit etc.
    All Interfaces for NMS & CB - Includes ClassTrap & MCUtils

    What does an Interface for a Object look like?
    Code:
    public interface CraftPlayer {
        public Object getHandle();
    }
    
    That could be used to get the EntityPlayer behind a Player object in it's simplest form and you would use the following code to execute that:

    Code:
    CraftPlayer cp = Trap.create(CraftPlayer.class, sender);
    EntityPlayer ep = (EntityPlayer) cp.getHandle();
    This simply maps CraftPlayer to the sender and allows you to use it through it, it does get a bit more advanced with creating the interface.

    For example to get PlayerConnection from EntityPlayer:
    Code:
    public interface EntityPlayer {
            @TrapTag(type = TrapTagType.GETTER, value = "playerConnection")
            public Object getPlayerConnection();
    }
    
    We use the TrapTag annotation to provide extra infomation, you have 3 TrapTagType's: GETTER, SETTER & METHOD. This will choose what the library will do when the method is called, in this case it will get the variable playerConnection when getPlayerConnection() is called. Note: This will auto-magically ignore private fields/methods.

    Questions?
    Feel free to ask questions below, this library is rather simple but can be rather effective when handling multiple versions etc. I'll happily answer any questions & fix any possible issues which may arise within the libraries specification.

    License?
    Do anything you want, just don't say you wrote the original code and i'll be happy!
     
  2. Offline

    MCForger

    MylesIsCool
    Very nice. I did a similar kind of system and might be able to post some interfaces using your library so users do not need to figure out some NMS classes on their own.
     
    MylesIsCool likes this.
  3. Offline

    xTrollxDudex

    Impressive. So, a modified mockito?
     
  4. Offline

    RawCode

    Code:
    	public static <T> T create(Class<T> classInterface, Object o) {
    		return (T) Proxy.newProxyInstance(classInterface.getClassLoader(), new Class[]{classInterface}, new Trap(o));
    	}
    
    wrapper over other library?
     
    Garris0n likes this.
  5. Offline

    MylesIsCool

    Yeah essentially it uses the inbuilt Java proxy class and allows and handles the method calls using it.
     
  6. Offline

    RawCode

    ok then, what "procs" of this over invoking NMS methods directly?
     
  7. Offline

    MylesIsCool

    "Pros?", a much cleaner interface which you can allow other plugins to use as a bonus as well as essentially Object Oriented Reflection.
     
    bobacadodl likes this.
  8. Offline

    MylesIsCool

    Added some small changes to the library (simply redownload the original gist etc).

    Changes:
    • Now auto-unboxes objects given as parameters which have the annotation TrapTag & boxes return types which have the annotation of this too.
    Example using auto-boxing

    Don't have any examples for auto unboxing but essentially this would allow you to pass parameters which are 'Traps' and it'll get the object behind them.
    Also added a method which allows the retrieval of the raw object behind a trap, Trap.getInternalObject(object); which is what unboxing depends on.


    I've created a quick script to generate interfaces...

    You can find all interfaces for CB / NMS here.

    There are no private getters / setters so you may want to add them if you are planning to use them & you may want to exclude unused classes from compilation as there is quite a few classes.

    If you want to see the rather messy code behind this, you can click here. This was designed to do it's job and not for pretty form.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 9, 2016
  9. Offline

    xTigerRebornx

    MylesIsCool I'm agreeing with RawCode , I fail to see why this should be used as opposed to directly invoking NMS methods. The interfaces are still version-dependent if any of the methods change from version-to version, it just seems like the interfaces are there to "avoid the version" import, yet all they do is add another (still version dependent) layer between the developer and calling version-dependent code.
    Don't get me wrong, the idea is cool. It is an interesting way to do reflection, but I don't see any "pros" for using it versus directly using NMS/CB and having versioned-support within your plugin.
     
    Traks likes this.
  10. Offline

    RawCode

    not checked how exactly this feature implemented by misc.emit but most likely this is "accessor level" when class with method that just invoke other method invoked.

    from code it looks like reflectionless invocation of arbitrary method, BUT reflection access will compile into exactly same accessor after 15th invocation.

    in case of version change - accessor class may compile into invalid bytecode and will result in exactly same error like nms with version gate.
     
    xTigerRebornx likes this.
  11. Offline

    MylesIsCool


    In some cases it is nicer over using NMS where you have some methods which are compatible over versions. The general use of this was to aim to create a way for when reusing methods a lot then you can already have methods setup.
    But yeah opinion is fine and I respect that :p.

    Care to elaborate? Are there any articles on this as it seems interesting but after a few google searches I was unable to find anything on the subject.
     
  12. Offline

    RawCode

    Proxy classes are public, final, and not abstract.
    The unqualified name of a proxy class is unspecified. The space of class names that begin with the string "$Proxy" should be, however, reserved for proxy classes.
    A proxy class extends java.lang.reflect.Proxy.
    A proxy class implements exactly the interfaces specified at its creation, in the same order.
    If a proxy class implements a non-public interface, then it will be defined in the same package as that interface. Otherwise, the package of a proxy class is also unspecified. Note that package sealing will not prevent a proxy class from being successfully defined in a particular package at runtime, and neither will classes already defined by the same class loader and the same package with particular signers.
    Since a proxy class implements all of the interfaces specified at its creation, invoking getInterfaces on its Class object will return an array containing the same list of interfaces (in the order specified at its creation), invoking getMethods on its Class object will return an array of Method objects that include all of the methods in those interfaces, and invoking getMethod will find methods in the proxy interfaces as would be expected.
    The Proxy.isProxyClass method will return true if it is passed a proxy class-- a class returned by Proxy.getProxyClass or the class of an object returned by Proxy.newProxyInstance-- and false otherwise.
    The java.security.ProtectionDomain of a proxy class is the same as that of system classes loaded by the bootstrap class loader, such as java.lang.Object, because the code for a proxy class is generated by trusted system code. This protection domain will typically be granted java.security.AllPermission.
    Each proxy class has one public constructor that takes one argument, an implementation of the interface InvocationHandler, to set the invocation handler for a proxy instance. Rather than having to use the reflection API to access the public constructor, a proxy instance can be also be created by calling the Proxy.newProxyInstance method, which combines the actions of calling Proxy.getProxyClass with invoking the constructor with an invocation handler.
     
  13. Offline

    MylesIsCool

    As much as I enjoy the extended points from http://www.docjar.org/docs/api/java/lang/reflect/Proxy.html, you could just link the article to save the effort.

    Any chance you could tell me more about
    as well as something I can read to get more information about these limitations as it's very interesting to learn this as i've only seen the surface of reflection it seems.
     
  14. Offline

    xTrollxDudex

  15. Offline

    RawCode

    http://grepcode.com/file/repository...Class(java.lang.String,java.lang.Class[],int)

    final of all proxy stuff is runtime generated class that feature bytecode level access to methods of all interfaces it extends.

    exactly same feature used with reflection access to field and methods after 15th invocation.

    before 15th invocation - native level access is performed, after ~300 JIT compiled level access will be initialized.
     
    MylesIsCool likes this.
  16. Offline

    RawCode

    I can suggest to evoid all wrappers and provide library to write bytecode directly (ASM and jassist aready exists but still).

    compiled classes will looks like

    someproxy implements interface

    method a
    {
    invoke specific method of class B
    }
     
    xTrollxDudex likes this.
  17. Offline

    TheGamersCave


    I see what you mean about manually handling all your NMS, though with a proxy system in place like this one, you're able to create version-specific and maintainable code bases.

    On a small scale it's more work than what's needed; If you feel like it'd be easier to maintain all the NMS code yourself, then awesome: Do it. In the end, it's really the way you choose to do things.
     
    MylesIsCool likes this.
Thread Status:
Not open for further replies.

Share This Page