PaperMC/nms-patches/Entity.patch
md_5 257d6cd04f Process entity portalling towards the end of a tick.
Cross world teleportation works by taking a copy of an entity and moving it to a new world. After this happens the original entity is marked as dead so as to be removed from the original world, however it still undergoes one further tick in the main world, but with some information from the new world. It is not so easy to break out of this tick cycle if needed, so instead we move the portalling process towards the end of an existing tick. This ensures that the entity will not be spuriously ticked.
2017-03-20 15:41:15 +11:00

722 lines
30 KiB
Diff

--- a/net/minecraft/server/Entity.java
+++ b/net/minecraft/server/Entity.java
@@ -16,8 +16,48 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+// CraftBukkit start
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Server;
+import org.bukkit.TravelAgent;
+import org.bukkit.block.BlockFace;
+import org.bukkit.entity.Hanging;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Vehicle;
+import org.bukkit.event.entity.EntityCombustByEntityEvent;
+import org.bukkit.event.hanging.HangingBreakByEntityEvent;
+import org.bukkit.event.vehicle.VehicleBlockCollisionEvent;
+import org.bukkit.event.vehicle.VehicleEnterEvent;
+import org.bukkit.event.vehicle.VehicleExitEvent;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.craftbukkit.entity.CraftEntity;
+import org.bukkit.craftbukkit.entity.CraftPlayer;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.event.entity.EntityAirChangeEvent;
+import org.bukkit.event.entity.EntityCombustEvent;
+import org.bukkit.event.entity.EntityPortalEvent;
+import org.bukkit.plugin.PluginManager;
+// CraftBukkit end
+
public abstract class Entity implements ICommandListener {
+ // CraftBukkit start
+ private static final int CURRENT_LEVEL = 2;
+ static boolean isLevelAtLeast(NBTTagCompound tag, int level) {
+ return tag.hasKey("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level;
+ }
+
+ protected CraftEntity bukkitEntity;
+
+ public CraftEntity getBukkitEntity() {
+ if (bukkitEntity == null) {
+ bukkitEntity = CraftEntity.getEntity(world.getServer(), this);
+ }
+ return bukkitEntity;
+ }
+ // CraftBukikt end
+
private static final Logger a = LogManager.getLogger();
private static final List<ItemStack> b = Collections.emptyList();
private static final AxisAlignedBB c = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
@@ -101,6 +141,9 @@
private boolean aH;
private double[] aI;
private long aJ;
+ public boolean valid; // CraftBukkit
+ public org.bukkit.projectiles.ProjectileSource projectileSource; // CraftBukkit - For projectiles only
+ public boolean forceExplosionKnockback; // CraftBukkit - SPIGOT-949
public Entity(World world) {
this.id = Entity.entityCount++;
@@ -206,6 +249,33 @@
}
protected void setYawPitch(float f, float f1) {
+ // CraftBukkit start - yaw was sometimes set to NaN, so we need to set it back to 0
+ if (Float.isNaN(f)) {
+ f = 0;
+ }
+
+ if (f == Float.POSITIVE_INFINITY || f == Float.NEGATIVE_INFINITY) {
+ if (this instanceof EntityPlayer) {
+ this.world.getServer().getLogger().warning(this.getName() + " was caught trying to crash the server with an invalid yaw");
+ ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Nope");
+ }
+ f = 0;
+ }
+
+ // pitch was sometimes set to NaN, so we need to set it back to 0
+ if (Float.isNaN(f1)) {
+ f1 = 0;
+ }
+
+ if (f1 == Float.POSITIVE_INFINITY || f1 == Float.NEGATIVE_INFINITY) {
+ if (this instanceof EntityPlayer) {
+ this.world.getServer().getLogger().warning(this.getName() + " was caught trying to crash the server with an invalid pitch");
+ ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Nope");
+ }
+ f1 = 0;
+ }
+ // CraftBukkit end
+
this.yaw = f % 360.0F;
this.pitch = f1 % 360.0F;
}
@@ -228,6 +298,51 @@
this.U();
}
+ // CraftBukkit start
+ public void postTick() {
+ // No clean way to break out of ticking once the entity has been copied to a new world, so instead we move the portalling later in the tick cycle
+ if (!this.world.isClientSide && this.world instanceof WorldServer) {
+ this.world.methodProfiler.a("portal");
+ if (this.ak) {
+ MinecraftServer minecraftserver = this.world.getMinecraftServer();
+
+ if (true || minecraftserver.getAllowNether()) { // CraftBukkit
+ if (!this.isPassenger()) {
+ int i = this.V();
+
+ if (this.al++ >= i) {
+ this.al = i;
+ this.portalCooldown = this.aE();
+ byte b0;
+
+ if (this.world.worldProvider.getDimensionManager().getDimensionID() == -1) {
+ b0 = 0;
+ } else {
+ b0 = -1;
+ }
+
+ this.c(b0);
+ }
+ }
+
+ this.ak = false;
+ }
+ } else {
+ if (this.al > 0) {
+ this.al -= 4;
+ }
+
+ if (this.al < 0) {
+ this.al = 0;
+ }
+ }
+
+ this.H();
+ this.world.methodProfiler.b();
+ }
+ }
+ // CraftBukkit end
+
public void U() {
this.world.methodProfiler.a("entityBaseTick");
if (this.isPassenger() && this.bB().dead) {
@@ -244,12 +359,14 @@
this.lastZ = this.locZ;
this.lastPitch = this.pitch;
this.lastYaw = this.yaw;
+ // Moved up to postTick
+ /*
if (!this.world.isClientSide && this.world instanceof WorldServer) {
this.world.methodProfiler.a("portal");
if (this.ak) {
MinecraftServer minecraftserver = this.world.getMinecraftServer();
- if (minecraftserver.getAllowNether()) {
+ if (true || minecraftserver.getAllowNether()) { // CraftBukkit
if (!this.isPassenger()) {
int i = this.V();
@@ -283,6 +400,7 @@
this.H();
this.world.methodProfiler.b();
}
+ */
this.am();
this.ak();
@@ -334,6 +452,27 @@
protected void burnFromLava() {
if (!this.fireProof) {
this.damageEntity(DamageSource.LAVA, 4.0F);
+
+ // CraftBukkit start - Fallen in lava TODO: this event spams!
+ if (this instanceof EntityLiving) {
+ if (fireTicks <= 0) {
+ // not on fire yet
+ // TODO: shouldn't be sending null for the block
+ org.bukkit.block.Block damager = null; // ((WorldServer) this.l).getWorld().getBlockAt(i, j, k);
+ org.bukkit.entity.Entity damagee = this.getBukkitEntity();
+ EntityCombustEvent combustEvent = new org.bukkit.event.entity.EntityCombustByBlockEvent(damager, damagee, 15);
+ this.world.getServer().getPluginManager().callEvent(combustEvent);
+
+ if (!combustEvent.isCancelled()) {
+ this.setOnFire(combustEvent.getDuration());
+ }
+ } else {
+ // This will be called every single tick the entity is in lava, so don't throw an event
+ this.setOnFire(15);
+ }
+ return;
+ }
+ // CraftBukkit end - we also don't throw an event unless the object in lava is living, to save on some event calls
this.setOnFire(15);
}
}
@@ -374,6 +513,22 @@
this.a(this.getBoundingBox().d(d0, d1, d2));
this.recalcPosition();
} else {
+ // CraftBukkit start - Don't do anything if we aren't moving
+ // We need to do this regardless of whether or not we are moving thanks to portals
+ try {
+ this.checkBlockCollisions();
+ } catch (Throwable throwable) {
+ CrashReport crashreport = CrashReport.a(throwable, "Checking entity block collision");
+ CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being checked for collision");
+
+ this.appendEntityCrashDetails(crashreportsystemdetails);
+ throw new ReportedException(crashreport);
+ }
+ // Check if we're moving
+ if (d0 == 0 && d1 == 0 && d2 == 0 && this.isVehicle() && this.isPassenger()) {
+ return;
+ }
+ // CraftBukkit end
if (enummovetype == EnumMoveType.PISTON) {
long i = this.world.getTime();
@@ -515,7 +670,7 @@
}
}
- boolean flag = this.onGround || d1 != d1 && d1 < 0.0D;
+ boolean flag = this.onGround || d1 != d8 && d1 < 0.0D; // CraftBukkit - decompile error
double d11;
if (this.P > 0.0F && flag && (d7 != d0 || d9 != d2)) {
@@ -613,7 +768,7 @@
this.world.methodProfiler.a("rest");
this.recalcPosition();
this.positionChanged = d7 != d0 || d9 != d2;
- this.B = d1 != d1;
+ this.B = d1 != d8; // CraftBukkit - decompile error
this.onGround = this.B && d8 < 0.0D;
this.C = this.positionChanged || this.B;
l = MathHelper.floor(this.locX);
@@ -648,6 +803,28 @@
block1.a(this.world, this);
}
+ // CraftBukkit start
+ if (positionChanged && getBukkitEntity() instanceof Vehicle) {
+ Vehicle vehicle = (Vehicle) this.getBukkitEntity();
+ org.bukkit.block.Block bl = this.world.getWorld().getBlockAt(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ));
+
+ if (d6 > d0) {
+ bl = bl.getRelative(BlockFace.EAST);
+ } else if (d6 < d0) {
+ bl = bl.getRelative(BlockFace.WEST);
+ } else if (d8 > d2) {
+ bl = bl.getRelative(BlockFace.SOUTH);
+ } else if (d8 < d2) {
+ bl = bl.getRelative(BlockFace.NORTH);
+ }
+
+ if (bl.getType() != org.bukkit.Material.AIR) {
+ VehicleBlockCollisionEvent event = new VehicleBlockCollisionEvent(vehicle, bl);
+ world.getServer().getPluginManager().callEvent(event);
+ }
+ }
+ // CraftBukkit end
+
if (this.playStepSound() && (!this.onGround || !this.isSneaking() || !(this instanceof EntityHuman)) && !this.isPassenger()) {
double d22 = this.locX - d4;
double d23 = this.locY - d5;
@@ -681,6 +858,8 @@
}
}
+ // CraftBukkit start - Move to the top of the method
+ /*
try {
this.checkBlockCollisions();
} catch (Throwable throwable) {
@@ -690,6 +869,8 @@
this.appendEntityCrashDetails(crashreportsystemdetails);
throw new ReportedException(crashreport);
}
+ */
+ // CraftBukkit end
boolean flag1 = this.ai();
@@ -698,7 +879,14 @@
if (!flag1) {
++this.fireTicks;
if (this.fireTicks == 0) {
- this.setOnFire(8);
+ // CraftBukkit start
+ EntityCombustEvent event = new org.bukkit.event.entity.EntityCombustByBlockEvent(null, getBukkitEntity(), 8);
+ world.getServer().getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ this.setOnFire(event.getDuration());
+ }
+ // CraftBukkit end
}
}
} else if (this.fireTicks <= 0) {
@@ -819,7 +1007,7 @@
return null;
}
- protected void burn(int i) {
+ protected void burn(float i) { // CraftBukkit - int -> float
if (!this.fireProof) {
this.damageEntity(DamageSource.FIRE, (float) i);
}
@@ -987,6 +1175,13 @@
}
public void spawnIn(World world) {
+ // CraftBukkit start
+ if (world == null) {
+ die();
+ this.world = ((CraftWorld) Bukkit.getServer().getWorlds().get(0)).getHandle();
+ return;
+ }
+ // CraftBukkit end
this.world = world;
}
@@ -1219,6 +1414,18 @@
try {
nbttagcompound.set("Pos", this.a(new double[] { this.locX, this.locY, this.locZ}));
nbttagcompound.set("Motion", this.a(new double[] { this.motX, this.motY, this.motZ}));
+
+ // CraftBukkit start - Checking for NaN pitch/yaw and resetting to zero
+ // TODO: make sure this is the best way to address this.
+ if (Float.isNaN(this.yaw)) {
+ this.yaw = 0;
+ }
+
+ if (Float.isNaN(this.pitch)) {
+ this.pitch = 0;
+ }
+ // CraftBukkit end
+
nbttagcompound.set("Rotation", this.a(new float[] { this.yaw, this.pitch}));
nbttagcompound.setFloat("FallDistance", this.fallDistance);
nbttagcompound.setShort("Fire", (short) this.fireTicks);
@@ -1228,6 +1435,12 @@
nbttagcompound.setBoolean("Invulnerable", this.invulnerable);
nbttagcompound.setInt("PortalCooldown", this.portalCooldown);
nbttagcompound.a("UUID", this.getUniqueID());
+ // CraftBukkit start
+ // PAIL: Check above UUID reads 1.8 properly, ie: UUIDMost / UUIDLeast
+ nbttagcompound.setLong("WorldUUIDLeast", this.world.getDataManager().getUUID().getLeastSignificantBits());
+ nbttagcompound.setLong("WorldUUIDMost", this.world.getDataManager().getUUID().getMostSignificantBits());
+ nbttagcompound.setInt("Bukkit.updateLevel", CURRENT_LEVEL);
+ // CraftBukkit end
if (this.hasCustomName()) {
nbttagcompound.setString("CustomName", this.getCustomName());
}
@@ -1303,6 +1516,8 @@
this.motX = nbttaglist1.e(0);
this.motY = nbttaglist1.e(1);
this.motZ = nbttaglist1.e(2);
+
+ /* CraftBukkit start - Moved section down
if (Math.abs(this.motX) > 10.0D) {
this.motX = 0.0D;
}
@@ -1314,6 +1529,7 @@
if (Math.abs(this.motZ) > 10.0D) {
this.motZ = 0.0D;
}
+ // CraftBukkit end */
this.locX = nbttaglist.e(0);
this.locY = nbttaglist.e(1);
@@ -1371,6 +1587,58 @@
this.setPosition(this.locX, this.locY, this.locZ);
}
+ // CraftBukkit start
+ if (this instanceof EntityLiving) {
+ EntityLiving entity = (EntityLiving) this;
+
+ // Reset the persistence for tamed animals
+ if (entity instanceof EntityTameableAnimal && !isLevelAtLeast(nbttagcompound, 2) && !nbttagcompound.getBoolean("PersistenceRequired")) {
+ EntityInsentient entityinsentient = (EntityInsentient) entity;
+ entityinsentient.persistent = !entityinsentient.isTypeNotPersistent();
+ }
+ }
+ // CraftBukkit end
+
+ // CraftBukkit start - Exempt Vehicles from notch's sanity check
+ if (!(getBukkitEntity() instanceof Vehicle)) {
+ if (Math.abs(this.motX) > 10.0D) {
+ this.motX = 0.0D;
+ }
+
+ if (Math.abs(this.motY) > 10.0D) {
+ this.motY = 0.0D;
+ }
+
+ if (Math.abs(this.motZ) > 10.0D) {
+ this.motZ = 0.0D;
+ }
+ }
+ // CraftBukkit end
+
+ // CraftBukkit start - Reset world
+ if (this instanceof EntityPlayer) {
+ Server server = Bukkit.getServer();
+ org.bukkit.World bworld = null;
+
+ // TODO: Remove World related checks, replaced with WorldUID
+ String worldName = nbttagcompound.getString("world");
+
+ if (nbttagcompound.hasKey("WorldUUIDMost") && nbttagcompound.hasKey("WorldUUIDLeast")) {
+ UUID uid = new UUID(nbttagcompound.getLong("WorldUUIDMost"), nbttagcompound.getLong("WorldUUIDLeast"));
+ bworld = server.getWorld(uid);
+ } else {
+ bworld = server.getWorld(worldName);
+ }
+
+ if (bworld == null) {
+ EntityPlayer entityPlayer = (EntityPlayer) this;
+ bworld = ((org.bukkit.craftbukkit.CraftServer) server).getServer().getWorldServer(entityPlayer.dimension).getWorld();
+ }
+
+ spawnIn(bworld == null? null : ((CraftWorld) bworld).getHandle());
+ }
+ // CraftBukkit end
+
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.a(throwable, "Loading entity NBT");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being loaded");
@@ -1438,6 +1706,12 @@
if (itemstack.isEmpty()) {
return null;
} else {
+ // CraftBukkit start - Capture drops for death event
+ if (this instanceof EntityLiving && !((EntityLiving) this).forceDrops) {
+ ((EntityLiving) this).drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack));
+ return null;
+ }
+ // CraftBukkit end
EntityItem entityitem = new EntityItem(this.world, this.locX, this.locY + (double) f, this.locZ, itemstack);
entityitem.q();
@@ -1563,6 +1837,24 @@
if (entity.bB() != this) {
throw new IllegalStateException("Use x.startRiding(y), not y.addPassenger(x)");
} else {
+ // CraftBukkit start
+ com.google.common.base.Preconditions.checkState(!entity.passengers.contains(this), "Circular entity riding! %s %s", this, entity);
+
+ CraftEntity craft = (CraftEntity) entity.getBukkitEntity().getVehicle();
+ Entity orig = craft == null ? null : craft.getHandle();
+ if (getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity && entity.world.isChunkLoaded((int) entity.locX >> 4, (int) entity.locZ >> 4, false)) { // Boolean not used
+ VehicleEnterEvent event = new VehicleEnterEvent(
+ (Vehicle) getBukkitEntity(),
+ entity.getBukkitEntity()
+ );
+ Bukkit.getPluginManager().callEvent(event);
+ CraftEntity craftn = (CraftEntity) entity.getBukkitEntity().getVehicle();
+ Entity n = craftn == null ? null : craftn.getHandle();
+ if (event.isCancelled() || n != orig) {
+ return;
+ }
+ }
+ // CraftBukkit end
if (!this.world.isClientSide && entity instanceof EntityHuman && !(this.bw() instanceof EntityHuman)) {
this.passengers.add(0, entity);
} else {
@@ -1576,6 +1868,22 @@
if (entity.bB() == this) {
throw new IllegalStateException("Use x.stopRiding(y), not y.removePassenger(x)");
} else {
+ // CraftBukkit start
+ CraftEntity craft = (CraftEntity) entity.getBukkitEntity().getVehicle();
+ Entity orig = craft == null ? null : craft.getHandle();
+ if (getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity) {
+ VehicleExitEvent event = new VehicleExitEvent(
+ (Vehicle) getBukkitEntity(),
+ (LivingEntity) entity.getBukkitEntity()
+ );
+ Bukkit.getPluginManager().callEvent(event);
+ CraftEntity craftn = (CraftEntity) entity.getBukkitEntity().getVehicle();
+ Entity n = craftn == null ? null : craftn.getHandle();
+ if (event.isCancelled() || n != orig) {
+ return;
+ }
+ }
+ // CraftBukkit end
this.passengers.remove(entity);
entity.j = 60;
}
@@ -1715,14 +2023,49 @@
}
public void setAirTicks(int i) {
- this.datawatcher.set(Entity.az, Integer.valueOf(i));
+ // CraftBukkit start
+ EntityAirChangeEvent event = new EntityAirChangeEvent(this.getBukkitEntity(), i);
+ event.getEntity().getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return;
+ }
+ this.datawatcher.set(Entity.az, Integer.valueOf(event.getAmount()));
+ // CraftBukkit end
}
public void onLightningStrike(EntityLightning entitylightning) {
- this.damageEntity(DamageSource.LIGHTNING, 5.0F);
+ // CraftBukkit start
+ final org.bukkit.entity.Entity thisBukkitEntity = this.getBukkitEntity();
+ final org.bukkit.entity.Entity stormBukkitEntity = entitylightning.getBukkitEntity();
+ final PluginManager pluginManager = Bukkit.getPluginManager();
+
+ if (thisBukkitEntity instanceof Hanging) {
+ HangingBreakByEntityEvent hangingEvent = new HangingBreakByEntityEvent((Hanging) thisBukkitEntity, stormBukkitEntity);
+ pluginManager.callEvent(hangingEvent);
+
+ if (hangingEvent.isCancelled()) {
+ return;
+ }
+ }
+
+ if (this.fireProof) {
+ return;
+ }
+ CraftEventFactory.entityDamage = entitylightning;
+ if (!this.damageEntity(DamageSource.LIGHTNING, 5.0F)) {
+ CraftEventFactory.entityDamage = null;
+ return;
+ }
+ // CraftBukkit end
++this.fireTicks;
if (this.fireTicks == 0) {
- this.setOnFire(8);
+ // CraftBukkit start - Call a combust event when lightning strikes
+ EntityCombustByEntityEvent entityCombustEvent = new EntityCombustByEntityEvent(stormBukkitEntity, thisBukkitEntity, 8);
+ pluginManager.callEvent(entityCombustEvent);
+ if (!entityCombustEvent.isCancelled()) {
+ this.setOnFire(entityCombustEvent.getDuration());
+ }
+ // CraftBukkit end
}
}
@@ -1867,19 +2210,70 @@
if (!this.world.isClientSide && !this.dead) {
this.world.methodProfiler.a("changeDimension");
MinecraftServer minecraftserver = this.B_();
- int j = this.dimension;
- WorldServer worldserver = minecraftserver.getWorldServer(j);
- WorldServer worldserver1 = minecraftserver.getWorldServer(i);
+ // CraftBukkit start - Move logic into new function "teleportTo(Location,boolean)"
+ // int j = this.dimension;
+ // WorldServer worldserver = minecraftserver.getWorldServer(j);
+ // WorldServer worldserver1 = minecraftserver.getWorldServer(i);
+ WorldServer exitWorld = null;
+ if (this.dimension < CraftWorld.CUSTOM_DIMENSION_OFFSET) { // Plugins must specify exit from custom Bukkit worlds
+ // Only target existing worlds (compensate for allow-nether/allow-end as false)
+ for (WorldServer world : minecraftserver.worlds) {
+ if (world.dimension == i) {
+ exitWorld = world;
+ }
+ }
+ }
+
+ BlockPosition blockposition = null; // PAIL: CHECK
+ Location enter = this.getBukkitEntity().getLocation();
+ Location exit;
+ if (exitWorld != null) {
+ if (blockposition != null) {
+ exit = new Location(exitWorld.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ());
+ } else {
+ exit = minecraftserver.getPlayerList().calculateTarget(enter, minecraftserver.getWorldServer(i));
+ }
+ }
+ else {
+ exit = null;
+ }
+ boolean useTravelAgent = exitWorld != null && !(this.dimension == 1 && exitWorld.dimension == 1); // don't use agent for custom worlds or return from THE_END
+
+ TravelAgent agent = exit != null ? (TravelAgent) ((CraftWorld) exit.getWorld()).getHandle().getTravelAgent() : org.bukkit.craftbukkit.CraftTravelAgent.DEFAULT; // return arbitrary TA to compensate for implementation dependent plugins
+ EntityPortalEvent event = new EntityPortalEvent(this.getBukkitEntity(), enter, exit, agent);
+ event.useTravelAgent(useTravelAgent);
+ event.getEntity().getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled() || event.getTo() == null || event.getTo().getWorld() == null || !this.isAlive()) {
+ return null;
+ }
+ exit = event.useTravelAgent() ? event.getPortalTravelAgent().findOrCreate(event.getTo()) : event.getTo();
+ // Need to make sure the profiler state is reset afterwards (but we still want to time the call)
+ Entity entity = this.teleportTo(exit, true);
+ this.world.methodProfiler.b();
+ return entity;
+ }
+ return null;
+ }
+
+ public Entity teleportTo(Location exit, boolean portal) {
+ if (true) {
+ WorldServer worldserver = ((CraftWorld) getBukkitEntity().getLocation().getWorld()).getHandle();
+ WorldServer worldserver1 = ((CraftWorld) exit.getWorld()).getHandle();
+ int i = worldserver1.dimension;
+ // CraftBukkit end
this.dimension = i;
+ /* CraftBukkit start - TODO: Check if we need this
if (j == 1 && i == 1) {
worldserver1 = minecraftserver.getWorldServer(0);
this.dimension = 0;
}
+ // CraftBukkit end */
this.world.kill(this);
this.dead = false;
this.world.methodProfiler.a("reposition");
+ /* CraftBukkit start - Handled in calculateTarget
BlockPosition blockposition;
if (i == 1) {
@@ -1908,12 +2302,18 @@
blockposition = new BlockPosition(this);
}
- worldserver.entityJoinedWorld(this, false);
+ // CraftBukkit end */
+ // CraftBukkit start - Ensure chunks are loaded in case TravelAgent is not used which would initially cause chunks to load during find/create
+ // minecraftserver.getPlayerList().changeWorld(this, j, worldserver, worldserver1);
+ worldserver1.getMinecraftServer().getPlayerList().repositionEntity(this, exit, portal);
+ // worldserver.entityJoinedWorld(this, false); // Handled in repositionEntity
+ // CraftBukkit end
this.world.methodProfiler.c("reloading");
Entity entity = EntityTypes.a(this.getClass(), (World) worldserver1);
if (entity != null) {
entity.a(this);
+ /* CraftBukkit start - We need to do this...
if (j == 1 && i == 1) {
BlockPosition blockposition1 = worldserver1.q(worldserver1.getSpawn());
@@ -1921,6 +2321,7 @@
} else {
entity.setPositionRotation(blockposition, entity.yaw, entity.pitch);
}
+ // CraftBukkit end */
boolean flag = entity.attachedToPlayer;
@@ -1928,13 +2329,21 @@
worldserver1.addEntity(entity);
entity.attachedToPlayer = flag;
worldserver1.entityJoinedWorld(entity, false);
+ // CraftBukkit start - Forward the CraftEntity to the new entity
+ this.getBukkitEntity().setHandle(entity);
+ entity.bukkitEntity = this.getBukkitEntity();
+
+ if (this instanceof EntityInsentient) {
+ ((EntityInsentient)this).unleash(true, false); // Unleash to prevent duping of leads.
+ }
+ // CraftBukkit end
}
this.dead = true;
this.world.methodProfiler.b();
worldserver.m();
worldserver1.m();
- this.world.methodProfiler.b();
+ // this.world.methodProfiler.b(); // CraftBukkit: Moved up to keep balanced
return entity;
} else {
return null;
@@ -2038,6 +2447,11 @@
}
public void setCustomName(String s) {
+ // CraftBukkit start - Add a sane limit for name length
+ if (s.length() > 256) {
+ s = s.substring(0, 256);
+ }
+ // CraftBukkit end
this.datawatcher.set(Entity.aA, s);
}
@@ -2095,7 +2509,26 @@
}
public void a(AxisAlignedBB axisalignedbb) {
- this.boundingBox = axisalignedbb;
+ // CraftBukkit start - block invalid bounding boxes
+ double a = axisalignedbb.a,
+ b = axisalignedbb.b,
+ c = axisalignedbb.c,
+ d = axisalignedbb.d,
+ e = axisalignedbb.e,
+ f = axisalignedbb.f;
+ double len = axisalignedbb.d - axisalignedbb.a;
+ if (len < 0) d = a;
+ if (len > 64) d = a + 64.0;
+
+ len = axisalignedbb.e - axisalignedbb.b;
+ if (len < 0) e = b;
+ if (len > 64) e = b + 64.0;
+
+ len = axisalignedbb.f - axisalignedbb.c;
+ if (len < 0) f = c;
+ if (len > 64) f = c + 64.0;
+ this.boundingBox = new AxisAlignedBB(a, b, c, d, e, f);
+ // CraftBukkit end
}
public float getHeadHeight() {
@@ -2269,7 +2702,7 @@
for (Iterator iterator = this.bx().iterator(); iterator.hasNext(); entity.a(oclass, set)) {
entity = (Entity) iterator.next();
if (oclass.isAssignableFrom(entity.getClass())) {
- set.add(entity);
+ set.add((T) entity); // CraftBukkit - decompile error
}
}