[TUTORIAL|INTERMEDIATE] Custom Entities - Meteor

Discussion in 'Resources' started by Icyene, Aug 16, 2012.

  1. Offline

    Icyene

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    As part of my 1-tutorial/2-day goal, today I present a way to create a realistic meteor in MineCraft.

    By the end of this tutorial, you should be able to create an entity that can cause this:

    [IMG]
    With minimal lag. Yes, that is a meteor hole, not a natural hole.

    So without further ado, lets begin!

    This tutorial assumes you have a basic understanding of NMS entities.

    Now, we don't really want to create our own entity, entity physics, entity AI and all that by ourselves: that would take MUCH too long. For a meteor, the closest thing that already exists in MineCraft is a fireball.

    We now have to create our class, EntityMeteor for this tutorial, and make it extend EntityFireball. This will give us all the properties of a fireball. Your class should look something like this:

    Code:
    public class EntityMeteor extends EntityFireball {
     
        public EntityMeteor(World world) {
     
        super(world);
        a(1.0F, 1.0F);
        }
     
    }
     
    
    super(world) makes the EntityFireball do all the rest of the necessary initializations. super calls the virtual function being overrided by the current function. So super(world) in related to EntityFireball(world) in theory.

    So now we have the base of the meteor that we want to make. Now, lets do somethings to spice it a bit up: a fireball looks nothing like a meteor in its current case. Create three variables:

    Code:
        private float speedMod = 1.10F;
        private float explosionRadius = 70F;
        private float trailPower = 10F;
    
    An explanation of what we will do with these:

    We will make meteors get faster as they move, similarly to how fireballs get slower.
    We will make the meteor cause a giant explosion when it impacts.
    We will make the meteor have an exploding trail. Note that, if the trail power is over 7, it will destroy blocks around the meteor while it travels, meaning that the meteor will NOT explode until it reaches bedrock and impacts that. This allows for epic meteors.

    We have to implement these. By analyzing EntityFireball in net.minecraft.server with MCP, we find that public void h_() in EntityFireball.class is called whenever the fireball moves, similar to a PlayerMoveEvent. That is where we shall create our explosion. Create a method public void h_() in your meteor class, and add an @Override annotation to it (as you would with @EventHandler). This forces MC to run our h_ instead of EntityFireball's.

    Now, add the following code into your h_:

    Code:
    final Fireball fireball = (Fireball) this.getBukkitEntity();
    fireball.getWorld().createExplosion(fireball.getLocation(), trailPower);
    
    Pretty simple stuff. Creates an explosion at the fireball's location. fireball is final, allowing the JVM to make small improvements to performance. Useful, given that h_ is called 20 times a second (once a tick).

    Now we want to make it speed up. Minecraft slows a fireball down like this:

    Code:
    motX *= 0.95F;
    motY *= 0.95F;
    motZ *= 0.95F;
    
    Meaning that its motion will slowly dissapear. To counter this, add the following code to your h_:

    Code:
    motX *= speedMod;
    motY *= speedMod;
    motZ *= speedMod;
    
    And finally, call super.h_(), to let the fireball actually move. Note that your speedMod actually has to be .5F higher then what you actually want, as super.h_() multiplies it by 0.95F, as if it was a normal fireball.

    By now, your h_ should look like this:

    Code:
    @Override
    public void h_() {
     
        final Fireball fireball = (Fireball) this.getBukkitEntity();
        fireball.getWorld().createExplosion(fireball.getLocation(), trailPower);
     
        motX *= speedMod;
        motY *= speedMod;
        motZ *= speedMod;
     
        super.h_();
    }
    
    Now, for the last part. The actual explosion. I will spill the beans quickly now: a function called a is called when the fireball explodes by contact with a block. Therefore, paste this code into your class:

    Code:
    @Override
    public void a(MovingObjectPosition movingobjectposition)
    {
     
    }
    
    We want it to play nice with WorldGuard and other plugins, and hence allow them to protect your buildings against your meteor. To do this we must throw an EntityExplodeEvent.

    This is just a bit of copy+pasting from the original EntityFireball, add this into a:

    Code:
    if (!world.isStatic)
        {
            if (movingobjectposition.entity != null)
            movingobjectposition.entity.damageEntity(
                DamageSource.fireball(this, shooter), 6);
            ExplosionPrimeEvent event = new ExplosionPrimeEvent(
                (Explosive) CraftEntity.getEntity(world.getServer(), this));
            world.getServer().getPluginManager().callEvent(event);
            if (!event.isCancelled()) {
     
    //We will add some exploding code here
     
     
    }
    }
    
    Finally, replace my comment with the following code, which creates an explosion:

    Code:
    world.createExplosion(
                this,
                locX,
                locY,
                locZ,
                explosionRadius,
                event.getFire());
    
    The neat thing with using the above method instead of Bukkit's wrapper is that the above method actually sets blocks around the explosion on fire, like a fireball. Make sure you call die() at the end of a, so that you don't get an invincible fireball.

    Now, unless you want to have fun spawning and controlling your meteors the MC way, I suggest you paste the below code into your class. Its an implementation of Bukkit's API, but works on our meteor. It also contains a couple of meteor-specific functions, such as setSpeedModifier(), getTrailPower() etc.

    Code:
    public Vector getDirection() {
        return new Vector(this.dirX, this.dirY, this.dirZ);
        }
     
        public void setDirection(Vector direction) {
        this.setDirection(direction.getX(), direction.getY(), direction.getZ());
        }
     
        public Vector getVelocity() {
        return new Vector(this.motX, this.motY, this.motZ);
        }
     
        public void setVelocity(Vector vel) {
        this.motX = vel.getX();
        this.motY = vel.getY();
        this.motZ = vel.getZ();
        this.velocityChanged = true;
        }
     
        public CraftWorld getWorld() {
        return ((WorldServer) this.world).getWorld();
        }
     
        public boolean teleport(Location location) {
        return teleport(location, TeleportCause.PLUGIN);
        }
     
        public boolean teleport(Location location, TeleportCause cause) {
        this.world = ((CraftWorld) location.getWorld()).getHandle();
        this.setLocation(location.getX(), location.getY(), location.getZ(),
            location.getYaw(), location.getPitch());
        // entity.setLocation() throws no event, and so cannot be cancelled
        return true;
        }
     
        public void setYield(float yield) {
        this.yield = yield;
        }
     
        public float getYield() {
        return this.yield;
        }
     
        public void setYaw(float yaw) {
        this.yaw = yaw;
        }
     
        public float getYaw() {
        return this.yaw;
        }
     
        public void setPitch(float pitch) {
        this.pitch = pitch;
        }
     
        public float getPitch() {
        return this.pitch;
        }
     
        public void setSpeedModifier(float modifier) {
        this.speedMod = modifier;
        }
     
        public float getSpeedModifier(float modifier) {
        return this.speedMod;
        }
     
        public void setExplosionRadius(float radius) {
        this.explosionRadius = radius;
        }
     
        public float getExplosionRadius() {
        return this.explosionRadius;
        }
     
        public void setTrailPower(float power) {
        this.trailPower = power;
        }
     
        public float getTrailPower() {
        return this.trailPower;
        }
    
    Now its spawnable! My meteor API is a bit different from Bukkit's, especially on the point of spawning. You will have to add the meteor into the world...... The MineCraft way.

    Don't worry, its not that hard:

    Code:
    CraftWorld cWorld = (CraftWorld) <<WorldToSpawnIn>>;
    EntityMeteor eMeteor = new EntityMeteor(cWorld.getHandle());
    cWorld.getHandle().addEntity(eMeteor, SpawnReason.NATURAL);
    eMeteor.setPosition(<<x>>, <<y>>, <<z>>);
    
    But today I will show you something much more interesting, and much more epic. A functional way to compute a vector path from a spawn location to a desired location. This took me 12 hours (6 of them because I was stupid), but I shall give you the code and explain it.

    Its a simple-looking function. You give it a start location, and end location, and it spawns and sets the meteor on that path.

    Code:
    public static void spawnMeteorAndTarget(Location targetLoc,
            Location spawnLoc)
        {
        // Target coords
        final double x1 = targetLoc.getX();
        final double y1 = targetLoc.getY();
        final double z1 = targetLoc.getZ();
     
        // Spawn coords
        final double x0 = spawnLoc.getX();
        final double y0 = spawnLoc.getY();
        final double z0 = spawnLoc.getZ();
     
        final double vx = (x1 - x0) / 10;
        final double vy = (y1 - y0) / 10;
        final double vz = (z1 - z0) / 10;
     
        CraftWorld cWorld = (CraftWorld) targetLoc.getWorld();
     
        EntityMeteor eMeteor = new EntityMeteor(cWorld.getHandle());
     
        cWorld.getHandle().addEntity(eMeteor, SpawnReason.NATURAL);
     
        eMeteor.setPosition(x0, y0, z0);
     
        Vector translation = new Vector(x1 - x0, y1 - y0, z1 - z0);
        eMeteor.setDirection(translation);
     
        Vector velocity = new Vector(vx, vy, vz);
        eMeteor.setVelocity(velocity.multiply(3)); //make a bit faster
     
        System.out.println("\n----\n" + "Spawned meteor: " + eMeteor + "\n"
            + "Target coords: "
            + x1 + ": " + y1 + ": " + z1
            + "\n" + "Spawn coords: " + x0 + ": " + y0 + ": " + z0 + "\n"
            + "Velocity vector: " + velocity + "\n" + "Direction vector: "
            + translation + "\n" + "World: " + cWorld.getName());
     
        }
    
    What the HECK did I do there? Its a small vector translation. Basically, directionX = targetX - spawnX, and so on till Z. in this case, x1 is targetX, and x0 is spawnX.

    vx (velocity x) is equal to (x1 - x0) / 10, ensuring that it will travel at a speed ensuring that it will arrive on target in 10 seconds, regardless of how far it is. Note that if you spawn the meteor hundreds of chunks away from the target, and make it get there in 10 seconds, the amount of chunk it will have to load will most likely crash your server. Be reasonable, that's all I can say.

    In the end, your meteor class should look like this: http://pastie.org/4522847

    A final note: I used Hungarian notation for eMeteor. This is considered bad. I only did it because I couldn't think of a better name :)

    I hope this you found this article informative! If you meteor doesn't spawn, make sure you are doing two things correctly:

    1. The chunk you are trying to spawn the meteor in is loaded.
    2. You have patched your entity to MineCraft. If not, read this tutorial by @Jacek which shows you how to do so. You will have to change the number to 12, however.

    The method I use to patch it is this:

    Code:
    public static void load() {
          try {
     
            Method a = net.minecraft.server.EntityTypes.class
                .getDeclaredMethod("a", Class.class, String.class,
                    int.class);
            a.setAccessible(true);
     
            a.invoke(a, EntityMeteor.class, "Fireball", 12);
     
        } catch (Exception e) {
          //Dispose of error silently :D I suggest your report it.
        }
        }
    
    LICENSE: All the snippets are licensed under the WTFPL - the Do Whatever the F*ck You Want License. But really, just mention that I wrote those snippets if you ever use them in your plugins. I spend hours of my time writing these tutorials for free for you guys; I think I deserve a tiny bit of credit.

    UPDATE: Found a way to make meteors more bright. Light up the night sky!

    Just add these methods, and I think the API I provided is simple enough to understand:

    Code:
    private float brightness = 10F;
     
    @Override
        public float c(float f)
        {
        return this.brightness;
        }
    
        public void setBrightness(float brightness) {
        this.brightness = brightness;
        }
        
        public float getBrightness() {
        return this.brightness;
        }
    

    This post has been edited 9 times. It was last edited by Icyene Aug 17, 2012.
  2. Offline

    one4me

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Imagine using this to make meteor showers a type of Minecraft weather. If someone did that they may want to turn the explosions off and just leave the damage if that's possible so that the entire landscape doesn't get griefed. And as long as multiple meteors don't cause lag, it would be amazing.
  3. Offline

    Icyene

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    @one4me

    This code was pulled from my plugin Storm, which already has meteors, and I am planning to add meteor showers as well :)
    one4me likes this.
  4. Offline

    Jacek BukkitDev Staff

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Awesome ! Nice to see someone did somethign cool with this method :p
  5. Offline

    Icyene

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Thanks :p I also noticed some very strange occurrences with 1.3.1, which occur of either one of 2 things.

    1. Once you apply the patch, it is never needed to be reapplied, not even if server restarts.
    2. You do not require the patch to add an entity anymore.

    Because for quite a while I was forgetting to patch it, and my entities were will spawning. Maybe the patch makes them visible? That might explain it: unpatched entities are not visible, but still update positions etc. Because I almost never see the actual fireball of the meteor, I always see it's trail.

    Food for thought :p

    P.S. I think that your Tutorial on customizing entities and mbaxter's "Too cool not to share - simple config" are probably some of (if not the) most useful threads here in the resources section for people interested to take their plugins a notch* higher than the "average" plugin.

    *Yes.

    This post has been edited 1 time. It was last edited by Icyene Aug 17, 2012.
    Jacek likes this.
  6. Offline

    Jacek BukkitDev Staff

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Yeah if you don't update the class reference thing they are invisible. It's really odd that that happens though, it should just give loads of errors really :s It could be that it sends the wrong entity id to the client so there is no skin to use actually.

    Glad you like the tutorial anyway :p
  7. Offline

    Icyene

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    That is another interesting thing: all my other NMS entities had to be updated for 1.3.1 (loads of errors), but I saw meteors still worked, so I was like "well, this works, I don't know why, so better update the patch as well".
  8. Offline

    V10lator

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    I have a few suggestions:
    1. No need to call a(1.0F, 1.0F); as this is called by super(world);
    2.
    This:
    Code:java
    1. final Fireball fireball = (Fireball) this.getBukkitEntity(); fireball.getWorld().createExplosion(fireball.getLocation(), trailPower);

    gave me NPEs with a plugin listening for the EntityExplodeEvent, so I changed it to this:
    world.createExplosion(this, locX, locY, locZ, trailPower, true);
    3. I don't need your API as I can just get the Fireball (bukkit entity) and use it's API.

    So this is what I have till now:
    Code:java
    1. package de.V10lator.MayaApocalypse.Entity;
    2.  
    3. import net.minecraft.server.EntityFireball;
    4. import net.minecraft.server.MovingObjectPosition;
    5. import net.minecraft.server.World;
    6.  
    7. // Based on: [url]http://forums.bukkit.org/threads/tutorial-custom-entities-meteor.93899/[/url]
    8. public class MayaMeteor extends EntityFireball
    9. {
    10. private float speedMod = 1.1F;
    11. private float explosionRadius = 70F;
    12. private float trailPower = 10F;
    13. private float brightness = 10F;
    14.  
    15. public MayaMeteor(World world)
    16. {
    17. super(world);
    18. // No need to call a(1.0F, 1.0F); as super(world); still does this.
    19. }
    20.  
    21. @Override
    22. public void h_()
    23. {
    24. world.createExplosion(this, locX, locY, locZ, trailPower, true);
    25.  
    26. motX *= speedMod;
    27. motY *= speedMod;
    28. motZ *= speedMod;
    29.  
    30. super.h_();
    31. }
    32.  
    33. @Override
    34. public void a(MovingObjectPosition movingobjectposition)
    35. {
    36. world.createExplosion(this, locX, locY, locZ, explosionRadius, true);
    37. die();
    38. }
    39.  
    40. @Override
    41. public float c(float f)
    42. {
    43. return this.brightness;
    44. }
    45. }

    And this is how I spawn it:
    Code:java
    1. MayaMeteor mm = new MayaMeteor(nw);
    2. nw.addEntity(mm, SpawnReason.CUSTOM);
    3. Fireball meteor = (Fireball)mm.getBukkitEntity();
    4. meteor.teleport(loc);
    5.  
    6. meteor.setDirection(vec);
    7. meteor.setVelocity(vec);
    8. meteor.setBounce(false);
    9. meteor.setIsIncendiary(true);
    10. meteor.setYield(2 + rand.nextInt(14));

    Please not that this was quick&dirty and I'll improve it more future later. :)
  9. Offline

    Icyene

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    I was wondering when someone would catch on to that :p. Correct, a(1.0....) is not needed. Yes, you can also get the Bukkit entity to use its API. However, specific things must be noted: some of my API was meteor-specific, such as setSpeedModifier() etc. Additionally, I would advise greatly against
    Code:
     meteor.setVelocity(vec);
    
    because when I used the same thing for meteor targetting, meteors will fail to spawn when you want them to. If you stay online for long enough, however, all meteors that haven't spawned yet will spawn at the same time, causing a massive lag spike that more often than not lags clients incredibly, even if damage isn't catastrophic.
  10. Offline

    Icyene

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Additionally, and I'm just guessing here, but does world.createExplosion() fire an explosion event? If not, your meteor won't play nice with WorldGuard and other people's buildings. :p
  11. Offline

    V10lator

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    I think so (but I didn't check it... Will do that later).
    Well, that's not needed for me. In fact another plugin cancelling it wouldn't play nice with MayaApocalypse, but that's another story... ;)
  12. Offline

    V10lator

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    I checked it and the method does fire the events, too. :)

    But my meteors doesn't make the damage you showed in your screenshot. Well, that may have something to do with my method to stop the blocks from dropping their items.

    @Icyene A bit off topic, but do you want to join the MayaApocalypse team (if @beleg isn't against it) ? :)
  13. Offline

    Icyene

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    1. Awesome!

    2. That is because that was under ideal circumstances, and kinda happened because of a bug I patched :D. To do it now, add an int called 'lives', and in a(), check if lives is 0. If so, make the big boom, and broadcast message. If not, make an explosion of decent size, and --lives. The explosion will allow the meteor to progress further, then impact again, burrow, and so on until lives = 0, at which point it will explode. To make it go to bedrock, use a lives like 10-20, and set the burrow explosion to around 15 (I had it at 10 for the image, but it doesn't account for the direction / velocity its coming down at (a lives of 20 at velocity.multiply(3) isnt that same as a lives of 20 @ normal velocity; same applies for pitch).

    Sure! Thanks :D I've wondered how you did the floods without lag :p

    This post has been edited 1 time. It was last edited by Icyene Aug 20, 2012.
  14. Offline

    V10lator

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
  15. Offline

    Icyene

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    With

    Code:
    else {
    world.createExplosion(this, locX, locY, locZ, burrowPower, true);
    }
    
  16. Offline

    V10lator

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    @Icyene Isn't the else still handled by h_() ? BTW: PMed you. ;)
  17. Offline

    Icyene

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Right, forgot that you were using an oversized fireball explosion. And I replied :)

    This post has been edited 1 time. It was last edited by Icyene Jan 30, 2013.
  18. Offline

    Icyene

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Update: To add onto the "efficiency" part. The meteor posted above will kill your FPS for quite a few seconds... This is because it is exploding 20x a second. Adding this as your h_ makes it only do it 2.5 times/second, as well as deletes itself if it goes under height 1, or higher than 255.

    Code:Java
    1.  
    2. @Override
    3. public void h_() {
    4. do {
    5. h_lock = !h_lock;
    6. if (h_lock) break;
    7. h_lock_2 = !h_lock_2;
    8. if (h_lock_2) break;
    9. h_lock_3 = !h_lock_3;
    10. if (h_lock_3) break;
    11.  
    12. int locY = (int)(this.locY);
    13. if ((locY & 0xFFFFFF00) != 0) { //locY < 1 or locY > 255
    14. this.dead = true; // Die silently
    15. return;
    16. }
    17.  
    18. if ((locY & 0xFFFFFFE0) == 0) { // locY < 32
    19. explode();
    20. return;
    21. }
    22.  
    23. world.createExplosion(this, locX, locY, locZ, trailPower, true);
    24. } while (false);
    25.  
    26. super.h_();
    27. }
    28.  


    All the _locks are booleans set to false at declaration. Note I am masking locY quite alot, I have commented to explain what each does.

    Enjoy! This improved the meteor's efficiency greatly, actually allowing me to move my camera around pretty well while the meteor was flying.
  19. Online

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Excellent tutorial. Not only is the subject material interesting, but it's also well explained and laid out. :)

    Just a small quibble - should you divide by 0.95 then? Like so:
    Code:
    motX /= 0.95F;
    motY /= 0.95F;
    motZ /= 0.95F;
    
    Otherwise the end result would be 0.95 * 1.10 = 1.0526, which results in 5% more velocity in each direction.
  20. Offline

    V10lator

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    If my math is correct 0.95 * 1.1 = 1.045 but anyway, wouldn't this be better:
    Code:java
    1. motX *= 0.91F;
    2. motY *= 0.91F;
    3. motZ *= 0.91F;

    Cause 0.91 * 1.1 = 1.001, so it still speeds up, but just by 0.1% - We could tell this is cause of gravity... :D
    or:
    Code:java
    1. motX *= 0.909F;
    2. motY *= 0.909F;
    3. motZ *= 0.909F;

    Then we have 0.909 * 1.1 = 0.999, so a little slowdown (0.1% again) - We could tell this is cause of the airs frictional resistance :D

    This post has been edited 1 time. It was last edited by V10lator Sep 11, 2012.
  21. Online

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Oh wow, how could I miss that one? It's not like I had a long, detailed tutorial to write. :p

    In any case, the value I (incorrectly) gave is actually approximately equal to 1 / 0.95, the inverse of the original multiplication.

    Yeah, just like that. :p

    But if you read the tutorial, you'll notice that it's 0.95 we want to reverse, not 1.1:
    Code:
    motX *= 0.95F;
    motY *= 0.95F;
    motZ *= 0.95F;
    To reverse it:
    Code:
    motX *= 1.052632F;
    motY *= 1.052632F;
    motZ *= 1.052632F;
    Where 1.052632 * 0.95 ≈ 1. And this time I doubled checked the math. :p
    Icyene likes this.
  22. Offline

    Icyene

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Thanks. Didn't know exactly what value I needed, so I just threw in 1.10 there :p
  23. Offline

    AlphaCoder

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    I can't find h_()! help
  24. Offline

    V10lator

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    @AlphaCoder that's because this method is obfuscated and this obfuscation changes with every minecraft release. If you aren't experienced enough to handle this then don't use it.
  25. Offline

    krazytraynz

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Would you have to go about things this way for custom entities, or could you also just code like you would for a client mod and use the @Override annotation a lot?
  26. Offline

    bob7

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Having fun with this :)
    Icyene likes this.
  27. Offline

    TechTeller96

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    I just want to know how to see which class corresponds to which .java file after decompilation, for example, I want to see the class file that corresponds to EntityMinecart.java
  28. Offline

    Icyene

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    They get decompiled respectively... i.e. EntityMinecart.class will get decompiled to EntityMinecart.java. With the exception of class files containing nested classes, of course. Those are designated by 'A$B' where 'B' is the nested class. Even then, most modern decompilers merges 'A' and 'B' together.
  29. Offline

    TechTeller96

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    No, but as you figured out that h_() corresponds to EntityFireball, how do I figure out which class file corresponds to EntityMinecart?
  30. Offline

    Icyene

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    I am confused as to what you are asking... h_() is a method, not a class... h_() is j_() in the latest versions, and is part of the base Entity class.

Share This Page