aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0981-Optimize-Collision-to-not-load-chunks.patch
blob: 327fd5c6c2c2ced5c31df6b97699528e5cbf1a73 (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
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 781de82a8bee2836bf154341c91f23b34d98d4ad..661498e404bd1b0e4857e159b79a3eeca6df1d8a 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -826,6 +826,7 @@ public abstract class PlayerList {
         Vec3 vec3d = teleporttransition.position();
 
         entityplayer1.forceSetPositionRotation(vec3d.x, vec3d.y, vec3d.z, teleporttransition.yRot(), teleporttransition.xRot());
+        worldserver.getChunkSource().addRegionTicket(net.minecraft.server.level.TicketType.POST_TELEPORT, new net.minecraft.world.level.ChunkPos(net.minecraft.util.Mth.floor(vec3d.x()) >> 4, net.minecraft.util.Mth.floor(vec3d.z()) >> 4), 1, entityplayer.getId()); // Paper
         // CraftBukkit end
         if (teleporttransition.missingRespawnBlock()) {
             entityplayer1.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.NO_RESPAWN_BLOCK_AVAILABLE, 0.0F));
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 9a37a898eba0cd9ab18ec5ee241cf054c8a71b32..aa7d3383c773d3537335e449636f33d69cde12bb 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -258,6 +258,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
     // Paper end - Share random for entities to make them more random
     public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason
 
+    public boolean collisionLoadChunks = false; // Paper
     private CraftEntity bukkitEntity;
 
     public CraftEntity getBukkitEntity() {
diff --git a/src/main/java/net/minecraft/world/level/BlockCollisions.java b/src/main/java/net/minecraft/world/level/BlockCollisions.java
index 12a14b8fdb079fd8c367e51299486761ebdaeb5b..b360594424118d9d11d00ea140160485048591ca 100644
--- a/src/main/java/net/minecraft/world/level/BlockCollisions.java
+++ b/src/main/java/net/minecraft/world/level/BlockCollisions.java
@@ -72,16 +72,37 @@ public class BlockCollisions<T> extends AbstractIterator<T> {
     @Override
     protected T computeNext() {
         while (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 - OBFHELPER
+            int j = this.cursor.nextY(); final int y = j; // Paper - OBFHELPER
+            int k = this.cursor.nextZ(); final int z = k; // Paper - OBFHELPER
             int l = this.cursor.getNextType();
             if (l != 3) {
-                BlockGetter blockGetter = this.getChunk(i, k);
-                if (blockGetter != null) {
-                    this.pos.set(i, j, k);
-                    BlockState blockState = blockGetter.getBlockState(this.pos);
-                    if ((!this.onlySuffocatingBlocks || blockState.isSuffocating(blockGetter, this.pos))
+                // Paper start - ensure we don't load chunks
+                // BlockGetter blockGetter = this.getChunk(i, k);
+                if (true) {
+                    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);
+                    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))));
+                        }
+                        continue;
+                    }
+                    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
                         && (l != 1 || blockState.hasLargeCollisionShape())
                         && (l != 2 || blockState.is(Blocks.MOVING_PISTON))) {
                         VoxelShape voxelShape = this.context.getCollisionShape(blockState, this.collisionGetter, this.pos);
diff --git a/src/main/java/net/minecraft/world/level/CollisionGetter.java b/src/main/java/net/minecraft/world/level/CollisionGetter.java
index 6b63dc10c0a4db8a484588a6d67911ea1023457e..f4c251c2bcfa96ba22e338b97a1de7f0abac5a6f 100644
--- a/src/main/java/net/minecraft/world/level/CollisionGetter.java
+++ b/src/main/java/net/minecraft/world/level/CollisionGetter.java
@@ -50,11 +50,13 @@ public interface CollisionGetter extends BlockGetter {
     }
 
     default boolean noCollision(@Nullable Entity entity, AABB box, boolean checkFluid) {
-        for (VoxelShape voxelShape : checkFluid ? this.getBlockAndLiquidCollisions(entity, box) : this.getBlockCollisions(entity, box)) {
+        try { if (entity != null) entity.collisionLoadChunks = true; // Paper
+            for (VoxelShape voxelShape : checkFluid ? this.getBlockAndLiquidCollisions(entity, box) : 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;