geforkt von Mirrors/Paper
c2e4e91b1b
## **Current API** The current world generation API is very old and limited when you want to make more complex world generation. Resulting in some hard to fix bugs such as that you cannot modify blocks outside the chunk in the BlockPopulator (which should and was per the docs possible), or strange behavior such as SPIGOT-5880. ## **New API** With the new API, the generation is more separate in multiple methods and is more in line with Vanilla chunk generation. The new API is designed to as future proof as possible. If for example a new generation step is added it can easily also be added as a step in API by simply creating the method for it. On the other side if a generation step gets removed, the method can easily be called after another, which is the case with surface and bedrock. The new API and changes are also fully backwards compatible with old chunk generators. ### **Changes in the new api** **Extra generation steps:** Noise, surface, bedrock and caves are added as steps. With those generation steps three extra methods for Vanilla generation are also added. Those new methods provide the ChunkData instead of returning one. The reason for this is, that the ChunkData is now backed by a ChunkAccess. With this, each step has the information of the step before and the Vanilla information (if chosen by setting a 'should' method to true). The old method is deprecated. **New class BiomeProvider** The BiomeProvider acts as Biome source and wrapper for the NMS class WorldChunkManager. With this the underlying Vanilla ChunkGeneration knows which Biome to use for the structure and decoration generation. (Fixes: SPIGOT-5880). Although the List of Biomes which is required in BiomeProvider, is currently not much in use in Vanilla, I decided to add it to future proof the API when it may be required in later versions of Minecraft. The BiomeProvider is also separated from the ChunkGenerator for plugins which only want to change the biome map, such as single Biome worlds or if some biomes should be more present than others. **Deprecated isParallelCapable** Mojang has and is pushing to a more multi threaded chunk generation. This should also be the case for custom chunk generators. This is why the new API only supports multi threaded generation. This does not affect the old API, which is still checking this. **Base height method added** This method was added to also bring the Minecraft generator and Bukkit generator more in line. With this it is possible to return the max height of a location (before decorations). This is useful to let most structures know were to place them. This fixes SPIGOT-5567. (This fixes not all structures placement, desert pyramids for example are still way up at y-level 64, This however is more a vanilla bug and should be fixed at Mojangs end). **WorldInfo Class** The World object was swapped for a WorldInfo object. This is because many methods of the World object won't work during world generation and would mostly likely result in a deadlock. It contains any information a plugin should need to identify the world. **BlockPopulator Changes** Instead of directly manipulating a chunk, changes are now made to a new class LimitedRegion, this class provides methods to populated the chunk and its surrounding area. The wrapping is done so that the population can be moved into the place where Minecraft generates decorations. Where there is no chunk to access yet. By moving it into this place the generation is now async and the surrounding area of the chunk can also be used. For common methods between the World and LimitedRegion a RegionAccessor was added. By: DerFrZocker <derrieple@gmail.com>
773 Zeilen
34 KiB
Diff
773 Zeilen
34 KiB
Diff
--- a/net/minecraft/world/entity/Entity.java
|
|
+++ b/net/minecraft/world/entity/Entity.java
|
|
@@ -116,8 +116,58 @@
|
|
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.block.BlockFace;
|
|
+import org.bukkit.command.CommandSender;
|
|
+import org.bukkit.craftbukkit.event.CraftPortalEvent;
|
|
+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.entity.Pose;
|
|
+import org.bukkit.event.entity.EntityAirChangeEvent;
|
|
+import org.bukkit.event.entity.EntityCombustEvent;
|
|
+import org.bukkit.event.entity.EntityDropItemEvent;
|
|
+import org.bukkit.event.entity.EntityPortalEvent;
|
|
+import org.bukkit.event.entity.EntityPoseChangeEvent;
|
|
+import org.bukkit.event.player.PlayerTeleportEvent;
|
|
+import org.bukkit.plugin.PluginManager;
|
|
+// CraftBukkit end
|
|
+
|
|
public abstract class Entity implements INamableTileEntity, EntityAccess, 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;
|
|
+ }
|
|
+
|
|
+ private CraftEntity bukkitEntity;
|
|
+
|
|
+ public CraftEntity getBukkitEntity() {
|
|
+ if (bukkitEntity == null) {
|
|
+ bukkitEntity = CraftEntity.getEntity(level.getCraftServer(), this);
|
|
+ }
|
|
+ return bukkitEntity;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public CommandSender getBukkitSender(CommandListenerWrapper wrapper) {
|
|
+ return getBukkitEntity();
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
protected static final Logger LOGGER = LogManager.getLogger();
|
|
public static final String ID_TAG = "id";
|
|
public static final String PASSENGERS_TAG = "Passengers";
|
|
@@ -224,6 +274,22 @@
|
|
private float crystalSoundIntensity;
|
|
private int lastCrystalSoundPlayTick;
|
|
public boolean hasVisualFire;
|
|
+ // CraftBukkit start
|
|
+ public boolean persist = true;
|
|
+ public boolean valid;
|
|
+ public boolean generation;
|
|
+ public org.bukkit.projectiles.ProjectileSource projectileSource; // For projectiles only
|
|
+ public boolean forceExplosionKnockback; // SPIGOT-949
|
|
+ public boolean persistentInvisibility = false;
|
|
+
|
|
+ public float getBukkitYaw() {
|
|
+ return this.yRot;
|
|
+ }
|
|
+
|
|
+ public boolean isChunkLoaded() {
|
|
+ return level.isChunkLoaded((int) Math.floor(this.locX()) >> 4, (int) Math.floor(this.locZ()) >> 4);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
|
|
public Entity(EntityTypes<?> entitytypes, World world) {
|
|
this.id = Entity.ENTITY_COUNTER.incrementAndGet();
|
|
@@ -359,6 +425,12 @@
|
|
public void ae() {}
|
|
|
|
public void setPose(EntityPose entitypose) {
|
|
+ // CraftBukkit start
|
|
+ if (entitypose == this.getPose()) {
|
|
+ return;
|
|
+ }
|
|
+ this.level.getCraftServer().getPluginManager().callEvent(new EntityPoseChangeEvent(this.getBukkitEntity(), Pose.values()[entitypose.ordinal()]));
|
|
+ // CraftBukkit end
|
|
this.entityData.set(Entity.DATA_POSE, entitypose);
|
|
}
|
|
|
|
@@ -375,6 +447,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.level.getCraftServer().getLogger().warning(this.getName() + " was caught trying to crash the server with an invalid yaw");
|
|
+ ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Infinite yaw (Hacking?)");
|
|
+ }
|
|
+ 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.level.getCraftServer().getLogger().warning(this.getName() + " was caught trying to crash the server with an invalid pitch");
|
|
+ ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Infinite pitch (Hacking?)");
|
|
+ }
|
|
+ f1 = 0;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
this.setYRot(f % 360.0F);
|
|
this.setXRot(f1 % 360.0F);
|
|
}
|
|
@@ -416,6 +515,15 @@
|
|
this.entityBaseTick();
|
|
}
|
|
|
|
+ // 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 instanceof EntityPlayer)) {
|
|
+ this.doPortalTick();
|
|
+ }
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
public void entityBaseTick() {
|
|
this.level.getMethodProfiler().enter("entityBaseTick");
|
|
if (this.isPassenger() && this.getVehicle().isRemoved()) {
|
|
@@ -429,7 +537,7 @@
|
|
this.walkDistO = this.walkDist;
|
|
this.xRotO = this.getXRot();
|
|
this.yRotO = this.getYRot();
|
|
- this.doPortalTick();
|
|
+ if (this instanceof EntityPlayer) this.doPortalTick(); // CraftBukkit - // Moved up to postTick
|
|
if (this.aV()) {
|
|
this.aW();
|
|
}
|
|
@@ -507,7 +615,23 @@
|
|
|
|
public void burnFromLava() {
|
|
if (!this.isFireProof()) {
|
|
- this.setOnFire(15);
|
|
+ // CraftBukkit start - Fallen in lava TODO: this event spams!
|
|
+ if (this instanceof EntityLiving && remainingFireTicks <= 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.level.getCraftServer().getPluginManager().callEvent(combustEvent);
|
|
+
|
|
+ if (!combustEvent.isCancelled()) {
|
|
+ this.setOnFire(combustEvent.getDuration(), false);
|
|
+ }
|
|
+ } else {
|
|
+ // This will be called every single tick the entity is in lava, so don't throw an event
|
|
+ this.setOnFire(15, false);
|
|
+ }
|
|
+ // CraftBukkit end - we also don't throw an event unless the object in lava is living, to save on some event calls
|
|
if (this.damageEntity(DamageSource.LAVA, 4.0F)) {
|
|
this.playSound(SoundEffects.GENERIC_BURN, 0.4F, 2.0F + this.random.nextFloat() * 0.4F);
|
|
}
|
|
@@ -516,6 +640,22 @@
|
|
}
|
|
|
|
public void setOnFire(int i) {
|
|
+ // CraftBukkit start
|
|
+ this.setOnFire(i, true);
|
|
+ }
|
|
+
|
|
+ public void setOnFire(int i, boolean callEvent) {
|
|
+ if (callEvent) {
|
|
+ EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), i);
|
|
+ this.level.getCraftServer().getPluginManager().callEvent(event);
|
|
+
|
|
+ if (event.isCancelled()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ i = event.getDuration();
|
|
+ }
|
|
+ // CraftBukkit end
|
|
int j = i * 20;
|
|
|
|
if (this instanceof EntityLiving) {
|
|
@@ -614,6 +754,28 @@
|
|
block.a((IBlockAccess) this.level, this);
|
|
}
|
|
|
|
+ // CraftBukkit start
|
|
+ if (horizontalCollision && getBukkitEntity() instanceof Vehicle) {
|
|
+ Vehicle vehicle = (Vehicle) this.getBukkitEntity();
|
|
+ org.bukkit.block.Block bl = this.level.getWorld().getBlockAt(MathHelper.floor(this.locX()), MathHelper.floor(this.locY()), MathHelper.floor(this.locZ()));
|
|
+
|
|
+ if (vec3d.x > vec3d1.x) {
|
|
+ bl = bl.getRelative(BlockFace.EAST);
|
|
+ } else if (vec3d.x < vec3d1.x) {
|
|
+ bl = bl.getRelative(BlockFace.WEST);
|
|
+ } else if (vec3d.z > vec3d1.z) {
|
|
+ bl = bl.getRelative(BlockFace.SOUTH);
|
|
+ } else if (vec3d.z < vec3d1.z) {
|
|
+ bl = bl.getRelative(BlockFace.NORTH);
|
|
+ }
|
|
+
|
|
+ if (!bl.getType().isAir()) {
|
|
+ VehicleBlockCollisionEvent event = new VehicleBlockCollisionEvent(vehicle, bl);
|
|
+ level.getCraftServer().getPluginManager().callEvent(event);
|
|
+ }
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
if (this.onGround && !this.bE()) {
|
|
block.stepOn(this.level, blockposition, iblockdata, this);
|
|
}
|
|
@@ -1276,6 +1438,7 @@
|
|
this.yo = d1;
|
|
this.zo = d4;
|
|
this.setPosition(d3, d1, d4);
|
|
+ if (valid) level.getChunkAt((int) Math.floor(this.locX()) >> 4, (int) Math.floor(this.locZ()) >> 4); // CraftBukkit
|
|
}
|
|
|
|
public void d(Vec3D vec3d) {
|
|
@@ -1466,6 +1629,12 @@
|
|
return false;
|
|
}
|
|
|
|
+ // CraftBukkit start - collidable API
|
|
+ public boolean canCollideWithBukkit(Entity entity) {
|
|
+ return isCollidable();
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
public void a(Entity entity, int i, DamageSource damagesource) {
|
|
if (entity instanceof EntityPlayer) {
|
|
CriterionTriggers.ENTITY_KILLED_PLAYER.a((EntityPlayer) entity, this, damagesource);
|
|
@@ -1499,7 +1668,7 @@
|
|
} else {
|
|
String s = this.getSaveID();
|
|
|
|
- if (s == null) {
|
|
+ if (!this.persist || s == null) { // CraftBukkit - persist flag
|
|
return false;
|
|
} else {
|
|
nbttagcompound.setString("id", s);
|
|
@@ -1524,6 +1693,18 @@
|
|
Vec3D vec3d = this.getMot();
|
|
|
|
nbttagcompound.set("Motion", this.newDoubleList(vec3d.x, vec3d.y, vec3d.z));
|
|
+
|
|
+ // 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.yRot)) {
|
|
+ this.yRot = 0;
|
|
+ }
|
|
+
|
|
+ if (Float.isNaN(this.xRot)) {
|
|
+ this.xRot = 0;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
nbttagcompound.set("Rotation", this.newFloatList(this.getYRot(), this.getXRot()));
|
|
nbttagcompound.setFloat("FallDistance", this.fallDistance);
|
|
nbttagcompound.setShort("Fire", (short) this.remainingFireTicks);
|
|
@@ -1532,6 +1713,18 @@
|
|
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", ((WorldServer) this.level).getWorld().getUID().getLeastSignificantBits());
|
|
+ nbttagcompound.setLong("WorldUUIDMost", ((WorldServer) this.level).getWorld().getUID().getMostSignificantBits());
|
|
+ nbttagcompound.setInt("Bukkit.updateLevel", CURRENT_LEVEL);
|
|
+ if (!this.persist) {
|
|
+ nbttagcompound.setBoolean("Bukkit.persist", this.persist);
|
|
+ }
|
|
+ if (this.persistentInvisibility) {
|
|
+ nbttagcompound.setBoolean("Bukkit.invisible", this.persistentInvisibility);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
IChatBaseComponent ichatbasecomponent = this.getCustomName();
|
|
|
|
if (ichatbasecomponent != null) {
|
|
@@ -1599,6 +1792,11 @@
|
|
}
|
|
}
|
|
|
|
+ // CraftBukkit start - stores eventually existing bukkit values
|
|
+ if (this.bukkitEntity != null) {
|
|
+ this.bukkitEntity.storeBukkitValues(nbttagcompound);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
return nbttagcompound;
|
|
} catch (Throwable throwable) {
|
|
CrashReport crashreport = CrashReport.a(throwable, "Saving entity NBT");
|
|
@@ -1680,6 +1878,49 @@
|
|
} else {
|
|
throw new IllegalStateException("Entity has invalid position");
|
|
}
|
|
+
|
|
+ // 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.setPersistenceRequired(!entityinsentient.isTypeNotPersistent(0));
|
|
+ }
|
|
+ }
|
|
+ this.persist = !nbttagcompound.hasKey("Bukkit.persist") || nbttagcompound.getBoolean("Bukkit.persist");
|
|
+ // 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) {
|
|
+ bworld = ((org.bukkit.craftbukkit.CraftServer) server).getServer().getWorldServer(World.OVERWORLD).getWorld();
|
|
+ }
|
|
+
|
|
+ ((EntityPlayer) this).spawnIn(bworld == null ? null : ((CraftWorld) bworld).getHandle());
|
|
+ }
|
|
+ this.getBukkitEntity().readBukkitValues(nbttagcompound);
|
|
+ if (nbttagcompound.hasKey("Bukkit.invisible")) {
|
|
+ boolean bukkitInvisible = nbttagcompound.getBoolean("Bukkit.invisible");
|
|
+ this.setInvisible(bukkitInvisible);
|
|
+ this.persistentInvisibility = bukkitInvisible;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
} catch (Throwable throwable) {
|
|
CrashReport crashreport = CrashReport.a(throwable, "Loading entity NBT");
|
|
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being loaded");
|
|
@@ -1755,9 +1996,22 @@
|
|
} else if (this.level.isClientSide) {
|
|
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.level, this.locX(), this.locY() + (double) f, this.locZ(), itemstack);
|
|
|
|
entityitem.defaultPickupDelay();
|
|
+ // CraftBukkit start
|
|
+ EntityDropItemEvent event = new EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) entityitem.getBukkitEntity());
|
|
+ Bukkit.getPluginManager().callEvent(event);
|
|
+ if (event.isCancelled()) {
|
|
+ return null;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
this.level.addEntity(entityitem);
|
|
return entityitem;
|
|
}
|
|
@@ -1849,7 +2103,7 @@
|
|
|
|
this.setPose(EntityPose.STANDING);
|
|
this.vehicle = entity;
|
|
- this.vehicle.addPassenger(this);
|
|
+ if (!this.vehicle.addPassenger(this)) this.vehicle = null; // CraftBukkit
|
|
entity.n().filter((entity2) -> {
|
|
return entity2 instanceof EntityPlayer;
|
|
}).forEach((entity2) -> {
|
|
@@ -1880,7 +2134,7 @@
|
|
Entity entity = this.vehicle;
|
|
|
|
this.vehicle = null;
|
|
- entity.removePassenger(this);
|
|
+ if (!entity.removePassenger(this)) this.vehicle = entity; // CraftBukkit
|
|
}
|
|
|
|
}
|
|
@@ -1889,10 +2143,31 @@
|
|
this.bo();
|
|
}
|
|
|
|
- protected void addPassenger(Entity entity) {
|
|
+ protected boolean addPassenger(Entity entity) { // CraftBukkit
|
|
if (entity.getVehicle() != 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) {
|
|
+ VehicleEnterEvent event = new VehicleEnterEvent(
|
|
+ (Vehicle) getBukkitEntity(),
|
|
+ entity.getBukkitEntity()
|
|
+ );
|
|
+ // Suppress during worldgen
|
|
+ if (this.valid) {
|
|
+ Bukkit.getPluginManager().callEvent(event);
|
|
+ }
|
|
+ CraftEntity craftn = (CraftEntity) entity.getBukkitEntity().getVehicle();
|
|
+ Entity n = craftn == null ? null : craftn.getHandle();
|
|
+ if (event.isCancelled() || n != orig) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ // CraftBukkit end
|
|
if (this.passengers.isEmpty()) {
|
|
this.passengers = ImmutableList.of(entity);
|
|
} else {
|
|
@@ -1908,12 +2183,32 @@
|
|
}
|
|
|
|
}
|
|
+ return true; // CraftBukkit
|
|
}
|
|
|
|
- protected void removePassenger(Entity entity) {
|
|
+ protected boolean removePassenger(Entity entity) { // CraftBukkit
|
|
if (entity.getVehicle() == 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()
|
|
+ );
|
|
+ // Suppress during worldgen
|
|
+ if (this.valid) {
|
|
+ Bukkit.getPluginManager().callEvent(event);
|
|
+ }
|
|
+ CraftEntity craftn = (CraftEntity) entity.getBukkitEntity().getVehicle();
|
|
+ Entity n = craftn == null ? null : craftn.getHandle();
|
|
+ if (event.isCancelled() || n != orig) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ // CraftBukkit end
|
|
if (this.passengers.size() == 1 && this.passengers.get(0) == entity) {
|
|
this.passengers = ImmutableList.of();
|
|
} else {
|
|
@@ -1924,6 +2219,7 @@
|
|
|
|
entity.boardingCooldown = 60;
|
|
}
|
|
+ return true; // CraftBukkit
|
|
}
|
|
|
|
protected boolean o(Entity entity) {
|
|
@@ -1974,14 +2270,20 @@
|
|
|
|
if (this.isInsidePortal) {
|
|
MinecraftServer minecraftserver = worldserver.getMinecraftServer();
|
|
- ResourceKey<World> resourcekey = this.level.getDimensionKey() == World.NETHER ? World.OVERWORLD : World.NETHER;
|
|
+ ResourceKey<World> resourcekey = this.level.getTypeKey() == DimensionManager.NETHER_LOCATION ? World.OVERWORLD : World.NETHER; // CraftBukkit
|
|
WorldServer worldserver1 = minecraftserver.getWorldServer(resourcekey);
|
|
|
|
- if (worldserver1 != null && minecraftserver.getAllowNether() && !this.isPassenger() && this.portalTime++ >= i) {
|
|
+ if (true && !this.isPassenger() && this.portalTime++ >= i) { // CraftBukkit
|
|
this.level.getMethodProfiler().enter("portal");
|
|
this.portalTime = i;
|
|
this.resetPortalCooldown();
|
|
- this.b(worldserver1);
|
|
+ // CraftBukkit start
|
|
+ if (this instanceof EntityPlayer) {
|
|
+ ((EntityPlayer) this).b(worldserver1, PlayerTeleportEvent.TeleportCause.NETHER_PORTAL);
|
|
+ } else {
|
|
+ this.b(worldserver1);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
this.level.getMethodProfiler().exit();
|
|
}
|
|
|
|
@@ -2099,6 +2401,13 @@
|
|
}
|
|
|
|
public void setSwimming(boolean flag) {
|
|
+ // CraftBukkit start
|
|
+ if (valid && this.isSwimming() != flag && this instanceof EntityLiving) {
|
|
+ if (CraftEventFactory.callToggleSwimEvent((EntityLiving) this, flag).isCancelled()) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ // CraftBukkit end
|
|
this.setFlag(4, flag);
|
|
}
|
|
|
|
@@ -2147,8 +2456,12 @@
|
|
return this.getScoreboardTeam() != null ? this.getScoreboardTeam().isAlly(scoreboardteambase) : false;
|
|
}
|
|
|
|
+ // CraftBukkit - start
|
|
public void setInvisible(boolean flag) {
|
|
- this.setFlag(5, flag);
|
|
+ if (!this.persistentInvisibility) { // Prevent Minecraft from removing our invisibility flag
|
|
+ this.setFlag(5, flag);
|
|
+ }
|
|
+ // CraftBukkit - end
|
|
}
|
|
|
|
public boolean getFlag(int i) {
|
|
@@ -2175,7 +2488,17 @@
|
|
}
|
|
|
|
public void setAirTicks(int i) {
|
|
- this.entityData.set(Entity.DATA_AIR_SUPPLY_ID, i);
|
|
+ // CraftBukkit start
|
|
+ EntityAirChangeEvent event = new EntityAirChangeEvent(this.getBukkitEntity(), i);
|
|
+ // Suppress during worldgen
|
|
+ if (this.valid) {
|
|
+ event.getEntity().getServer().getPluginManager().callEvent(event);
|
|
+ }
|
|
+ if (event.isCancelled()) {
|
|
+ return;
|
|
+ }
|
|
+ this.entityData.set(Entity.DATA_AIR_SUPPLY_ID, event.getAmount());
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
public int getTicksFrozen() {
|
|
@@ -2202,11 +2525,41 @@
|
|
|
|
public void onLightningStrike(WorldServer worldserver, EntityLightning entitylightning) {
|
|
this.setFireTicks(this.remainingFireTicks + 1);
|
|
+ // CraftBukkit start
|
|
+ final org.bukkit.entity.Entity thisBukkitEntity = this.getBukkitEntity();
|
|
+ final org.bukkit.entity.Entity stormBukkitEntity = entitylightning.getBukkitEntity();
|
|
+ final PluginManager pluginManager = Bukkit.getPluginManager();
|
|
+ // CraftBukkit end
|
|
+
|
|
if (this.remainingFireTicks == 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(), false);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ }
|
|
+
|
|
+ // CraftBukkit start
|
|
+ if (thisBukkitEntity instanceof Hanging) {
|
|
+ HangingBreakByEntityEvent hangingEvent = new HangingBreakByEntityEvent((Hanging) thisBukkitEntity, stormBukkitEntity);
|
|
+ pluginManager.callEvent(hangingEvent);
|
|
+
|
|
+ if (hangingEvent.isCancelled()) {
|
|
+ return;
|
|
+ }
|
|
}
|
|
|
|
- this.damageEntity(DamageSource.LIGHTNING_BOLT, 5.0F);
|
|
+ if (this.isFireProof()) {
|
|
+ return;
|
|
+ }
|
|
+ CraftEventFactory.entityDamage = entitylightning;
|
|
+ if (!this.damageEntity(DamageSource.LIGHTNING_BOLT, 5.0F)) {
|
|
+ CraftEventFactory.entityDamage = null;
|
|
+ return;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
public void k(boolean flag) {
|
|
@@ -2356,15 +2709,32 @@
|
|
|
|
@Nullable
|
|
public Entity b(WorldServer worldserver) {
|
|
+ // CraftBukkit start
|
|
+ return teleportTo(worldserver, null);
|
|
+ }
|
|
+
|
|
+ @Nullable
|
|
+ public Entity teleportTo(WorldServer worldserver, BlockPosition location) {
|
|
+ // CraftBukkit end
|
|
if (this.level instanceof WorldServer && !this.isRemoved()) {
|
|
this.level.getMethodProfiler().enter("changeDimension");
|
|
- this.decouple();
|
|
+ // CraftBukkit start
|
|
+ // this.decouple();
|
|
+ if (worldserver == null) {
|
|
+ return null;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
this.level.getMethodProfiler().enter("reposition");
|
|
- ShapeDetectorShape shapedetectorshape = this.a(worldserver);
|
|
+ ShapeDetectorShape shapedetectorshape = (location == null) ? this.a(worldserver) : new ShapeDetectorShape(new Vec3D(location.getX(), location.getY(), location.getZ()), Vec3D.ZERO, this.yRot, this.xRot, worldserver, null); // CraftBukkit
|
|
|
|
if (shapedetectorshape == null) {
|
|
return null;
|
|
} else {
|
|
+ // CraftBukkit start
|
|
+ worldserver = shapedetectorshape.world;
|
|
+ this.decouple();
|
|
+ // CraftBukkit end
|
|
+
|
|
this.level.getMethodProfiler().exitEnter("reloading");
|
|
Entity entity = this.getEntityType().a((World) worldserver);
|
|
|
|
@@ -2373,9 +2743,17 @@
|
|
entity.setPositionRotation(shapedetectorshape.pos.x, shapedetectorshape.pos.y, shapedetectorshape.pos.z, shapedetectorshape.yRot, entity.getXRot());
|
|
entity.setMot(shapedetectorshape.speed);
|
|
worldserver.addEntityTeleport(entity);
|
|
- if (worldserver.getDimensionKey() == World.END) {
|
|
- WorldServer.a(worldserver);
|
|
+ if (worldserver.getTypeKey() == DimensionManager.END_LOCATION) { // CraftBukkit
|
|
+ WorldServer.a(worldserver, this); // CraftBukkit
|
|
+ }
|
|
+ // 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.cc();
|
|
@@ -2396,13 +2774,18 @@
|
|
|
|
@Nullable
|
|
protected ShapeDetectorShape a(WorldServer worldserver) {
|
|
- boolean flag = this.level.getDimensionKey() == World.END && worldserver.getDimensionKey() == World.OVERWORLD;
|
|
- boolean flag1 = worldserver.getDimensionKey() == World.END;
|
|
+ // CraftBukkit start
|
|
+ if (worldserver == null) {
|
|
+ return null;
|
|
+ }
|
|
+ boolean flag = this.level.getTypeKey() == DimensionManager.END_LOCATION && worldserver.getTypeKey() == DimensionManager.OVERWORLD_LOCATION; // fromEndToOverworld
|
|
+ boolean flag1 = worldserver.getTypeKey() == DimensionManager.END_LOCATION; // targetIsEnd
|
|
+ // CraftBukkit end
|
|
|
|
if (!flag && !flag1) {
|
|
- boolean flag2 = worldserver.getDimensionKey() == World.NETHER;
|
|
+ boolean flag2 = worldserver.getTypeKey() == DimensionManager.NETHER_LOCATION; // CraftBukkit
|
|
|
|
- if (this.level.getDimensionKey() != World.NETHER && !flag2) {
|
|
+ if (this.level.getTypeKey() != DimensionManager.NETHER_LOCATION && !flag2) {
|
|
return null;
|
|
} else {
|
|
WorldBorder worldborder = worldserver.getWorldBorder();
|
|
@@ -2412,8 +2795,16 @@
|
|
double d3 = Math.min(2.9999872E7D, worldborder.h() - 16.0D);
|
|
double d4 = DimensionManager.a(this.level.getDimensionManager(), worldserver.getDimensionManager());
|
|
BlockPosition blockposition = new BlockPosition(MathHelper.a(this.locX() * d4, d0, d2), this.locY(), MathHelper.a(this.locZ() * d4, d1, d3));
|
|
+ // CraftBukkit start
|
|
+ CraftPortalEvent event = callPortalEvent(this, worldserver, blockposition, PlayerTeleportEvent.TeleportCause.NETHER_PORTAL, flag2 ? 16 : 128, 16);
|
|
+ if (event == null) {
|
|
+ return null;
|
|
+ }
|
|
+ final WorldServer worldserverFinal = worldserver = ((CraftWorld) event.getTo().getWorld()).getHandle();
|
|
+ blockposition = new BlockPosition(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ());
|
|
|
|
- return (ShapeDetectorShape) this.findOrCreatePortal(worldserver, blockposition, flag2).map((blockutil_rectangle) -> {
|
|
+ return (ShapeDetectorShape) this.findOrCreatePortal(worldserver, blockposition, flag2, event.getSearchRadius(), event.getCanCreatePortal(), event.getCreationRadius()).map((blockutil_rectangle) -> {
|
|
+ // CraftBukkit end
|
|
IBlockData iblockdata = this.level.getType(this.portalEntrancePos);
|
|
EnumDirection.EnumAxis enumdirection_enumaxis;
|
|
Vec3D vec3d;
|
|
@@ -2430,8 +2821,8 @@
|
|
vec3d = new Vec3D(0.5D, 0.0D, 0.0D);
|
|
}
|
|
|
|
- return BlockPortalShape.a(worldserver, blockutil_rectangle, enumdirection_enumaxis, vec3d, this.a(this.getPose()), this.getMot(), this.getYRot(), this.getXRot());
|
|
- }).orElse((Object) null);
|
|
+ return BlockPortalShape.a(worldserverFinal, blockutil_rectangle, enumdirection_enumaxis, vec3d, this.a(this.getPose()), this.getMot(), this.getYRot(), this.getXRot(), event); // CraftBukkit
|
|
+ }).orElse(null); // CraftBuukkit - decompile error
|
|
}
|
|
} else {
|
|
BlockPosition blockposition1;
|
|
@@ -2441,8 +2832,15 @@
|
|
} else {
|
|
blockposition1 = worldserver.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, worldserver.getSpawn());
|
|
}
|
|
+ // CraftBukkit start
|
|
+ CraftPortalEvent event = callPortalEvent(this, worldserver, blockposition1, PlayerTeleportEvent.TeleportCause.END_PORTAL, 0, 0);
|
|
+ if (event == null) {
|
|
+ return null;
|
|
+ }
|
|
+ blockposition1 = new BlockPosition(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ());
|
|
|
|
- return new ShapeDetectorShape(new Vec3D((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY(), (double) blockposition1.getZ() + 0.5D), this.getMot(), this.getYRot(), this.getXRot());
|
|
+ return new ShapeDetectorShape(new Vec3D((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY(), (double) blockposition1.getZ() + 0.5D), this.getMot(), this.getYRot(), this.getXRot(), ((CraftWorld) event.getTo().getWorld()).getHandle(), event);
|
|
+ // CraftBukkit end
|
|
}
|
|
}
|
|
|
|
@@ -2450,8 +2848,23 @@
|
|
return BlockPortalShape.a(blockutil_rectangle, enumdirection_enumaxis, this.getPositionVector(), this.a(this.getPose()));
|
|
}
|
|
|
|
- protected Optional<BlockUtil.Rectangle> findOrCreatePortal(WorldServer worldserver, BlockPosition blockposition, boolean flag) {
|
|
- return worldserver.getTravelAgent().findPortal(blockposition, flag);
|
|
+ // CraftBukkit start
|
|
+ protected CraftPortalEvent callPortalEvent(Entity entity, WorldServer exitWorldServer, BlockPosition exitPosition, PlayerTeleportEvent.TeleportCause cause, int searchRadius, int creationRadius) {
|
|
+ org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity();
|
|
+ Location enter = bukkitEntity.getLocation();
|
|
+ Location exit = new Location(exitWorldServer.getWorld(), exitPosition.getX(), exitPosition.getY(), exitPosition.getZ());
|
|
+
|
|
+ EntityPortalEvent event = new EntityPortalEvent(bukkitEntity, enter, exit, searchRadius);
|
|
+ event.getEntity().getServer().getPluginManager().callEvent(event);
|
|
+ if (event.isCancelled() || event.getTo() == null || event.getTo().getWorld() == null || !entity.isAlive()) {
|
|
+ return null;
|
|
+ }
|
|
+ return new CraftPortalEvent(event);
|
|
+ }
|
|
+
|
|
+ protected Optional<BlockUtil.Rectangle> findOrCreatePortal(WorldServer worldserver, BlockPosition blockposition, boolean flag, int searchRadius, boolean canCreatePortal, int createRadius) {
|
|
+ return worldserver.getTravelAgent().findPortal(blockposition, searchRadius);
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
public boolean canPortal() {
|
|
@@ -2660,7 +3073,26 @@
|
|
}
|
|
|
|
public final void a(AxisAlignedBB axisalignedbb) {
|
|
- this.bb = axisalignedbb;
|
|
+ // CraftBukkit start - block invalid bounding boxes
|
|
+ double minX = axisalignedbb.minX,
|
|
+ minY = axisalignedbb.minY,
|
|
+ minZ = axisalignedbb.minZ,
|
|
+ maxX = axisalignedbb.maxX,
|
|
+ maxY = axisalignedbb.maxY,
|
|
+ maxZ = axisalignedbb.maxZ;
|
|
+ double len = axisalignedbb.maxX - axisalignedbb.minX;
|
|
+ if (len < 0) maxX = minX;
|
|
+ if (len > 64) maxX = minX + 64.0;
|
|
+
|
|
+ len = axisalignedbb.maxY - axisalignedbb.minY;
|
|
+ if (len < 0) maxY = minY;
|
|
+ if (len > 64) maxY = minY + 64.0;
|
|
+
|
|
+ len = axisalignedbb.maxZ - axisalignedbb.minZ;
|
|
+ if (len < 0) maxZ = minZ;
|
|
+ if (len > 64) maxZ = minZ + 64.0;
|
|
+ this.bb = new AxisAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
protected float getHeadHeight(EntityPose entitypose, EntitySize entitysize) {
|