aboutsummaryrefslogtreecommitdiffhomepage
path: root/Spigot-Server-Patches/0476-Optimise-entity-hard-collision-checking.patch
diff options
context:
space:
mode:
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.patch216
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");