2021-06-14 00:05:18 +02: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.
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
2023-09-22 04:31:59 +02:00
index b4ded40387006821433db1de85ed1c2347757155..6a170e73f0ca6233a742dfa13ce496e0b770b0ac 100644
2021-06-14 00:05:18 +02:00
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
2023-09-22 04:31:59 +02:00
@@ -824,6 +824,7 @@ public abstract class PlayerList {
entityplayer1.setShiftKeyDown(false);
2021-06-14 00:05:18 +02:00
entityplayer1.forceSetPositionRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
+ worldserver1.getChunkSource().addRegionTicket(net.minecraft.server.level.TicketType.POST_TELEPORT, new net.minecraft.world.level.ChunkPos(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper
2022-03-01 06:43:03 +01:00
while (avoidSuffocation && !worldserver1.noCollision((Entity) entityplayer1) && entityplayer1.getY() < (double) worldserver1.getMaxBuildHeight()) {
2023-09-22 04:31:59 +02:00
// CraftBukkit end
2021-06-14 00:05:18 +02:00
entityplayer1.setPos(entityplayer1.getX(), entityplayer1.getY() + 1.0D, entityplayer1.getZ());
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
2023-09-22 04:31:59 +02:00
index 1badb25665edaeb5eb274b770ea50598ba056b22..961213739ba810c6e39aca76a32d74bcf2a58ad3 100644
2021-06-14 00:05:18 +02:00
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
2023-05-12 13:10:08 +02:00
@@ -237,6 +237,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
2022-06-08 06:22:42 +02:00
public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper
2021-06-14 00:05:18 +02:00
public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper
+ public boolean collisionLoadChunks = false; // Paper
private CraftEntity bukkitEntity;
2021-12-13 05:31:32 +01:00
public @org.jetbrains.annotations.Nullable net.minecraft.server.level.ChunkMap.TrackedEntity tracker; // Paper
2021-11-24 19:46:06 +01:00
diff --git a/src/main/java/net/minecraft/world/level/BlockCollisions.java b/src/main/java/net/minecraft/world/level/BlockCollisions.java
2023-09-22 19:31:02 +02:00
index 3e2be725b3803b19558bf1e2c588f6c0d7b41ba4..6284701b83883e93838c7de11c6cd05eaef0c086 100644
2021-11-24 19:46:06 +01:00
--- a/src/main/java/net/minecraft/world/level/BlockCollisions.java
+++ b/src/main/java/net/minecraft/world/level/BlockCollisions.java
2023-06-08 00:41:25 +02:00
@@ -65,22 +65,41 @@ public class BlockCollisions<T> extends AbstractIterator<T> {
protected T computeNext() {
2021-06-14 00:05:18 +02:00
while(true) {
if (this.cursor.advance()) {
- int i = this.cursor.nextX();
- int j = this.cursor.nextY();
- int k = this.cursor.nextZ();
2021-11-24 19:46:06 +01:00
+ int i = this.cursor.nextX(); final int x = i; // Paper
+ int j = this.cursor.nextY(); final int y = j; // Paper
+ int k = this.cursor.nextZ(); final int z = k; // Paper
2021-06-14 00:05:18 +02:00
int l = this.cursor.getNextType();
if (l == 3) {
continue;
}
2021-11-24 19:46:06 +01:00
+ // Paper start - ensure we don't load chunks
+ final @Nullable Entity source = this.context instanceof net.minecraft.world.phys.shapes.EntityCollisionContext entityContext ? entityContext.getEntity() : null;
2022-10-24 21:43:46 +02:00
+ boolean far = source != null && io.papermc.paper.util.MCUtil.distanceSq(source.getX(), y, source.getZ(), x, y, z) > 14;
2021-11-24 19:46:06 +01:00
+ this.pos.set(x, y, z);
2021-06-14 00:05:18 +02:00
- BlockGetter blockGetter = this.getChunk(i, k);
- if (blockGetter == null) {
2021-07-09 12:16:57 +02:00
+ BlockState blockState;
+ if (this.collisionGetter instanceof net.minecraft.server.level.WorldGenRegion) {
2021-11-24 19:46:06 +01:00
+ BlockGetter blockGetter = this.getChunk(x, z);
+ if (blockGetter == null) {
2021-07-09 12:16:57 +02:00
+ continue;
2021-11-24 19:46:06 +01:00
+ }
+ blockState = blockGetter.getBlockState(this.pos);
+ } else if ((!far && source instanceof net.minecraft.server.level.ServerPlayer) || (source != null && source.collisionLoadChunks)) {
2021-07-09 12:16:57 +02:00
+ blockState = this.collisionGetter.getBlockState(this.pos);
+ } else {
2021-12-06 00:32:02 +01:00
+ blockState = this.collisionGetter.getBlockStateIfLoaded(this.pos);
2021-07-09 12:16:57 +02:00
+ }
2021-06-14 00:05:18 +02:00
+
+ if (blockState == null) {
2023-06-08 01:21:20 +02:00
+ if (!(source instanceof net.minecraft.server.level.ServerPlayer) || source.level().paperConfig().chunks.preventMovingIntoUnloadedChunks) {
2023-06-08 02:30:05 +02:00
+ return this.resultProvider.apply(new BlockPos.MutableBlockPos(x, y, z), Shapes.create(far ? source.getBoundingBox() : new AABB(new BlockPos(x, y, z))));
2021-06-14 00:05:18 +02:00
+ }
2021-11-24 19:46:06 +01:00
+ // Paper end
2021-06-14 00:05:18 +02:00
continue;
}
- this.pos.set(i, j, k);
- BlockState blockState = blockGetter.getBlockState(this.pos);
2021-11-24 19:46:06 +01:00
- if (this.onlySuffocatingBlocks && !blockState.isSuffocating(blockGetter, this.pos) || l == 1 && !blockState.hasLargeCollisionShape() || l == 2 && !blockState.is(Blocks.MOVING_PISTON)) {
+ // Paper - moved up
+ if (/*this.onlySuffocatingBlocks && (!blockState.isSuffocating(blockGetter, this.pos)) ||*/ l == 1 && !blockState.hasLargeCollisionShape() || l == 2 && !blockState.is(Blocks.MOVING_PISTON)) { // Paper - onlySuffocatingBlocks is only true on the client, so we don't care about it here
2021-06-14 00:05:18 +02:00
continue;
}
2021-11-24 19:46:06 +01:00
diff --git a/src/main/java/net/minecraft/world/level/CollisionGetter.java b/src/main/java/net/minecraft/world/level/CollisionGetter.java
2023-09-22 19:31:02 +02:00
index 95d919ed9ab071afda571f86adb99e4ffb96858d..bbace777cbbd5014c0115380aa05d179c0e3fe22 100644
2021-11-24 19:46:06 +01:00
--- a/src/main/java/net/minecraft/world/level/CollisionGetter.java
+++ b/src/main/java/net/minecraft/world/level/CollisionGetter.java
@@ -44,11 +44,13 @@ public interface CollisionGetter extends BlockGetter {
}
default boolean noCollision(@Nullable Entity entity, AABB box) {
+ try { if (entity != null) entity.collisionLoadChunks = true; // Paper
for(VoxelShape voxelShape : this.getBlockCollisions(entity, box)) {
if (!voxelShape.isEmpty()) {
return false;
}
}
+ } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper
if (!this.getEntityCollisions(entity, box).isEmpty()) {
return false;