diff --git a/leaf-server/minecraft-patches/features/0295-Prevent-entities-from-moving-into-weak-loaded-chunks.patch b/leaf-server/minecraft-patches/features/0295-Prevent-entities-from-moving-into-weak-loaded-chunks.patch new file mode 100644 index 0000000000..93fb220dcb --- /dev/null +++ b/leaf-server/minecraft-patches/features/0295-Prevent-entities-from-moving-into-weak-loaded-chunks.patch @@ -0,0 +1,209 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> +Date: Tue, 9 Nov 2077 00:00:00 +0800 +Subject: [PATCH] Prevent entities from moving into weak loaded chunks + + +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index cf2a365bcbb1a278e5553c16ffef6d9ae81f4d41..1a37a469924da67cfc34a9564a346fee9f3dbe7e 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -250,7 +250,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } + + // Paper start +- public final boolean areChunksLoadedForMove(AABB box) { ++ public final boolean areChunksLoadedForMove(AABB box) { // Leaf - diff on change - update areChunksLoadedForEntityMove if this changed + // copied code from collision methods, so that we can guarantee that they won't load chunks (we don't override + // CollisionGetter methods for VoxelShapes) + // be more strict too, add a block (dumb plugins in move events?) +@@ -276,8 +276,40 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } + } + ++ return true; ++ } // Leaf - diff on change - update areChunksLoadedForEntityMove if this changed ++ ++ // Leaf start - Prevent entity from moving into weak loaded chunks ++ public final boolean areChunksLoadedForEntityMove(AABB box) { ++ // copied code from collision methods, so that we can guarantee that they won't load chunks (we don't override ++ // CollisionGetter methods for VoxelShapes) ++ // be more strict too, add a block (dumb plugins in move events?) ++ int minBlockX = Mth.floor(box.minX - 1.0E-7D) - 3; ++ int maxBlockX = Mth.floor(box.maxX + 1.0E-7D) + 3; ++ ++ int minBlockZ = Mth.floor(box.minZ - 1.0E-7D) - 3; ++ int maxBlockZ = Mth.floor(box.maxZ + 1.0E-7D) + 3; ++ ++ int minChunkX = minBlockX >> 4; ++ int maxChunkX = maxBlockX >> 4; ++ ++ int minChunkZ = minBlockZ >> 4; ++ int maxChunkZ = maxBlockZ >> 4; ++ ++ ServerChunkCache chunkProvider = this.getChunkSource(); ++ ++ for (int cx = minChunkX; cx <= maxChunkX; ++cx) { ++ for (int cz = minChunkZ; cz <= maxChunkZ; ++cz) { ++ LevelChunk chunk = chunkProvider.getChunkAtIfLoadedImmediately(cx, cz); ++ if (chunk == null || !chunk.getFullStatus().isOrAfter(FullChunkStatus.ENTITY_TICKING)) { // check chunk status ++ return false; ++ } ++ } ++ } ++ + return true; + } ++ // Leaf end - Prevent entity from moving into weak loaded chunks + + public final void loadChunksForMoveAsync(AABB box, ca.spottedleaf.concurrentutil.util.Priority priority, + java.util.function.Consumer> onLoad) { +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index 8d092716cdcc48b829a1c0ee2e5416d648143a37..259b9dae8fbf115e31da5e8f8b5d127e07b95372 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -387,6 +387,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name + public boolean isTemporarilyActive; + public long activatedImmunityTick = Integer.MIN_VALUE; + public @Nullable Boolean immuneToFire = null; // Purpur - Fire immune API ++ protected boolean preventMoveIntoWeakLoadedChunks = false; + + public void inactiveTick() { + } +diff --git a/net/minecraft/world/entity/projectile/LlamaSpit.java b/net/minecraft/world/entity/projectile/LlamaSpit.java +index 56d78249c3fd7da0ff963712fe3a5c722b907c09..1a42e405ee0372d1c68662c9d5192c2a95e56b01 100644 +--- a/net/minecraft/world/entity/projectile/LlamaSpit.java ++++ b/net/minecraft/world/entity/projectile/LlamaSpit.java +@@ -62,6 +62,17 @@ public class LlamaSpit extends Projectile { + } else { + this.setDeltaMovement(deltaMovement.scale(0.99F)); + this.applyGravity(); ++ // Leaf start - Prevent entity from moving into weak loaded chunks ++ if (this.preventMoveIntoWeakLoadedChunks) { ++ if (this.level() instanceof net.minecraft.server.level.ServerLevel serverLevel) { ++ if (!serverLevel.areChunksLoadedForEntityMove(this.getBoundingBox().expandTowards(this.getDeltaMovement()))) { ++ this.setDeltaMovement(Vec3.ZERO); ++ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); ++ return; ++ } ++ } ++ } ++ // Leaf end - Prevent entity from moving into weak loaded chunks + this.setPos(d, d1, d2); + } + } +diff --git a/net/minecraft/world/entity/projectile/Projectile.java b/net/minecraft/world/entity/projectile/Projectile.java +index c95ca23bc51780c9c68bc791bdd33822894ddf3c..7035d9fb406ea846fc8053017aeb6095ce596278 100644 +--- a/net/minecraft/world/entity/projectile/Projectile.java ++++ b/net/minecraft/world/entity/projectile/Projectile.java +@@ -48,6 +48,7 @@ public abstract class Projectile extends Entity implements TraceableEntity { + + protected Projectile(EntityType type, Level level) { + super(type, level); ++ this.preventMoveIntoWeakLoadedChunks = org.dreeam.leaf.config.modules.fixes.PreventMoveIntoWeakLoadedChunks.isProjectileEnabled(); // Leaf - Prevent entity from moving into weak loaded chunks + } + + // Gale start - Airplane - reduce projectile chunk loading +diff --git a/net/minecraft/world/entity/projectile/ShulkerBullet.java b/net/minecraft/world/entity/projectile/ShulkerBullet.java +index 7ab335a90c2b873b7d4eff148303bcc3f031dd64..5e7d8d184d943470978b2086c18fc78f96c56cec 100644 +--- a/net/minecraft/world/entity/projectile/ShulkerBullet.java ++++ b/net/minecraft/world/entity/projectile/ShulkerBullet.java +@@ -227,6 +227,13 @@ public class ShulkerBullet extends Projectile { + } + + Vec3 deltaMovement = this.getDeltaMovement(); ++ // Leaf start - Prevent entity from moving into weak loaded chunks ++ if (this.preventMoveIntoWeakLoadedChunks && this.level() instanceof ServerLevel serverLevel && !serverLevel.areChunksLoadedForEntityMove(this.getBoundingBox().expandTowards(deltaMovement))) { ++ this.setDeltaMovement(Vec3.ZERO); ++ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); ++ return; ++ } ++ // Leaf end - Prevent entity from moving into weak loaded chunks + this.setPos(this.position().add(deltaMovement)); + this.applyEffectsFromBlocks(); + if (this.portalProcess != null && this.portalProcess.isInsidePortalThisTick()) { +diff --git a/net/minecraft/world/entity/projectile/ThrowableProjectile.java b/net/minecraft/world/entity/projectile/ThrowableProjectile.java +index 47f2e940b68738cb02719cd82a5c0fff56fba062..b4cecd58a0e63542aaa3846a0a24ae4e395868ba 100644 +--- a/net/minecraft/world/entity/projectile/ThrowableProjectile.java ++++ b/net/minecraft/world/entity/projectile/ThrowableProjectile.java +@@ -57,7 +57,20 @@ public abstract class ThrowableProjectile extends Projectile { + if (hitResultOnMoveVector.getType() != HitResult.Type.MISS) { + location = hitResultOnMoveVector.getLocation(); + } else { +- location = this.position().add(this.getDeltaMovement()); ++ // Leaf start - Prevent entity from moving into weak loaded chunks ++ if (this.preventMoveIntoWeakLoadedChunks) { ++ Vec3 previousLocation = this.position(); ++ location = this.position().add(this.getDeltaMovement()); ++ if (this.level() instanceof net.minecraft.server.level.ServerLevel serverLevel) { ++ if (!serverLevel.areChunksLoadedForEntityMove(this.getBoundingBox().expandTowards(location.subtract(previousLocation)))) { ++ location = previousLocation; ++ this.setDeltaMovement(Vec3.ZERO); ++ } ++ } ++ } else { ++ location = this.position().add(this.getDeltaMovement()); ++ } ++ // Leaf end - Prevent entity from moving into weak loaded chunks + } + + this.setPos(location); +diff --git a/net/minecraft/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectile.java b/net/minecraft/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectile.java +index 6edff432866ae72db657b47bf9726be9a4ef1562..e0f75669792932a14898ff35f3cc7be25d43c356 100644 +--- a/net/minecraft/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectile.java ++++ b/net/minecraft/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectile.java +@@ -24,6 +24,7 @@ public abstract class AbstractHurtingProjectile extends Projectile { + public double accelerationPower = 0.1; + public float bukkitYield = 1; // CraftBukkit + public boolean isIncendiary = true; // CraftBukkit ++ private boolean scheduleForRemoval = false; // Leaf - Prevent entity from moving into weak loaded chunks + + protected AbstractHurtingProjectile(EntityType type, Level level) { + super(type, level); +@@ -70,13 +71,27 @@ public abstract class AbstractHurtingProjectile extends Projectile { + public void tick() { + Entity owner = this.getOwner(); + this.applyInertia(); +- if (this.level().isClientSide() || (owner == null || !owner.isRemoved()) && this.level().hasChunkAt(this.blockPosition())) { ++ if (this.level().isClientSide() || (owner == null || !owner.isRemoved()) && this.level().hasChunkAt(this.blockPosition()) && !this.scheduleForRemoval) { // Leaf - Prevent entity from moving into weak loaded chunks + HitResult hitResultOnMoveVector = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity, this.getClipType()); + Vec3 location; + if (hitResultOnMoveVector.getType() != HitResult.Type.MISS) { + location = hitResultOnMoveVector.getLocation(); + } else { +- location = this.position().add(this.getDeltaMovement()); ++ // Leaf start - Prevent entity from moving into weak loaded chunks ++ if (this.preventMoveIntoWeakLoadedChunks) { ++ Vec3 previousLocation = this.position(); ++ location = this.position().add(this.getDeltaMovement()); ++ if (this.level() instanceof net.minecraft.server.level.ServerLevel serverLevel) { ++ if (!serverLevel.areChunksLoadedForEntityMove(this.getBoundingBox().expandTowards(location.subtract(previousLocation)))) { ++ location = previousLocation; ++ this.setDeltaMovement(Vec3.ZERO); ++ this.scheduleForRemoval = true; ++ } ++ } ++ } else { ++ location = this.position().add(this.getDeltaMovement()); ++ } ++ // Leaf end - Prevent entity from moving into weak loaded chunks + } + + ProjectileUtil.rotateTowardsMovement(this, 0.2F); +diff --git a/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java b/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java +index 4d416d3a8402a78b8ad6383d842f589a821ddbe9..2fe748ab39602b1f3d2c4f9982375e582806828c 100644 +--- a/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java ++++ b/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java +@@ -33,10 +33,12 @@ public class ThrownEnderpearl extends ThrowableItemProjectile { + + public ThrownEnderpearl(EntityType type, Level level) { + super(type, level); ++ this.preventMoveIntoWeakLoadedChunks = false; // Leaf - Prevent entity from moving into weak loaded chunks + } + + public ThrownEnderpearl(Level level, LivingEntity owner, ItemStack item) { + super(EntityType.ENDER_PEARL, owner, level, item); ++ this.preventMoveIntoWeakLoadedChunks = false; // Leaf - Prevent entity from moving into weak loaded chunks + } + + @Override diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/fixes/PreventMoveIntoWeakLoadedChunks.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/fixes/PreventMoveIntoWeakLoadedChunks.java new file mode 100644 index 0000000000..218c4063b2 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/fixes/PreventMoveIntoWeakLoadedChunks.java @@ -0,0 +1,36 @@ +package org.dreeam.leaf.config.modules.fixes; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class PreventMoveIntoWeakLoadedChunks extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.FIXES.getBaseKeyName() + ".prevent-moving-into-weak-loaded-chunks"; + } + + public static boolean enabled = false; + public static boolean projectiles = false; + + public static boolean isProjectileEnabled() { + return enabled && projectiles; + } + + @Override + public void onLoaded() { + config.addCommentRegionBased(getBasePath(), + "Prevents entities from moving into weak loaded chunks.", + "阻止实体进入弱加载区块。" + ); + + enabled = config.getBoolean(getBasePath() + ".enabled", enabled, config().pickStringRegionBased( + "Set to true to enable features below.", + "设置为 true 以启用以下功能。" + )); + + projectiles = config.getBoolean(getBasePath() + ".projectiles", projectiles, config().pickStringRegionBased( + "Prevents projectiles from moving into weak loaded chunks.", + "阻止弹射物进入弱加载区块。" + )); + } +}