[SOLVED] Need some help with extending off of EntityZombie

Discussion in 'Plugin Development' started by DrBowe, Nov 22, 2011.

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

    DrBowe

    Okay, as per a recent change to Net Minecraft Server code, Notch has coded in 'hard-set' values for the max health of each entity. I'm not exactly a fan of this, so I'm trying to do something that @fullwall suggested and make my own Entity with an overrided getMaxHealth() method.

    Here's what I came up with:
    Code:java
    1.  
    2. package me.DrBoweNur.DeadMines;
    3.  
    4.  
    5. import org.bukkit.Location;
    6.  
    7. import net.minecraft.server.EntityZombie;
    8. import net.minecraft.server.World;
    9.  
    10. public class DeadMinesZombie extends EntityZombie{
    11. public DeadMinesZombie(World world, Location loc) {
    12. super(world);
    13. this.locX = loc.getX();
    14. this.locY = loc.getY();
    15. this.locZ = loc.getZ();
    16. }
    17. @Override
    18. public int getMaxHealth(){
    19. return 200;
    20. }
    21.  
    22. }
    23.  


    The location is added in there so I can use net.minecraft.server.World.add(Entity) to place the mob in the world, without having to worry about where it will spawn. However, this is throwing some weird errors in the console:

    http://pastebin.com/ba1h7tS5

    For those that are curious, the piece of code that it mentions in the error (though I don't think anything in there is a direct cause of the error itself, as it seems to lie in how I extended EntityZombie) is this:

    Code:java
    1.  
    2. public static Entity spawnZombie(Location loc, CreatureType type){
    3. net.minecraft.server.World w = ((CraftWorld)loc.getWorld()).getHandle();
    4. switch(type){
    5. case ZOMBIE:
    6. DeadMinesZombie z = new DeadMinesZombie(w, loc);
    7. w.addEntity(z); //line 341
    8. return z.getBukkitEntity();
    9. }
    10. return null;
    11. }
    12.  


    If anyone knows what dark magic lies in manipulating NMS code, and knows how to fix this issue I'm having, I'd appreciate it :)
     
  2. Offline

    DDoS

    It seems as you can't add your entity because the server is failing at getting the type, for the packet handler. Is there any getters for the entity type you could override? (Do you have a link to the code for the class you're extending?)
     
  3. Offline

    DrBowe

  4. Offline

    DDoS

    I found your answer in the EntityType class this method:

    PHP:
    private static void a(Class oclassString sint i) {
            
    a.put(soclass);
            
    b.put(oclasss);
            
    c.put(Integer.valueOf(i), oclass);
            
    d.put(oclassInteger.valueOf(i));
    }
    informs us that the Map "d" contains a class as a key, and this:

    PHP:
    public static int a(Entity entity) {
            return ((Integer) 
    d.get(entity.getClass())).intValue();
    }
    is the problematic method (the one that raises the exception). Your new class is not part of the "d" map, it returns null, and then (after casting to Integer, which keeps it null) the method tries to call the inValue() method on it, raising the exception.

    I'm not a pro in class inheritance stuff, but I suggest trying to cast your new DeathMinesZombie instance to a EntityZombie instance, which is probably part of the map. I'm really not sure if this will work (I have huge doubts about this D: ).
     
  5. Offline

    DrBowe

    @DDoS
    Seemed like a solid suggestion, but the same exact error is spat out by the console.
    I hate working with NMS. :|
     
  6. Offline

    fullwall

    @DrBowe - looks like you'll need to use reflection to call that method with your DeadMineZombie class and have it point to the same value as a normal zombie.
     
  7. Offline

    DDoS

    You could try interacting directly with the EntityType class and adding your class to the mapping. But this might cause bugs with the client,
     
  8. Offline

    DrBowe

    @fullwall
    I might need a little guidance with that.

    Reflection is one of those things that still shoots over my head, as I've never had a reason to use it. If I understand it correctly, it's basically a way to rip 'private/protected' code out of an API, no?
     
  9. Offline

    fullwall

    @DrBowe - basically, reflection is interacting with a class' methods or fields at run-time, rather than compile time or before. It can be used to access private methods and fields. However, because the type is unknown, it's a little bit slower because some optimisations can't be performed. However, it's not too bad to do it once off (as in this case) or by caching the lookup of methods/fields.
    What we want to do is to use the class methods of EntityTypes to access a method. Be warned that quite a lot of exceptions are thrown in reflection.
    Code:
    Class<?>[] parameterTypes = {Class.class, String.class, int.class}; // reflection uses the parameter classes during the lookup process.
    Method method = EntityTypes.class.getDeclaredMethod("a", parameterTypes); // look up the method object using the class of EntityTypes.
    method.setAccessible(true); // since the method is private, we make sure we can access it.
    Object[] methodArgs = {DeadMinesZombie.class, "Zombie", 54}; // set up our method arguments.
    method.invoke(null, methodArgs); // to invoke a method, we need an instance of the object and the arguments. Since the method is static, no instance is needed, so we use null.
    
     
  10. Offline

    DrBowe

    @fullwall
    Huh. Okay, I get the concept of retrieving the method, but where would I want to invoke this method so that everything is overridden properly? And more importantly, where would I want to store the Method itself (Since as you said, it's a very slow operation, so I'll only want to make the reflection call once)

    Sorry if this is coming off as a bit 'amateur-ish'. I usually don't like to get spoon fed, but this kind of stuff still hurts my head while trying to wrap my mind around it. NMS is bad enough on its own, but reflection seems to be an entirely different beast.
     
  11. Offline

    fullwall

    @DrBowe - you only need to invoke the method once, it's a setup method. Basically, EntityTypes stores a HashMap of class-integer for use in packets. All you need to do is add it your class to the map once in onEnable or even in a static initialiser in your main plugin class (since onEnable could be called more than once). Method.invoke is only slightly slower than normal invocation - as you noticed, it's the retrieval of the method object that's expensive.
    Code:
    static {
     // method stuff here
    }
     
Thread Status:
Not open for further replies.

Share This Page