From 339da44a9908893e5370c546ebebc723779ef9e5 Mon Sep 17 00:00:00 2001 From: Ivan Pekov Date: Mon, 31 Aug 2020 21:35:20 +0300 Subject: [PATCH] Fix oversized block collisions We went thru a bunch of effort for this fix, but its finally been patched. In the meantime I ported jellysquid's latest changes to entity collisions which also work completely fine. Thanks to Simon ( @smonnnn ) who is also going to be the co-author of this commit cuz he was the one to properly implement the problematic patch . These changes should lower your mspt. Fixes #165 Fixes #178 Co-authored-by: Simon L --- ...0035-lithium-collision-optimizations.patch | 80 ++++++++++++------- .../0037-lithium-MixinChunkSection.patch | 75 ++++------------- 2 files changed, 64 insertions(+), 91 deletions(-) diff --git a/patches/server/0035-lithium-collision-optimizations.patch b/patches/server/0035-lithium-collision-optimizations.patch index f5c52170..025bd586 100644 --- a/patches/server/0035-lithium-collision-optimizations.patch +++ b/patches/server/0035-lithium-collision-optimizations.patch @@ -178,10 +178,10 @@ index 0000000000000000000000000000000000000000..20f80ae80de91615ea02f0771f7c020c +} 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 +index 0000000000000000000000000000000000000000..a009068a2ac9c1671978cfd942b83c5b414b3c7a --- /dev/null +++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/LithiumEntityCollisions.java -@@ -0,0 +1,193 @@ +@@ -0,0 +1,179 @@ +package me.jellysquid.mods.lithium.common.entity; + +import dev.tr7zw.yatopia.EntityFilter; @@ -228,27 +228,13 @@ index 0000000000000000000000000000000000000000..7ef9775d351446637dd70e7949716491 + public static Producer getBlockCollisionProducerAABB(ICollisionAccess world, Entity entity, AxisAlignedBB box) { + final ChunkAwareBlockCollisionSweeper sweeper = new ChunkAwareBlockCollisionSweeper(world, entity, box); + -+ return new Producer() { -+ private boolean skipWorldBorderCheck = entity == null; -+ -+ @Override -+ public boolean computeNext(Consumer 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; ++ return consumer -> { ++ VoxelShape shape = sweeper.getNextCollidedShape(); ++ if (shape != null) { ++ consumer.accept(shape.getBoundingBox()); ++ return true; + } ++ return false; + }; + } + @@ -264,7 +250,7 @@ index 0000000000000000000000000000000000000000..7ef9775d351446637dd70e7949716491 + + final ChunkAwareBlockCollisionSweeper sweeper = new ChunkAwareBlockCollisionSweeper(world, entity, box); + -+ VoxelShape shape = sweeper.step(); ++ VoxelShape shape = sweeper.getNextCollidedShape(); + return shape != null; + } + @@ -351,7 +337,7 @@ index 0000000000000000000000000000000000000000..7ef9775d351446637dd70e7949716491 + * + * @return True if the {@param box} is fully within the {@param border}, otherwise false. + */ -+ public static boolean isBoxFullyWithinWorldBorder(WorldBorder border, AxisAlignedBB box) { ++ public static boolean isWithinWorldBorder(WorldBorder border, AxisAlignedBB box) { + double wboxMinX = Math.floor(border.getMinX()); + double wboxMinZ = Math.floor(border.getMinZ()); + @@ -362,11 +348,11 @@ index 0000000000000000000000000000000000000000..7ef9775d351446637dd70e7949716491 + box.maxX >= wboxMinX && box.maxX < wboxMaxX && box.maxZ >= wboxMinZ && box.maxZ < wboxMaxZ; + } + -+ private static boolean canEntityCollideWithWorldBorder(ICollisionAccess world, Entity entity) { ++ public 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)); ++ boolean isInsideBorder = isWithinWorldBorder(border, entity.getBoundingBox().shrink(EPSILON)); ++ boolean isCrossingBorder = isWithinWorldBorder(border, entity.getBoundingBox().grow(EPSILON)); + + return !isInsideBorder && isCrossingBorder; + } @@ -377,12 +363,13 @@ index 0000000000000000000000000000000000000000..7ef9775d351446637dd70e7949716491 +} 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 +index 0000000000000000000000000000000000000000..508fe7cf5281cff7528b90e584c17e73fc5a2388 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/movement/ChunkAwareBlockCollisionSweeper.java -@@ -0,0 +1,247 @@ +@@ -0,0 +1,279 @@ +package me.jellysquid.mods.lithium.common.entity.movement; + ++import me.jellysquid.mods.lithium.common.entity.LithiumEntityCollisions; +import net.minecraft.server.AxisAlignedBB; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.Blocks; @@ -422,6 +409,8 @@ index 0000000000000000000000000000000000000000..b9d279d1a9703c17221d4219b86b8f31 + private final ICollisionAccess view; + private final VoxelShapeCollision context; + ++ private final Entity entity; ++ + //limits of the area without extension for oversized blocks + private final int minX, minY, minZ, maxX, maxY, maxZ; + @@ -437,6 +426,7 @@ index 0000000000000000000000000000000000000000..b9d279d1a9703c17221d4219b86b8f31 + private boolean sectionOversizedBlocks; + private IChunkAccess cachedChunk; + private ChunkSection cachedChunkSection; ++ private boolean needEntityCollisionCheck; + + public ChunkAwareBlockCollisionSweeper(ICollisionAccess view, Entity entity, AxisAlignedBB box) { + this.box = box; @@ -444,6 +434,8 @@ index 0000000000000000000000000000000000000000..b9d279d1a9703c17221d4219b86b8f31 + // todo: ivan, make obfhelpers + this.context = entity == null ? VoxelShapeCollision.a() : VoxelShapeCollision.a(entity); + this.view = view; ++ this.entity = entity; ++ this.needEntityCollisionCheck = entity != null; + + this.minX = MathHelper.floor(box.minX - EPSILON); + this.maxX = MathHelper.floor(box.maxX + EPSILON); @@ -517,17 +509,33 @@ index 0000000000000000000000000000000000000000..b9d279d1a9703c17221d4219b86b8f31 + return true; + } + ++ public VoxelShape getNextCollidedShape() { ++ VoxelShape shape = null; ++ ++ if (this.needEntityCollisionCheck) { ++ shape = this.getNextEntityCollision(); ++ ++ this.needEntityCollisionCheck = false; ++ } ++ ++ if (shape == null) { ++ shape = this.getNextBlockCollision(); ++ } ++ ++ return shape; ++ } ++ + + /** + * 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() { ++ private VoxelShape getNextBlockCollision() { + while (true) { + if (this.cIterated >= this.cTotalSize) { + if (!this.nextSection()) { -+ return null; ++ break; + } + } + this.cIterated++; @@ -578,6 +586,16 @@ index 0000000000000000000000000000000000000000..b9d279d1a9703c17221d4219b86b8f31 + } + } + } ++ ++ return null; ++ } ++ ++ private VoxelShape getNextEntityCollision() { ++ if (LithiumEntityCollisions.canEntityCollideWithWorldBorder(this.view, this.entity)) { ++ return this.view.getWorldBorder().asVoxelShape(); ++ } ++ ++ return null; + } + + /** diff --git a/patches/server/0037-lithium-MixinChunkSection.patch b/patches/server/0037-lithium-MixinChunkSection.patch index e099da09..feb6fec1 100644 --- a/patches/server/0037-lithium-MixinChunkSection.patch +++ b/patches/server/0037-lithium-MixinChunkSection.patch @@ -7,61 +7,20 @@ Original code by JellySquid, licensed under LGPLv3 you can find the original code on https://github.com/jellysquid3/lithium-fabric/ (Yarn mappings) diff --git a/src/main/java/net/minecraft/server/BlockBase.java b/src/main/java/net/minecraft/server/BlockBase.java -index 1d02880e5818d22c13d67d3f44844a2cca51c164..4ece27e3e6885246bc8b54b9277e01211db4f989 100644 +index 1d02880e5818d22c13d67d3f44844a2cca51c164..13f541390040d07229de0fbb1cfa4bcfe7d0794f 100644 --- a/src/main/java/net/minecraft/server/BlockBase.java +++ b/src/main/java/net/minecraft/server/BlockBase.java -@@ -339,10 +339,11 @@ public abstract class BlockBase { - // Paper end - - // Tuinity start - micro the hell out of this call -- protected boolean shapeExceedsCube = true; -+ protected boolean shapeExceedsCube = this.shapeExceedsCube(); // Yatopia start - re add this function or collisions break - public final boolean shapeExceedsCube() { -- return this.shapeExceedsCube; -+ return this.a == null || this.a.c; +@@ -345,6 +345,8 @@ public abstract class BlockBase { } -+ // Yatiopia end // Tuinity end ++ public final boolean shapeExceedsCubeUncached() { return this.a == null || this.a.c; } // Yatopia - uncached shapeExceedsCube due to collisions stuff ++ // Tuinity start -@@ -356,8 +357,6 @@ public abstract class BlockBase { - if (!this.getBlock().o()) { - this.a = new BlockBase.BlockData.Cache(this.p()); - } -- this.shapeExceedsCube = this.a == null || this.a.c; // Tuinity - moved from actual method to here -- - } - - public Block getBlock() { -@@ -607,7 +606,25 @@ public abstract class BlockBase { - } - - public IBlockData updateState(EnumDirection enumdirection, IBlockData iblockdata, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { -- return this.getBlock().updateState(this.p(), enumdirection, iblockdata, generatoraccess, blockposition, blockposition1); -+ // Yatopia start -+ IBlockData updated = this.getBlock().updateState(this.p(), enumdirection, iblockdata, generatoraccess, blockposition, blockposition1); -+ if (updated.shapeExceedsCube()) { -+ IChunkAccess chunk = generatoraccess.getChunkIfLoadedImmediately(blockposition1.getX() >> 4, blockposition1.getZ() >> 4); -+ if (chunk != null) { -+ ChunkSection section = chunk.getSections()[blockposition1.getY() >> 4]; -+ if (section != null) section.oversizedBlockCount++; -+ } -+ } else { -+ if (iblockdata.shapeExceedsCube()) { -+ IChunkAccess chunk = generatoraccess.getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4); -+ if (chunk != null) { -+ ChunkSection section = chunk.getSections()[blockposition.getY() >> 4]; -+ if (section != null) section.oversizedBlockCount--; -+ } -+ } -+ } -+ return updated; -+ // Yatopia end - } - - public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + protected boolean isTicking; + protected Fluid fluid; diff --git a/src/main/java/net/minecraft/server/ChunkSection.java b/src/main/java/net/minecraft/server/ChunkSection.java -index cebd808e273dbdb88feb16920dd7a2f60390b34f..478af91f52272424da61d626534298290d6385f5 100644 +index cebd808e273dbdb88feb16920dd7a2f60390b34f..d210ac3d5775491e9beb885dde608f5e8fda8eb2 100644 --- a/src/main/java/net/minecraft/server/ChunkSection.java +++ b/src/main/java/net/minecraft/server/ChunkSection.java @@ -4,7 +4,7 @@ import java.util.function.Predicate; @@ -77,31 +36,27 @@ index cebd808e273dbdb88feb16920dd7a2f60390b34f..478af91f52272424da61d62653429829 short tickingBlockCount; // Paper - private -> package-private private short e; final DataPaletteBlock blockIds; // Paper - package-private -+ short oversizedBlockCount = 0; // Yatopia ++ private short oversizedBlockCount = 0; // Yatopia final com.destroystokyo.paper.util.maplist.IBlockDataList tickingList = new com.destroystokyo.paper.util.maplist.IBlockDataList(); // Paper -@@ -67,6 +68,9 @@ public class ChunkSection { +@@ -67,6 +68,7 @@ public class ChunkSection { if (!iblockdata1.isAir()) { --this.nonEmptyBlockCount; -+ if(iblockdata1.shapeExceedsCube()){ //Yatopia - lithium oversized blocks count -+ --this.oversizedBlockCount; -+ } // Yatopia end ++ if (iblockdata1.shapeExceedsCubeUncached()) this.oversizedBlockCount--; // Yatopia if (iblockdata1.isTicking()) { --this.tickingBlockCount; // Paper start -@@ -81,6 +85,9 @@ public class ChunkSection { +@@ -81,6 +83,7 @@ public class ChunkSection { if (!iblockdata.isAir()) { ++this.nonEmptyBlockCount; -+ if(iblockdata.shapeExceedsCube()){ //Yatopia - lithium oversized blocks count -+ ++this.oversizedBlockCount; -+ } // Yatopia end ++ if (iblockdata.shapeExceedsCubeUncached()) this.oversizedBlockCount++; // Yatopia if (iblockdata.isTicking()) { ++this.tickingBlockCount; // Paper start -@@ -126,10 +133,12 @@ public class ChunkSection { +@@ -126,10 +129,12 @@ public class ChunkSection { // Paper start this.tickingList.clear(); // Paper end @@ -110,11 +65,11 @@ index cebd808e273dbdb88feb16920dd7a2f60390b34f..478af91f52272424da61d62653429829 this.tickingBlockCount = 0; this.e = 0; this.blockIds.forEachLocation((iblockdata, location) -> { // Paper -+ if (iblockdata.shapeExceedsCube()) this.oversizedBlockCount += location; // Yatopia ++ if (iblockdata.shapeExceedsCubeUncached()) this.oversizedBlockCount += location; // Yatopia Fluid fluid = iblockdata.getFluid(); if (!iblockdata.isAir()) { -@@ -173,4 +182,11 @@ public class ChunkSection { +@@ -173,4 +178,11 @@ public class ChunkSection { public boolean a(Predicate predicate) { return this.blockIds.contains(predicate); }