mirror of
https://github.com/YatopiaMC/Yatopia.git
synced 2024-08-22 05:42:48 +02:00
Dropped hopper optimizations patch by tr7zw. Sorry buddy, but the patch was making more problems than it was solving. By no means this is an unneded patch, we will reimplement it in the future the way it should've been implemented. Fixes #148
1064 lines
49 KiB
Diff
1064 lines
49 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: JellySquid <jellysquid+atwork@protonmail.com>
|
|
Date: Thu, 20 Aug 2020 15:26:53 +0300
|
|
Subject: [PATCH] lithium collision optimizations
|
|
|
|
Original code by JellySquid, licensed under LGPLv3
|
|
you can find the original code on https://github.com/jellysquid3/lithium-fabric/ (Yarn mappings)
|
|
|
|
Co-authored-by: Ivan Pekov <ivan@mrivanplays.com>
|
|
|
|
diff --git a/src/main/java/dev/tr7zw/yatopia/HoldingConsumer.java b/src/main/java/dev/tr7zw/yatopia/HoldingConsumer.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..f0ead67b4c5354c4077c615dd873bc4ec7b81676
|
|
--- /dev/null
|
|
+++ b/src/main/java/dev/tr7zw/yatopia/HoldingConsumer.java
|
|
@@ -0,0 +1,17 @@
|
|
+package dev.tr7zw.yatopia;
|
|
+
|
|
+import java.util.function.Consumer;
|
|
+
|
|
+public final class HoldingConsumer<T> implements Consumer<T> {
|
|
+
|
|
+ private T value;
|
|
+
|
|
+ @Override
|
|
+ public void accept(T t) {
|
|
+ this.value = t;
|
|
+ }
|
|
+
|
|
+ public T getValue() {
|
|
+ return value;
|
|
+ }
|
|
+}
|
|
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..b400a5870b64b9eb92b6a8311793f5c8ea8df9af
|
|
--- /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<Class<?>, 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<EntityClassGroup> 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("aY");
|
|
+ } 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("j", Entity.class);
|
|
+ } catch (NoSuchMethodException e) {
|
|
+ overwritten = false;
|
|
+ entityClass = entityClass.getSuperclass();
|
|
+ }
|
|
+ if (overwritten)
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ );
|
|
+
|
|
+ private final Map<Class<?>, Object> classGroup; //value unused, no set variant available
|
|
+ private final Function<Class<?>, Boolean> classFitEvaluator;
|
|
+
|
|
+ public EntityClassGroup(Function<Class<?>, 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<Class<?>> 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..20f80ae80de91615ea02f0771f7c020c3e4c6d1e
|
|
--- /dev/null
|
|
+++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/EntityClassGroupHelper.java
|
|
@@ -0,0 +1,32 @@
|
|
+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.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<Entity> getEntitiesWithCollisionBoxForEntity(IEntityAccess entityView, AxisAlignedBB selection, Entity entity, boolean loadChunks) {
|
|
+ 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, loadChunks);
|
|
+ }
|
|
+ }
|
|
+}
|
|
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..7ef9775d351446637dd70e79497164913845c729
|
|
--- /dev/null
|
|
+++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/LithiumEntityCollisions.java
|
|
@@ -0,0 +1,193 @@
|
|
+package me.jellysquid.mods.lithium.common.entity;
|
|
+
|
|
+import dev.tr7zw.yatopia.EntityFilter;
|
|
+import java.util.Iterator;
|
|
+import java.util.List;
|
|
+import java.util.function.Consumer;
|
|
+import java.util.function.Predicate;
|
|
+import java.util.stream.Stream;
|
|
+import me.jellysquid.mods.lithium.common.entity.movement.ChunkAwareBlockCollisionSweeper;
|
|
+import me.jellysquid.mods.lithium.common.util.Producer;
|
|
+import net.minecraft.server.AxisAlignedBB;
|
|
+import net.minecraft.server.Entity;
|
|
+import net.minecraft.server.ICollisionAccess;
|
|
+import net.minecraft.server.IEntityAccess;
|
|
+import net.minecraft.server.VoxelShape;
|
|
+import net.minecraft.server.VoxelShapes;
|
|
+import net.minecraft.server.WorldBorder;
|
|
+
|
|
+public class LithiumEntityCollisions {
|
|
+ public static final double EPSILON = 1.0E-7D;
|
|
+
|
|
+ /**
|
|
+ * [VanillaCopy] CollisionView#getBlockCollisions(Entity, Box)
|
|
+ * This is a much, much faster implementation which uses simple collision testing against full-cube block shapes.
|
|
+ * Checks against the world border are replaced with our own optimized functions which do not go through the
|
|
+ * VoxelShape system.
|
|
+ */
|
|
+ public static Stream<VoxelShape> getBlockCollisions(ICollisionAccess world, Entity entity, AxisAlignedBB box) {
|
|
+ if (isBoxEmpty(box)) {
|
|
+ return Stream.empty();
|
|
+ }
|
|
+
|
|
+ return Producer.asStream(getBlockCollisionProducerAABB(world, entity, box).map(VoxelShapes::of));
|
|
+ }
|
|
+
|
|
+ public static void fillBlockCollisionsList(ICollisionAccess world, List<AxisAlignedBB> filled, Entity entity, AxisAlignedBB box) {
|
|
+ if (isBoxEmpty(box)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Producer.fillList(getBlockCollisionProducerAABB(world, entity, box), filled);
|
|
+ }
|
|
+
|
|
+ public static Producer<AxisAlignedBB> getBlockCollisionProducerAABB(ICollisionAccess world, Entity entity, AxisAlignedBB box) {
|
|
+ final ChunkAwareBlockCollisionSweeper sweeper = new ChunkAwareBlockCollisionSweeper(world, entity, box);
|
|
+
|
|
+ return new Producer<AxisAlignedBB>() {
|
|
+ private boolean skipWorldBorderCheck = entity == null;
|
|
+
|
|
+ @Override
|
|
+ public boolean computeNext(Consumer<? super AxisAlignedBB> consumer) {
|
|
+ if (!this.skipWorldBorderCheck) {
|
|
+ this.skipWorldBorderCheck = true;
|
|
+
|
|
+ if (canEntityCollideWithWorldBorder(world, entity)) {
|
|
+ consumer.accept(world.getWorldBorder().asVoxelShape().getBoundingBox());
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ VoxelShape shape = sweeper.step();
|
|
+ if (shape != null) {
|
|
+ consumer.accept(shape.getBoundingBox());
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ };
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * See {@link LithiumEntityCollisions#getBlockCollisions(ICollisionAccess, Entity, AxisAlignedBB)}
|
|
+ *
|
|
+ * @return True if the box (possibly that of an entity's) collided with any blocks
|
|
+ */
|
|
+ public static boolean doesBoxCollideWithBlocks(ICollisionAccess world, Entity entity, AxisAlignedBB box) {
|
|
+ if (isBoxEmpty(box)) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ final ChunkAwareBlockCollisionSweeper sweeper = new ChunkAwareBlockCollisionSweeper(world, entity, box);
|
|
+
|
|
+ VoxelShape shape = sweeper.step();
|
|
+ return shape != null;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * See {@link LithiumEntityCollisions#getEntityCollisions(IEntityAccess, Entity, AxisAlignedBB, Predicate)}
|
|
+ *
|
|
+ * @return True if the box (possibly that of an entity's) collided with any other entities
|
|
+ */
|
|
+ public static boolean doesBoxCollideWithEntities(IEntityAccess view, Entity entity, AxisAlignedBB box, Predicate<Entity> predicate) {
|
|
+ if (isBoxEmpty(box)) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return getEntityCollisionProducer(view, entity, box.grow(EPSILON), predicate, false).computeNext(null);
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Returns a stream of entity collision boxes.
|
|
+ */
|
|
+ public static Stream<VoxelShape> getEntityCollisions(IEntityAccess view, Entity entity, AxisAlignedBB box, Predicate<Entity> predicate) {
|
|
+ if (isBoxEmpty(box)) {
|
|
+ return Stream.empty();
|
|
+ }
|
|
+
|
|
+ return Producer.asStream(getEntityCollisionProducer(view, entity, box.grow(EPSILON), predicate, false).map(VoxelShapes::of));
|
|
+ }
|
|
+
|
|
+ public static void fillEntityCollisionsList(IEntityAccess view, Entity entity, List<AxisAlignedBB> filled, AxisAlignedBB box, boolean loadChunks) {
|
|
+ if (isBoxEmpty(box)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Producer.fillList(getEntityCollisionProducer(view, entity, box.grow(EPSILON), EntityFilter.getFilter(entity), loadChunks), filled);
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * [VanillaCopy] EntityView#getEntityCollisions
|
|
+ * Re-implements the function named above without stream code or unnecessary allocations. This can provide a small
|
|
+ * boost in some situations (such as heavy entity crowding) and reduces the allocation rate significantly.
|
|
+ */
|
|
+ public static Producer<AxisAlignedBB> getEntityCollisionProducer(IEntityAccess view, Entity entity, AxisAlignedBB box, Predicate<Entity> predicate, boolean loadChunks) {
|
|
+ return new Producer<AxisAlignedBB>() {
|
|
+ private Iterator<Entity> it;
|
|
+
|
|
+ @Override
|
|
+ public boolean computeNext(Consumer<? super AxisAlignedBB> consumer) {
|
|
+ if (it == null) {
|
|
+ /*
|
|
+ * 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, loadChunks).iterator();
|
|
+ }
|
|
+
|
|
+ while (this.it.hasNext()) {
|
|
+ Entity otherEntity = this.it.next();
|
|
+
|
|
+ if (!predicate.test(otherEntity)) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (entity == null) {
|
|
+ if (!otherEntity.collisionBoxIsHard()) {
|
|
+ continue;
|
|
+ }
|
|
+ } else if (!entity.hardCollidesWith(otherEntity)) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (consumer != null) {
|
|
+ consumer.accept(otherEntity.getBoundingBox());
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+ }
|
|
+ };
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * This provides a faster check for seeing if an entity is within the world border as it avoids going through
|
|
+ * the slower shape system.
|
|
+ *
|
|
+ * @return True if the {@param box} is fully within the {@param border}, otherwise false.
|
|
+ */
|
|
+ public static boolean isBoxFullyWithinWorldBorder(WorldBorder border, AxisAlignedBB box) {
|
|
+ double wboxMinX = Math.floor(border.getMinX());
|
|
+ double wboxMinZ = Math.floor(border.getMinZ());
|
|
+
|
|
+ double wboxMaxX = Math.ceil(border.getMaxX());
|
|
+ double wboxMaxZ = Math.ceil(border.getMaxZ());
|
|
+
|
|
+ return box.minX >= wboxMinX && box.minX < wboxMaxX && box.minZ >= wboxMinZ && box.minZ < wboxMaxZ &&
|
|
+ box.maxX >= wboxMinX && box.maxX < wboxMaxX && box.maxZ >= wboxMinZ && box.maxZ < wboxMaxZ;
|
|
+ }
|
|
+
|
|
+ private static boolean canEntityCollideWithWorldBorder(ICollisionAccess world, Entity entity) {
|
|
+ WorldBorder border = world.getWorldBorder();
|
|
+
|
|
+ boolean isInsideBorder = isBoxFullyWithinWorldBorder(border, entity.getBoundingBox().shrink(EPSILON));
|
|
+ boolean isCrossingBorder = isBoxFullyWithinWorldBorder(border, entity.getBoundingBox().grow(EPSILON));
|
|
+
|
|
+ return !isInsideBorder && isCrossingBorder;
|
|
+ }
|
|
+
|
|
+ private static boolean isBoxEmpty(AxisAlignedBB box) {
|
|
+ return box.getAverageSideLength() <= EPSILON;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/movement/ChunkAwareBlockCollisionSweeper.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/movement/ChunkAwareBlockCollisionSweeper.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..b9d279d1a9703c17221d4219b86b8f31bdcc2895
|
|
--- /dev/null
|
|
+++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/movement/ChunkAwareBlockCollisionSweeper.java
|
|
@@ -0,0 +1,247 @@
|
|
+package me.jellysquid.mods.lithium.common.entity.movement;
|
|
+
|
|
+import net.minecraft.server.AxisAlignedBB;
|
|
+import net.minecraft.server.BlockPosition;
|
|
+import net.minecraft.server.Blocks;
|
|
+import net.minecraft.server.Chunk;
|
|
+import net.minecraft.server.ChunkSection;
|
|
+import net.minecraft.server.Entity;
|
|
+import net.minecraft.server.IBlockData;
|
|
+import net.minecraft.server.IChunkAccess;
|
|
+import net.minecraft.server.ICollisionAccess;
|
|
+import net.minecraft.server.MathHelper;
|
|
+import net.minecraft.server.OperatorBoolean;
|
|
+import net.minecraft.server.VoxelShape;
|
|
+import net.minecraft.server.VoxelShapeCollision;
|
|
+import net.minecraft.server.VoxelShapes;
|
|
+
|
|
+import static me.jellysquid.mods.lithium.common.entity.LithiumEntityCollisions.EPSILON;
|
|
+
|
|
+/**
|
|
+ * ChunkAwareBlockCollisionSweeper iterates over blocks in one chunk section at a time. Together with the chunk
|
|
+ * section keeping track of the amount of oversized blocks inside the number of iterations can often be reduced.
|
|
+ */
|
|
+public class ChunkAwareBlockCollisionSweeper {
|
|
+ private static final boolean OVERSIZED_BLOCK_COUNTING_ENABLED = OversizedBlocksCounter.class.isAssignableFrom(ChunkSection.class);
|
|
+
|
|
+ private final BlockPosition.MutableBlockPosition pos = new BlockPosition.MutableBlockPosition();
|
|
+
|
|
+ /**
|
|
+ * The collision box being swept through the world.
|
|
+ */
|
|
+ private final AxisAlignedBB box;
|
|
+
|
|
+ /**
|
|
+ * The VoxelShape of the collision box being swept through the world.
|
|
+ */
|
|
+ private final VoxelShape shape;
|
|
+
|
|
+ private final ICollisionAccess view;
|
|
+ private final VoxelShapeCollision context;
|
|
+
|
|
+ //limits of the area without extension for oversized blocks
|
|
+ private final int minX, minY, minZ, maxX, maxY, maxZ;
|
|
+
|
|
+ //variables prefixed with c refer to the iteration of the currently cached chunk section
|
|
+ private int chunkX, chunkY, chunkZ;
|
|
+ private int cStartX, cStartZ;
|
|
+ private int cEndX, cEndZ;
|
|
+ private int cX, cY, cZ;
|
|
+
|
|
+ private int cTotalSize;
|
|
+ private int cIterated;
|
|
+
|
|
+ private boolean sectionOversizedBlocks;
|
|
+ private IChunkAccess cachedChunk;
|
|
+ private ChunkSection cachedChunkSection;
|
|
+
|
|
+ public ChunkAwareBlockCollisionSweeper(ICollisionAccess view, Entity entity, AxisAlignedBB box) {
|
|
+ this.box = box;
|
|
+ this.shape = VoxelShapes.of(box);
|
|
+ // todo: ivan, make obfhelpers
|
|
+ this.context = entity == null ? VoxelShapeCollision.a() : VoxelShapeCollision.a(entity);
|
|
+ this.view = view;
|
|
+
|
|
+ this.minX = MathHelper.floor(box.minX - EPSILON);
|
|
+ this.maxX = MathHelper.floor(box.maxX + EPSILON);
|
|
+ this.minY = MathHelper.clamp((int) (box.minY - EPSILON), 0, 255);
|
|
+ this.maxY = MathHelper.clamp((int) (box.maxY + EPSILON), 0, 255);
|
|
+ this.minZ = MathHelper.floor(box.minZ - EPSILON);
|
|
+ this.maxZ = MathHelper.floor(box.maxZ + EPSILON);
|
|
+
|
|
+ this.chunkX = (this.minX - 1) >> 4;
|
|
+ this.chunkZ = (this.minZ - 1) >> 4;
|
|
+
|
|
+ this.cIterated = 0;
|
|
+ this.cTotalSize = 0;
|
|
+
|
|
+ //decrement as first nextSection call will increment it again
|
|
+ this.chunkX--;
|
|
+ }
|
|
+
|
|
+ private boolean nextSection() {
|
|
+ do {
|
|
+ do {
|
|
+ //find the coordinates of the next section inside the area expanded by 1 block on all sides
|
|
+ //note: this.minX, maxX etc are not expanded, so there are lots of +1 and -1 around.
|
|
+ if (this.cachedChunk != null && this.chunkY < 15 && this.chunkY < ((this.maxY + 1) >> 4)) {
|
|
+ this.chunkY++;
|
|
+ this.cachedChunkSection = this.cachedChunk.getSections()[this.chunkY];
|
|
+ } else {
|
|
+ this.chunkY = MathHelper.clamp((this.minY - 1) >> 4, 0, 15);
|
|
+
|
|
+ if ((this.chunkX < ((this.maxX + 1) >> 4))) {
|
|
+ //first initialization takes this branch
|
|
+ this.chunkX++;
|
|
+ } else {
|
|
+ this.chunkX = (this.minX - 1) >> 4;
|
|
+
|
|
+ if (this.chunkZ < ((this.maxZ + 1) >> 4)) {
|
|
+ this.chunkZ++;
|
|
+ } else {
|
|
+ return false; //no more sections to iterate
|
|
+ }
|
|
+ }
|
|
+ //Casting to Chunk is not checked, together with other mods this could cause a ClassCastException
|
|
+ this.cachedChunk = (IChunkAccess) this.view.c(this.chunkX, this.chunkZ);
|
|
+ if (this.cachedChunk != null) {
|
|
+ this.cachedChunkSection = this.cachedChunk.getSections()[this.chunkY];
|
|
+ }
|
|
+ }
|
|
+ //skip empty chunks and empty chunk sections
|
|
+ } while (this.cachedChunk == null || ChunkSection.isEmpty(this.cachedChunkSection));
|
|
+
|
|
+ this.sectionOversizedBlocks = hasChunkSectionOversizedBlocks(this.cachedChunk, this.chunkY);
|
|
+
|
|
+ int sizeExtension = this.sectionOversizedBlocks ? 1 : 0;
|
|
+
|
|
+ this.cEndX = Math.min(this.maxX + sizeExtension, 15 + (this.chunkX << 4));
|
|
+ int cEndY = Math.min(this.maxY + sizeExtension, 15 + (this.chunkY << 4));
|
|
+ this.cEndZ = Math.min(this.maxZ + sizeExtension, 15 + (this.chunkZ << 4));
|
|
+
|
|
+ this.cStartX = Math.max(this.minX - sizeExtension, this.chunkX << 4);
|
|
+ int cStartY = Math.max(this.minY - sizeExtension, this.chunkY << 4);
|
|
+ this.cStartZ = Math.max(this.minZ - sizeExtension, this.chunkZ << 4);
|
|
+ this.cX = this.cStartX;
|
|
+ this.cY = cStartY;
|
|
+ this.cZ = this.cStartZ;
|
|
+
|
|
+ this.cTotalSize = (this.cEndX - this.cStartX + 1) * (cEndY - cStartY + 1) * (this.cEndZ - this.cStartZ + 1);
|
|
+ //skip completely empty section iterations
|
|
+ } while (this.cTotalSize == 0);
|
|
+ this.cIterated = 0;
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+
|
|
+ /**
|
|
+ * Advances the sweep forward until finding a block with a box-colliding VoxelShape
|
|
+ *
|
|
+ * @return null if no VoxelShape is left in the area, otherwise the next VoxelShape
|
|
+ */
|
|
+ public VoxelShape step() {
|
|
+ while (true) {
|
|
+ if (this.cIterated >= this.cTotalSize) {
|
|
+ if (!this.nextSection()) {
|
|
+ return null;
|
|
+ }
|
|
+ }
|
|
+ this.cIterated++;
|
|
+
|
|
+
|
|
+ final int x = this.cX;
|
|
+ final int y = this.cY;
|
|
+ final int z = this.cZ;
|
|
+
|
|
+ //The iteration order within a chunk section is chosen so that it causes a mostly linear array access in the storage.
|
|
+ //In net.minecraft.world.chunk.PalettedContainer.toIndex x gets the 4 least significant bits, z the 4 above, and y the 4 even higher ones.
|
|
+ //Linearly accessing arrays might be slightly faster than other access patterns.
|
|
+ //This code hasn't been benchmarked in comparison to another access order.
|
|
+ if (this.cX < this.cEndX) {
|
|
+ this.cX++;
|
|
+ } else if (this.cZ < this.cEndZ) {
|
|
+ this.cX = this.cStartX;
|
|
+ this.cZ++;
|
|
+ } else {
|
|
+ this.cX = this.cStartX;
|
|
+ this.cZ = this.cStartZ;
|
|
+ this.cY++;
|
|
+ //stop condition was already checked using this.cIterated at the start of the method
|
|
+ }
|
|
+
|
|
+ //using < minX and > maxX instead of <= and >= in vanilla, because minX, maxX are the coordinates
|
|
+ //of the box that wasn't extended for oversized blocks yet.
|
|
+ final int edgesHit = this.sectionOversizedBlocks ?
|
|
+ (x < this.minX || x > this.maxX ? 1 : 0) +
|
|
+ (y < this.minY || y > this.maxY ? 1 : 0) +
|
|
+ (z < this.minZ || z > this.maxZ ? 1 : 0) : 0;
|
|
+
|
|
+ if (edgesHit == 3) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ final IBlockData state = this.cachedChunkSection.getType(x & 15, y & 15, z & 15);
|
|
+
|
|
+ if (canInteractWithBlock(state, edgesHit)) {
|
|
+ this.pos.setValues(x, y, z);
|
|
+ VoxelShape collisionShape = state.getCollisionShape(this.view, this.pos, this.context);
|
|
+
|
|
+ if (collisionShape != VoxelShapes.empty()) {
|
|
+ VoxelShape collidedShape = getCollidedShape(this.box, this.shape, collisionShape, x, y, z);
|
|
+ if (collidedShape != null) {
|
|
+ return collidedShape;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * This is an artifact from vanilla which is used to avoid testing shapes in the extended portion of a volume
|
|
+ * unless they are a shape which exceeds their voxel. Pistons must be special-cased here.
|
|
+ *
|
|
+ * @return True if the shape can be interacted with at the given edge boundary
|
|
+ */
|
|
+ private static boolean canInteractWithBlock(IBlockData state, int edgesHit) {
|
|
+ return (edgesHit != 1 || state.shapeExceedsCube()) && (edgesHit != 2 || state.getBlock() == Blocks.MOVING_PISTON);
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Checks if the {@param entityShape} or {@param entityBox} intersects the given {@param shape} which is translated
|
|
+ * to the given position. This is a very specialized implementation which tries to avoid going through VoxelShape
|
|
+ * for full-cube shapes.
|
|
+ *
|
|
+ * @return A {@link VoxelShape} which contains the shape representing that which was collided with, otherwise null
|
|
+ */
|
|
+ private static VoxelShape getCollidedShape(AxisAlignedBB entityBox, VoxelShape entityShape, VoxelShape shape, int x, int y, int z) {
|
|
+ if (shape.intersects(entityBox)) {
|
|
+ return shape.offset(x, y, z);
|
|
+ } else {
|
|
+ shape = shape.offset(x, y, z);
|
|
+
|
|
+ if (VoxelShapes.applyOperation(shape, entityShape, OperatorBoolean.AND)) {
|
|
+ return shape;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Checks the cached information whether the {@param chunkY} section of the {@param chunk} has oversized blocks.
|
|
+ *
|
|
+ * @return Whether there are any oversized blocks in the chunk section.
|
|
+ */
|
|
+ private static boolean hasChunkSectionOversizedBlocks(IChunkAccess chunk, int chunkY) {
|
|
+ if (OVERSIZED_BLOCK_COUNTING_ENABLED) {
|
|
+ ChunkSection section = chunk.getSections()[chunkY];
|
|
+ return section != null && ((OversizedBlocksCounter) section).hasOversizedBlocks();
|
|
+ }
|
|
+ return true; //like vanilla, assume that a chunk section has oversized blocks, when the section mixin isn't loaded
|
|
+ }
|
|
+
|
|
+ public interface OversizedBlocksCounter {
|
|
+ boolean hasOversizedBlocks();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/util/Producer.java b/src/main/java/me/jellysquid/mods/lithium/common/util/Producer.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..0ee0aa4b2a20183cc78cb639c9e3e608e3eb46c9
|
|
--- /dev/null
|
|
+++ b/src/main/java/me/jellysquid/mods/lithium/common/util/Producer.java
|
|
@@ -0,0 +1,49 @@
|
|
+package me.jellysquid.mods.lithium.common.util;
|
|
+
|
|
+import dev.tr7zw.yatopia.HoldingConsumer;
|
|
+import java.util.List;
|
|
+import java.util.Spliterator;
|
|
+import java.util.Spliterators;
|
|
+import java.util.function.Consumer;
|
|
+import java.util.function.Function;
|
|
+import java.util.stream.Stream;
|
|
+import java.util.stream.StreamSupport;
|
|
+
|
|
+public interface Producer<T> {
|
|
+ /**
|
|
+ * Computes the next sequence of values in a collection. If a null value is passed for {@param consumer}, then
|
|
+ * the producer will only return whether or not elements existed.
|
|
+ *
|
|
+ * @param consumer The (nullable) consumer which will accept the computed values during this run.
|
|
+ * @return True if the producer produced any values, otherwise false
|
|
+ */
|
|
+ boolean computeNext(Consumer<? super T> consumer);
|
|
+
|
|
+ default <U> Producer<U> map(Function<T, U> mapper) {
|
|
+ return consumer -> {
|
|
+ Consumer<? super T> con = (t) -> consumer.accept(mapper.apply(t));
|
|
+ return Producer.this.computeNext(con);
|
|
+ };
|
|
+ }
|
|
+
|
|
+ static <T> Stream<T> asStream(Producer<T> producer) {
|
|
+ return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE, Spliterator.ORDERED | Spliterator.NONNULL) {
|
|
+ @Override
|
|
+ public boolean tryAdvance(Consumer<? super T> action) {
|
|
+ return producer.computeNext(action);
|
|
+ }
|
|
+ }, false);
|
|
+ }
|
|
+
|
|
+ static <T> void fillList(Producer<T> producer, List<T> list) {
|
|
+ HoldingConsumer<T> consumer = new HoldingConsumer<>();
|
|
+ while (producer.computeNext(consumer)) list.add(consumer.getValue());
|
|
+ }
|
|
+
|
|
+ Producer<?> EMPTY_PRODUCER = consumer -> false;
|
|
+
|
|
+ @SuppressWarnings("unchecked")
|
|
+ static <T> Producer<T> empty() {
|
|
+ return (Producer<T>) 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..700d391675286cf736be8bc42366118147c5fd60
|
|
--- /dev/null
|
|
+++ b/src/main/java/me/jellysquid/mods/lithium/common/world/WorldHelper.java
|
|
@@ -0,0 +1,59 @@
|
|
+package me.jellysquid.mods.lithium.common.world;
|
|
+
|
|
+import com.google.common.collect.Lists;
|
|
+import java.util.List;
|
|
+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<Entity> getEntitiesOfClassGroup(World world, Entity excluded, EntityClassGroup type, AxisAlignedBB box_1, boolean loadChunks) {
|
|
+ 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<Entity> 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 = loadChunks ? world.getChunkAt(int_5, int_6) : world.getChunkIfLoaded(int_5, int_6);
|
|
+ if (worldChunk_1 != null) {
|
|
+ WorldHelper.getEntitiesOfClassGroup(worldChunk_1, excluded, type, box_1, list_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<Entity> list_1) {
|
|
+ EntitySlice<Entity>[] 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)) {
|
|
+ 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..493103d755c222f2c6031ffb8f5c0b7a2e3fde77
|
|
--- /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<T> {
|
|
+ Collection<T> 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.Type, HeightMap> heightMap;
|
|
private final ChunkConverter i;
|
|
public final Map<BlockPosition, TileEntity> tileEntities;
|
|
- public final List<Entity>[] entitySlices; // Spigot
|
|
+ public final EntitySlice<Entity>[] entitySlices; // Spigot // Yatopia - md_5 is dumb
|
|
private final Map<StructureGenerator<?>, StructureStart<?>> l;
|
|
private final Map<StructureGenerator<?>, 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<Entity> nextSlice = this.entitySlices[k]; // the next list to be added to
|
|
- List<Entity> currentSlice = entity.entitySlice;
|
|
+ EntitySlice<Entity> nextSlice = this.entitySlices[k]; // the next list to be added to // Yatopia - paper not stupid, md_5 is
|
|
+ EntitySlice<Entity> 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<Entity> entityslice = this.entitySlices[k]; // Spigot
|
|
- List<Entity> list1 = entityslice; // Spigot
|
|
+ EntitySlice<Entity> entityslice = this.entitySlices[k]; // Spigot // Yatopia - md_5 is stupid
|
|
+ EntitySlice<Entity> 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<Entity>[] getEntitySlices() { // Spigot
|
|
+ public EntitySlice<Entity>[] 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 55916c77365076450e6c1b6c7e530433172d95c7..60d040eb58c44790214f0869e7294bc70b021ec5 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<Entity> entitySlice = null;
|
|
+ EntitySlice<Entity> 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<T> extends AbstractCollection<T> {
|
|
+// 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<T> extends AbstractCollection<T> implements me.jellysquid.mods.lithium.common.world.chunk.ClassGroupFilterableList<T> { // Yatopia
|
|
|
|
private final Map<Class<?>, List<T>> a = Maps.newHashMap();
|
|
private final Class<T> b;
|
|
private final List<T> c = Lists.newArrayList();
|
|
|
|
+ private Reference2ReferenceOpenHashMap<EntityClassGroup, ReferenceLinkedOpenHashSet<T>> entitiesByGroup; // Yatopia
|
|
+
|
|
public EntitySlice(Class<T> 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<EntityClassGroup, ReferenceLinkedOpenHashSet<T>> 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<T> extends AbstractCollection<T> {
|
|
}
|
|
|
|
public boolean remove(Object object) {
|
|
+ // Yatopia start
|
|
+ for (Map.Entry<EntityClassGroup, ReferenceLinkedOpenHashSet<T>> 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<T> extends AbstractCollection<T> {
|
|
if (!this.b.isAssignableFrom(oclass)) {
|
|
throw new IllegalArgumentException("Don't know how to search for " + oclass);
|
|
} else {
|
|
- List<T> list = (List) this.a.computeIfAbsent(oclass, (oclass1) -> {
|
|
- Stream stream = this.c.stream();
|
|
-
|
|
- oclass1.getClass();
|
|
- return (List) stream.filter(oclass1::isInstance).collect(Collectors.toList());
|
|
+ List<S> list = (List<S>) this.a.computeIfAbsent(oclass, (oclass1) -> { // Yatopia - decompile fix
|
|
+ // Yatopia start - how about we nuke stream?
|
|
+ List<T> 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<T> extends AbstractCollection<T> {
|
|
public int size() {
|
|
return this.c.size();
|
|
}
|
|
+
|
|
+ // Yatopia start
|
|
+ @Override
|
|
+ public Collection<T> getAllOfGroupType(EntityClassGroup type) {
|
|
+ Collection<T> collection = this.entitiesByGroup.get(type);
|
|
+
|
|
+ if (collection == null) {
|
|
+ collection = this.createAllOfGroupType(type);
|
|
+ }
|
|
+
|
|
+ return Collections.unmodifiableCollection(collection);
|
|
+ }
|
|
+
|
|
+ private <S> Collection<T> createAllOfGroupType(EntityClassGroup type) {
|
|
+ ReferenceLinkedOpenHashSet<T> 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
|
|
+++ b/src/main/java/net/minecraft/server/ICollisionAccess.java
|
|
@@ -52,7 +52,14 @@ public interface ICollisionAccess extends IBlockAccess {
|
|
default boolean getCubes(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Predicate<Entity> predicate) {
|
|
// Tuinity end - allow overriding in WorldServer
|
|
try { if (entity != null) entity.collisionLoadChunks = true; // Paper
|
|
- return this.d(entity, axisalignedbb, predicate).allMatch(VoxelShape::isEmpty);
|
|
+ // Yatopia start
|
|
+ //return this.d(entity, axisalignedbb, predicate).allMatch(VoxelShape::isEmpty);
|
|
+ boolean ret = !me.jellysquid.mods.lithium.common.entity.LithiumEntityCollisions.doesBoxCollideWithBlocks(this, entity, axisalignedbb);
|
|
+ if (ret && this instanceof IEntityAccess) {
|
|
+ ret = !me.jellysquid.mods.lithium.common.entity.LithiumEntityCollisions.doesBoxCollideWithEntities((IEntityAccess) this, entity, axisalignedbb, predicate);
|
|
+ }
|
|
+ return ret;
|
|
+ // Yatopia end
|
|
} finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper
|
|
}
|
|
|
|
@@ -63,7 +70,7 @@ public interface ICollisionAccess extends IBlockAccess {
|
|
}
|
|
|
|
default Stream<VoxelShape> b(@Nullable Entity entity, AxisAlignedBB axisalignedbb) {
|
|
- return StreamSupport.stream(new VoxelShapeSpliterator(this, entity, axisalignedbb), false);
|
|
+ return me.jellysquid.mods.lithium.common.entity.LithiumEntityCollisions.getBlockCollisions(this, entity, axisalignedbb); // Yatopia
|
|
}
|
|
|
|
default Stream<VoxelShape> b(@Nullable Entity entity, AxisAlignedBB axisalignedbb, BiPredicate<IBlockData, BlockPosition> bipredicate) {
|
|
diff --git a/src/main/java/net/minecraft/server/IEntityAccess.java b/src/main/java/net/minecraft/server/IEntityAccess.java
|
|
index 882b82d8952d34f6e3c639404d1a1521dedf1bb0..ccf1416000354b78ccef78b072062ce081826e1a 100644
|
|
--- a/src/main/java/net/minecraft/server/IEntityAccess.java
|
|
+++ b/src/main/java/net/minecraft/server/IEntityAccess.java
|
|
@@ -62,6 +62,10 @@ public interface IEntityAccess {
|
|
// Tuinity end - optimise hard collision
|
|
|
|
default Stream<VoxelShape> c(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Predicate<Entity> predicate) {
|
|
+ // Yatopia start - replace this
|
|
+ if (predicate == null) predicate = (e) -> true; // Tuinity - allow nullable
|
|
+ return me.jellysquid.mods.lithium.common.entity.LithiumEntityCollisions.getEntityCollisions(this, entity, axisalignedbb, predicate);
|
|
+ /*
|
|
if (axisalignedbb.a() < 1.0E-7D) {
|
|
return Stream.empty();
|
|
} else {
|
|
@@ -91,6 +95,7 @@ public interface IEntityAccess {
|
|
return flag;
|
|
}); return ((entity != null && entity.hardCollides()) ? this.getEntities(entity, axisalignedbb1, predicate) : this.getHardCollidingEntities(entity, axisalignedbb1, predicate)).stream().map(Entity::getBoundingBox).map(VoxelShapes::a); // Tuinity - optimise entity hard collisions
|
|
}
|
|
+ */ // Yatopia end
|
|
}
|
|
|
|
default EntityHuman findNearbyPlayer(Entity entity, double d0, @Nullable Predicate<Entity> predicate) { return this.findNearbyPlayer(entity.locX(), entity.locY(), entity.locZ(), d0, predicate); } // Paper
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
index a0c696f951824bd1988cf9922dc3829c325f41b5..2711ce7a2b4f1868542088729bf4aa2919ee153e 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<Entity> list = null;
|
|
- List<Entity>[] aentityslice = chunk.getEntitySlices(); // Spigot
|
|
+ EntitySlice<Entity>[] aentityslice = chunk.getEntitySlices(); // Spigot // Yatopia - md_5 is stupid
|
|
int i = aentityslice.length;
|
|
|
|
for (int j = 0; j < i; ++j) {
|
|
- List<Entity> entityslice = aentityslice[j]; // Spigot
|
|
+ EntitySlice<Entity> 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<Entity> entities : chunk.getEntitySlices()) {
|
|
+ for (EntitySlice<Entity> 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 31a97c4ccfa23ef0547c783e9ca9da656689ee7d..214904925963188c037c01c226d5cf501dbeaa1c 100644
|
|
--- a/src/main/java/net/minecraft/server/WorldServer.java
|
|
+++ b/src/main/java/net/minecraft/server/WorldServer.java
|
|
@@ -649,8 +649,14 @@ public class WorldServer extends World implements GeneratorAccessSeed {
|
|
}
|
|
|
|
public final void getCollisions(@Nullable Entity entity, AxisAlignedBB axisalignedbb, List<AxisAlignedBB> list, boolean loadChunks) {
|
|
+ // Yatopia start - jellysquid is better
|
|
+ /*
|
|
this.getCollisionsForBlocksOrWorldBorder(entity, axisalignedbb, list, loadChunks);
|
|
this.getEntityHardCollisions(entity, axisalignedbb, null, list);
|
|
+ */
|
|
+ me.jellysquid.mods.lithium.common.entity.LithiumEntityCollisions.fillEntityCollisionsList(this, entity, list, axisalignedbb, loadChunks);
|
|
+ me.jellysquid.mods.lithium.common.entity.LithiumEntityCollisions.fillBlockCollisionsList(this, list, entity, axisalignedbb);
|
|
+ // Yatopia end
|
|
}
|
|
|
|
@Override
|
|
@@ -1744,12 +1750,12 @@ public class WorldServer extends World implements GeneratorAccessSeed {
|
|
}
|
|
// Spigot End
|
|
this.tileEntityListUnload.addAll(chunk.getTileEntities().values());
|
|
- List[] aentityslice = chunk.getEntitySlices(); // Spigot
|
|
+ EntitySlice<Entity>[] aentityslice = chunk.getEntitySlices(); // Spigot // Yatopia - md_5 is stupid
|
|
int i = aentityslice.length;
|
|
|
|
java.util.List<Entity> toMoveChunks = new java.util.ArrayList<>(); // Paper
|
|
for (int j = 0; j < i; ++j) {
|
|
- List<Entity> entityslice = aentityslice[j]; // Spigot
|
|
+ EntitySlice<Entity> entityslice = aentityslice[j]; // Spigot // Yatopia - md_5 is stupid
|
|
Iterator iterator = entityslice.iterator();
|
|
|
|
while (iterator.hasNext()) {
|