diff --git a/patches/server/0036-lithium-collision-optimizations.patch b/patches/server/0036-lithium-collision-optimizations.patch index f0ea70a0..a30bd6df 100644 --- a/patches/server/0036-lithium-collision-optimizations.patch +++ b/patches/server/0036-lithium-collision-optimizations.patch @@ -8,12 +8,158 @@ you can find the original code on https://github.com/jellysquid3/lithium-fabric/ Co-authored-by: Ivan Pekov +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/EntityClassGroup.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/EntityClassGroup.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a6f3349eafe7ab0bb2cb8dcda38ffdb05da0d413 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/EntityClassGroup.java +@@ -0,0 +1,101 @@ ++package me.jellysquid.mods.lithium.common.entity; ++ ++import java.util.ArrayList; ++import java.util.Collection; ++import java.util.List; ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++import java.util.function.Function; ++import net.minecraft.server.Entity; ++ ++/** ++ * Class for grouping Entity classes that meet some requirement for use in TypeFilterableList ++ * Designed to allow create groups of entity classes that are updated when mods add new entities that fit into the group. ++ * ++ * @author 2No2Name ++ */ ++public class EntityClassGroup { ++ //Keep a set of classes that were already added to matching class groups, so we only analyse them once. ++ private static final Map, Object> knownEntityClasses = new ConcurrentHashMap<>(); //value unused, no set variant available ++ //Keep track of available class groups for updating them in case an entity class is instantiated for the first time ++ private static final List entityClassGroups = new ArrayList<>(); ++ ++ public static final EntityClassGroup COLLISION_BOX_OVERRIDE = new EntityClassGroup( ++ (entityClass) -> { ++ boolean overwritten; ++ while (entityClass != null && entityClass != Entity.class) { ++ try { ++ overwritten = true; ++ entityClass.getDeclaredMethod("hardCollides"); ++ } catch (NoSuchMethodException e) { ++ overwritten = false; ++ entityClass = entityClass.getSuperclass(); ++ } ++ if (overwritten) { ++ return true; ++ } ++ } ++ return false; ++ } ++ ); ++ public static final EntityClassGroup HARD_COLLISION_BOX_OVERRIDE = new EntityClassGroup( ++ (entityClass) -> { ++ boolean overwritten; ++ while (entityClass != null && entityClass != Entity.class) { ++ try { ++ overwritten = true; ++ entityClass.getDeclaredMethod("hardCollidesWith", Entity.class); ++ } catch (NoSuchMethodException e) { ++ overwritten = false; ++ entityClass = entityClass.getSuperclass(); ++ } ++ if (overwritten) ++ return true; ++ } ++ return false; ++ } ++ ); ++ ++ private final Map, Object> classGroup; //value unused, no set variant available ++ private final Function, Boolean> classFitEvaluator; ++ ++ public EntityClassGroup(Function, Boolean> classFitEvaluator) { ++ this.classGroup = new ConcurrentHashMap<>(); ++ EntityClassGroup.entityClassGroups.add(this); ++ this.classFitEvaluator = classFitEvaluator; ++ } ++ ++ public EntityClassGroup add(Class entityClass) { ++ this.classGroup.put(entityClass, entityClass); ++ return this; ++ } ++ ++ public boolean contains(Class entityClass) { ++ EntityClassGroup.analyseEntityClass(entityClass); ++ return this.classGroup.containsKey(entityClass); ++ } ++ ++ public Collection> getCollection() { ++ return this.classGroup.keySet(); ++ } ++ ++ public void addClassIfFitting(Class discoveredEntityClass) { ++ if (this.classGroup.containsKey(discoveredEntityClass)) { ++ return; ++ } ++ if (this.classFitEvaluator != null && this.classFitEvaluator.apply(discoveredEntityClass)) { ++ this.classGroup.put(discoveredEntityClass, discoveredEntityClass); ++ } ++ } ++ ++ public static void analyseEntityClass(Class entityClass) { ++ if (EntityClassGroup.knownEntityClasses.containsKey(entityClass)) { ++ return; ++ } ++ EntityClassGroup.knownEntityClasses.put(entityClass, entityClass); ++ ++ for (EntityClassGroup entityClassGroup : EntityClassGroup.entityClassGroups) { ++ entityClassGroup.addClassIfFitting(entityClass); ++ } ++ } ++} +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/EntityClassGroupHelper.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/EntityClassGroupHelper.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d0d8980571c8ac8eb8ef46f602fed7bbf91c05d9 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/EntityClassGroupHelper.java +@@ -0,0 +1,33 @@ ++package me.jellysquid.mods.lithium.common.entity; ++ ++import java.util.List; ++import me.jellysquid.mods.lithium.common.world.WorldHelper; ++import net.minecraft.server.AxisAlignedBB; ++import net.minecraft.server.Entity; ++import net.minecraft.server.IEntityAccess; ++import net.minecraft.server.IEntitySelector; ++import net.minecraft.server.World; ++ ++public class EntityClassGroupHelper { ++ ++ /** ++ * Partial [VanillaCopy] Classes overriding Entity.getHardCollisionBox(Entity other) or Entity.getCollisionBox() ++ * The returned entity list is only used to call getCollisionBox and getHardCollisionBox. As most entities return null ++ * for both of these methods, getting those is not necessary. This is why we only get entities when they overwrite ++ * getCollisionBox ++ * ++ * @param entityView the world ++ * @param selection the box the entities have to collide with ++ * @param entity the entity that is searching for the colliding entities ++ * @return list of entities with collision boxes ++ */ ++ public static List getEntitiesWithCollisionBoxForEntity(IEntityAccess entityView, AxisAlignedBB selection, Entity entity) { ++ if (entity != null && EntityClassGroup.HARD_COLLISION_BOX_OVERRIDE.contains(entity.getClass()) || !(entityView instanceof World)) { ++ //use vanilla code when method_30949 (previously getHardCollisionBox(Entity other)) is overwritten, as every entity could be relevant as argument of getHardCollisionBox ++ return entityView.getEntities(entity, selection); ++ } else { ++ //only get entities that overwrite method_30948 (previously getCollisionBox) ++ return WorldHelper.getEntitiesOfClassGroup((World) entityView, entity, EntityClassGroup.COLLISION_BOX_OVERRIDE, selection, IEntitySelector.exceptSpectator()); ++ } ++ } ++} diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/LithiumEntityCollisions.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/LithiumEntityCollisions.java new file mode 100644 -index 0000000000000000000000000000000000000000..e16ac78efa67b5e6b7c2c4b247f3da56782b6b20 +index 0000000000000000000000000000000000000000..260ba36384a3f8ba047ba2338abb71e1369ed65e --- /dev/null +++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/LithiumEntityCollisions.java -@@ -0,0 +1,178 @@ +@@ -0,0 +1,176 @@ +package me.jellysquid.mods.lithium.common.entity; + +import java.util.Iterator; @@ -127,13 +273,11 @@ index 0000000000000000000000000000000000000000..e16ac78efa67b5e6b7c2c4b247f3da56 + @Override + public boolean computeNext(Consumer consumer) { + if (this.it == null) { -+ // Yatopia start - how about we get tuinity's hard collision optimizations here? -+ if (entity != null && entity.hardCollides()) { -+ this.it = view.getEntities(entity, box).iterator(); -+ } else { -+ this.it = view.getHardCollidingEntities(entity, box, predicate).iterator(); -+ } -+ // Yatopia end ++ /* ++ * In case entity's class is overriding method_30949, all types of entities may be (=> are assumed to be) required. ++ * Otherwise only get entities that override method_30948 are required, as other entities cannot collide. ++ */ ++ this.it = EntityClassGroupHelper.getEntitiesWithCollisionBoxForEntity(view, box, entity).iterator(); + } + + while (this.it.hasNext()) { @@ -485,6 +629,272 @@ index 0000000000000000000000000000000000000000..883581e12a014ce86dc7aea8ecab141e + return (Producer) EMPTY_PRODUCER; + } +} +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/world/WorldHelper.java b/src/main/java/me/jellysquid/mods/lithium/common/world/WorldHelper.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3b7b997576518d0dde1c4e3f31006dab60e5ea21 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/world/WorldHelper.java +@@ -0,0 +1,60 @@ ++package me.jellysquid.mods.lithium.common.world; ++ ++import com.google.common.collect.Lists; ++import java.util.List; ++import java.util.function.Predicate; ++import me.jellysquid.mods.lithium.common.entity.EntityClassGroup; ++import me.jellysquid.mods.lithium.common.world.chunk.ClassGroupFilterableList; ++import net.minecraft.server.AxisAlignedBB; ++import net.minecraft.server.Chunk; ++import net.minecraft.server.Entity; ++import net.minecraft.server.EntitySlice; ++import net.minecraft.server.MathHelper; ++import net.minecraft.server.World; ++ ++public class WorldHelper { ++ ++ /** ++ * Method that allows getting entities of a class group. ++ * [VanillaCopy] but custom combination of: get class filtered entities together with excluding one entity ++ */ ++ public static List getEntitiesOfClassGroup(World world, Entity excluded, EntityClassGroup type, AxisAlignedBB box_1, Predicate predicate_1) { ++ int int_1 = MathHelper.floor((box_1.minX - 2.0D) / 16.0D); ++ int int_2 = MathHelper.f((box_1.maxX + 2.0D) / 16.0D); ++ int int_3 = MathHelper.floor((box_1.minZ - 2.0D) / 16.0D); ++ int int_4 = MathHelper.f((box_1.maxZ + 2.0D) / 16.0D); ++ List list_1 = Lists.newArrayList(); ++ ++ for (int int_5 = int_1; int_5 < int_2; ++int_5) { ++ for (int int_6 = int_3; int_6 < int_4; ++int_6) { ++ Chunk worldChunk_1 = world.getChunkIfLoaded(int_5, int_6); ++ if (worldChunk_1 != null) { ++ WorldHelper.getEntitiesOfClassGroup(worldChunk_1, excluded, type, box_1, list_1, predicate_1); ++ } ++ } ++ } ++ ++ return list_1; ++ } ++ ++ /** ++ * Method that allows getting entities of a class group. ++ * [VanillaCopy] but custom combination of: get class filtered entities together with excluding one entity ++ */ ++ public static void getEntitiesOfClassGroup(Chunk worldChunk, Entity excluded, EntityClassGroup type, AxisAlignedBB box_1, List list_1, Predicate predicate_1) { ++ EntitySlice[] entitySections = worldChunk.getEntitySlices(); ++ int int_1 = MathHelper.floor((box_1.minY - 2.0D) / 16.0D); ++ int int_2 = MathHelper.floor((box_1.maxY + 2.0D) / 16.0D); ++ int_1 = MathHelper.clamp(int_1, 0, entitySections.length - 1); ++ int_2 = MathHelper.clamp(int_2, 0, entitySections.length - 1); ++ ++ for (int int_3 = int_1; int_3 <= int_2; ++int_3) { ++ //noinspection rawtypes ++ for (Object entity_1 : ((ClassGroupFilterableList) entitySections[int_3]).getAllOfGroupType(type)) { ++ if (entity_1 != excluded && ((Entity) entity_1).getBoundingBox().intersects(box_1) && (predicate_1 == null || predicate_1.test((Entity) entity_1))) { ++ list_1.add((Entity) entity_1); ++ } ++ } ++ } ++ } ++} +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/world/chunk/ClassGroupFilterableList.java b/src/main/java/me/jellysquid/mods/lithium/common/world/chunk/ClassGroupFilterableList.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a019cf5cb497e57d747fe53b5d0f912a37c41c9b +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/world/chunk/ClassGroupFilterableList.java +@@ -0,0 +1,8 @@ ++package me.jellysquid.mods.lithium.common.world.chunk; ++ ++import java.util.Collection; ++import me.jellysquid.mods.lithium.common.entity.EntityClassGroup; ++ ++public interface ClassGroupFilterableList { ++ Collection getAllOfGroupType(EntityClassGroup type); ++} +diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java +index ac6e5e3309affc830d4db07fd9b8d809c3085033..37a3b26cdeb02acb8051f0a99313de7923ebae3b 100644 +--- a/src/main/java/net/minecraft/server/Chunk.java ++++ b/src/main/java/net/minecraft/server/Chunk.java +@@ -36,7 +36,7 @@ public class Chunk implements IChunkAccess { + public final Map heightMap; + private final ChunkConverter i; + public final Map tileEntities; +- public final List[] entitySlices; // Spigot ++ public final EntitySlice[] entitySlices; // Spigot // Yatopia - md_5 is dumb + private final Map, StructureStart> l; + private final Map, LongSet> m; + private final ShortList[] n; +@@ -149,7 +149,7 @@ public class Chunk implements IChunkAccess { + this.l = Maps.newHashMap(); + this.m = Maps.newHashMap(); + this.n = new ShortList[16]; +- this.entitySlices = (List[]) (new List[16]); // Spigot ++ this.entitySlices = new EntitySlice[16]; // Spigot // Yatopia - md_5 is stupid + this.world = (WorldServer) world; // CraftBukkit - type + this.loc = chunkcoordintpair; this.coordinateKey = MCUtil.getCoordinateKey(chunkcoordintpair); // Paper - cache coordinate key + this.i = chunkconverter; +@@ -165,7 +165,7 @@ public class Chunk implements IChunkAccess { + } + + for (int l = 0; l < this.entitySlices.length; ++l) { +- this.entitySlices[l] = new org.bukkit.craftbukkit.util.UnsafeList(); // Spigot ++ this.entitySlices[l] = new EntitySlice<>(Entity.class); // Spigot // Yatopia - md_5 is stupid + } + + this.d = biomestorage; +@@ -615,8 +615,8 @@ public class Chunk implements IChunkAccess { + k = this.entitySlices.length - 1; + } + // Paper - remove from any old list if its in one +- List nextSlice = this.entitySlices[k]; // the next list to be added to +- List currentSlice = entity.entitySlice; ++ EntitySlice nextSlice = this.entitySlices[k]; // the next list to be added to // Yatopia - paper not stupid, md_5 is ++ EntitySlice currentSlice = entity.entitySlice; // Yatopia - paper not stupid, md_5 is + if (nextSlice == currentSlice) { + if (World.DEBUG_ENTITIES) MinecraftServer.LOGGER.warn("Entity was already in this chunk!" + entity, new Throwable()); + return; // ??? silly plugins +@@ -930,12 +930,12 @@ public class Chunk implements IChunkAccess { + j = MathHelper.clamp(j, 0, this.entitySlices.length - 1); + + for (int k = i; k <= j; ++k) { +- List entityslice = this.entitySlices[k]; // Spigot +- List list1 = entityslice; // Spigot ++ EntitySlice entityslice = this.entitySlices[k]; // Spigot // Yatopia - md_5 is stupid ++ EntitySlice list1 = entityslice; // Spigot // Yatopia - md_5 is stupid + int l = list1.size(); + +- for (int i1 = 0; i1 < l; ++i1) { +- Entity entity1 = (Entity) list1.get(i1); ++ for (Entity entity1 : list1) { // Yatopia ++ //Entity entity1 = (Entity) list1.get(i1); // Yatopia + if (entity1.shouldBeRemoved) continue; // Paper + + if (entity1.getBoundingBox().c(axisalignedbb) && entity1 != entity) { +@@ -1066,7 +1066,7 @@ public class Chunk implements IChunkAccess { + return this.tileEntities; + } + +- public List[] getEntitySlices() { // Spigot ++ public EntitySlice[] getEntitySlices() { // Spigot // Yatopia - md_5 is stupid + return this.entitySlices; + } + +diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java +index 03849c7709a5ea4852b23e828030dad2220523d3..d7341889b91c2319d89c1480a003166ec5069d82 100644 +--- a/src/main/java/net/minecraft/server/Entity.java ++++ b/src/main/java/net/minecraft/server/Entity.java +@@ -75,7 +75,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke + } + } + }; +- List entitySlice = null; ++ EntitySlice entitySlice = null; // Yatopia - change to EntitySlice + public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; + // Paper end + +diff --git a/src/main/java/net/minecraft/server/EntitySlice.java b/src/main/java/net/minecraft/server/EntitySlice.java +index 1250c3cbe915815939627701c153ba6254fc05f0..5b5f45a987b58bf639292ac8398ae08b3cb9a1bf 100644 +--- a/src/main/java/net/minecraft/server/EntitySlice.java ++++ b/src/main/java/net/minecraft/server/EntitySlice.java +@@ -14,18 +14,35 @@ import java.util.Map.Entry; + import java.util.stream.Collectors; + import java.util.stream.Stream; + +-public class EntitySlice extends AbstractCollection { ++// Yatopia start ++import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; ++import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; ++import me.jellysquid.mods.lithium.common.entity.EntityClassGroup; ++// Yatopia end ++ ++public class EntitySlice extends AbstractCollection implements me.jellysquid.mods.lithium.common.world.chunk.ClassGroupFilterableList { // Yatopia + + private final Map, List> a = Maps.newHashMap(); + private final Class b; + private final List c = Lists.newArrayList(); + ++ private Reference2ReferenceOpenHashMap> entitiesByGroup; // Yatopia ++ + public EntitySlice(Class oclass) { + this.b = oclass; + this.a.put(oclass, this.c); ++ this.entitiesByGroup = new Reference2ReferenceOpenHashMap<>(); // Yatopia + } + + public boolean add(T t0) { ++ // Yatopia start ++ for (Map.Entry> entityGroupAndSet : this.entitiesByGroup.entrySet()) { ++ EntityClassGroup entityGroup = entityGroupAndSet.getKey(); ++ if (entityGroup.contains(((Entity)t0).getClass())) { ++ entityGroupAndSet.getValue().add((t0)); ++ } ++ } ++ // Yatopia end + boolean flag = false; + Iterator iterator = this.a.entrySet().iterator(); + +@@ -41,6 +58,11 @@ public class EntitySlice extends AbstractCollection { + } + + public boolean remove(Object object) { ++ // Yatopia start ++ for (Map.Entry> entityGroupAndSet : this.entitiesByGroup.entrySet()) { ++ entityGroupAndSet.getValue().remove(object); ++ } ++ // Yatopia end + boolean flag = false; + Iterator iterator = this.a.entrySet().iterator(); + +@@ -65,11 +87,16 @@ public class EntitySlice extends AbstractCollection { + if (!this.b.isAssignableFrom(oclass)) { + throw new IllegalArgumentException("Don't know how to search for " + oclass); + } else { +- List list = (List) this.a.computeIfAbsent(oclass, (oclass1) -> { +- Stream stream = this.c.stream(); +- +- oclass1.getClass(); +- return (List) stream.filter(oclass1::isInstance).collect(Collectors.toList()); ++ List list = (List) this.a.computeIfAbsent(oclass, (oclass1) -> { // Yatopia - decompile fix ++ // Yatopia start - how about we nuke stream? ++ List ret = Lists.newArrayList(); ++ for (T t : c) { ++ if (oclass1.isInstance(t)) { ++ ret.add(t); ++ } ++ } ++ return ret; ++ // Yatopia end + }); + + return Collections.unmodifiableCollection(list); +@@ -87,4 +114,29 @@ public class EntitySlice extends AbstractCollection { + public int size() { + return this.c.size(); + } ++ ++ // Yatopia start ++ @Override ++ public Collection getAllOfGroupType(EntityClassGroup type) { ++ Collection collection = this.entitiesByGroup.get(type); ++ ++ if (collection == null) { ++ collection = this.createAllOfGroupType(type); ++ } ++ ++ return Collections.unmodifiableCollection(collection); ++ } ++ ++ private Collection createAllOfGroupType(EntityClassGroup type) { ++ ReferenceLinkedOpenHashSet allOfType = new ReferenceLinkedOpenHashSet<>(); ++ ++ for (T entity : this.c) { ++ if (type.contains(entity.getClass())) { ++ allOfType.add(entity); ++ } ++ } ++ this.entitiesByGroup.put(type, allOfType); ++ return allOfType; ++ } ++ // Yatopia end + } diff --git a/src/main/java/net/minecraft/server/ICollisionAccess.java b/src/main/java/net/minecraft/server/ICollisionAccess.java index b66c802d5e27518069bf42e577bcc9a26c4d873e..22728e5ba8b2dd6efc3164d06ea791693de50936 100644 --- a/src/main/java/net/minecraft/server/ICollisionAccess.java @@ -537,3 +947,61 @@ index 882b82d8952d34f6e3c639404d1a1521dedf1bb0..ccf1416000354b78ccef78b072062ce0 } default EntityHuman findNearbyPlayer(Entity entity, double d0, @Nullable Predicate predicate) { return this.findNearbyPlayer(entity.locX(), entity.locY(), entity.locZ(), d0, predicate); } // Paper +diff --git a/src/main/java/net/minecraft/server/IEntitySelector.java b/src/main/java/net/minecraft/server/IEntitySelector.java +index 06ce390b6492f23343e57e648bff241d96fdb7b6..624432bb7be214eb7f302ce698b8a4ca4472c862 100644 +--- a/src/main/java/net/minecraft/server/IEntitySelector.java ++++ b/src/main/java/net/minecraft/server/IEntitySelector.java +@@ -23,6 +23,7 @@ public final class IEntitySelector { + public static final Predicate f = (entity) -> { + return !(entity instanceof EntityHuman) || !entity.isSpectator() && !((EntityHuman) entity).isCreative() && entity.world.getDifficulty() != EnumDifficulty.PEACEFUL; + }; ++ public static final Predicate exceptSpectator() { return g; } // Yatopia - OBFHELPER + public static final Predicate g = (entity) -> { + return !entity.isSpectator(); + }; +diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java +index f2134c4ec6877ae3475f56928ad218c709c0e59d..c4ea24195ac8744e4abf4a9043d79ad1c1c848a0 100644 +--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java ++++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java +@@ -1339,11 +1339,11 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + chunk.setLoaded(true); + this.world.a(chunk.getTileEntities().values()); + List list = null; +- List[] aentityslice = chunk.getEntitySlices(); // Spigot ++ EntitySlice[] aentityslice = chunk.getEntitySlices(); // Spigot // Yatopia - md_5 is stupid + int i = aentityslice.length; + + for (int j = 0; j < i; ++j) { +- List entityslice = aentityslice[j]; // Spigot ++ EntitySlice entityslice = aentityslice[j]; // Spigot // Yatopia - md_5 is stupid + Iterator iterator = entityslice.iterator(); + + while (iterator.hasNext()) { +@@ -1636,7 +1636,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + // CraftBukkit - decompile error + csvwriter.a(chunkcoordintpair.x, chunkcoordintpair.z, playerchunk.getTicketLevel(), optional.isPresent(), optional.map(IChunkAccess::getChunkStatus).orElse(null), optional1.map(Chunk::getState).orElse(null), a(playerchunk.c()), a(playerchunk.a()), a(playerchunk.b()), this.chunkDistanceManager.c(entry.getLongKey()), !this.isOutsideOfRange(chunkcoordintpair), optional1.map((chunk) -> { + int sum = 0; +- for (List entities : chunk.getEntitySlices()) { ++ for (EntitySlice entities : chunk.getEntitySlices()) { // Yatopia - this was caused due to md_5 being stupid + int size = entities.size(); + sum += size; + } +diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java +index f339bc126e97bf57806fe673a27886125b3ce207..c99df02b65c2056b1cfffcfae68f8289d7ed051e 100644 +--- a/src/main/java/net/minecraft/server/WorldServer.java ++++ b/src/main/java/net/minecraft/server/WorldServer.java +@@ -1744,12 +1744,12 @@ public class WorldServer extends World implements GeneratorAccessSeed { + } + // Spigot End + this.tileEntityListUnload.addAll(chunk.getTileEntities().values()); +- List[] aentityslice = chunk.getEntitySlices(); // Spigot ++ EntitySlice[] aentityslice = chunk.getEntitySlices(); // Spigot // Yatopia - md_5 is stupid + int i = aentityslice.length; + + java.util.List toMoveChunks = new java.util.ArrayList<>(); // Paper + for (int j = 0; j < i; ++j) { +- List entityslice = aentityslice[j]; // Spigot ++ EntitySlice entityslice = aentityslice[j]; // Spigot // Yatopia - md_5 is stupid + Iterator iterator = entityslice.iterator(); + + while (iterator.hasNext()) {