#1266: Add support for virtual entities

By: Jishuna <joshl5324@gmail.com>
This commit is contained in:
CraftBukkit/Spigot 2023-11-19 19:03:35 +13:00
parent 9a3c24e787
commit 95e4221adf
13 changed files with 472 additions and 61 deletions

View File

@ -592,7 +592,18 @@
}
@Override
@@ -1751,6 +1967,7 @@
@@ -1703,7 +1919,9 @@
a() {}
- public void onCreated(Entity entity) {}
+ public void onCreated(Entity entity) {
+ entity.inWorld = true; // CraftBukkit - Mark entity as in world
+ }
public void onDestroyed(Entity entity) {
WorldServer.this.getScoreboard().entityRemoved(entity);
@@ -1751,6 +1969,7 @@
}
entity.updateDynamicGameEventListener(DynamicGameEventListener::add);
@ -600,7 +611,7 @@
}
public void onTrackingEnd(Entity entity) {
@@ -1787,6 +2004,14 @@
@@ -1787,6 +2006,14 @@
}
entity.updateDynamicGameEventListener(DynamicGameEventListener::remove);

View File

@ -75,7 +75,7 @@
public int tickCount;
private int remainingFireTicks;
public boolean wasTouchingWater;
@@ -241,6 +298,25 @@
@@ -241,6 +298,26 @@
public boolean hasVisualFire;
@Nullable
private IBlockData feetBlockState;
@ -83,6 +83,7 @@
+ public boolean persist = true;
+ public boolean visibleByDefault = true;
+ public boolean valid;
+ public boolean inWorld = false;
+ public boolean generation;
+ public int maxAirTicks = getDefaultMaxAirSupply(); // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
+ public org.bukkit.projectiles.ProjectileSource projectileSource; // For projectiles only
@ -101,7 +102,7 @@
public Entity(EntityTypes<?> entitytypes, World world) {
this.id = Entity.ENTITY_COUNTER.incrementAndGet();
@@ -374,6 +450,12 @@
@@ -374,6 +451,12 @@
public void onClientRemoval() {}
public void setPose(EntityPose entitypose) {
@ -114,7 +115,7 @@
this.entityData.set(Entity.DATA_POSE, entitypose);
}
@@ -398,6 +480,33 @@
@@ -398,6 +481,33 @@
}
protected void setRot(float f, float f1) {
@ -148,7 +149,7 @@
this.setYRot(f % 360.0F);
this.setXRot(f1 % 360.0F);
}
@@ -439,6 +548,15 @@
@@ -439,6 +549,15 @@
this.baseTick();
}
@ -164,7 +165,7 @@
public void baseTick() {
this.level().getProfiler().push("entityBaseTick");
this.feetBlockState = null;
@@ -453,7 +571,7 @@
@@ -453,7 +572,7 @@
this.walkDistO = this.walkDist;
this.xRotO = this.getXRot();
this.yRotO = this.getYRot();
@ -173,7 +174,7 @@
if (this.canSpawnSprintParticle()) {
this.spawnSprintParticle();
}
@@ -488,6 +606,10 @@
@@ -488,6 +607,10 @@
if (this.isInLava()) {
this.lavaHurt();
this.fallDistance *= 0.5F;
@ -184,7 +185,7 @@
}
this.checkBelowWorld();
@@ -539,15 +661,48 @@
@@ -539,15 +662,48 @@
public void lavaHurt() {
if (!this.fireImmune()) {
@ -234,7 +235,7 @@
int j = i * 20;
if (this instanceof EntityLiving) {
@@ -698,6 +853,28 @@
@@ -698,6 +854,28 @@
block.updateEntityAfterFallOn(this.level(), this);
}
@ -263,7 +264,7 @@
if (this.onGround()) {
block.stepOn(this.level(), blockposition, iblockdata, this);
}
@@ -1025,6 +1202,20 @@
@@ -1025,6 +1203,20 @@
return SoundEffects.GENERIC_SPLASH;
}
@ -284,7 +285,7 @@
protected void checkInsideBlocks() {
AxisAlignedBB axisalignedbb = this.getBoundingBox();
BlockPosition blockposition = BlockPosition.containing(axisalignedbb.minX + 1.0E-7D, axisalignedbb.minY + 1.0E-7D, axisalignedbb.minZ + 1.0E-7D);
@@ -1439,6 +1630,7 @@
@@ -1439,6 +1631,7 @@
this.yo = d1;
this.zo = d4;
this.setPos(d3, d1, d4);
@ -292,7 +293,7 @@
}
public void moveTo(Vec3D vec3d) {
@@ -1633,6 +1825,12 @@
@@ -1633,6 +1826,12 @@
return false;
}
@ -305,7 +306,18 @@
public void awardKillScore(Entity entity, int i, DamageSource damagesource) {
if (entity instanceof EntityPlayer) {
CriterionTriggers.ENTITY_KILLED_PLAYER.trigger((EntityPlayer) entity, this, damagesource);
@@ -1666,7 +1864,7 @@
@@ -1661,16 +1860,22 @@
}
public boolean saveAsPassenger(NBTTagCompound nbttagcompound) {
+ // CraftBukkit start - allow excluding certain data when saving
+ return saveAsPassenger(nbttagcompound, true);
+ }
+
+ public boolean saveAsPassenger(NBTTagCompound nbttagcompound, boolean includeAll) {
+ // CraftBukkit end
if (this.removalReason != null && !this.removalReason.shouldSave()) {
return false;
} else {
String s = this.getEncodeId();
@ -314,7 +326,36 @@
return false;
} else {
nbttagcompound.putString("id", s);
@@ -1691,6 +1889,18 @@
- this.saveWithoutId(nbttagcompound);
+ this.saveWithoutId(nbttagcompound, includeAll); // CraftBukkit - pass on includeAll
return true;
}
}
@@ -1681,16 +1886,38 @@
}
public NBTTagCompound saveWithoutId(NBTTagCompound nbttagcompound) {
+ // CraftBukkit start - allow excluding certain data when saving
+ return saveWithoutId(nbttagcompound, true);
+ }
+
+ public NBTTagCompound saveWithoutId(NBTTagCompound nbttagcompound, boolean includeAll) {
+ // CraftBukkit end
try {
- if (this.vehicle != null) {
- nbttagcompound.put("Pos", this.newDoubleList(this.vehicle.getX(), this.getY(), this.vehicle.getZ()));
- } else {
- nbttagcompound.put("Pos", this.newDoubleList(this.getX(), this.getY(), this.getZ()));
+ // CraftBukkit start - selectively save position
+ if (includeAll) {
+ if (this.vehicle != null) {
+ nbttagcompound.put("Pos", this.newDoubleList(this.vehicle.getX(), this.getY(), this.vehicle.getZ()));
+ } else {
+ nbttagcompound.put("Pos", this.newDoubleList(this.getX(), this.getY(), this.getZ()));
+ }
}
+ // CraftBukkit end
Vec3D vec3d = this.getDeltaMovement();
nbttagcompound.put("Motion", this.newDoubleList(vec3d.x, vec3d.y, vec3d.z));
@ -333,14 +374,18 @@
nbttagcompound.put("Rotation", this.newFloatList(this.getYRot(), this.getXRot()));
nbttagcompound.putFloat("FallDistance", this.fallDistance);
nbttagcompound.putShort("Fire", (short) this.remainingFireTicks);
@@ -1699,6 +1909,25 @@
@@ -1698,7 +1925,28 @@
nbttagcompound.putBoolean("OnGround", this.onGround());
nbttagcompound.putBoolean("Invulnerable", this.invulnerable);
nbttagcompound.putInt("PortalCooldown", this.portalCooldown);
nbttagcompound.putUUID("UUID", this.getUUID());
+ // CraftBukkit start
- nbttagcompound.putUUID("UUID", this.getUUID());
+ // CraftBukkit start - selectively save uuid and world
+ if (includeAll) {
+ nbttagcompound.putUUID("UUID", this.getUUID());
+ // PAIL: Check above UUID reads 1.8 properly, ie: UUIDMost / UUIDLeast
+ nbttagcompound.putLong("WorldUUIDLeast", ((WorldServer) this.level).getWorld().getUID().getLeastSignificantBits());
+ nbttagcompound.putLong("WorldUUIDMost", ((WorldServer) this.level).getWorld().getUID().getMostSignificantBits());
+ }
+ nbttagcompound.putInt("Bukkit.updateLevel", CURRENT_LEVEL);
+ if (!this.persist) {
+ nbttagcompound.putBoolean("Bukkit.persist", this.persist);
@ -359,7 +404,25 @@
IChatBaseComponent ichatbasecomponent = this.getCustomName();
if (ichatbasecomponent != null) {
@@ -1766,6 +1995,11 @@
@@ -1747,7 +1995,7 @@
nbttagcompound.put("Tags", nbttaglist);
}
- this.addAdditionalSaveData(nbttagcompound);
+ this.addAdditionalSaveData(nbttagcompound, includeAll); // CraftBukkit - pass on includeAll
if (this.isVehicle()) {
nbttaglist = new NBTTagList();
iterator = this.getPassengers().iterator();
@@ -1756,7 +2004,7 @@
Entity entity = (Entity) iterator.next();
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
- if (entity.saveAsPassenger(nbttagcompound1)) {
+ if (entity.saveAsPassenger(nbttagcompound1, includeAll)) { // CraftBukkit - pass on includeAll
nbttaglist.add(nbttagcompound1);
}
}
@@ -1766,6 +2014,11 @@
}
}
@ -371,7 +434,7 @@
return nbttagcompound;
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.forThrowable(throwable, "Saving entity NBT");
@@ -1849,6 +2083,45 @@
@@ -1849,6 +2102,45 @@
} else {
throw new IllegalStateException("Entity has invalid position");
}
@ -417,7 +480,20 @@
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.forThrowable(throwable, "Loading entity NBT");
CrashReportSystemDetails crashreportsystemdetails = crashreport.addCategory("Entity being loaded");
@@ -1924,9 +2197,22 @@
@@ -1870,6 +2162,12 @@
return entitytypes.canSerialize() && minecraftkey != null ? minecraftkey.toString() : null;
}
+ // CraftBukkit start - allow excluding certain data when saving
+ protected void addAdditionalSaveData(NBTTagCompound nbttagcompound, boolean includeAll) {
+ addAdditionalSaveData(nbttagcompound);
+ }
+ // CraftBukkit end
+
protected abstract void readAdditionalSaveData(NBTTagCompound nbttagcompound);
protected abstract void addAdditionalSaveData(NBTTagCompound nbttagcompound);
@@ -1924,9 +2222,22 @@
} else if (this.level().isClientSide) {
return null;
} else {
@ -440,7 +516,7 @@
this.level().addFreshEntity(entityitem);
return entityitem;
}
@@ -2024,6 +2310,18 @@
@@ -2024,6 +2335,18 @@
if (!flag && (!this.canRide(entity) || !entity.canAddPassenger(this))) {
return false;
} else {
@ -459,7 +535,7 @@
if (this.isPassenger()) {
this.stopRiding();
}
@@ -2057,7 +2355,7 @@
@@ -2057,7 +2380,7 @@
Entity entity = this.vehicle;
this.vehicle = null;
@ -468,7 +544,7 @@
}
}
@@ -2088,10 +2386,29 @@
@@ -2088,10 +2411,29 @@
}
}
@ -499,7 +575,7 @@
if (this.passengers.size() == 1 && this.passengers.get(0) == entity) {
this.passengers = ImmutableList.of();
} else {
@@ -2103,6 +2420,7 @@
@@ -2103,6 +2445,7 @@
entity.boardingCooldown = 60;
this.gameEvent(GameEvent.ENTITY_DISMOUNT, entity);
}
@ -507,7 +583,7 @@
}
protected boolean canAddPassenger(Entity entity) {
@@ -2189,14 +2507,20 @@
@@ -2189,14 +2532,20 @@
if (this.isInsidePortal) {
MinecraftServer minecraftserver = worldserver.getServer();
@ -531,7 +607,7 @@
this.level().getProfiler().pop();
}
@@ -2320,6 +2644,13 @@
@@ -2320,6 +2669,13 @@
}
public void setSwimming(boolean flag) {
@ -545,7 +621,7 @@
this.setSharedFlag(4, flag);
}
@@ -2369,8 +2700,12 @@
@@ -2369,8 +2725,12 @@
return this.getTeam() != null ? this.getTeam().isAlliedTo(scoreboardteambase) : false;
}
@ -559,7 +635,7 @@
}
public boolean getSharedFlag(int i) {
@@ -2389,7 +2724,7 @@
@@ -2389,7 +2749,7 @@
}
public int getMaxAirSupply() {
@ -568,7 +644,7 @@
}
public int getAirSupply() {
@@ -2397,7 +2732,18 @@
@@ -2397,7 +2757,18 @@
}
public void setAirSupply(int i) {
@ -588,7 +664,7 @@
}
public int getTicksFrozen() {
@@ -2424,11 +2770,41 @@
@@ -2424,11 +2795,41 @@
public void thunderHit(WorldServer worldserver, EntityLightning entitylightning) {
this.setRemainingFireTicks(this.remainingFireTicks + 1);
@ -632,7 +708,7 @@
}
public void onAboveBubbleCol(boolean flag) {
@@ -2593,15 +2969,38 @@
@@ -2593,15 +2994,38 @@
@Nullable
public Entity changeDimension(WorldServer worldserver) {
@ -673,27 +749,33 @@
this.level().getProfiler().popPush("reloading");
Entity entity = this.getType().create(worldserver);
@@ -2610,9 +3009,17 @@
@@ -2609,10 +3033,22 @@
entity.restoreFrom(this);
entity.moveTo(shapedetectorshape.pos.x, shapedetectorshape.pos.y, shapedetectorshape.pos.z, shapedetectorshape.yRot, entity.getXRot());
entity.setDeltaMovement(shapedetectorshape.speed);
worldserver.addDuringTeleport(entity);
- worldserver.addDuringTeleport(entity);
- if (worldserver.dimension() == World.END) {
- WorldServer.makeObsidianPlatform(worldserver);
+ // CraftBukkit start - Don't spawn the new entity if the current entity isn't spawned
+ if (this.inWorld) {
+ worldserver.addDuringTeleport(entity);
+ if (worldserver.getTypeKey() == WorldDimension.END) { // CraftBukkit
+ WorldServer.makeObsidianPlatform(worldserver, this); // CraftBukkit
}
+ }
+ }
+ // CraftBukkit end
+ // CraftBukkit start - Forward the CraftEntity to the new entity
+ this.getBukkitEntity().setHandle(entity);
+ entity.bukkitEntity = this.getBukkitEntity();
+
+ if (this instanceof EntityInsentient) {
+ ((EntityInsentient) this).dropLeash(true, false); // Unleash to prevent duping of leads.
+ }
}
+ // CraftBukkit end
}
this.removeAfterChangingDimensions();
@@ -2633,20 +3040,34 @@
@@ -2633,20 +3069,34 @@
@Nullable
protected ShapeDetectorShape findDimensionEntryPoint(WorldServer worldserver) {
@ -733,7 +815,7 @@
IBlockData iblockdata = this.level().getBlockState(this.portalEntrancePos);
EnumDirection.EnumAxis enumdirection_enumaxis;
Vec3D vec3d;
@@ -2663,8 +3084,8 @@
@@ -2663,8 +3113,8 @@
vec3d = new Vec3D(0.5D, 0.0D, 0.0D);
}
@ -744,7 +826,7 @@
}
} else {
BlockPosition blockposition1;
@@ -2674,8 +3095,14 @@
@@ -2674,8 +3124,14 @@
} else {
blockposition1 = worldserver.getHeightmapPos(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, worldserver.getSharedSpawnPos());
}
@ -760,7 +842,7 @@
}
}
@@ -2683,8 +3110,23 @@
@@ -2683,8 +3139,23 @@
return BlockPortalShape.getRelativePosition(blockutil_rectangle, enumdirection_enumaxis, this.position(), this.getDimensions(this.getPose()));
}
@ -786,7 +868,7 @@
}
public boolean canChangeDimensions() {
@@ -2804,6 +3246,12 @@
@@ -2804,6 +3275,12 @@
}
}
@ -799,7 +881,20 @@
public boolean teleportTo(WorldServer worldserver, double d0, double d1, double d2, Set<RelativeMovement> set, float f, float f1) {
float f2 = MathHelper.clamp(f1, -90.0F, 90.0F);
@@ -2929,7 +3377,26 @@
@@ -2823,7 +3300,11 @@
entity.moveTo(d0, d1, d2, f, f2);
entity.setYHeadRot(f);
this.setRemoved(Entity.RemovalReason.CHANGED_DIMENSION);
- worldserver.addDuringTeleport(entity);
+ // CraftBukkit start - Don't spawn the new entity if the current entity isn't spawned
+ if (inWorld) {
+ worldserver.addDuringTeleport(entity);
+ }
+ // CraftBukkit end
}
return true;
@@ -2929,7 +3410,26 @@
}
public final void setBoundingBox(AxisAlignedBB axisalignedbb) {
@ -827,7 +922,7 @@
}
protected float getEyeHeight(EntityPose entitypose, EntitySize entitysize) {
@@ -3240,6 +3707,11 @@
@@ -3240,6 +3740,11 @@
vec3d = vec3d.add(vec3d1);
++k1;
}

