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 setting 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.

Fixes #3321
This commit is contained in:
Aikar 2020-05-09 15:47:08 -04:00
parent ceb824dbde
commit 8e85f841d8
No known key found for this signature in database
GPG Key ID: 401ADFC9891FAAFE
3 changed files with 75 additions and 42 deletions

View File

@ -1,39 +0,0 @@
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 Chunk lookup and avoid loading far chunks
Try to use a faster chunk lookup for collision detection, and only
fall back to the original for nearby 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.
diff --git a/src/main/java/net/minecraft/server/ICollisionAccess.java b/src/main/java/net/minecraft/server/ICollisionAccess.java
index f851ed11df14fd9aa8017f44d82fb6cfc3bde345..d154487294b89d5a316929ed665c75d4dd3cfc6b 100644
--- a/src/main/java/net/minecraft/server/ICollisionAccess.java
+++ b/src/main/java/net/minecraft/server/ICollisionAccess.java
@@ -83,15 +83,19 @@ public interface ICollisionAccess extends IBlockAccess {
}
while (cursorposition.a()) {
- int k1 = cursorposition.b();
- int l1 = cursorposition.c();
- int i2 = cursorposition.d();
+ int k1 = cursorposition.b();int x = k1; // Paper
+ int l1 = cursorposition.c();int y = l1; // Paper
+ int i2 = cursorposition.d();int z = i2; // Paper
int j2 = cursorposition.e();
if (j2 != 3) {
int k2 = k1 >> 4;
int l2 = i2 >> 4;
- IBlockAccess iblockaccess = ICollisionAccess.this.c(k2, l2);
+ // Paper start - ensure we don't load chunks
+ boolean far = entity != null && MCUtil.distance(entity.locX(), entity.locY(), entity.locZ(), x, y, z) > 32;
+ IBlockAccess iblockaccess = ICollisionAccess.this instanceof WorldServer ? ((WorldServer) ICollisionAccess.this).getChunkProvider().getChunkAtIfLoadedMainThread(k2, l2) : null;
+ if (!far && iblockaccess == null) iblockaccess = ICollisionAccess.this.c(k2, l2);
+ // Paper end
if (iblockaccess != null) {
blockposition_mutableblockposition.d(k1, l1, i2);

View File

@ -0,0 +1,72 @@
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/ICollisionAccess.java b/src/main/java/net/minecraft/server/ICollisionAccess.java
index f851ed11df14fd9aa8017f44d82fb6cfc3bde345..d9aeb27a3d6cc2fef2030eafd01d92ef7758111c 100644
--- a/src/main/java/net/minecraft/server/ICollisionAccess.java
+++ b/src/main/java/net/minecraft/server/ICollisionAccess.java
@@ -83,19 +83,28 @@ public interface ICollisionAccess extends IBlockAccess {
}
while (cursorposition.a()) {
- int k1 = cursorposition.b();
- int l1 = cursorposition.c();
- int i2 = cursorposition.d();
+ int k1 = cursorposition.b();int x = k1; // Paper
+ int l1 = cursorposition.c();int y = l1; // Paper
+ int i2 = cursorposition.d();int z = i2; // Paper
int j2 = cursorposition.e();
if (j2 != 3) {
- int k2 = k1 >> 4;
- int l2 = i2 >> 4;
- IBlockAccess iblockaccess = ICollisionAccess.this.c(k2, l2);
-
- if (iblockaccess != null) {
- blockposition_mutableblockposition.d(k1, l1, i2);
- IBlockData iblockdata = iblockaccess.getType(blockposition_mutableblockposition);
+ // Paper start - ensure we don't load chunks
+ //int k2 = k1 >> 4;
+ //int l2 = i2 >> 4;
+ //boolean far = entity != null && MCUtil.distance(entity.locX(), entity.locY(), entity.locZ(), x, y, z) > 8;
+ blockposition_mutableblockposition.setValues(x, y, z); // Paper - moved up
+ IBlockData iblockdata = ICollisionAccess.this.getTypeIfLoaded(blockposition_mutableblockposition);
+ if (iblockdata == null) {
+ if (!(entity instanceof EntityPlayer) || entity.world.paperConfig.preventMovingIntoUnloadedChunks) {
+ VoxelShape voxelshape3 = VoxelShapes.a(new AxisAlignedBB(new BlockPosition(x, y, z)));
+ consumer.accept(voxelshape3);
+ return true;
+ }
+ } else {
+ //blockposition_mutableblockposition.d(k1, l1, i2); // moved up
+ //IBlockData iblockdata = iblockaccess.getType(blockposition_mutableblockposition); // moved up
+ // Paper end
if ((j2 != 1 || iblockdata.f()) && (j2 != 2 || iblockdata.getBlock() == Blocks.MOVING_PISTON)) {
VoxelShape voxelshape2 = iblockdata.b((IBlockAccess) ICollisionAccess.this, blockposition_mutableblockposition, voxelshapecollision);
diff --git a/src/main/java/net/minecraft/server/VoxelShapes.java b/src/main/java/net/minecraft/server/VoxelShapes.java
index 08c83c62dfe875600162a46a0ca4b835b0bfe199..240aa3ea19ca30cf3a21f47611e8a5d2be0683c8 100644
--- a/src/main/java/net/minecraft/server/VoxelShapes.java
+++ b/src/main/java/net/minecraft/server/VoxelShapes.java
@@ -237,7 +237,8 @@ public final class VoxelShapes {
if (k2 < 3) {
blockposition_mutableblockposition.a(enumaxiscycle1, i2, j2, l1);
- IBlockData iblockdata = iworldreader.getType(blockposition_mutableblockposition);
+ IBlockData iblockdata = iworldreader.getTypeIfLoaded(blockposition_mutableblockposition); // Paper
+ if (iblockdata == null) return 0.0D; // Paper
if ((k2 != 1 || iblockdata.f()) && (k2 != 2 || iblockdata.getBlock() == Blocks.MOVING_PISTON)) {
d0 = iblockdata.b((IBlockAccess) iworldreader, blockposition_mutableblockposition, voxelshapecollision).a(enumdirection_enumaxis2, axisalignedbb.d((double) (-blockposition_mutableblockposition.getX()), (double) (-blockposition_mutableblockposition.getY()), (double) (-blockposition_mutableblockposition.getZ())), d0);

View File

@ -75,10 +75,10 @@ index 71d2ae2a9c5a05351241b5a313e66ca15b0624ef..232b0023773008c19f19ad4658eb40fc
this.b = new IntArrayList(i1); this.b = new IntArrayList(i1);
this.c = new IntArrayList(i1); this.c = new IntArrayList(i1);
diff --git a/src/main/java/net/minecraft/server/VoxelShapes.java b/src/main/java/net/minecraft/server/VoxelShapes.java diff --git a/src/main/java/net/minecraft/server/VoxelShapes.java b/src/main/java/net/minecraft/server/VoxelShapes.java
index 08c83c62dfe875600162a46a0ca4b835b0bfe199..bb3a1a97df96157d7e1b0dfcbc935689088f4818 100644 index 240aa3ea19ca30cf3a21f47611e8a5d2be0683c8..8fde6f081655e9762c76ef9ed5475ca0d519a2ef 100644
--- a/src/main/java/net/minecraft/server/VoxelShapes.java --- a/src/main/java/net/minecraft/server/VoxelShapes.java
+++ b/src/main/java/net/minecraft/server/VoxelShapes.java +++ b/src/main/java/net/minecraft/server/VoxelShapes.java
@@ -316,9 +316,21 @@ public final class VoxelShapes { @@ -317,9 +317,21 @@ public final class VoxelShapes {
} }
@VisibleForTesting @VisibleForTesting
@ -101,7 +101,7 @@ index 08c83c62dfe875600162a46a0ca4b835b0bfe199..bb3a1a97df96157d7e1b0dfcbc935689
if (doublelist instanceof VoxelShapeCubePoint && doublelist1 instanceof VoxelShapeCubePoint) { if (doublelist instanceof VoxelShapeCubePoint && doublelist1 instanceof VoxelShapeCubePoint) {
long l = a(j, k); long l = a(j, k);
@@ -328,7 +340,22 @@ public final class VoxelShapes { @@ -329,7 +341,22 @@ public final class VoxelShapes {
} }
} }