diff options
Diffstat (limited to 'Spigot-Server-Patches/0476-Optimise-entity-hard-collision-checking.patch')
-rw-r--r-- | Spigot-Server-Patches/0476-Optimise-entity-hard-collision-checking.patch | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/Spigot-Server-Patches/0476-Optimise-entity-hard-collision-checking.patch b/Spigot-Server-Patches/0476-Optimise-entity-hard-collision-checking.patch new file mode 100644 index 0000000000..b80a505ed9 --- /dev/null +++ b/Spigot-Server-Patches/0476-Optimise-entity-hard-collision-checking.patch @@ -0,0 +1,216 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Wed, 15 Apr 2020 18:08:53 -0700 +Subject: [PATCH] Optimise entity hard collision checking + +Very few entities actually hard collide, so store them in their own +entity slices and provide a special getEntites type call just for them. +This reduces entity collision checking impact (in my testing) by 25% +for crammed entities (shove 130 cows into an 8x6 area in one chunk). +Less crammed entities are likely to show significantly less benefit. +Effectively, this patch optimises crammed entity situations. + +diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java +index 74d110874d0a9c1603d6b2d77753179ba49811ea..cf86ce24e12068d6ff7ae43cb1fd6fe665c24932 100644 +--- a/src/main/java/net/minecraft/server/Chunk.java ++++ b/src/main/java/net/minecraft/server/Chunk.java +@@ -90,6 +90,54 @@ public class Chunk implements IChunkAccess { + private final int[] inventoryEntityCounts = new int[16]; + // Paper end + ++ // Paper start - optimise hard collision handling ++ final com.destroystokyo.paper.util.maplist.EntityList[] hardCollidingEntities = new com.destroystokyo.paper.util.maplist.EntityList[16]; ++ ++ { ++ for (int i = 0, len = this.hardCollidingEntities.length; i < len; ++i) { ++ this.hardCollidingEntities[i] = new com.destroystokyo.paper.util.maplist.EntityList(); ++ } ++ } ++ ++ public final void getHardCollidingEntities(@Nullable Entity entity, AxisAlignedBB axisalignedbb, List<Entity> into) { ++ // copied from getEntities ++ int min = MathHelper.floor((axisalignedbb.minY - 2.0D) / 16.0D); ++ int max = MathHelper.floor((axisalignedbb.maxY + 2.0D) / 16.0D); ++ ++ min = MathHelper.clamp(min, 0, this.hardCollidingEntities.length - 1); ++ max = MathHelper.clamp(max, 0, this.hardCollidingEntities.length - 1); ++ ++ for (int k = min; k <= max; ++k) { ++ com.destroystokyo.paper.util.maplist.EntityList entityList = this.hardCollidingEntities[k]; ++ Entity[] entities = entityList.getRawData(); ++ ++ for (int i = 0, len = entityList.size(); i < len; ++i) { ++ Entity entity1 = entities[i]; ++ if (entity1.shouldBeRemoved) continue; // Paper ++ ++ if (entity1 != entity && entity1.getBoundingBox().intersects(axisalignedbb)) { ++ into.add(entity1); ++ ++ if (!(entity1 instanceof EntityEnderDragon)) { ++ continue; ++ } ++ ++ EntityComplexPart[] aentitycomplexpart = ((EntityEnderDragon) entity1).getComplexParts(); ++ int l = aentitycomplexpart.length; ++ ++ for (int i1 = 0; i1 < l; ++i1) { ++ EntityComplexPart entitycomplexpart = aentitycomplexpart[i1]; ++ ++ if (entitycomplexpart != entity && entitycomplexpart.getBoundingBox().intersects(axisalignedbb)) { ++ into.add(entitycomplexpart); ++ } ++ } ++ } ++ } ++ } ++ } ++ // Paper end - optimise hard collision handling ++ + public Chunk(World world, ChunkCoordIntPair chunkcoordintpair, BiomeStorage biomestorage, ChunkConverter chunkconverter, TickList<Block> ticklist, TickList<FluidType> ticklist1, long i, @Nullable ChunkSection[] achunksection, @Nullable Consumer<Chunk> consumer) { + this.sections = new ChunkSection[16]; + this.e = Maps.newHashMap(); +@@ -545,7 +593,7 @@ public class Chunk implements IChunkAccess { + entity.chunkY = k; + entity.chunkZ = this.loc.z; + this.entities.add(entity); // Paper - per chunk entity list +- this.entitySlices[k].add(entity); ++ this.entitySlices[k].add(entity); if (entity.hardCollides()) this.hardCollidingEntities[k].add(entity); // Paper - optimise hard colliding entities + // Paper start + if (entity instanceof EntityItem) { + itemCounts[k]++; +@@ -582,7 +630,7 @@ public class Chunk implements IChunkAccess { + entity.entitySlice = null; + entity.inChunk = false; + } +- if (!this.entitySlices[i].remove(entity)) { ++ if (entity.hardCollides()) this.hardCollidingEntities[i].remove(entity); if (!this.entitySlices[i].remove(entity)) { // Paper - optimise hard colliding entities + return; + } + if (entity instanceof EntityItem) { +diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java +index 7fb34a86dc35cb4bf51e1ce5220e56642096fece..f20f798f0f7bb765ffdab8672f4bf77a60fa52d2 100644 +--- a/src/main/java/net/minecraft/server/Entity.java ++++ b/src/main/java/net/minecraft/server/Entity.java +@@ -211,6 +211,40 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke + } + // CraftBukkit end + ++ // Paper start ++ /** ++ * Overriding this field will cause memory leaks. ++ */ ++ private final boolean hardCollides; ++ ++ private static final java.util.Map<Class<? extends Entity>, Boolean> cachedOverrides = java.util.Collections.synchronizedMap(new java.util.WeakHashMap<>()); ++ { ++ Boolean hardCollides = cachedOverrides.get(this.getClass()); ++ if (hardCollides == null) { ++ try { ++ Object getHardCollisionBoxMethod = Entity.class.getMethod("au"); ++ Object getHardCollisionBoxEntityMethod = Entity.class.getMethod("j", Entity.class); ++ if (!this.getClass().getMethod("au").equals(getHardCollisionBoxMethod)) { ++ hardCollides = Boolean.TRUE; ++ } else if (!this.getClass().getMethod("j", Entity.class).equals(getHardCollisionBoxEntityMethod)) { ++ hardCollides = Boolean.TRUE; ++ } else { ++ hardCollides = Boolean.FALSE; ++ } ++ cachedOverrides.put(this.getClass(), hardCollides); ++ } catch (Throwable thr) { ++ // shouldn't happen, just explode ++ throw new RuntimeException(thr); ++ } ++ } ++ this.hardCollides = hardCollides.booleanValue(); ++ } ++ ++ public final boolean hardCollides() { ++ return this.hardCollides; ++ } ++ // Paper end ++ + public Entity(EntityTypes<?> entitytypes, World world) { + this.id = Entity.entityCount.incrementAndGet(); + this.passengers = Lists.newArrayList(); +diff --git a/src/main/java/net/minecraft/server/EntityEnderDragon.java b/src/main/java/net/minecraft/server/EntityEnderDragon.java +index 5bf99e0028b886a6ba1d2f39f8dc4cd3c9aa0943..aecdaacfc7da560759bc513680d76f55820d5046 100644 +--- a/src/main/java/net/minecraft/server/EntityEnderDragon.java ++++ b/src/main/java/net/minecraft/server/EntityEnderDragon.java +@@ -847,6 +847,7 @@ public class EntityEnderDragon extends EntityInsentient implements IMonster { + @Override + public void checkDespawn() {} + ++ public final EntityComplexPart[] getComplexParts() { return this.eo(); } // Paper - OBFHELPER + public EntityComplexPart[] eo() { + return this.children; + } +diff --git a/src/main/java/net/minecraft/server/IEntityAccess.java b/src/main/java/net/minecraft/server/IEntityAccess.java +index 4157e50e4d99c029759bffcb48a8d645487554c8..5135308fb6137a34ed6fd061f0a210de6de4e81c 100644 +--- a/src/main/java/net/minecraft/server/IEntityAccess.java ++++ b/src/main/java/net/minecraft/server/IEntityAccess.java +@@ -42,17 +42,26 @@ public interface IEntityAccess { + return this.b(oclass, axisalignedbb, IEntitySelector.f); + } + ++ // Paper start - optimise hard collision ++ /** ++ * Not guaranteed to only return hard colliding entites ++ */ ++ default List<Entity> getHardCollidingEntities(@Nullable Entity entity, AxisAlignedBB axisalignedbb) { ++ return this.getEntities(entity, axisalignedbb); ++ } ++ // Paper end - optimise hard collision ++ + default Stream<VoxelShape> b(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set) { + if (axisalignedbb.a() < 1.0E-7D) { + return Stream.empty(); + } else { + AxisAlignedBB axisalignedbb1 = axisalignedbb.g(1.0E-7D); +- Stream<AxisAlignedBB> stream = this.getEntities(entity, axisalignedbb1).stream().filter((entity1) -> { // Paper - decompile fix ++ Stream<AxisAlignedBB> stream = ((entity != null && entity.hardCollides()) ? this.getEntities(entity, axisalignedbb) : this.getHardCollidingEntities(entity, axisalignedbb1)).stream().filter((entity1) -> { // Paper - decompile fix // Paper - optimise hard collision + return !set.contains(entity1); + }).filter((entity1) -> { + return entity == null || !entity.isSameVehicle(entity1); + }).flatMap((entity1) -> { +- return Stream.of(entity1.au(), entity == null ? null : entity.j(entity1)); ++ return Stream.of(entity1.au(), entity == null ? null : entity.j(entity1)); // Paper - optimise hard collision - diff on change, these are the methods that only hard colliding entities override + }).filter(Objects::nonNull); + + return stream.filter(axisalignedbb1::c).map(VoxelShapes::a); +diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java +index bdd0908adb7d3cd5370b43be196c975e6c48a70f..1c7955d3ae7ddd1c2d924cec20a91202cf090f40 100644 +--- a/src/main/java/net/minecraft/server/World.java ++++ b/src/main/java/net/minecraft/server/World.java +@@ -1179,6 +1179,32 @@ public abstract class World implements GeneratorAccess, AutoCloseable { + return this.getChunkAt(i, j, ChunkStatus.FULL, false); + } + ++ // Paper start - optimise hard collision handling ++ @Override ++ public List<Entity> getHardCollidingEntities(@Nullable Entity entity, AxisAlignedBB axisalignedbb) { ++ // copied from below ++ List<Entity> list = Lists.newArrayList(); ++ int i = MathHelper.floor((axisalignedbb.minX - 2.0D) / 16.0D); ++ int j = MathHelper.floor((axisalignedbb.maxX + 2.0D) / 16.0D); ++ int k = MathHelper.floor((axisalignedbb.minZ - 2.0D) / 16.0D); ++ int l = MathHelper.floor((axisalignedbb.maxZ + 2.0D) / 16.0D); ++ ++ ChunkProviderServer chunkProvider = ((ChunkProviderServer)this.chunkProvider); ++ ++ for (int i1 = i; i1 <= j; ++i1) { ++ for (int j1 = k; j1 <= l; ++j1) { ++ Chunk chunk = chunkProvider.getChunkAtIfLoadedMainThread(i1, j1); ++ ++ if (chunk != null) { ++ chunk.getHardCollidingEntities(entity, axisalignedbb, list); ++ } ++ } ++ } ++ ++ return list; ++ } ++ // Paper end - optimise hard collision handling ++ + @Override + public List<Entity> getEntities(@Nullable Entity entity, AxisAlignedBB axisalignedbb, @Nullable Predicate<? super Entity> predicate) { + this.getMethodProfiler().c("getEntities"); |