aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0997-Optimize-Collision-to-not-load-chunks.patch
blob: 106d5bd3fb2c99e58c86a57e012bbba3dba8ea76 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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
index 461c27292af06a5150de8ec263d0c8527e8c5278..37245ff682837e7e8c9647f4afe30f0dd87cb384 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -935,6 +935,7 @@ public abstract class PlayerList {
         entityplayer1.setShiftKeyDown(false);
         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
         while (avoidSuffocation && !worldserver1.noCollision((Entity) entityplayer1) && entityplayer1.getY() < (double) worldserver1.getMaxBuildHeight()) {
             // CraftBukkit end
             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
index dd785d11461541e8a93544142f7a3b4e43b2173d..f8ce3c92597b9fb2b8743a08088f33a7bf237c5d 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -242,6 +242,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
     public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason
 
     public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper
+    public boolean collisionLoadChunks = false; // Paper
     private CraftEntity bukkitEntity;
 
     public @org.jetbrains.annotations.Nullable net.minecraft.server.level.ChunkMap.TrackedEntity tracker; // Paper
diff --git a/src/main/java/net/minecraft/world/level/BlockCollisions.java b/src/main/java/net/minecraft/world/level/BlockCollisions.java
index f2c423154ed6a00882a46d93b69ed4f6ba73782c..a3eaf80b020c3bbc0306c5d17659ee661dfd275b 100644
--- a/src/main/java/net/minecraft/world/level/BlockCollisions.java
+++ b/src/main/java/net/minecraft/world/level/BlockCollisions.java
@@ -65,22 +65,41 @@ public class BlockCollisions<T> extends AbstractIterator<T> {
     protected T computeNext() {
         while(true) {
             if (this.cursor.advance()) {
-                int i = this.cursor.nextX();
-                int j = this.cursor.nextY();
-                int k = this.cursor.nextZ();
+                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
                 int l = this.cursor.getNextType();
                 if (l == 3) {
                     continue;
                 }
+                // 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;
+                boolean far = source != null && io.papermc.paper.util.MCUtil.distanceSq(source.getX(), y, source.getZ(), x, y, z) > 14;
+                this.pos.set(x, y, z);
 
-                BlockGetter blockGetter = this.getChunk(i, k);
-                if (blockGetter == null) {
+                BlockState blockState;
+                if (this.collisionGetter instanceof net.minecraft.server.level.WorldGenRegion) {
+                    BlockGetter blockGetter = this.getChunk(x, z);
+                    if (blockGetter == null) {
+                       continue;
+                    }
+                    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);
+                }
+
+                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))));
+                    }
+                    // Paper end
                     continue;
                 }
 
-                this.pos.set(i, j, k);
-                BlockState blockState = blockGetter.getBlockState(this.pos);
-                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
                     continue;
                 }
 
diff --git a/src/main/java/net/minecraft/world/level/CollisionGetter.java b/src/main/java/net/minecraft/world/level/CollisionGetter.java
index 140d10807a3a6806578cd203ba58383590c2f2c0..c476e37df8a75d77f5093b2a449e04f25ef2c2dd 100644
--- 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;