From 0478e0417b217d443d7ff8a52e3a97a1b7147d15 Mon Sep 17 00:00:00 2001 From: Jishuna Date: Sun, 19 Nov 2023 19:03:35 +1300 Subject: [PATCH] #1266: Add support for virtual entities --- .../minecraft/server/level/WorldServer.patch | 15 +- .../net/minecraft/world/entity/Entity.patch | 187 +++++++++++++----- .../world/entity/animal/EntityBee.patch | 36 +++- .../entity/decoration/EntityHanging.patch | 17 +- .../world/entity/projectile/EntityEgg.patch | 2 +- .../craftbukkit/CraftRegionAccessor.java | 30 ++- .../org/bukkit/craftbukkit/CraftWorld.java | 5 + .../block/CraftCreatureSpawner.java | 82 ++++++++ .../craftbukkit/entity/CraftEntity.java | 42 +++- .../entity/CraftEntitySnapshot.java | 83 ++++++++ .../craftbukkit/entity/CraftHumanEntity.java | 11 ++ .../generator/CraftLimitedRegion.java | 9 +- .../inventory/CraftMetaSpawnEgg.java | 14 ++ 13 files changed, 472 insertions(+), 61 deletions(-) create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftEntitySnapshot.java diff --git a/nms-patches/net/minecraft/server/level/WorldServer.patch b/nms-patches/net/minecraft/server/level/WorldServer.patch index 62f0b0a4d..b328aa03f 100644 --- a/nms-patches/net/minecraft/server/level/WorldServer.patch +++ b/nms-patches/net/minecraft/server/level/WorldServer.patch @@ -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); diff --git a/nms-patches/net/minecraft/world/entity/Entity.patch b/nms-patches/net/minecraft/world/entity/Entity.patch index 6d4f25bfd..7bb87d2d9 100644 --- a/nms-patches/net/minecraft/world/entity/Entity.patch +++ b/nms-patches/net/minecraft/world/entity/Entity.patch @@ -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 -+ // 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.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); -+ if (worldserver.getTypeKey() == WorldDimension.END) { // CraftBukkit -+ WorldServer.makeObsidianPlatform(worldserver, this); // CraftBukkit - } ++ // 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 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; } diff --git a/nms-patches/net/minecraft/world/entity/animal/EntityBee.patch b/nms-patches/net/minecraft/world/entity/animal/EntityBee.patch index a474e004e..88399604e 100644 --- a/nms-patches/net/minecraft/world/entity/animal/EntityBee.patch +++ b/nms-patches/net/minecraft/world/entity/animal/EntityBee.patch @@ -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 { c(EntityBee entitybee) { diff --git a/nms-patches/net/minecraft/world/entity/decoration/EntityHanging.patch b/nms-patches/net/minecraft/world/entity/decoration/EntityHanging.patch index ab985853b..82589f590 100644 --- a/nms-patches/net/minecraft/world/entity/decoration/EntityHanging.patch +++ b/nms-patches/net/minecraft/world/entity/decoration/EntityHanging.patch @@ -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(); diff --git a/nms-patches/net/minecraft/world/entity/projectile/EntityEgg.patch b/nms-patches/net/minecraft/world/entity/projectile/EntityEgg.patch index 80d43395c..ba9188ddf 100644 --- a/nms-patches/net/minecraft/world/entity/projectile/EntityEgg.patch +++ b/nms-patches/net/minecraft/world/entity/projectile/EntityEgg.patch @@ -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); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java index 857ab700d..2566bc633 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java @@ -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 getNMSEntities(); + @Override + @SuppressWarnings("unchecked") + public T createEntity(Location location, Class 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 spawn(Location location, Class 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 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 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 clazz) throws IllegalArgumentException { + public net.minecraft.world.entity.Entity makeEntity(Location location, Class clazz) throws IllegalArgumentException { return createEntity(location, clazz, true); } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index ec441ab6c..8d93355e3 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -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 getNearbyEntities(Location location, double x, double y, double z) { return this.getNearbyEntities(location, x, y, z, null); diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java b/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java index 5f22f59d0..d9bb4bf6c 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java @@ -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 implements CreatureSpawner { @@ -46,6 +56,78 @@ public class CraftCreatureSpawner extends CraftBlockEntityState 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 entries) { + SimpleWeightedRandomList.a 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 getPotentialSpawns() { + List entries = new ArrayList<>(); + + for (b 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 blockLight = rule.blockLightLimit(); + InclusiveRange skyLight = rule.skyLightLimit(); + + return new SpawnRule(blockLight.maxInclusive(), blockLight.maxInclusive(), skyLight.minInclusive(), skyLight.maxInclusive()); + } + @Override public String getCreatureTypeName() { MobSpawnerData spawnData = this.getSnapshot().getSpawner().nextSpawnData; diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index 4c6122822..c587411ac 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -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()); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntitySnapshot.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntitySnapshot.java new file mode 100644 index 000000000..2c133e8fa --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntitySnapshot.java @@ -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); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java index a2c7b1dcb..9670d7086 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java @@ -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"); + } } diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java index c73afec09..1d6da2fcd 100644 --- a/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java +++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java @@ -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); + } } diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java index 5a57131bc..453c64275 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java @@ -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)) {