Hey, I posted this in the Bukkit+ section a while ago and someone just PMed me asking how to do it, so reposting here This... But more specifically, if you want to mess with the way Zombies work, you have to do this. First create a class that extends the zombie class making the necessary replacements for example this is my one from BloodMoon Code:java public class BloodMoonEntityZombie extends net.minecraft.server.EntityZombie { public BloodMoonEntityZombie(World world) { super(world); } @Override public void s_(){ Zombie zombie = (Zombie) this.getBukkitEntity(); Location from = new Location(zombie.getWorld(), this.lastX, this.lastY, this.lastZ, this.lastYaw, this.lastPitch); Location to = new Location(zombie.getWorld(), this.locX, this.locY, this.locZ, this.yaw, this.pitch); ZombieMoveEvent event = new ZombieMoveEvent(zombie, from, to); this.world.getServer().getPluginManager().callEvent(event); if (event.isCancelled() && zombie.isDead() == false){ return; } super.s_(); } } All I am doing here is adding a move event for the mob so there is nothing that looks too fancy. super.s_(); is just calling the s_() method from the actual class. Now there is a problem, you cant just create an instance of this class and spawn it as an entity because the game links each mob to a specific class file. So you need to update that link to make this new class apply to zombies. You do can do this in your onEnable method like so. Code:java 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.EntityTypes.class.getDeclaredMethod("a", args); a.setAccessible(true); a.invoke(a, BloodMoonEntityZombie.class, "Zombie", 54); }catch (Exception e){ e.printStackTrace(); this.setEnabled(false); } Now you will be able to spawn your customized zombie. BUT there is one more problem, the game does not know about your new zombie class so any naturally spawning zombies will still use the old one. To fix this you need to add a listener for the creature_spawn event and replace any zombies spawns with the custom zombie. Code:java public void onCreatureSpawn(CreatureSpawnEvent event){ if (event.isCancelled()) return; Location location = event.getLocation(); Entity entity = event.getEntity(); CreatureType creatureType = event.getCreatureType(); World world = location.getWorld(); net.minecraft.server.World mcWorld = ((CraftWorld) world).getHandle(); net.minecraft.server.Entity mcEntity = (((CraftEntity) entity).getHandle()); if (creatureType == CreatureType.ZOMBIE && mcEntity instanceof BloodMoonEntityZombie == false){ BloodMoonEntityZombie bloodMoonEntityZombie = new BloodMoonEntityZombie(mcWorld); bloodMoonEntityZombie.setPosition(location.getX(), location.getY(), location.getZ()); mcWorld.removeEntity((net.minecraft.server.EntityZombie) mcEntity); mcWorld.addEntity(bloodMoonEntityZombie, SpawnReason.CUSTOM); return; } } NOTE: Where I used s_() you probably need to use m_(), both seem to work but I don't really know what either does. My best guess is one is onTick and one is onMove.
Would be cleaner for the sake of our eyes if you used ['syntax=java]"the java code"['/syntax] xD Else, very good!
Great tutorial Glad to see something like this. Should help people alot! EDIT: 1) What the guy above said ^_^ 2) mcWorld.removeEntity((net.minecraft.server.EntityZombie) mcEntity); Do you really have to cast?
Looking at it, probably not, I must have done that for some reason though.Perhaps there was no remove() for a general entity. Also I changed the syntax tags
Looks much better Just thought you could remove the cast, as it might be kinda confusing to new people
I'll test without it and remove if it's not actually needed. I still think I didn't just do it for fun Updating SkylandsPlus for 1.1 at the moment.
The onEnable() section needs to be explained a lot more. What's the 54 parameter? What's the "a" parameter?
54 comes from this list https://github.com/Bukkit/mc-dev/blob/master/net/minecraft/server/EntityTypes.java#L94 and a() is the method used to update the map (defined here https://github.com/Bukkit/mc-dev/blob/master/net/minecraft/server/EntityTypes.java#L15)
Thanks for that, it's all clear now One issue I found, is that with 1.1 (didn't try with earlier builds) the line "Fetching addPacket for removed entity: CraftZombie" would be output to the console every time. Do yo know how to avoid this?
That has been a problem since 1.8, it happens every time a entity is removed form the world. The only fix would be to modify the craftbukkit source.
I've found a workaround, but I haven't done any testing to find out if it's a suitable solution. Code:Java bloodMoonEntityZombie.setPosition(location.getX(), location.getY(), location.getZ());event.setCancelled(true);mcWorld.addEntity(bloodMoonEntityZombie, event.getSpawnReason());return; This will produce the desired effect (the original mob doesn't spawn while the new one does) without the message.
You cant really rely on cancelling the event since another plugin may uncancel it leading to double mobs and a crash because of the modifications we made to the enetitytypes list.
I've been building on this quite heavily, with many issues that need to be resolved, but there's one thing I can't seem to do. Hopefully it's something easy and I'm being stupid Is there an easy way to do the reverse of this line: net.minecraft.server.Entity mcEntity = (((CraftEntity) entity).getHandle()); i.e. cast a Minecraft entity back to a Bukkit entity?
There is often a .getBukkitEntity() method, try that ? EDIT: Example on this line form the above code Code:java Zombie zombie = (Zombie) this.getBukkitEntity();
@Jacek Good tutorial, using reflection but I have a question about that : Because you replaced the entity Class in the EntityType, that if I right, is used by the game to spawn Entities. Then why do you need to replace them on spawn ? Maybe bukkit changed the way that the creature are spawned and use their own CreatureType ( https://github.com/Bukkit/Bukkit/blob/master/src/main/java/org/bukkit/entity/CreatureType.java ), if you change there the class used, I think it will do the trick and avoid to listen to the event. (Just an hypothesis) Oh and of course by doing that, you have to create another Zombie (a Bukkit entity) that will be linked to your minecraft entity.
It doesn't seem to replace all of them. Spawn eggs and spawners will spawn the new class without needing to be removed and added, but mobs that are spawned from code (and naturally I believe) use the old class.
I'm trying to create a custom zombie class that can see farther than 16 blocks, but it doesnt seem to work. If I extend the EntityZombie class with my own, I can spawn the new class and everything works just perfectly. The sight for the mobs is set in their parent class however... Somehow I need to override the method in EntityMonster I believe. Since I don't think it's possible from a grandchild class, I changed my custom class to also extend EntityMonster like the original EntityZombie does. This more or less seems to run ok, but no zombie appears when I attempt to spawn it. Any ideas?
If you don't extend EntityZombie you will need to copy all of the code from it into your class. You should be able to override a method from EntityMonster if you extend EntityZombie though, is it actually a method and not a field ?
I did that. Copied all the code and all. It would appear to be a near perfect clone of EntityZombie except for the override for findTarget. And I just said method, it may be a field, I have no idea. I'm teaching myself Java coming from a C background and I don't know all the terms and definitions.
Okay well a field is a property or a variable or not-a-function I don't think copying the entire zombie class is the way to go really, it will be impossible to keep up with updates to the game.
After looking at the source code and the bukkit modification, we can't change "easily" which class is used, because of the code used in CraftWorld to spawn an entity. https://github.com/Bukkit/CraftBukk...a/org/bukkit/craftbukkit/CraftWorld.java#L699 It's checking the Assignable Class and change the class by the Bukkit Entity in all the case. That explain why we need to use the onSpawn event.
May anyone be interested in this, this is the source code of TrainCarts where I swap two minecarts: Code: public static void replaceMinecarts(EntityMinecart toreplace, EntityMinecart with) { with.yaw = toreplace.yaw; with.pitch = toreplace.pitch; with.locX = toreplace.locX; with.locY = toreplace.locY; with.locZ = toreplace.locZ; with.motX = toreplace.motX; with.motY = toreplace.motY; with.motZ = toreplace.motZ; with.b = toreplace.b; with.c = toreplace.c; with.fallDistance = toreplace.fallDistance; with.ticksLived = toreplace.ticksLived; with.uniqueId = toreplace.uniqueId; with.setDamage(toreplace.getDamage()); ItemUtil.transfer(toreplace, with); with.dead = false; toreplace.dead = true; with.setDerailedVelocityMod(toreplace.getDerailedVelocityMod()); with.setFlyingVelocityMod(toreplace.getFlyingVelocityMod()); //longer public in 1.0.0... :-( //with.e = toreplace.e; //swap MinecartSwapEvent.call(toreplace, with); ((WorldServer) toreplace.world).tracker.untrackEntity(toreplace); toreplace.world.removeEntity(toreplace); with.world.addEntity(with); if (toreplace.passenger != null) toreplace.passenger.setPassengerOf(with); }
This might be a dumb question. But why do you use net.minecraft.server? Is the bukkit api not sufficient enough?