View File

@ -13,7 +13,29 @@
public class EntityBee extends EntityAnimal implements IEntityAngerable, EntityBird {
public static final float FLAP_DEGREES_PER_TICK = 120.32113F;
@@ -242,7 +248,7 @@
@@ -187,12 +193,19 @@
@Override
public void addAdditionalSaveData(NBTTagCompound nbttagcompound) {
+ // CraftBukkit start - selectively save data
+ addAdditionalSaveData(nbttagcompound, true);
+ }
+
+ @Override
+ public void addAdditionalSaveData(NBTTagCompound nbttagcompound, boolean includeAll) {
+ // CraftBukkit end
super.addAdditionalSaveData(nbttagcompound);
- if (this.hasHive()) {
+ if (includeAll && this.hasHive()) { // CraftBukkit - selectively save hive
nbttagcompound.put("HivePos", GameProfileSerializer.writeBlockPos(this.getHivePos()));
}
- if (this.hasSavedFlowerPos()) {
+ if (includeAll && this.hasSavedFlowerPos()) { // CraftBukkit - selectively save flower
nbttagcompound.put("FlowerPos", GameProfileSerializer.writeBlockPos(this.getSavedFlowerPos()));
}
@@ -242,7 +255,7 @@
}
if (b0 > 0) {
@ -22,7 +44,7 @@
}
}
@@ -640,11 +646,14 @@
@@ -640,11 +653,14 @@
if (this.isInvulnerableTo(damagesource)) {
return false;
} else {
@ -39,7 +61,7 @@
}
}
@@ -999,7 +1008,7 @@
@@ -999,7 +1015,7 @@
e() {
super();
@ -48,7 +70,7 @@
this.blacklistedTargets = Lists.newArrayList();
this.setFlags(EnumSet.of(PathfinderGoal.Type.MOVE));
}
@@ -1116,7 +1125,7 @@
@@ -1116,7 +1132,7 @@
f() {
super();
@ -57,7 +79,7 @@
this.setFlags(EnumSet.of(PathfinderGoal.Type.MOVE));
}
@@ -1216,7 +1225,7 @@
@@ -1216,7 +1232,7 @@
}
}
@ -66,7 +88,7 @@
EntityBee.this.level().levelEvent(2005, blockposition, 0);
EntityBee.this.level().setBlockAndUpdate(blockposition, iblockdata1);
EntityBee.this.incrementNumCropsGrownSincePollination();
@@ -1289,7 +1298,7 @@
@@ -1289,7 +1305,7 @@
@Override
protected void alertOther(EntityInsentient entityinsentient, EntityLiving entityliving) {
if (entityinsentient instanceof EntityBee && this.mob.hasLineOfSight(entityliving)) {
@ -75,7 +97,7 @@
}
}
@@ -1298,7 +1307,7 @@
@@ -1298,7 +1314,7 @@
private static class c extends PathfinderGoalNearestAttackableTarget<EntityHuman> {
c(EntityBee entitybee) {

View File

@ -147,7 +147,7 @@
this.kill();
this.dropItem((Entity) null);
}
@@ -186,7 +251,7 @@
@@ -186,13 +251,22 @@
@Override
public void push(double d0, double d1, double d2) {
@ -156,3 +156,18 @@
this.kill();
this.dropItem((Entity) null);
}
}
+ // CraftBukkit start - selectively save tile position
+ @Override
+ public void addAdditionalSaveData(NBTTagCompound nbttagcompound, boolean includeAll) {
+ if (includeAll) {
+ addAdditionalSaveData(nbttagcompound);
+ }
+ }
+ // CraftBukkit end
+
@Override
public void addAdditionalSaveData(NBTTagCompound nbttagcompound) {
BlockPosition blockposition = this.getPos();

View File

@ -54,7 +54,7 @@
+
for (int i = 0; i < b0; ++i) {
- EntityChicken entitychicken = (EntityChicken) EntityTypes.CHICKEN.create(this.level());
+ Entity entitychicken = this.level().getWorld().createEntity(new org.bukkit.Location(this.level().getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F), hatchingType.getEntityClass()); // CraftBukkit
+ Entity entitychicken = this.level().getWorld().makeEntity(new org.bukkit.Location(this.level().getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F), hatchingType.getEntityClass()); // CraftBukkit
if (entitychicken != null) {
- entitychicken.setAge(-24000);

View File

@ -64,6 +64,7 @@ import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.block.CraftBiome;
import org.bukkit.craftbukkit.block.CraftBlock;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.potion.CraftPotionUtil;
import org.bukkit.craftbukkit.util.BlockStateListPopulator;
@ -528,6 +529,18 @@ public abstract class CraftRegionAccessor implements RegionAccessor {
public abstract Iterable<net.minecraft.world.entity.Entity> getNMSEntities();
@Override
@SuppressWarnings("unchecked")
public <T extends Entity> T createEntity(Location location, Class<T> clazz) throws IllegalArgumentException {
net.minecraft.world.entity.Entity entity = createEntity(location, clazz, true);
if (!isNormalWorld()) {
entity.generation = true;
}
return (T) entity.getBukkitEntity();
}
@Override
public <T extends Entity> T spawn(Location location, Class<T> clazz) throws IllegalArgumentException {
return spawn(location, clazz, null, CreatureSpawnEvent.SpawnReason.CUSTOM);
@ -553,6 +566,19 @@ public abstract class CraftRegionAccessor implements RegionAccessor {
return addEntity(entity, reason, function, randomizeData);
}
@Override
@SuppressWarnings("unchecked")
public <T extends Entity> T addEntity(T entity) {
Preconditions.checkArgument(!entity.isInWorld(), "Entity has already been added to a world");
net.minecraft.world.entity.Entity nmsEntity = ((CraftEntity) entity).getHandle();
if (nmsEntity.level() != getHandle().getLevel()) {
nmsEntity = nmsEntity.changeDimension(getHandle().getLevel());
}
addEntityWithPassengers(nmsEntity, CreatureSpawnEvent.SpawnReason.CUSTOM);
return (T) nmsEntity.getBukkitEntity();
}
@SuppressWarnings("unchecked")
public <T extends Entity> T addEntity(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason) throws IllegalArgumentException {
return addEntity(entity, reason, null, true);
@ -580,8 +606,10 @@ public abstract class CraftRegionAccessor implements RegionAccessor {
public abstract void addEntityToWorld(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason);
public abstract void addEntityWithPassengers(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason);
@SuppressWarnings("unchecked")
public net.minecraft.world.entity.Entity createEntity(Location location, Class<? extends Entity> clazz) throws IllegalArgumentException {
public net.minecraft.world.entity.Entity makeEntity(Location location, Class<? extends Entity> clazz) throws IllegalArgumentException {
return createEntity(location, clazz, true);
}

View File

@ -822,6 +822,11 @@ public class CraftWorld extends CraftRegionAccessor implements World {
getHandle().addFreshEntity(entity, reason);
}
@Override
public void addEntityWithPassengers(net.minecraft.world.entity.Entity entity, SpawnReason reason) {
getHandle().tryAddFreshEntityWithPassengers(entity, reason);
}
@Override
public Collection<Entity> getNearbyEntities(Location location, double x, double y, double z) {
return this.getNearbyEntities(location, x, y, z, null);

View File

@ -1,15 +1,25 @@
package org.bukkit.craftbukkit.block;
import com.google.common.base.Preconditions;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.InclusiveRange;
import net.minecraft.util.RandomSource;
import net.minecraft.util.random.SimpleWeightedRandomList;
import net.minecraft.util.random.WeightedEntry.b;
import net.minecraft.world.entity.EntityTypes;
import net.minecraft.world.level.MobSpawnerData;
import net.minecraft.world.level.block.entity.TileEntityMobSpawner;
import org.bukkit.World;
import org.bukkit.block.CreatureSpawner;
import org.bukkit.block.spawner.SpawnRule;
import org.bukkit.block.spawner.SpawnerEntry;
import org.bukkit.craftbukkit.entity.CraftEntitySnapshot;
import org.bukkit.craftbukkit.entity.CraftEntityType;
import org.bukkit.entity.EntitySnapshot;
import org.bukkit.entity.EntityType;
public class CraftCreatureSpawner extends CraftBlockEntityState<TileEntityMobSpawner> implements CreatureSpawner {
@ -46,6 +56,78 @@ public class CraftCreatureSpawner extends CraftBlockEntityState<TileEntityMobSpa
this.getSnapshot().setEntityId(CraftEntityType.bukkitToMinecraft(entityType), rand);
}
@Override
public EntitySnapshot getSpawnedEntity() {
MobSpawnerData spawnData = this.getSnapshot().getSpawner().nextSpawnData;
if (spawnData == null) {
return null;
}
return CraftEntitySnapshot.create(spawnData.getEntityToSpawn());
}
@Override
public void setSpawnedEntity(EntitySnapshot snapshot) {
NBTTagCompound compoundTag = ((CraftEntitySnapshot) snapshot).getData();
this.getSnapshot().getSpawner().spawnPotentials = SimpleWeightedRandomList.empty();
this.getSnapshot().getSpawner().nextSpawnData = new MobSpawnerData(compoundTag, Optional.empty());
}
@Override
public void addPotentialSpawn(EntitySnapshot snapshot, int weight, SpawnRule spawnRule) {
NBTTagCompound compoundTag = ((CraftEntitySnapshot) snapshot).getData();
SimpleWeightedRandomList.a<MobSpawnerData> builder = SimpleWeightedRandomList.builder(); // PAIL rename Builder
this.getSnapshot().getSpawner().spawnPotentials.unwrap().forEach(entry -> builder.add(entry.getData(), entry.getWeight().asInt()));
builder.add(new MobSpawnerData(compoundTag, Optional.ofNullable(toMinecraftRule(spawnRule))), weight);
this.getSnapshot().getSpawner().spawnPotentials = builder.build();
}
@Override
public void addPotentialSpawn(SpawnerEntry spawnerEntry) {
addPotentialSpawn(spawnerEntry.getSnapshot(), spawnerEntry.getSpawnWeight(), spawnerEntry.getSpawnRule());
}
@Override
public void setPotentialSpawns(Collection<SpawnerEntry> entries) {
SimpleWeightedRandomList.a<MobSpawnerData> builder = SimpleWeightedRandomList.builder();
for (SpawnerEntry spawnerEntry : entries) {
NBTTagCompound compoundTag = ((CraftEntitySnapshot) spawnerEntry.getSnapshot()).getData();
builder.add(new MobSpawnerData(compoundTag, Optional.ofNullable(toMinecraftRule(spawnerEntry.getSpawnRule()))), spawnerEntry.getSpawnWeight());
}
this.getSnapshot().getSpawner().spawnPotentials = builder.build();
}
@Override
public List<SpawnerEntry> getPotentialSpawns() {
List<SpawnerEntry> entries = new ArrayList<>();
for (b<MobSpawnerData> entry : this.getSnapshot().getSpawner().spawnPotentials.unwrap()) { // PAIL rename Wrapper
CraftEntitySnapshot snapshot = CraftEntitySnapshot.create(entry.getData().getEntityToSpawn());
if (snapshot != null) {
SpawnRule rule = entry.getData().customSpawnRules().map(this::fromMinecraftRule).orElse(null);
entries.add(new SpawnerEntry(snapshot, entry.getWeight().asInt(), rule));
}
}
return entries;
}
private MobSpawnerData.a toMinecraftRule(SpawnRule rule) { // PAIL rename CustomSpawnRules
if (rule == null) {
return null;
}
return new MobSpawnerData.a(new InclusiveRange<>(rule.getMinBlockLight(), rule.getMaxBlockLight()), new InclusiveRange<>(rule.getMinSkyLight(), rule.getMaxSkyLight()));
}
private SpawnRule fromMinecraftRule(MobSpawnerData.a rule) {
InclusiveRange<Integer> blockLight = rule.blockLightLimit();
InclusiveRange<Integer> skyLight = rule.skyLightLimit();
return new SpawnRule(blockLight.maxInclusive(), blockLight.maxInclusive(), skyLight.minInclusive(), skyLight.maxInclusive());
}
@Override
public String getCreatureTypeName() {
MobSpawnerData spawnData = this.getSnapshot().getSpawner().nextSpawnData;

View File

@ -24,6 +24,7 @@ import net.minecraft.world.entity.EntityFlying;
import net.minecraft.world.entity.EntityLightning;
import net.minecraft.world.entity.EntityLiving;
import net.minecraft.world.entity.EntityTameableAnimal;
import net.minecraft.world.entity.EntityTypes;
import net.minecraft.world.entity.GlowSquid;
import net.minecraft.world.entity.Interaction;
import net.minecraft.world.entity.Marker;
@ -164,6 +165,7 @@ import net.minecraft.world.entity.vehicle.EntityMinecartTNT;
import net.minecraft.world.phys.AxisAlignedBB;
import org.bukkit.EntityEffect;
import org.bukkit.Location;
import org.bukkit.Registry;
import org.bukkit.Server;
import org.bukkit.Sound;
import org.bukkit.World;
@ -177,8 +179,10 @@ import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer;
import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.craftbukkit.util.CraftLocation;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.craftbukkit.util.CraftSpawnCategory;
import org.bukkit.craftbukkit.util.CraftVector;
import org.bukkit.entity.EntitySnapshot;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.Pose;
@ -641,7 +645,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
@Override
public boolean isValid() {
return entity.isAlive() && entity.valid && entity.isChunkLoaded();
return entity.isAlive() && entity.valid && entity.isChunkLoaded() && isInWorld();
}
@Override
@ -1114,6 +1118,42 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
return CraftSpawnCategory.toBukkit(getHandle().getType().getCategory());
}
@Override
public boolean isInWorld() {
return getHandle().inWorld;
}
@Override
public EntitySnapshot createSnapshot() {
return CraftEntitySnapshot.create(this);
}
@Override
public org.bukkit.entity.Entity copy() {
Entity copy = copy(getHandle().level());
Preconditions.checkArgument(copy != null, "Error creating new entity.");
return copy.getBukkitEntity();
}
@Override
public org.bukkit.entity.Entity copy(Location location) {
Preconditions.checkArgument(location.getWorld() != null, "Location has no world");
Entity copy = copy(((CraftWorld) location.getWorld()).getHandle());
Preconditions.checkArgument(copy != null, "Error creating new entity.");
copy.setPos(location.getX(), location.getY(), location.getZ());
return location.getWorld().addEntity(copy.getBukkitEntity());
}
private Entity copy(net.minecraft.world.level.World level) {
NBTTagCompound compoundTag = new NBTTagCompound();
getHandle().saveAsPassenger(compoundTag, false);
return EntityTypes.loadEntityRecursive(compoundTag, level, java.util.function.Function.identity());
}
public void storeBukkitValues(NBTTagCompound c) {
if (!this.persistentDataContainer.isEmpty()) {
c.put("BukkitValues", this.persistentDataContainer.toTagCompound());

View File

@ -0,0 +1,83 @@
package org.bukkit.craftbukkit.entity;
import com.google.common.base.Preconditions;
import java.util.function.Function;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.entity.EntityTypes;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntitySnapshot;
import org.bukkit.entity.EntityType;
public class CraftEntitySnapshot implements EntitySnapshot {
private final NBTTagCompound data;
private final EntityType type;
private CraftEntitySnapshot(NBTTagCompound data, EntityType type) {
this.data = data;
this.type = type;
}
@Override
public EntityType getEntityType() {
return type;
}
@Override
public Entity createEntity(World world) {
net.minecraft.world.entity.Entity internal = createInternal(world);
return internal.getBukkitEntity();
}
@Override
public Entity createEntity(Location location) {
Preconditions.checkArgument(location.getWorld() != null, "Location has no world");
net.minecraft.world.entity.Entity internal = createInternal(location.getWorld());
internal.setPos(location.getX(), location.getY(), location.getZ());
return location.getWorld().addEntity(internal.getBukkitEntity());
}
private net.minecraft.world.entity.Entity createInternal(World world) {
net.minecraft.world.level.World nms = ((CraftWorld) world).getHandle();
net.minecraft.world.entity.Entity internal = EntityTypes.loadEntityRecursive(data, nms, Function.identity());
if (internal == null) { // Try creating by type
internal = CraftEntityType.bukkitToMinecraft(type).create(nms);
}
Preconditions.checkArgument(internal != null, "Error creating new entity."); // This should only fail if the stored NBTTagCompound is malformed.
internal.load(data);
return internal;
}
public NBTTagCompound getData() {
return data;
}
public static CraftEntitySnapshot create(CraftEntity entity) {
NBTTagCompound tag = new NBTTagCompound();
if (!entity.getHandle().saveAsPassenger(tag, false)) {
return null;
}
return new CraftEntitySnapshot(tag, entity.getType());
}
public static CraftEntitySnapshot create(NBTTagCompound tag, EntityType type) {
if (tag == null || tag.isEmpty() || type == null) {
return null;
}
return new CraftEntitySnapshot(tag, type);
}
public static CraftEntitySnapshot create(NBTTagCompound tag) {
EntityType type = EntityTypes.by(tag).map(CraftEntityType::minecraftToBukkit).orElse(null);
return create(tag, type);
}
}

View File

@ -38,6 +38,7 @@ import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.entity.memory.CraftMemoryMapper;
import org.bukkit.craftbukkit.event.CraftEventFactory;
import org.bukkit.craftbukkit.inventory.CraftContainer;
@ -684,4 +685,14 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
boolean success = getHandle().level().addFreshEntity(fireworks, SpawnReason.CUSTOM);
return success ? (Firework) fireworks.getBukkitEntity() : null;
}
@Override
public org.bukkit.entity.Entity copy() {
throw new UnsupportedOperationException("Cannot copy human entities");
}
@Override
public org.bukkit.entity.Entity copy(Location location) {
throw new UnsupportedOperationException("Cannot copy human entities");
}
}

View File

@ -116,12 +116,12 @@ public class CraftLimitedRegion extends CraftRegionAccessor implements LimitedRe
if (entity.isAlive()) {
// check if entity is still in region or if it got teleported outside it
Preconditions.checkState(region.contains(entity.getX(), entity.getY(), entity.getZ()), "Entity %s is not in the region", entity);
access.addFreshEntity(entity);
access.addFreshEntityWithPassengers(entity);
}
}
for (net.minecraft.world.entity.Entity entity : outsideEntities) {
access.addFreshEntity(entity);
access.addFreshEntityWithPassengers(entity);
}
}
@ -250,4 +250,9 @@ public class CraftLimitedRegion extends CraftRegionAccessor implements LimitedRe
public void addEntityToWorld(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason) {
entities.add(entity);
}
@Override
public void addEntityWithPassengers(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason) {
entities.add(entity);
}
}

View File

@ -1,5 +1,6 @@
package org.bukkit.craftbukkit.inventory;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.Sets;
import java.util.Map;
@ -9,7 +10,9 @@ import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.resources.MinecraftKey;
import org.bukkit.Material;
import org.bukkit.configuration.serialization.DelegateDeserialization;
import org.bukkit.craftbukkit.entity.CraftEntitySnapshot;
import org.bukkit.craftbukkit.util.CraftLegacy;
import org.bukkit.entity.EntitySnapshot;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.meta.SpawnEggMeta;
import org.bukkit.material.MaterialData;
@ -216,6 +219,17 @@ public class CraftMetaSpawnEgg extends CraftMetaItem implements SpawnEggMeta {
throw new UnsupportedOperationException("Must change item type to set spawned type");
}
@Override
public EntitySnapshot getSpawnedEntity() {
return CraftEntitySnapshot.create(this.entityTag);
}
@Override
public void setSpawnedEntity(EntitySnapshot snapshot) {
Preconditions.checkArgument(snapshot.getEntityType().isSpawnable(), "Entity is not spawnable");
this.entityTag = ((CraftEntitySnapshot) snapshot).getData();
}
@Override
boolean equalsCommon(CraftMetaItem meta) {
if (!super.equalsCommon(meta)) {