mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-24 01:47:42 +01:00
ab347c4c96
Upstream has released updates that appears 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: 42d5a714 SPIGOT-5899: Hoglins API similar to Piglins 2c1ee10e SPIGOT-5887: ClickType doesn't include off hand swaps 5ff7c7ce SPIGOT-5886: Missing BlockData CraftBukkit Changes:7560f5f5
SPIGOT-5905: Fix hex colours not being allowed in MOTDd47c47ee
SPIGOT-5889: Villager using composter should call EntityChangeBlockEvent2fe6b4a3
SPIGOT-5899: Hoglins API similar to Piglinse09dbeca
SPIGOT-5887: ClickType doesn't include off hand swaps23aac2a5
SPIGOT-5903: EntityDismountEvent cannot be triggered asynchronously92cbf656
SPIGOT-5884: Tab completions lost on reloadData / minecraft:reloadfb4e54ad
SPIGOT-5902: PlayerRespawnEvent places player at spawn before event is calledaa8f3d5a
SPIGOT-5901: Structures are generated in all worlds based on the setting for the main worlda0c35937
SPIGOT-5895: PlayerChangedWorldEvent#getFrom is incorrect89c0a5c3
SPIGOT-5886: Missing BlockData Spigot Changes: 0287a20d SPIGOT-5903: EntityDismountEvent cannot be triggered asynchronously
229 lines
11 KiB
Diff
229 lines
11 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
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 cb24f27bc443880b18ffc36236f6ec7174c9d493..b65ae2d6919a67498d0646c5522735086fec00c1 100644
|
|
--- a/src/main/java/net/minecraft/server/Chunk.java
|
|
+++ b/src/main/java/net/minecraft/server/Chunk.java
|
|
@@ -91,6 +91,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, Predicate<Entity> predicate) {
|
|
+ // 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) && (predicate == null || predicate.test(entity1))) {
|
|
+ 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) && (predicate == null || predicate.test(entitycomplexpart))) {
|
|
+ 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();
|
|
@@ -546,7 +594,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]++;
|
|
@@ -583,7 +631,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 152f613159ef88977de867c0deec94bfa4353f7a..a0249d7a60c1381ebd7131d735a73fa2e9250543 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("ay");
|
|
+ Object getHardCollisionBoxEntityMethod = Entity.class.getMethod("j", Entity.class);
|
|
+ if (!this.getClass().getMethod("ay").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 96f898acdeae1917a4aaf99ec4a48bccf3904488..73e9859e675902d9fc5942547966b52426a496a2 100644
|
|
--- a/src/main/java/net/minecraft/server/EntityEnderDragon.java
|
|
+++ b/src/main/java/net/minecraft/server/EntityEnderDragon.java
|
|
@@ -844,6 +844,7 @@ public class EntityEnderDragon extends EntityInsentient implements IMonster {
|
|
@Override
|
|
public void checkDespawn() {}
|
|
|
|
+ public final EntityComplexPart[] getComplexParts() { return this.eK(); } // Paper - OBFHELPER
|
|
public EntityComplexPart[] eK() {
|
|
return this.children;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/IEntityAccess.java b/src/main/java/net/minecraft/server/IEntityAccess.java
|
|
index 74d4c28246e7db850e6d993e07a84b2a6ca24ce2..267a6baae89c181eed545e6758fac7115eb3882f 100644
|
|
--- a/src/main/java/net/minecraft/server/IEntityAccess.java
|
|
+++ b/src/main/java/net/minecraft/server/IEntityAccess.java
|
|
@@ -53,24 +53,36 @@ public interface IEntityAccess {
|
|
return this.b(oclass, axisalignedbb, IEntitySelector.g);
|
|
}
|
|
|
|
+ // Paper start - optimise hard collision
|
|
+ /**
|
|
+ * Not guaranteed to only return hard colliding entites
|
|
+ */
|
|
+ default List<Entity> getHardCollidingEntities(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Predicate<Entity> predicate) {
|
|
+ return this.getEntities(entity, axisalignedbb, predicate);
|
|
+ }
|
|
+ // Paper end - optimise hard collision
|
|
+
|
|
default Stream<VoxelShape> c(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Predicate<Entity> predicate) {
|
|
if (axisalignedbb.a() < 1.0E-7D) {
|
|
return Stream.empty();
|
|
} else {
|
|
AxisAlignedBB axisalignedbb1 = axisalignedbb.g(1.0E-7D);
|
|
|
|
- return this.getEntities(entity, axisalignedbb1, predicate.and((entity1) -> {
|
|
+ // Paper start
|
|
+ Predicate<Entity> effectivePredicate = predicate.and((entity1) -> {
|
|
return entity == null || !entity.isSameVehicle(entity1);
|
|
- })).stream().flatMap((entity1) -> {
|
|
+ });
|
|
+
|
|
+ return ((entity != null && entity.hardCollides()) ? this.getEntities(entity, axisalignedbb, effectivePredicate) : this.getHardCollidingEntities(entity, axisalignedbb1, effectivePredicate)).stream().flatMap((entity1) -> {
|
|
if (entity != null) {
|
|
- AxisAlignedBB axisalignedbb2 = entity.j(entity1);
|
|
+ AxisAlignedBB axisalignedbb2 = entity.j(entity1); // Paper - diff on change, hard collision box method
|
|
|
|
if (axisalignedbb2 != null && axisalignedbb2.c(axisalignedbb1)) {
|
|
return Stream.of(entity1.ay(), axisalignedbb2);
|
|
}
|
|
}
|
|
|
|
- return Stream.of(entity1.ay());
|
|
+ return Stream.of(entity1.ay()); // Paper - diff on change, hard collision box method
|
|
}).filter(Objects::nonNull).map(VoxelShapes::a);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
|
|
index 418ba04a837ee2b17cc679edd35ba09830c01f28..365539e53efdb7e729b580f52fb23659cbde4f0a 100644
|
|
--- a/src/main/java/net/minecraft/server/World.java
|
|
+++ b/src/main/java/net/minecraft/server/World.java
|
|
@@ -1111,6 +1111,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, Predicate<Entity> predicate) {
|
|
+ // 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 = ((WorldServer)this).getChunkProvider();
|
|
+
|
|
+ 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, predicate);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ 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");
|