Solved How to spawn a custom entity

Discussion in 'Plugin Development' started by avatarDr, Dec 22, 2012.

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

    avatarDr

    I'm trying now to spawn custom entities (overriding native ones), but whenever I use World.addEntity method my client crushes.
    However, all what I did is just copy methods from CraftServer.spawn().
    Class:
    Code:
    final class MyZombie extends EntityZombie{
     
        public MyZombie(World w){super(w);}
     
        public final int getMaxHealth(){return 40;}
     
        static final void spawn(Zombie oldz){
            Location l=oldz.getLocation();
            EntityZombie z=new MyZombie(((CraftWorld)l.getWorld()).getHandle());
            z.setLocation(l.getX(),l.getY(),l.getZ(),l.getPitch(),l.getYaw());
            z.world.removeEntity(((CraftZombie)oldz).getHandle());
            z.world.addEntity(z,SpawnReason.CUSTOM);
        }
    }
     
  2. Offline

    fireblast709

    You could override it somewhere, so the server spawns your entities. Though not sure where that was
     
  3. Offline

    avatarDr

    Bukkit World and CraftWorld methods spawn only basic classes (checked their code) and the native World has only addEntity(), which causes crushes.
    (Don't you recommend to override CraftWorld? Because after that I'll have to run them instead of existing worlds.)

    Do you know any plugin which implements custom entities?
     
  4. Offline

    fireblast709

  5. Offline

    avatarDr

    Well, finally I did it. Thanks again for all your pieces of advice.
     
  6. Offline

    fireblast709

  7. Offline

    Major_Derp

  8. Offline

    avatarDr

    Code please.

    Btw, I still have the issues with my subclass. When I reload server, zombies stop being instances of MyZombie.
    I mean that z.getClass().toString() shows Class MyClass, but (z instanceof MyClass==false). Is it possible to fix this problem? May be by implementing some interface.
    And another one problem happens when I try to replace entities in onEnable() method. The constructor and it's invokation are the same and when it spawns in SpawnEvent everything ok, but when about 5 entities are being replaced at once it still crushes the client.
     
  9. Offline

    fireblast709

    Major_Derp Do you have the correct imports? As there is a change in the Craftbukkit and net.minecraft.server packages

    Try loading your plugin before the world (see the plugin.yml page on the wiki for this)

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

    Major_Derp

    I finally did get it working, and the changed packages where part of it. But alsoI found a solution it is a modification of this: http://forums.bukkit.org/threads/tutorial-how-to-customize-the-behaviour-of-a-mob-or-entity.54547/
    It works for the most part, but theres still some slight problems. The modified zombies can only be spawned from eggs, and the damage modification has no effect, its still the default damage. Here is the code if you guys want to use, and maybe someone could help figure out why some things don't work.

    The Super Zombie class:
    Code:
    import java.lang.reflect.Field;
     
    import net.minecraft.server.v1_4_5.EntityHuman;
    import net.minecraft.server.v1_4_5.EntityVillager;
    import net.minecraft.server.v1_4_5.PathfinderGoalBreakDoor;
    import net.minecraft.server.v1_4_5.PathfinderGoalFloat;
    import net.minecraft.server.v1_4_5.PathfinderGoalHurtByTarget;
    import net.minecraft.server.v1_4_5.PathfinderGoalLookAtPlayer;
    import net.minecraft.server.v1_4_5.PathfinderGoalMeleeAttack;
    import net.minecraft.server.v1_4_5.PathfinderGoalMoveThroughVillage;
    import net.minecraft.server.v1_4_5.PathfinderGoalMoveTowardsRestriction;
    import net.minecraft.server.v1_4_5.PathfinderGoalNearestAttackableTarget;
    import net.minecraft.server.v1_4_5.PathfinderGoalRandomLookaround;
    import net.minecraft.server.v1_4_5.PathfinderGoalRandomStroll;
     
    //import org.bukkit.World;
    import org.bukkit.craftbukkit.v1_4_5.util.UnsafeList;
     
    public class SuperZombie extends net.minecraft.server.v1_4_5.EntityZombie {
        public int damage;
     
        @SuppressWarnings("rawtypes")
        public SuperZombie(net.minecraft.server.v1_4_5.World world) {
            super(world);
            this.bw = .40F;  //Change this to your liking. This is were you set the speed
            this.damage = 15; // set the damage
    //There's also a ton of options of you do this.  play around with it
         
     
            try {
                Field gsa = net.minecraft.server.v1_4_5.PathfinderGoalSelector.class.getDeclaredField("a");
                gsa.setAccessible(true);
     
                gsa.set(this.goalSelector, new UnsafeList());
                gsa.set(this.targetSelector, new UnsafeList());
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
     
            this.goalSelector.a(0, new PathfinderGoalFloat(this));
            this.goalSelector.a(1, new PathfinderGoalBreakDoor(this));
            this.goalSelector.a(2, new PathfinderGoalMeleeAttack(this, EntityHuman.class, (float) (this.bw * 1.9f), false)); // this one to attack human
            this.goalSelector.a(3, new PathfinderGoalMeleeAttack(this, EntityVillager.class, (float) this.bw, true));
            this.goalSelector.a(4, new PathfinderGoalMoveTowardsRestriction(this, (float) this.bw));
            this.goalSelector.a(5, new PathfinderGoalMoveThroughVillage(this, (float) this.bw, false));
            this.goalSelector.a(6, new PathfinderGoalRandomStroll(this, (float) this.bw));
            this.goalSelector.a(7, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); // this one to look at human
            this.goalSelector.a(7, new PathfinderGoalRandomLookaround(this));
            this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, false));
            this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityHuman.class, 8.0F, 0, true)); // this one to target human
            this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityVillager.class, 16.0F, 0, false));
        }
    }
    The onEnable part(The part to add to onEnable):
    Code:
    try{
                @SuppressWarnings("rawtypes")
                Class[] args = new Class[3];
                args[0] = Class.class;
                args[1] = String.class;
                args[2] = int.class;
     
                Method a = net.minecraft.server.v1_4_5.EntityTypes.class.getDeclaredMethod("a", args);
                a.setAccessible(true);
     
                a.invoke(a, SuperZombie.class, "Zombie", 54);
            }catch (Exception e){
                e.printStackTrace();
                this.setEnabled(false);
            }
    And the @Eventhandler part(I put this part in the main class):
    Code:
    @EventHandler
        public void onCreatureSpawn(CreatureSpawnEvent event){
            if (event.isCancelled()) return;
     
            Location location = event.getLocation();
            Entity entity = event.getEntity();
            EntityType entityType = event.getEntityType();
            World world = location.getWorld();
     
            net.minecraft.server.v1_4_5.World mcWorld = ((CraftWorld) world).getHandle();
            net.minecraft.server.v1_4_5.Entity mcEntity = (((CraftEntity) entity).getHandle());
     
            if (entityType == EntityType.ZOMBIE && mcEntity instanceof SuperZombie == false){
                SuperZombie bloodMoonEntityZombie = new SuperZombie(mcWorld);
     
                bloodMoonEntityZombie.setPosition(location.getX(), location.getY(), location.getZ());
     
                mcWorld.removeEntity((net.minecraft.server.v1_4_5.EntityZombie) mcEntity);
                mcWorld.addEntity(bloodMoonEntityZombie, SpawnReason.CUSTOM);
     
                return;
            }
        }
     
Thread Status:
Not open for further replies.

Share This Page