Paper/patches/server/0393-Optimize-Collision-to-not-load-chunks.patch
Nassim Jahnke 17f71ac87b
Updated Upstream (Bukkit/CraftBukkit/Spigot) (#5904)
Upstream has released updates that appear to apply and compile correctly.
This update has not been tested by PaperMC and as with ANY update, please do your own testing

Bukkit Changes:
70d24eb8 SPIGOT-6587: Update documentation/error of drop chance API

CraftBukkit Changes:
470050ad SPIGOT-6587: Update documentation/error of drop chance API
1c39efa3 Fix Inventory#getViewers on the player inventory not returning the player first time their inventory is opened
d161627d Fix PrepareItemCraftEvent#isRepair
aa1fae73 SPIGOT-6586: EntityChangeBlockEvent for falling block does not cancel properly
8a04072e SPIGOT-6583: Throwing eggs doesn't make sounds

Spigot Changes:
f773da84 Remove redundant patch
cd367234 Rebuild patches
2021-06-20 21:25:59 +02:00

115 lines
7.4 KiB
Diff

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 43f2d2bbb2bcb4b77b4f11916143f3566b3dcaef..22c1aacb517c8bb4a745a52437cfa687de2fe272 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -789,6 +789,7 @@ public abstract class PlayerList {
entityplayer1.forceSetPositionRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
// CraftBukkit end
+ 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(entityplayer1) && entityplayer1.getY() < (double) worldserver1.getMaxBuildHeight()) {
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 d7a4fd6ce9d35500a7c6a21b456f5f0a075e43e8..1a3d6368b84aa24b6d0361dd30bc06b79f1d5133 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -172,6 +172,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
// Paper end
public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper
+ public boolean collisionLoadChunks = false; // Paper
private CraftEntity bukkitEntity;
public net.minecraft.server.level.ChunkMap.TrackedEntity tracker; // Paper
diff --git a/src/main/java/net/minecraft/world/level/CollisionGetter.java b/src/main/java/net/minecraft/world/level/CollisionGetter.java
index b980c26ab5cac02e03525177a9dc4fb0b6a2f9f6..2a784a8342e708e0813c7076a2ca8e429446ffd3 100644
--- a/src/main/java/net/minecraft/world/level/CollisionGetter.java
+++ b/src/main/java/net/minecraft/world/level/CollisionGetter.java
@@ -55,7 +55,9 @@ public interface CollisionGetter extends BlockGetter {
}
default boolean noCollision(@Nullable Entity entity, AABB box, Predicate<Entity> filter) {
+ try { if (entity != null) entity.collisionLoadChunks = true; // Paper
return this.getCollisions(entity, box, filter).allMatch(VoxelShape::isEmpty);
+ } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper
}
Stream<VoxelShape> getEntityCollisions(@Nullable Entity entity, AABB box, Predicate<Entity> predicate);
diff --git a/src/main/java/net/minecraft/world/level/CollisionSpliterator.java b/src/main/java/net/minecraft/world/level/CollisionSpliterator.java
index e6190bfb893de12e87e1da49001ebd963b3d6318..6f4acf1c7c98a6069b79db483b8c79a8f4b46d54 100644
--- a/src/main/java/net/minecraft/world/level/CollisionSpliterator.java
+++ b/src/main/java/net/minecraft/world/level/CollisionSpliterator.java
@@ -64,21 +64,35 @@ public class CollisionSpliterator extends AbstractSpliterator<VoxelShape> {
boolean collisionCheck(Consumer<? super VoxelShape> action) {
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;
+ int j = this.cursor.nextY(); final int y = j;
+ int k = this.cursor.nextZ(); final int z = k;
int l = this.cursor.getNextType();
if (l == 3) {
continue;
}
- BlockGetter blockGetter = this.getChunk(i, k);
- if (blockGetter == null) {
+ // Paper start - ensure we don't load chunks
+ boolean far = this.source != null && net.minecraft.server.MCUtil.distanceSq(this.source.getX(), y, this.source.getZ(), x, y, z) > 14;
+ this.pos.set(x, y, z);
+
+ boolean isRegionLimited = this.collisionGetter instanceof net.minecraft.server.level.WorldGenRegion;
+ BlockState blockState = isRegionLimited ? Blocks.VOID_AIR.defaultBlockState() : ((!far && this.source instanceof net.minecraft.server.level.ServerPlayer) || (this.source != null && this.source.collisionLoadChunks)
+ ? this.collisionGetter.getBlockState(this.pos)
+ : this.collisionGetter.getTypeIfLoaded(this.pos)
+ );
+
+ if (blockState == null) {
+ if (!(this.source instanceof net.minecraft.server.level.ServerPlayer) || this.source.level.paperConfig.preventMovingIntoUnloadedChunks) {
+ VoxelShape voxelshape3 = Shapes.create(far ? this.source.getBoundingBox() : new AABB(new BlockPos(x, y, z)));
+ action.accept(voxelshape3);
+ return true;
+ }
continue;
}
+ // Paper - moved up
+ // Paper end
- this.pos.set(i, j, k);
- BlockState blockState = blockGetter.getBlockState(this.pos);
if (!this.predicate.test(blockState, this.pos) || l == 1 && !blockState.hasLargeCollisionShape() || l == 2 && !blockState.is(Blocks.MOVING_PISTON)) {
continue;
}
diff --git a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
index 5af90e0f7222356cb0e905a9b6e0c4eac5617a41..ee5fa14d2232b145806aefcaffb5c6348a08058a 100644
--- a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
+++ b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
@@ -237,7 +237,8 @@ public final class Shapes {
if (s < 3) {
mutableBlockPos.set(axisCycle, q, r, p);
- BlockState blockState = world.getBlockState(mutableBlockPos);
+ BlockState blockState = world.getTypeIfLoaded(mutableBlockPos); // Paper
+ if (blockState == null) return 0.0D; // Paper
if ((s != 1 || blockState.hasLargeCollisionShape()) && (s != 2 || blockState.is(Blocks.MOVING_PISTON))) {
initial = blockState.getCollisionShape(world, mutableBlockPos, context).collide(axis3, box.move((double)(-mutableBlockPos.getX()), (double)(-mutableBlockPos.getY()), (double)(-mutableBlockPos.getZ())), initial);
if (Math.abs(initial) < 1.0E-7D) {