[UTIL|CLASS|TUTORIAL] BukkitTool - Accessing NMS and CB Classes with ease!

Discussion in 'Resources' started by CaptainBern, Sep 22, 2013.

Thread Status:
Not open for further replies.
  1. Hello there!
    Today I started creating a little util/lib which allows developers to easily create new instances of NMS or CB classes.
    This can be used to, as example, send packets or edit the HumanEntity class... Anyways the possibilities are endless.

    The library has 2 classes. The BukkitTool class, which can be used to get nms or cb classes by doing:
    Code:java
    1. BukkitTool.getNMSClass(<classname here>);
    2.  
    3. //or by doing
    4.  
    5. BukkitTool.getCBClass(<classname here>);

    It's kinda obvious that doing getNMSClass() will return an nms class and doing getCBClass() will return a cb class. It also has a getNMSPackageName() and a getOBCPackageName() method, those 2 will return the versioned package name.

    The class also contains a sub class, ClassType, more info on how to use it can be found down here.

    The other class is ServerClass, this class can be used to write or read from fields, etc.. It also contains some sub-classes:
    • ServerField
    • ServerMethod
    The ServerField class is a utility class that can be used to access fields with more ease. This also counts for the ServerMethod class.
    How to use it?

    You can use this in many diffrent ways, you can make your class extend the ServerClass and pass the class name to it's super so you can create proxy classes. Or you can just do:
    Code:java
    1. //the ClassType.NMS means it will look for an nms class, changing it to CB will make it look for a craftbukkkit class.
    2. ServerClass clazz = new ServerClass("ClassName", BukkitTool.ClassType.NMS);
    3.  

    To write to fields of this class you can do:
    Code:java
    1. clazz.write(<index>, value);
    2. //or
    3. clazz.write(<fieldname>, value);
    4. //or you can also retrieve a ServerField object by doing:
    5. clazz.getField(<fieldname>);

    Invoking methods is not really a big change, the only thing it does is allowing you to invoke a method without having to catch or throw exceptions.

    To end this thread I will post the code beneath:
    Code:java
    1. package me.captainbern.bukkittool;
    2.  
    3. import org.bukkit.Bukkit;
    4.  
    5. public class BukkitTool {
    6.  
    7. public static enum ClassType{
    8. NMS,
    9. CB;
    10. }
    11.  
    12. public static String getNMSPackageName() {
    13. return "net.minecraft.server." + Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3];
    14. }
    15.  
    16. public static String getOBCPackageName(){
    17. return "org.bukkit.craftbukkit" + Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3];
    18. }
    19.  
    20. public static Class getClass(String name){
    21. try {
    22. return Class.forName(name);
    23. } catch (ClassNotFoundException e) {
    24. System.out.print("Failed to find a valid class for: " + name);
    25. return null;
    26. }
    27. }
    28.  
    29. public static Class getNMSClass(String name){
    30. return getClass(getNMSPackageName() + "." + name);
    31. }
    32.  
    33. public static Class getCBClass(String name){
    34. return getClass(getOBCPackageName() + "." + name);
    35. }
    36. }

    and the ServerClass class:
    Code:java
    1. package me.captainbern.bukkittool;
    2.  
    3. import java.lang.reflect.Field;
    4. import java.lang.reflect.InvocationTargetException;
    5. import java.lang.reflect.Method;
    6. import java.util.ArrayList;
    7. import java.util.List;
    8.  
    9. public class ServerClass {
    10.  
    11. private Object handle;
    12. private List<ServerField> fields = new ArrayList<ServerField>();
    13.  
    14. public void setHandle(Object handle){
    15. if(handle == null){
    16. System.out.print("Cannot set handle to null!");
    17. return;
    18. }
    19. this.handle = handle;
    20. }
    21.  
    22. public Object getHandle(){
    23. return this.handle;
    24. }
    25.  
    26. public List<ServerField> getFields(){
    27. return this.fields;
    28. }
    29.  
    30. public ServerField getField(String fieldName){
    31. return new ServerField(getHandle().getClass(), fieldName);
    32. }
    33.  
    34. public ServerClass(Class<?> handleClass){
    35. try {
    36. setHandle(handleClass.newInstance());
    37.  
    38. for(Field field : handleClass.getDeclaredFields()){
    39. if(!field.isAccessible()){
    40. field.setAccessible(true);
    41. }
    42. this.fields.add(new ServerField(field));
    43. }
    44. } catch (InstantiationException e) {
    45. System.out.print("Could not instantiate class: " + handleClass.getSimpleName());
    46. } catch (IllegalAccessException e) {
    47. System.out.print("Could not create a new instance of class: " + handleClass.getSimpleName());
    48. }
    49. }
    50.  
    51. public ServerClass(String className, BukkitTool.ClassType type){
    52. switch(type){
    53. case NMS : new ServerClass(BukkitTool.getNMSClass(className));
    54. break;
    55. case CB : new ServerClass(BukkitTool.getCBClass(className));
    56. break;
    57. default : new ServerClass(BukkitTool.getClass(className));
    58. }
    59. }
    60.  
    61. public void write(String fieldName, Object value){
    62. new ServerField(getHandle().getClass(), fieldName).set(getHandle(), value);
    63. }
    64.  
    65. public void write(int index, Object value){
    66. getFields().get(index).set(getHandle(), value);
    67. }
    68.  
    69. public Object read(String fieldName){
    70. return new ServerField(getHandle().getClass(), fieldName).get(getHandle());
    71. }
    72.  
    73. public static ServerClass create(String className, BukkitTool.ClassType type){
    74. return new ServerClass(className, type);
    75. }
    76.  
    77. public Object read(int index){
    78. return getFields().get(index).get(getHandle());
    79. }
    80.  
    81. public ServerMethod getMethod(String methodName, Class... paramTypes){
    82. return new ServerMethod(getHandle().getClass(), methodName, paramTypes);
    83. }
    84.  
    85. /**
    86.   * Server field stuff
    87.   */
    88.  
    89. public static class ServerField{
    90.  
    91. private static Field field;
    92.  
    93. public ServerField(Field field){
    94. if(!field.isAccessible()){
    95. field.setAccessible(true);
    96. }
    97. this.field = field;
    98. }
    99.  
    100. public ServerField(Class<?> clazz, String fieldName){
    101. try {
    102. new ServerField(clazz.getDeclaredField(fieldName));
    103. } catch (NoSuchFieldException e) {
    104. System.out.print("No such field!");
    105. }
    106. }
    107.  
    108. public Field getField(){
    109. return this.field;
    110. }
    111.  
    112. public Object get(Object handle){
    113. try {
    114. return getField().get(handle);
    115. } catch (IllegalAccessException e) {
    116. System.out.print("Failed to access field!");
    117. }
    118. return null;
    119. }
    120.  
    121. public void set(Object handle, Object value){
    122. try {
    123. getField().set(handle, value);
    124. } catch (IllegalAccessException e) {
    125. System.out.print("Failed to access field!");
    126. }
    127. }
    128.  
    129. public static Object get(){
    130. try {
    131. return field.get(null);
    132. } catch (IllegalAccessException e) {
    133. System.out.print("Failed to access field!");
    134. }
    135. return null;
    136. }
    137.  
    138. }
    139.  
    140. /**
    141.   * Server method stucc
    142.   */
    143.  
    144. public static class ServerMethod{
    145.  
    146. private static Method method;
    147.  
    148. public ServerMethod(Method method){
    149. this.method = method;
    150. }
    151.  
    152. public ServerMethod(Class<?> clazz, String methodName, Class... params){
    153. try {
    154. new ServerMethod(clazz.getDeclaredMethod(methodName, params));
    155. } catch (NoSuchMethodException e) {
    156. System.out.print("No such method!");
    157. }
    158. }
    159.  
    160. public Method getMethod(){
    161. return this.method;
    162. }
    163.  
    164. public Object invoke(Object handle, Class... params){
    165. try {
    166. return method.invoke(handle, params);
    167. } catch (IllegalAccessException e) {
    168. System.out.print("Could not access method: " + method.toString());
    169. e.printStackTrace();
    170. }
    171. return null;
    172. }
    173.  
    174. public static Object invoke(Class... params){
    175. try {
    176. return method.invoke(null, params);
    177. } catch (IllegalAccessException e) {
    178. System.out.print("Could not access method: " + method.toString());
    179. e.printStackTrace();
    180. }
    181. return null;
    182. }
    183. }
    184. }


    If you have any questions on how to use this or have any additions, feel free to ask or post below ;) .

    Placeholder in case I find more cool stuff or just so I can say 'first!'

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

    DevRosemberg

    CaptainBern Mind PMing me your skype. I see you are really good with packets and i wana learn those.
     
  3. DevRosemberg Aww sorry I don't add strangers to my skype but you can always pm me :p
     
  4. Offline

    DevRosemberg

  5. Offline

    DSH105

    Select the text and then click the link icon (underneath the undo arrow).

    [​IMG]

    Nice work on this BTW ;).
     
    CaptainBern likes this.
  6. Offline

    chasechocolate

    I really like the way you did this, going to update my boss health bar class using it :)
     
  7. chasechocolate Oh in that case, I also created the same thing for the datawatcher, it's in the utils folder in my animationlib on my github :p
     
  8. Offline

    chasechocolate

  9. Offline

    DevRosemberg

    CaptainBern I think im gona PM you asking for help. Prepare for a TON of Questions :D
     
  10. Offline

    DevRosemberg

    CaptainBern *Cracks Neck and Fingers* Let em Questions RAIN!
     
  11. Offline

    Scizzr

    Like this? Google

    Method 1:
    {url=http://www.google.com/}Google{/url}
    Replace { and } with [ and ] respectively

    Method 2:
    Type the link's name (Google) and highlight it
    Click the chain icon at the top (formatting area)
    Type the link's destination (http://www.google.com/)
    Click Insert
     
  12. Scizzr No worries its already fixed, probably should change my message there..
     
    Scizzr likes this.
  13. Offline

    BungeeTheCookie

    CaptainBern
    This is a great utility for people not wanting to constantly update your plugin and send Packets with ease. How can you access NMS code that is not related to packets. Or does it work the same, or OBC code?
     
  14. BungeeTheCookie I'm creating a util for that too, Anyways, in fact you can use this for every nms-class. Just make your class extend packet, put a super("nms-class name") in the constructor and you're cready to set field values. Ofcourse the send() method will be broken if the nms-class isnt an instance of Packet.
     
  15. I updated the tool. It now should work faster, be better and easier to use. If anyone needs any help with this, feel free to ask :)
     
  16. Offline

    UnrealPowerz

    Kudos!
    Here, have a cookie.
    *gives cookie*
     
    CaptainBern likes this.
  17. Offline

    BungeeTheCookie

    CaptainBern
    I love you. You are the most amazing baby cookie coder ever. If I ever meet you, I will give you a hug. You made my life so much easier. *Thank you for updating the tool to meet my needs (Kind of lazy to learn reflection)* #NoHomo
     
    CaptainBern likes this.
  18. Offline

    BungeeTheCookie

    CaptainBern likes this.
  19. Offline

    BungeeTheCookie

    CaptainBern
    Could you provide an example of how to get like the CraftPlayer class in the getCBClass(). It's not letting me get the instance of the class. Like how do you get methods inside a CraftPlayer or CraftWorld class. Thanks~
     
  20. BungeeTheCookie Well to get the CraftPlayer object of a player you have to invoke the getHandle() method inside craftplayer, this will however return the EntityPlayer class so in theory you can invoke every class inside the CraftPlayer class by just doing:
    Code:java
    1.  
    2. Method method = player.getClass().getDeclaredMethod("methodName");
    3. method.invoke(player, params);
    4.  

    This will only work for methods that are in the CraftPlayer class. If you also want to use methods that are inside the EntityPlayer class then you have to invoke the getHandle() method inside the CraftPlayer class. This will return the EntityPlayer object of the player.

    I think it's easier to do this with the build-in reflection instead of using this tool. However if you do want to use the tool you can do this like so:
    Code:java
    1.  
    2. ServerMethod method = new ServerMethod(player.getClass(), "methodname", params);
    3. method.invoke(player);
    4.  

    Why would you use my class instead? Well you don't have to worry about the exceptions etc. But to be honest, I just put the ServerMethod in it because I already had a ServerField and ServerClass, it was more added to make the tool more "complete".
     
  21. Offline

    Not2EXceL

    I have one grip with the serverclass class. Other than that it looks very well written as its simple to see what's happening.

    When you're creating a new server class object.using the class name and the enum to determine whether its NMS or CB. Why are you creating a new.object.of server class? You should just call the constructor with this. Unless I'm missing something since reading it is hard on a phone.
     
  22. Not2EXceL It's easier to do it like that, else I would have to write this code twice, or even more:
    Code:java
    1. try {
    2. setHandle(handleClass.newInstance());
    3.  
    4. for(Field field : handleClass.getDeclaredFields()){
    5. if(!field.isAccessible()){
    6. field.setAccessible(true);
    7. }
    8. this.fields.add(new ServerField(field));
    9. }
    10. } catch (InstantiationException e) {
    11. System.out.print("Could not instantiate class: " + handleClass.getSimpleName());
    12. } catch (IllegalAccessException e) {
    13. System.out.print("Could not create a new instance of class: " + handleClass.getSimpleName());
    14. }
     
  23. Offline

    Not2EXceL

    CaptainBern no I know its easier to use two different constructors. I was curious to why you have the new ServerClass(handleClass) inside the constructor you call to make the ServerClass object.

    Both my way and yours work. I just wanted to understand your theory behind doing that new instance instead of this(handle class)
     
  24. Offline

    sgavster

    Very impressive!
     
  25. Offline

    Not2EXceL

    CaptainBern I apologize for my misread

    You should move the switch to a static method in bukkittool and just call the second constructor with this(switch method). Its more efficient than creating a second object. Reflection is sloe enough as it is
     
  26. Why do you replace the dots by commas instead of directly splitting at the dots?
     
  27. Offline

    MylesIsCool

    Most likely due to regex and it being a bit easier.
     
  28. You have to escape the dot, but i think it's still easier. My opinion ;)
    Code:java
    1. getName().replace(".", ",").split(",")[3];
    Code:java
    1. getName().split("\\.")[3];
     
Thread Status:
Not open for further replies.

Share This Page