2021-11-24 10:46:06 -08:00
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 2 Apr 2020 02:37:57 -0400
Subject: [PATCH] Optimize Collision to not load chunks
The collision code takes an AABB and generates a cuboid of checks rather
than a cylinder, so at high velocity this can generate a lot of chunk checks.
Treat an unloaded chunk as a collision for entities, and also for players if
the "prevent moving into unloaded chunks" setting is enabled.
If that serting is not enabled, collisions will be ignored for players, since
movement will load only the chunk the player enters anyways and avoids loading
massive amounts of surrounding chunks due to large AABB lookups.
2024-12-16 13:03:53 +01:00
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
2025-04-12 17:26:44 +02:00
index a8eaccde3ec9ed912cbc6df0b29e9f8136a46578..0f6ca6ef161ac2934ba761a1eca3215290c7262b 100644
2024-12-16 13:03:53 +01:00
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
2025-04-12 17:26:44 +02:00
@@ -223,6 +223,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
2024-05-26 12:51:15 -07:00
// Paper end - Share random for entities to make them more random
2025-04-12 17:26:44 +02:00
public @Nullable org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason
2021-11-24 10:46:06 -08:00
+ public boolean collisionLoadChunks = false; // Paper
2025-02-16 15:17:26 -03:00
private @Nullable org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity;
2021-11-24 10:46:06 -08:00
2024-12-16 13:03:53 +01:00
public org.bukkit.craftbukkit.entity.CraftEntity getBukkitEntity() {
diff --git a/net/minecraft/world/level/BlockCollisions.java b/net/minecraft/world/level/BlockCollisions.java
2025-04-12 17:26:44 +02:00
index ed6e4f9fd0c7ad1219e66bc1cb4038191dd6edd8..45a20dbb935b12d429153463dba5d6fd3385dd7a 100644
2024-12-16 13:03:53 +01:00
--- a/net/minecraft/world/level/BlockCollisions.java
+++ b/net/minecraft/world/level/BlockCollisions.java
@@ -80,16 +80,37 @@ public class BlockCollisions<T> extends AbstractIterator<T> {
2024-04-12 12:14:06 -07:00
@Override
2023-06-07 15:41:25 -07:00
protected T computeNext() {
2024-04-12 12:14:06 -07:00
while (this.cursor.advance()) {
- int i = this.cursor.nextX();
2024-12-16 13:03:53 +01:00
- int i1 = this.cursor.nextY();
- int i2 = this.cursor.nextZ();
2024-04-12 12:14:06 -07:00
+ int i = this.cursor.nextX(); final int x = i; // Paper - OBFHELPER
2024-12-16 13:03:53 +01:00
+ int i1 = this.cursor.nextY(); final int y = i1; // Paper - OBFHELPER
+ int i2 = this.cursor.nextZ(); final int z = i2; // Paper - OBFHELPER
int nextType = this.cursor.getNextType();
if (nextType != 3) {
- BlockGetter chunk = this.getChunk(i, i2);
- if (chunk != null) {
- this.pos.set(i, i1, i2);
- BlockState blockState = chunk.getBlockState(this.pos);
- if ((!this.onlySuffocatingBlocks || blockState.isSuffocating(chunk, this.pos))
2024-04-12 12:14:06 -07:00
+ // Paper start - ensure we don't load chunks
+ // BlockGetter blockGetter = this.getChunk(i, k);
+ if (true) {
2024-12-16 13:03:53 +01:00
+ @Nullable final Entity source = this.context instanceof net.minecraft.world.phys.shapes.EntityCollisionContext entityContext ? entityContext.getEntity() : null;
+ final boolean far = source != null && io.papermc.paper.util.MCUtil.distanceSq(source.getX(), y, source.getZ(), x, y, z) > 14;
2024-04-12 12:14:06 -07:00
+ this.pos.set(x, y, z);
+ BlockState blockState;
+ if (this.collisionGetter instanceof net.minecraft.server.level.WorldGenRegion) {
+ BlockGetter blockGetter = this.getChunk(x, z);
+ if (blockGetter == null) {
2024-10-24 22:29:29 +02:00
+ continue;
2024-04-12 12:14:06 -07:00
+ }
+ blockState = blockGetter.getBlockState(this.pos);
+ } else if ((!far && source instanceof net.minecraft.server.level.ServerPlayer) || (source != null && source.collisionLoadChunks)) {
+ blockState = this.collisionGetter.getBlockState(this.pos);
+ } else {
+ blockState = this.collisionGetter.getBlockStateIfLoaded(this.pos);
2021-11-24 10:46:06 -08:00
+ }
2024-04-12 12:14:06 -07:00
+ if (blockState == null) {
+ if (!(source instanceof net.minecraft.server.level.ServerPlayer) || source.level().paperConfig().chunks.preventMovingIntoUnloadedChunks) {
+ return this.resultProvider.apply(new BlockPos.MutableBlockPos(x, y, z), Shapes.create(far ? source.getBoundingBox() : new AABB(new BlockPos(x, y, z))));
+ }
+ continue;
2021-11-24 10:46:06 -08:00
+ }
2024-10-24 22:29:29 +02:00
+ if (true // onlySuffocatingBlocks is only true on the client, so we don't care about it here
+ // Paper end - ensure we don't load chunks
2024-12-16 13:03:53 +01:00
&& (nextType != 1 || blockState.hasLargeCollisionShape())
&& (nextType != 2 || blockState.is(Blocks.MOVING_PISTON))) {
VoxelShape collisionShape = this.context.getCollisionShape(blockState, this.collisionGetter, this.pos);
diff --git a/net/minecraft/world/level/CollisionGetter.java b/net/minecraft/world/level/CollisionGetter.java
2025-04-12 17:26:44 +02:00
index 79af1e4dd1f84580e509ac3e9a77bcd5531c8da6..a031d39854eb049a701f3de9e11c73419883d5ca 100644
2024-12-16 13:03:53 +01:00
--- a/net/minecraft/world/level/CollisionGetter.java
+++ b/net/minecraft/world/level/CollisionGetter.java
@@ -50,11 +50,13 @@ public interface CollisionGetter extends BlockGetter {
2021-11-24 10:46:06 -08:00
}
2024-12-16 13:03:53 +01:00
default boolean noCollision(@Nullable Entity entity, AABB collisionBox, boolean checkLiquid) {
2021-11-24 10:46:06 -08:00
+ try { if (entity != null) entity.collisionLoadChunks = true; // Paper
2024-12-16 13:03:53 +01:00
for (VoxelShape voxelShape : checkLiquid ? this.getBlockAndLiquidCollisions(entity, collisionBox) : this.getBlockCollisions(entity, collisionBox)) {
2021-11-24 10:46:06 -08:00
if (!voxelShape.isEmpty()) {
return false;
}
}
+ } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper
2024-12-16 13:03:53 +01:00
if (!this.getEntityCollisions(entity, collisionBox).isEmpty()) {
2021-11-24 10:46:06 -08:00
return false;