(Airplane-temporary) Collision cache
To drop when merged into Airplane's main branch
This commit is contained in:
522
patches/server/0040-Airplane-temporary-Collision-cache.patch
Normal file
522
patches/server/0040-Airplane-temporary-Collision-cache.patch
Normal file
@@ -0,0 +1,522 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Etil <81570777+etil2jz@users.noreply.github.com>
|
||||
Date: Tue, 21 Sep 2021 18:11:59 +0200
|
||||
Subject: [PATCH] (Airplane-temporary) Collision cache
|
||||
|
||||
|
||||
diff --git a/src/main/java/gg/airplane/entity/CollisionCache.java b/src/main/java/gg/airplane/entity/CollisionCache.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..db38d58016965daee8003b98202c32080c369688
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/gg/airplane/entity/CollisionCache.java
|
||||
@@ -0,0 +1,244 @@
|
||||
+package gg.airplane.entity;
|
||||
+
|
||||
+import io.papermc.paper.util.CollisionUtil;
|
||||
+import io.papermc.paper.util.WorldUtil;
|
||||
+import net.minecraft.core.BlockPos;
|
||||
+import net.minecraft.core.SectionPos;
|
||||
+import net.minecraft.server.level.ServerChunkCache;
|
||||
+import net.minecraft.util.Mth;
|
||||
+import net.minecraft.world.entity.Entity;
|
||||
+import net.minecraft.world.level.CollisionGetter;
|
||||
+import net.minecraft.world.level.block.Blocks;
|
||||
+import net.minecraft.world.level.block.state.BlockState;
|
||||
+import net.minecraft.world.level.chunk.LevelChunk;
|
||||
+import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
+import net.minecraft.world.phys.AABB;
|
||||
+import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
+import net.minecraft.world.phys.shapes.Shapes;
|
||||
+import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
+import org.bukkit.craftbukkit.util.UnsafeList;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+import org.simpleyaml.utils.Validate;
|
||||
+
|
||||
+import java.util.HashSet;
|
||||
+import java.util.List;
|
||||
+import java.util.Set;
|
||||
+import java.util.function.BiPredicate;
|
||||
+
|
||||
+public class CollisionCache {
|
||||
+
|
||||
+ private static record BlockEntry(int x, int y, int z, BlockState state, VoxelShape shape){}
|
||||
+
|
||||
+ @NotNull
|
||||
+ private final Entity entity;
|
||||
+ private final UnsafeList<BlockEntry> blocks = new UnsafeList<>();
|
||||
+ private Set<CollisionCacheList> chunkToList = new HashSet<>();
|
||||
+
|
||||
+ private boolean dirty = true;
|
||||
+
|
||||
+ private int previousMinBlockX;
|
||||
+ private int previousMaxBlockX;
|
||||
+ private int previousMinBlockY;
|
||||
+ private int previousMaxBlockY;
|
||||
+ private int previousMinBlockZ;
|
||||
+ private int previousMaxBlockZ;
|
||||
+
|
||||
+ public CollisionCache(@NotNull Entity entity) {
|
||||
+ this.entity = entity;
|
||||
+ }
|
||||
+
|
||||
+ public int getId() {
|
||||
+ return this.entity.getId();
|
||||
+ }
|
||||
+
|
||||
+ public void dirtySection(@NotNull SectionPos sectionPos) {
|
||||
+ this.dirty = true;
|
||||
+ }
|
||||
+
|
||||
+ public void onRemove() {
|
||||
+ this.blocks.setSize(0);
|
||||
+
|
||||
+ for (CollisionCacheList collisionCaches : this.chunkToList) {
|
||||
+ collisionCaches.remove(this);
|
||||
+ }
|
||||
+ this.chunkToList.clear();
|
||||
+ this.dirty = false;
|
||||
+ }
|
||||
+
|
||||
+ public boolean getCollisions(final CollisionGetter view, AABB aabb, List<AABB> into, boolean collidesWithUnloaded, boolean checkOnly, BiPredicate<BlockState, BlockPos> predicate) {
|
||||
+ boolean ret = false;
|
||||
+
|
||||
+ int minBlockX = Mth.floor(aabb.minX - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||
+ int maxBlockX = Mth.floor(aabb.maxX + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||
+
|
||||
+ int minBlockY = Mth.floor(aabb.minY - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||
+ int maxBlockY = Mth.floor(aabb.maxY + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||
+
|
||||
+ int minBlockZ = Mth.floor(aabb.minZ - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||
+ int maxBlockZ = Mth.floor(aabb.maxZ + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||
+
|
||||
+ // if nothing changed and the location didn't move out of our area, use previous set
|
||||
+ if (!this.dirty && minBlockX >= this.previousMinBlockX && maxBlockX <= this.previousMaxBlockX &&
|
||||
+ minBlockY >= this.previousMinBlockY && maxBlockY <= this.previousMaxBlockY &&
|
||||
+ minBlockZ >= this.previousMinBlockZ && maxBlockZ <= this.previousMaxBlockZ) {
|
||||
+ if (checkOnly) {
|
||||
+ BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
||||
+ for (int i = 0, length = this.blocks.size(); i < length; i++) {
|
||||
+ BlockEntry entry = this.blocks.unsafeGet(i);
|
||||
+ if (entry.shape.intersects(aabb) && predicate.test(entry.state, pos.set(entry.x, entry.y, entry.z))) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ for (int i = 0, length = this.blocks.size(); i < length; i++) {
|
||||
+ ret |= CollisionUtil.addBoxesToIfIntersects(this.blocks.unsafeGet(i).shape, aabb, into);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+ } else if (checkOnly) {
|
||||
+ // tl;dr this is only used by inWall right now, and we don't want to generate a cache for inWall because it'll always be smaller than a move cache anyways
|
||||
+ return CollisionUtil.getCollisionsForBlocksOrWorldBorder(view, this.entity, aabb, into, false, collidesWithUnloaded, false, checkOnly, predicate);
|
||||
+ }
|
||||
+
|
||||
+ Validate.isTrue(predicate == null, "predicate cannot be used without checkOnly");
|
||||
+
|
||||
+ this.previousMinBlockX = minBlockX;
|
||||
+ this.previousMaxBlockX = maxBlockX;
|
||||
+
|
||||
+ this.previousMinBlockY = minBlockY;
|
||||
+ this.previousMaxBlockY = maxBlockY;
|
||||
+
|
||||
+ this.previousMinBlockZ = minBlockZ;
|
||||
+ this.previousMaxBlockZ = maxBlockZ;
|
||||
+
|
||||
+ // remove old shapes, since we missed cache
|
||||
+ this.blocks.setSize(0);
|
||||
+ this.dirty = false;
|
||||
+
|
||||
+ final int minSection = WorldUtil.getMinSection(this.entity.level);
|
||||
+ final int maxSection = WorldUtil.getMaxSection(this.entity.level);
|
||||
+ final int minBlock = minSection << 4;
|
||||
+ final int maxBlock = (maxSection << 4) | 15;
|
||||
+
|
||||
+ BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
+ CollisionContext collisionShape = null;
|
||||
+
|
||||
+ // special cases:
|
||||
+ if (minBlockY > maxBlock || maxBlockY < minBlock) {
|
||||
+ // no point in checking
|
||||
+ if (!this.chunkToList.isEmpty()) {
|
||||
+ for (CollisionCacheList collisionCaches : this.chunkToList) {
|
||||
+ collisionCaches.remove(this);
|
||||
+ }
|
||||
+ this.chunkToList.clear();
|
||||
+ }
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ int minYIterate = Math.max(minBlock, minBlockY);
|
||||
+ int maxYIterate = Math.min(maxBlock, maxBlockY);
|
||||
+
|
||||
+ int minChunkX = minBlockX >> 4;
|
||||
+ int maxChunkX = maxBlockX >> 4;
|
||||
+
|
||||
+ int minChunkZ = minBlockZ >> 4;
|
||||
+ int maxChunkZ = maxBlockZ >> 4;
|
||||
+
|
||||
+ ServerChunkCache chunkProvider = (ServerChunkCache) this.entity.level.getChunkSource();
|
||||
+
|
||||
+ Set<CollisionCacheList> cacheLists = new HashSet<>(this.blocks.size());
|
||||
+
|
||||
+ for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
+ int minZ = currChunkZ == minChunkZ ? minBlockZ & 15 : 0; // coordinate in chunk
|
||||
+ int maxZ = currChunkZ == maxChunkZ ? maxBlockZ & 15 : 15; // coordinate in chunk
|
||||
+
|
||||
+ for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
||||
+ int minX = currChunkX == minChunkX ? minBlockX & 15 : 0; // coordinate in chunk
|
||||
+ int maxX = currChunkX == maxChunkX ? maxBlockX & 15 : 15; // coordinate in chunk
|
||||
+
|
||||
+ int chunkXGlobalPos = currChunkX << 4;
|
||||
+ int chunkZGlobalPos = currChunkZ << 4;
|
||||
+
|
||||
+ LevelChunk chunk = chunkProvider.getChunkAtIfLoadedImmediately(currChunkX, currChunkZ);
|
||||
+
|
||||
+ if (chunk == null) {
|
||||
+ if (collidesWithUnloaded) {
|
||||
+ into.add(CollisionUtil.getBoxForChunk(currChunkX, currChunkZ));
|
||||
+ ret = true;
|
||||
+ }
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ LevelChunkSection[] sections = chunk.getSections();
|
||||
+
|
||||
+ // bound y
|
||||
+
|
||||
+ for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
||||
+ int sectionIndex = SectionPos.blockToSectionCoord(currY) - minSection;
|
||||
+
|
||||
+ CollisionCacheList cacheList = chunk.collisionCaches[sectionIndex];
|
||||
+ if (cacheLists.add(cacheList)) {
|
||||
+ cacheList.add(this);
|
||||
+ }
|
||||
+
|
||||
+ LevelChunkSection section = sections[sectionIndex];
|
||||
+ if (section == null || section.isEmpty()) {
|
||||
+ // empty
|
||||
+ // skip to next section
|
||||
+ currY = (currY & ~(15)) + 15; // increment by 15: iterator loop increments by the extra one
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ net.minecraft.world.level.chunk.PalettedContainer<BlockState> blocks = section.states;
|
||||
+
|
||||
+ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
|
||||
+ for (int currX = minX; currX <= maxX; ++currX) {
|
||||
+ int localBlockIndex = (currX) | (currZ << 4) | ((currY & 15) << 8);
|
||||
+ int blockX = currX | chunkXGlobalPos;
|
||||
+ int blockY = currY;
|
||||
+ int blockZ = currZ | chunkZGlobalPos;
|
||||
+
|
||||
+ int edgeCount = ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) +
|
||||
+ ((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) +
|
||||
+ ((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0);
|
||||
+ if (edgeCount == 3) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ BlockState blockData = blocks.get(localBlockIndex);
|
||||
+ if (blockData.isAir()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if ((edgeCount != 1 || blockData.shapeExceedsCube()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON)) {
|
||||
+ mutablePos.set(blockX, blockY, blockZ);
|
||||
+ if (collisionShape == null) {
|
||||
+ collisionShape = new CollisionUtil.LazyEntityCollisionContext(entity);
|
||||
+ }
|
||||
+ VoxelShape voxelshape2 = blockData.getCollisionShape(this.entity.level, mutablePos, collisionShape);
|
||||
+ if (voxelshape2 != Shapes.empty()) {
|
||||
+ VoxelShape voxelshape3 = voxelshape2.move((double) blockX, (double) blockY, (double) blockZ);
|
||||
+
|
||||
+ this.blocks.add(new BlockEntry(blockX, blockY, blockZ, blockData, voxelshape3));
|
||||
+
|
||||
+ ret |= CollisionUtil.addBoxesToIfIntersects(voxelshape3, aabb, into);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for (CollisionCacheList cache : this.chunkToList) {
|
||||
+ if (!cacheLists.contains(cache)) {
|
||||
+ cache.remove(this);
|
||||
+ }
|
||||
+ }
|
||||
+ this.chunkToList = cacheLists;
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
\ No newline at end of file
|
||||
diff --git a/src/main/java/gg/airplane/entity/CollisionCacheList.java b/src/main/java/gg/airplane/entity/CollisionCacheList.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..3e22d2fc190995fd536338bcacda1b054a555801
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/gg/airplane/entity/CollisionCacheList.java
|
||||
@@ -0,0 +1,128 @@
|
||||
+package gg.airplane.entity;
|
||||
+
|
||||
+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
+
|
||||
+import java.util.Arrays;
|
||||
+import java.util.Iterator;
|
||||
+import java.util.NoSuchElementException;
|
||||
+
|
||||
+/**
|
||||
+ * @see com.destroystokyo.paper.util.maplist.EntityList
|
||||
+ */
|
||||
+public class CollisionCacheList implements Iterable<CollisionCache> {
|
||||
+
|
||||
+ protected final Int2IntOpenHashMap entityToIndex = new Int2IntOpenHashMap(2, 0.8f);
|
||||
+
|
||||
+ {
|
||||
+ this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE);
|
||||
+ }
|
||||
+
|
||||
+ protected static final CollisionCache[] EMPTY_LIST = new CollisionCache[0];
|
||||
+
|
||||
+ protected CollisionCache[] entities = EMPTY_LIST;
|
||||
+ protected int count;
|
||||
+
|
||||
+ public int size() {
|
||||
+ return this.count;
|
||||
+ }
|
||||
+
|
||||
+ public boolean contains(final CollisionCache entity) {
|
||||
+ return this.entityToIndex.containsKey(entity.getId());
|
||||
+ }
|
||||
+
|
||||
+ public boolean remove(final CollisionCache entity) {
|
||||
+ final int index = this.entityToIndex.remove(entity.getId());
|
||||
+ if (index == Integer.MIN_VALUE) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // move the entity at the end to this index
|
||||
+ final int endIndex = --this.count;
|
||||
+ final CollisionCache end = this.entities[endIndex];
|
||||
+ if (index != endIndex) {
|
||||
+ // not empty after this call
|
||||
+ this.entityToIndex.put(end.getId(), index); // update index
|
||||
+ }
|
||||
+ this.entities[index] = end;
|
||||
+ this.entities[endIndex] = null;
|
||||
+
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ public boolean add(final CollisionCache entity) {
|
||||
+ final int count = this.count;
|
||||
+ final int currIndex = this.entityToIndex.putIfAbsent(entity.getId(), count);
|
||||
+
|
||||
+ if (currIndex != Integer.MIN_VALUE) {
|
||||
+ return false; // already in this list
|
||||
+ }
|
||||
+
|
||||
+ CollisionCache[] list = this.entities;
|
||||
+
|
||||
+ if (list.length == count) {
|
||||
+ // resize required
|
||||
+ list = this.entities = Arrays.copyOf(list, (int) Math.max(4L, count * 2L)); // overflow results in negative
|
||||
+ }
|
||||
+
|
||||
+ list[count] = entity;
|
||||
+ this.count = count + 1;
|
||||
+
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ public CollisionCache getChecked(final int index) {
|
||||
+ if (index < 0 || index >= this.count) {
|
||||
+ throw new IndexOutOfBoundsException("Index: " + index + " is out of bounds, size: " + this.count);
|
||||
+ }
|
||||
+ return this.entities[index];
|
||||
+ }
|
||||
+
|
||||
+ public CollisionCache getUnchecked(final int index) {
|
||||
+ return this.entities[index];
|
||||
+ }
|
||||
+
|
||||
+ public CollisionCache[] getRawData() {
|
||||
+ return this.entities;
|
||||
+ }
|
||||
+
|
||||
+ public void clear() {
|
||||
+ this.entityToIndex.clear();
|
||||
+ Arrays.fill(this.entities, 0, this.count, null);
|
||||
+ this.count = 0;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Iterator<CollisionCache> iterator() {
|
||||
+ return new Iterator<CollisionCache>() {
|
||||
+
|
||||
+ CollisionCache lastRet;
|
||||
+ int current;
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean hasNext() {
|
||||
+ return this.current < CollisionCacheList.this.count;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public CollisionCache next() {
|
||||
+ if (this.current >= CollisionCacheList.this.count) {
|
||||
+ throw new NoSuchElementException();
|
||||
+ }
|
||||
+ return this.lastRet = CollisionCacheList.this.entities[this.current++];
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void remove() {
|
||||
+ final CollisionCache lastRet = this.lastRet;
|
||||
+
|
||||
+ if (lastRet == null) {
|
||||
+ throw new IllegalStateException();
|
||||
+ }
|
||||
+ this.lastRet = null;
|
||||
+
|
||||
+ CollisionCacheList.this.remove(lastRet);
|
||||
+ --this.current;
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
+}
|
||||
\ No newline at end of file
|
||||
diff --git a/src/main/java/io/papermc/paper/util/CollisionUtil.java b/src/main/java/io/papermc/paper/util/CollisionUtil.java
|
||||
index 98ca1199a823cdf55b913396ce0a24554e85f116..16952b7303f02373b4c9bd0ffe613d517598dab4 100644
|
||||
--- a/src/main/java/io/papermc/paper/util/CollisionUtil.java
|
||||
+++ b/src/main/java/io/papermc/paper/util/CollisionUtil.java
|
||||
@@ -546,6 +546,13 @@ public final class CollisionUtil {
|
||||
|
||||
return ret;
|
||||
}
|
||||
+
|
||||
+ public static boolean getEntityCollisionsWithCache(final net.minecraft.world.level.Level getter, Entity entity, AABB aabb, List<AABB> into,
|
||||
+ final boolean loadChunks, final boolean collidesWithUnloaded,
|
||||
+ final boolean checkBorder, final boolean checkOnly, final BiPredicate<BlockState, BlockPos> predicate) {
|
||||
+ return entity.collisionCache.getCollisions(getter, aabb, into, collidesWithUnloaded, checkOnly, predicate) ||
|
||||
+ getEntityHardCollisions(getter, entity, aabb, into, false, null);
|
||||
+ }
|
||||
|
||||
public static boolean getEntityHardCollisions(final CollisionGetter getter, final Entity entity, AABB aabb,
|
||||
final List<AABB> into, final boolean checkOnly, final Predicate<Entity> predicate) {
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index 3c272e6c7552cb5bc875cdaefc91aaac3751ec3a..c6d5451b326a34b4df638156b1cb5069ebeb7b59 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -341,6 +341,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
||||
// Airplane start
|
||||
public int activatedPriority = gg.airplane.AirplaneConfig.maximumActivationPrio; // golf score
|
||||
public final BlockPos.MutableBlockPos cachedBlockPos = new BlockPos.MutableBlockPos(); // used where needed
|
||||
+ public final gg.airplane.entity.CollisionCache collisionCache = new gg.airplane.entity.CollisionCache(this);
|
||||
// Airplane end
|
||||
|
||||
public float getBukkitYaw() {
|
||||
@@ -1290,8 +1291,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
||||
}
|
||||
}
|
||||
|
||||
- io.papermc.paper.util.CollisionUtil.getCollisions(world, this, collisionBox, potentialCollisions, false, true,
|
||||
- false, false, null, null);
|
||||
+ // Airplane start - use collision cache
|
||||
+ io.papermc.paper.util.CollisionUtil.getEntityCollisionsWithCache(world, this, collisionBox, potentialCollisions, false, true,
|
||||
+ false, false, null);
|
||||
+ // Airplane end
|
||||
|
||||
if (io.papermc.paper.util.CollisionUtil.isCollidingWithBorderEdge(world.getWorldBorder(), collisionBox)) {
|
||||
io.papermc.paper.util.CollisionUtil.addBoxesToIfIntersects(world.getWorldBorder().getCollisionShape(), collisionBox, potentialCollisions);
|
||||
@@ -2462,7 +2465,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
||||
AABB axisalignedbb = AABB.ofSize(this.getEyePosition(), (double) f, 1.0E-6D, (double) f);
|
||||
|
||||
// Paper start
|
||||
- return io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this.level, this, axisalignedbb, null,
|
||||
+ return io.papermc.paper.util.CollisionUtil.getEntityCollisionsWithCache(this.level, this, axisalignedbb, null, // Airplane - use cache
|
||||
false, false, false, true, this.level.isAlmostSuffocating); // Airplane - don't allocate lambda here
|
||||
// Paper end
|
||||
}
|
||||
@@ -3831,7 +3834,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
||||
}
|
||||
|
||||
public boolean updateFluidHeightAndDoFluidPushing(Tag<Fluid> tag, double d0) {
|
||||
- if (this.touchingUnloadedChunk()) {
|
||||
+ if (false && this.touchingUnloadedChunk()) { // Airplane - cost of a lookup here is the same cost as below, so skip
|
||||
return false;
|
||||
} else {
|
||||
AABB axisalignedbb = this.getBoundingBox().deflate(0.001D);
|
||||
@@ -3881,7 +3884,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
||||
|
||||
net.minecraft.world.level.chunk.ChunkAccess chunk = this.level.getChunkIfLoadedImmediately(currChunkX, currChunkZ);
|
||||
if (chunk == null) {
|
||||
- continue;
|
||||
+ return false; // if we're touching an unloaded chunk then it's false
|
||||
}
|
||||
|
||||
net.minecraft.world.level.chunk.LevelChunkSection[] sections = chunk.getSections();
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
index e265980f07d95a7912bf8873819033e51ef04c98..1174d94a032c1158e5d9a8be3beff1ba6f0d196b 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -185,6 +185,7 @@ public class LevelChunk implements ChunkAccess {
|
||||
// Airplane end
|
||||
|
||||
private final int levelHeight; private final int minBuildHeight; // Airplane
|
||||
+ public final gg.airplane.entity.CollisionCacheList[] collisionCaches; // Airplane
|
||||
public LevelChunk(Level world, ChunkPos pos, ChunkBiomeContainer biomes) {
|
||||
this(world, pos, biomes, UpgradeData.EMPTY, EmptyTickList.empty(), EmptyTickList.empty(), 0L, (LevelChunkSection[]) null, (Consumer) null);
|
||||
}
|
||||
@@ -223,6 +224,12 @@ public class LevelChunk implements ChunkAccess {
|
||||
this.inhabitedTime = inhabitedTime;
|
||||
this.postLoad = loadToWorldConsumer;
|
||||
this.sections = new LevelChunkSection[world.getSectionsCount()];
|
||||
+ // Airplane start
|
||||
+ this.collisionCaches = new gg.airplane.entity.CollisionCacheList[world.getSectionsCount()];
|
||||
+ for (int i = 0; i < this.collisionCaches.length; i++) {
|
||||
+ this.collisionCaches[i] = new gg.airplane.entity.CollisionCacheList();
|
||||
+ }
|
||||
+ // Airplane end
|
||||
if (sections != null) {
|
||||
if (this.sections.length == sections.length) {
|
||||
System.arraycopy(sections, 0, this.sections, 0, this.sections.length);
|
||||
@@ -669,6 +676,17 @@ public class LevelChunk implements ChunkAccess {
|
||||
int l = i & 15;
|
||||
int i1 = blockposition.getZ() & 15;
|
||||
BlockState iblockdata1 = chunksection.setBlockState(k, l, i1, iblockdata);
|
||||
+
|
||||
+ // Airplane start - notify dirty
|
||||
+ SectionPos pos = SectionPos.of(this.chunkPos, j);
|
||||
+ gg.airplane.entity.CollisionCache[] caches = this.collisionCaches[j].getRawData();
|
||||
+ for (int index = 0; index < caches.length; index++) {
|
||||
+ gg.airplane.entity.CollisionCache cache = caches[index];
|
||||
+ if (cache != null) {
|
||||
+ cache.dirtySection(pos);
|
||||
+ }
|
||||
+ }
|
||||
+ // Airplane end
|
||||
|
||||
if (iblockdata1 == iblockdata) {
|
||||
return null;
|
||||
diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
|
||||
index 1ed6573e0ca6b353d1de3b4486e199a5db9aa447..840ec9dabb787cea2bcd9b46f08443512d931db8 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
|
||||
@@ -606,6 +606,12 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
PersistentEntitySectionManager.this.knownUuids.remove(this.entity.getUUID());
|
||||
this.entity.setLevelCallback(PersistentEntitySectionManager.Callback.NULL);
|
||||
PersistentEntitySectionManager.this.removeSectionIfEmpty(this.currentSectionKey, this.currentSection);
|
||||
+
|
||||
+ // Airplane start
|
||||
+ if (this.entity instanceof Entity realEntity) {
|
||||
+ realEntity.collisionCache.onRemove();
|
||||
+ }
|
||||
+ // Airplane end
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user