From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar 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 10eb562d2089dc20c9ec33956c3e2f98084de748..b828d6b2ce20a058acbabba5594e743d91df3060 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -791,6 +791,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 f8c611a6a55dd56e5231834c9481c61727b628e9..1164fc5915f0121b697ea10fac73919597902026 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 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 getEntityCollisions(@Nullable Entity entity, AABB box, Predicate 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..e4122469b839103f5c0fce38822d408a903dc0a5 100644 --- a/src/main/java/net/minecraft/world/level/CollisionSpliterator.java +++ b/src/main/java/net/minecraft/world/level/CollisionSpliterator.java @@ -64,21 +64,42 @@ public class CollisionSpliterator extends AbstractSpliterator { boolean collisionCheck(Consumer 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); + + 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 && this.source instanceof net.minecraft.server.level.ServerPlayer) || (this.source != null && this.source.collisionLoadChunks)) { + blockState = this.collisionGetter.getBlockState(this.pos); + } else { + blockState = 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) {