From e51eb03769d243d26eb7c07d1fcc31cbb9ff3eda Mon Sep 17 00:00:00 2001 From: Samsuik Date: Mon, 15 Jul 2024 13:07:09 +0100 Subject: [PATCH] Updated Upstream (Paper) Upstream has released updates that appear to apply and compile correctly --- gradle.properties | 2 +- .../0003-Sakura-Configuration-Files.patch | 24 +- ...-Local-Config-and-Value-Storage-API.patch} | 8 +- ...Use-distance-map-to-optimise-entity-.patch | 340 -- ...er-Unapplied-Collision-optimisations.patch | 4723 ----------------- ... => 0005-Visibility-API-and-Command.patch} | 32 +- ...applied-Actually-optimise-explosions.patch | 521 -- ...06-Reduce-deltaMovement-Allocations.patch} | 33 +- ...007-Optional-Force-Position-Updates.patch} | 0 ...tch => 0008-Load-Chunks-on-Movement.patch} | 114 +- patches/server/0009-Optimise-rayTracing.patch | 134 - ...and.patch => 0009-TPS-Graph-Command.patch} | 12 +- ...h => 0010-Optimise-New-Liquid-Level.patch} | 2 +- ...ce-Packet-obfuscation-and-reduction.patch} | 22 +- ...0012-Use-Optimised-TrackedEntityMap.patch} | 4 +- ...tityList-methods-to-BasicEntityList.patch} | 0 ...Add-utility-methods-to-EntitySlices.patch} | 0 ...h => 0015-Optimise-paper-explosions.patch} | 125 +- ...tch => 0016-Store-Entity-Data-State.patch} | 20 +- ...patch => 0017-Merge-Cannon-Entities.patch} | 64 +- ...018-Replace-explosion-density-cache.patch} | 33 +- ...ise-explosions-in-protected-regions.patch} | 27 +- ...atch => 0020-Specialised-Explosions.patch} | 126 +- ...021-Optimise-cannon-entity-movement.patch} | 95 +- ... => 0022-Entity-pushed-by-fluid-API.patch} | 22 +- ...nics.patch => 0023-Cannon-Mechanics.patch} | 26 +- ...e-MovingBlockEntity-collision-shape.patch} | 0 ...ch => 0025-Optimise-TNT-fluid-state.patch} | 19 +- ...> 0026-Add-maxSearch-to-getEntities.patch} | 14 +- ...-Optimise-LivingEntity-pushEntities.patch} | 4 +- ...alling-blocks-inside-moving-pistons.patch} | 2 +- ... => 0029-Configure-Entity-Knockback.patch} | 36 +- ...ch => 0030-Explosion-Durable-Blocks.patch} | 26 +- ... => 0031-Destroy-Waterlogged-Blocks.patch} | 4 +- ...2-Cache-Vanillia-and-Eigen-Redstone.patch} | 12 +- ...Falling-Block-Stacking-Restrictions.patch} | 2 +- ...ItemEntity-s-that-ignore-explosions.patch} | 0 ...035-Add-option-to-disable-entity-ai.patch} | 0 ...=> 0036-Consistent-Explosion-Radius.patch} | 28 +- ...0037-Remove-spigot-max-tnt-per-tick.patch} | 6 +- ...-configure-entity-water-sensitivity.patch} | 4 +- ...Configure-cannon-physics-by-version.patch} | 263 +- ...h => 0040-Allow-water-in-the-nether.patch} | 0 ...igure-concrete-solidifying-in-water.patch} | 2 +- ...tion-for-fast-nether-dimension-lava.patch} | 2 +- ...43-Allow-explosions-to-destroy-lava.patch} | 4 +- ...e-columns-affecting-cannon-entities.patch} | 0 ...olid-blocks-as-full-when-moving-fast.patch | 68 + ...046-Add-redstone-implementation-API.patch} | 2 +- ...Reduce-entity-tracker-player-updates.patch | 54 + ...ion-for-legacy-lava-block-formation.patch} | 22 +- ...0049-Configure-mob-spawner-defaults.patch} | 0 ...able-blocks-as-full-while-moving-fas.patch | 79 - ...ing-random-dispenser-item-selection.patch} | 0 ...051-Add-instant-mob-death-animation.patch} | 4 +- ...Reduce-entity-tracker-player-updates.patch | 53 - ...-Configure-fluids-breaking-redstone.patch} | 4 +- ...-disable-explosions-hurting-players.patch} | 4 +- ...> 0054-Iron-golems-take-fall-damage.patch} | 0 ...dd-explosions-dropping-items-config.patch} | 4 +- ...mise-check-inside-blocks-and-fluids.patch} | 10 +- ...or-lava-if-throttled-water-flow-spe.patch} | 2 +- ...-biome-noise-once-per-chunk-section.patch} | 4 +- ...-gamerule-preventing-falling-blocks.patch} | 2 +- ...0-Add-entity-travel-distance-limits.patch} | 10 +- ...-speed-and-breaking-inside-entities.patch} | 0 ...pportingBlock-not-updating-last-chun.patch | 22 - ...dd-outline-colliison-to-enderpearls.patch} | 0 ...layer-poses-shrinking-collision-box.patch} | 4 +- ...patch => 0064-Mob-spawner-behaviour.patch} | 0 ...ance-for-crop-growth-instead-of-age.patch} | 0 ...rotect-block-shapes-against-plugins.patch} | 31 +- ...0067-Legacy-player-combat-mechanics.patch} | 14 +- ... 0068-Allow-disabling-sweep-attacks.patch} | 4 +- ...069-Change-shields-to-reduce-damage.patch} | 18 +- ...=> 0070-Old-enchanted-golden-apples.patch} | 4 +- ...=> 0071-Configure-fast-health-regen.patch} | 0 ...-for-fishing-hooks-pulling-entities.patch} | 0 ...-combat-sounds-and-particle-effects.patch} | 6 +- ...4-Protect-scaffolding-from-creepers.patch} | 4 +- ...0075-Entity-tracking-range-modifier.patch} | 10 +- 81 files changed, 804 insertions(+), 6572 deletions(-) rename patches/server/{0007-Local-Config-and-Value-Storage-API.patch => 0004-Local-Config-and-Value-Storage-API.patch} (97%) delete mode 100644 patches/server/0004-Paper-Unapplied-Use-distance-map-to-optimise-entity-.patch delete mode 100644 patches/server/0005-Paper-Unapplied-Collision-optimisations.patch rename patches/server/{0008-Visibility-API-and-Command.patch => 0005-Visibility-API-and-Command.patch} (95%) delete mode 100644 patches/server/0006-Paper-Unapplied-Actually-optimise-explosions.patch rename patches/server/{0010-Reduce-deltaMovement-Allocations.patch => 0006-Reduce-deltaMovement-Allocations.patch} (90%) rename patches/server/{0011-Optional-Force-Position-Updates.patch => 0007-Optional-Force-Position-Updates.patch} (100%) rename patches/server/{0012-Load-Chunks-on-Movement.patch => 0008-Load-Chunks-on-Movement.patch} (51%) delete mode 100644 patches/server/0009-Optimise-rayTracing.patch rename patches/server/{0013-TPS-Graph-Command.patch => 0009-TPS-Graph-Command.patch} (97%) rename patches/server/{0014-Optimise-New-Liquid-Level.patch => 0010-Optimise-New-Liquid-Level.patch} (97%) rename patches/server/{0015-Slice-Packet-obfuscation-and-reduction.patch => 0011-Slice-Packet-obfuscation-and-reduction.patch} (89%) rename patches/server/{0016-Use-Optimised-TrackedEntityMap.patch => 0012-Use-Optimised-TrackedEntityMap.patch} (86%) rename patches/server/{0017-Copy-EntityList-methods-to-BasicEntityList.patch => 0013-Copy-EntityList-methods-to-BasicEntityList.patch} (100%) rename patches/server/{0018-Add-utility-methods-to-EntitySlices.patch => 0014-Add-utility-methods-to-EntitySlices.patch} (100%) rename patches/server/{0019-Optimise-paper-explosions.patch => 0015-Optimise-paper-explosions.patch} (64%) rename patches/server/{0020-Store-Entity-Data-State.patch => 0016-Store-Entity-Data-State.patch} (83%) rename patches/server/{0021-Merge-Cannon-Entities.patch => 0017-Merge-Cannon-Entities.patch} (91%) rename patches/server/{0022-Replace-explosion-density-cache.patch => 0018-Replace-explosion-density-cache.patch} (91%) rename patches/server/{0023-Optimise-explosions-in-protected-regions.patch => 0019-Optimise-explosions-in-protected-regions.patch} (62%) rename patches/server/{0024-Specialised-Explosions.patch => 0020-Specialised-Explosions.patch} (84%) rename patches/server/{0025-Optimise-Fast-Movement.patch => 0021-Optimise-cannon-entity-movement.patch} (66%) rename patches/server/{0026-isPushedByFluid-API.patch => 0022-Entity-pushed-by-fluid-API.patch} (68%) rename patches/server/{0027-Cannon-Mechanics.patch => 0023-Cannon-Mechanics.patch} (81%) rename patches/server/{0028-Cache-MovingBlockEntity-collision-shape.patch => 0024-Cache-MovingBlockEntity-collision-shape.patch} (100%) rename patches/server/{0029-Optimise-TNT-fluid-state-and-pushing.patch => 0025-Optimise-TNT-fluid-state.patch} (74%) rename patches/server/{0030-Add-maxSearch-to-getEntities.patch => 0026-Add-maxSearch-to-getEntities.patch} (93%) rename patches/server/{0031-Optimise-LivingEntity-pushEntities.patch => 0027-Optimise-LivingEntity-pushEntities.patch} (90%) rename patches/server/{0032-Despawn-falling-blocks-inside-moving-pistons.patch => 0028-Despawn-falling-blocks-inside-moving-pistons.patch} (93%) rename patches/server/{0033-Configure-Entity-Knockback.patch => 0029-Configure-Entity-Knockback.patch} (80%) rename patches/server/{0034-Explosion-Durable-Blocks.patch => 0030-Explosion-Durable-Blocks.patch} (89%) rename patches/server/{0035-Destroy-Waterlogged-Blocks.patch => 0031-Destroy-Waterlogged-Blocks.patch} (89%) rename patches/server/{0036-Cache-Vanillia-and-Eigen-Redstone.patch => 0032-Cache-Vanillia-and-Eigen-Redstone.patch} (97%) rename patches/server/{0037-Falling-Block-Stacking-Restrictions.patch => 0033-Falling-Block-Stacking-Restrictions.patch} (93%) rename patches/server/{0038-Added-list-of-ItemEntity-s-that-ignore-explosions.patch => 0034-Added-list-of-ItemEntity-s-that-ignore-explosions.patch} (100%) rename patches/server/{0039-Add-option-to-disable-entity-ai.patch => 0035-Add-option-to-disable-entity-ai.patch} (100%) rename patches/server/{0040-Consistent-Explosion-Radius.patch => 0036-Consistent-Explosion-Radius.patch} (52%) rename patches/server/{0041-Remove-spigot-max-tnt-per-tick.patch => 0037-Remove-spigot-max-tnt-per-tick.patch} (81%) rename patches/server/{0042-Option-to-configure-entity-water-sensitivity.patch => 0038-Option-to-configure-entity-water-sensitivity.patch} (85%) rename patches/server/{0043-Configure-cannon-physics-by-version.patch => 0039-Configure-cannon-physics-by-version.patch} (85%) rename patches/server/{0044-Allow-water-in-the-nether.patch => 0040-Allow-water-in-the-nether.patch} (100%) rename patches/server/{0045-Configure-concrete-solidifying-in-water.patch => 0041-Configure-concrete-solidifying-in-water.patch} (93%) rename patches/server/{0046-Option-for-fast-nether-dimension-lava.patch => 0042-Option-for-fast-nether-dimension-lava.patch} (90%) rename patches/server/{0047-Allow-explosions-to-destroy-lava.patch => 0043-Allow-explosions-to-destroy-lava.patch} (90%) rename patches/server/{0048-Disable-bubble-columns-affecting-cannon-entities.patch => 0044-Disable-bubble-columns-affecting-cannon-entities.patch} (100%) create mode 100644 patches/server/0045-Treat-solid-blocks-as-full-when-moving-fast.patch rename patches/server/{0050-Add-redstone-implementation-API.patch => 0046-Add-redstone-implementation-API.patch} (98%) create mode 100644 patches/server/0047-Reduce-entity-tracker-player-updates.patch rename patches/server/{0052-Add-option-for-legacy-lava-block-formation.patch => 0048-Add-option-for-legacy-lava-block-formation.patch} (80%) rename patches/server/{0053-Configure-mob-spawner-defaults.patch => 0049-Configure-mob-spawner-defaults.patch} (100%) delete mode 100644 patches/server/0049-Treat-all-collidable-blocks-as-full-while-moving-fas.patch rename patches/server/{0054-Allow-disabling-random-dispenser-item-selection.patch => 0050-Allow-disabling-random-dispenser-item-selection.patch} (100%) rename patches/server/{0055-Add-instant-mob-death-animation.patch => 0051-Add-instant-mob-death-animation.patch} (85%) delete mode 100644 patches/server/0051-Reduce-entity-tracker-player-updates.patch rename patches/server/{0056-Configure-fluids-breaking-redstone.patch => 0052-Configure-fluids-breaking-redstone.patch} (89%) rename patches/server/{0057-Option-to-disable-explosions-hurting-players.patch => 0053-Option-to-disable-explosions-hurting-players.patch} (91%) rename patches/server/{0058-Iron-golems-take-fall-damage.patch => 0054-Iron-golems-take-fall-damage.patch} (100%) rename patches/server/{0059-Add-explosions-dropping-items-config.patch => 0055-Add-explosions-dropping-items-config.patch} (86%) rename patches/server/{0060-Optimise-check-inside-blocks-and-fluids.patch => 0056-Optimise-check-inside-blocks-and-fluids.patch} (93%) rename patches/server/{0062-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch => 0057-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch} (92%) rename patches/server/{0063-Calculate-biome-noise-once-per-chunk-section.patch => 0058-Calculate-biome-noise-once-per-chunk-section.patch} (88%) rename patches/server/{0064-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch => 0059-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch} (95%) rename patches/server/{0065-Add-entity-travel-distance-limits.patch => 0060-Add-entity-travel-distance-limits.patch} (86%) rename patches/server/{0066-Configure-potion-speed-and-breaking-inside-entities.patch => 0061-Configure-potion-speed-and-breaking-inside-entities.patch} (100%) delete mode 100644 patches/server/0061-Fix-paper-findSupportingBlock-not-updating-last-chun.patch rename patches/server/{0067-Add-outline-colliison-to-enderpearls.patch => 0062-Add-outline-colliison-to-enderpearls.patch} (100%) rename patches/server/{0068-Disable-player-poses-shrinking-collision-box.patch => 0063-Disable-player-poses-shrinking-collision-box.patch} (87%) rename patches/server/{0069-Mob-spawner-behaviour.patch => 0064-Mob-spawner-behaviour.patch} (100%) rename patches/server/{0070-Use-random-chance-for-crop-growth-instead-of-age.patch => 0065-Use-random-chance-for-crop-growth-instead-of-age.patch} (100%) rename patches/server/{0071-Protect-block-shapes-against-plugins.patch => 0066-Protect-block-shapes-against-plugins.patch} (77%) rename patches/server/{0072-Legacy-player-combat-mechanics.patch => 0067-Legacy-player-combat-mechanics.patch} (95%) rename patches/server/{0073-Allow-disabling-sweep-attacks.patch => 0068-Allow-disabling-sweep-attacks.patch} (86%) rename patches/server/{0074-Change-shields-to-reduce-damage.patch => 0069-Change-shields-to-reduce-damage.patch} (62%) rename patches/server/{0075-Old-enchanted-golden-apples.patch => 0070-Old-enchanted-golden-apples.patch} (93%) rename patches/server/{0076-Configure-fast-health-regen.patch => 0071-Configure-fast-health-regen.patch} (100%) rename patches/server/{0077-Add-option-for-fishing-hooks-pulling-entities.patch => 0072-Add-option-for-fishing-hooks-pulling-entities.patch} (100%) rename patches/server/{0078-Old-combat-sounds-and-particle-effects.patch => 0073-Old-combat-sounds-and-particle-effects.patch} (90%) rename patches/server/{0079-Protect-scaffolding-from-creepers.patch => 0074-Protect-scaffolding-from-creepers.patch} (90%) rename patches/server/{0080-Entity-tracking-range-modifier.patch => 0075-Entity-tracking-range-modifier.patch} (87%) diff --git a/gradle.properties b/gradle.properties index 02c6fa7..acd1cca 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ group=me.samsuik.sakura version=1.21-R0.1-SNAPSHOT mcVersion=1.21 -paperRef=dd49fba8c534d48c3693a751075ecb5836a9d458 +paperRef=8b35adca881414c92efeac9aba91a25ace9ea648 org.gradle.jvmargs=-Xmx2G diff --git a/patches/server/0003-Sakura-Configuration-Files.patch b/patches/server/0003-Sakura-Configuration-Files.patch index dfcfa90..adc0a04 100644 --- a/patches/server/0003-Sakura-Configuration-Files.patch +++ b/patches/server/0003-Sakura-Configuration-Files.patch @@ -606,7 +606,7 @@ index 0000000000000000000000000000000000000000..a22139d6f6775b7b8d635e126d2ea2bf +} diff --git a/src/main/java/me/samsuik/sakura/configuration/WorldConfiguration.java b/src/main/java/me/samsuik/sakura/configuration/WorldConfiguration.java new file mode 100644 -index 0000000000000000000000000000000000000000..2ed95ba385e3937fde99d4ee3e7845e77591aa89 +index 0000000000000000000000000000000000000000..4a92dfb3e28ab4b97f000b67883d3320192745e3 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/WorldConfiguration.java @@ -0,0 +1,219 @@ @@ -682,8 +682,8 @@ index 0000000000000000000000000000000000000000..2ed95ba385e3937fde99d4ee3e7845e7 + public boolean preventAtWorldHeight = false; + + public boolean isFallingBlockInBounds(FallingBlockEntity entity) { -+ return (!preventAgainstBorder || !io.papermc.paper.util.CollisionUtil.isCollidingWithBorder(entity.level().getWorldBorder(), entity.getBoundingBox().inflate(0.01))) -+ && (!preventAtWorldHeight || entity.blockPosition().getY() < entity.level().getMaxBuildHeight() - 1); ++ return (!this.preventAgainstBorder || !ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isCollidingWithBorder(entity.level().getWorldBorder(), entity.getBoundingBox().inflate(0.01))) ++ && (!this.preventAtWorldHeight || entity.blockPosition().getY() < entity.level().getMaxBuildHeight() - 1); + } + } + @@ -1040,7 +1040,7 @@ index 0000000000000000000000000000000000000000..4024f9738e039ffffd560a07a2210f75 +public record DurableMaterial(int durability, float resistance) { +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 68f60e77e0bfd42b6419491c1d59b6432974216b..1af29c2d6c591b11fd509821d54e6ea781c0929d 100644 +index 54e581db732c3a104142a2bbc228ecbe16aec0fc..49284f76714fea23b602d7fa6dd3fc468a851c00 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -317,6 +317,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - create paper world config & Anti-Xray + protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, Supplier sakuraWorldConfigCreator, java.util.concurrent.Executor executor) { // Sakura - sakura configuration files// Paper - create paper world config & Anti-Xray @@ -1141,7 +1141,7 @@ index caf6ff33b42472d30f28629470e12889f50490cc..1e2dcd25fc9e605481c809fcb3121b41 this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index 6e1f92b45504cef0c4dcbbebf3df339ef4cad0a1..c361c408b0382ac7bffa960ba56c37157544c25f 100644 +index 1acc0a579804dee7bb3d0f755c24240413ed7540..870ceadca6d60bf2cd2a171e3f1532f45f9eeef2 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java @@ -176,6 +176,14 @@ public class Main { diff --git a/patches/server/0007-Local-Config-and-Value-Storage-API.patch b/patches/server/0004-Local-Config-and-Value-Storage-API.patch similarity index 97% rename from patches/server/0007-Local-Config-and-Value-Storage-API.patch rename to patches/server/0004-Local-Config-and-Value-Storage-API.patch index 14deb42..0ad5b27 100644 --- a/patches/server/0007-Local-Config-and-Value-Storage-API.patch +++ b/patches/server/0004-Local-Config-and-Value-Storage-API.patch @@ -231,10 +231,10 @@ index 0000000000000000000000000000000000000000..3f518f3f1241d3dc1f76fab42e9fd789 + +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 1af29c2d6c591b11fd509821d54e6ea781c0929d..3f4997b642c9de1982a500d3a6ff07a9ca59da10 100644 +index 49284f76714fea23b602d7fa6dd3fc468a851c00..517a9b8c67eba6569aabea6ffb9260619540c3ea 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1747,6 +1747,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop -Date: Tue, 5 May 2020 20:18:05 -0700 -Subject: [PATCH] Paper Unapplied - Use distance map to optimise entity tracker - -Use the distance map to find candidate players for tracking. - -diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index c96740a82eac9101f74edeb44edf4b64d1d633e0..daad83ea92bac1432feed77d5fc2d5c5c15a1b96 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -169,12 +169,37 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - - // Paper start - distance maps - private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>(); -+ // Paper start - use distance map to optimise tracker -+ public static boolean isLegacyTrackingEntity(Entity entity) { -+ return entity.isLegacyTrackingEntity; -+ } -+ -+ // inlined EnumMap, TrackingRange.TrackingRangeType -+ static final org.spigotmc.TrackingRange.TrackingRangeType[] TRACKING_RANGE_TYPES = org.spigotmc.TrackingRange.TrackingRangeType.values(); -+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap[] playerEntityTrackerTrackMaps; -+ final int[] entityTrackerTrackRanges; -+ public final int getEntityTrackerRange(final int ordinal) { -+ return this.entityTrackerTrackRanges[ordinal]; -+ } -+ -+ private int convertSpigotRangeToVanilla(final int vanilla) { -+ return net.minecraft.server.MinecraftServer.getServer().getScaledTrackingDistance(vanilla); -+ } -+ // Paper end - use distance map to optimise tracker - - void addPlayerToDistanceMaps(ServerPlayer player) { - int chunkX = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getX()); - int chunkZ = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getZ()); - // Note: players need to be explicitly added to distance maps before they can be updated - this.nearbyPlayers.addPlayer(player); -+ // Paper start - use distance map to optimise entity tracker -+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) { -+ com.destroystokyo.paper.util.misc.PlayerAreaMap trackMap = this.playerEntityTrackerTrackMaps[i]; -+ int trackRange = this.entityTrackerTrackRanges[i]; -+ -+ trackMap.add(player, chunkX, chunkZ, Math.min(trackRange, io.papermc.paper.chunk.system.ChunkSystem.getSendViewDistance(player))); -+ } -+ // Paper end - use distance map to optimise entity tracker - } - - void removePlayerFromDistanceMaps(ServerPlayer player) { -@@ -182,6 +207,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - int chunkZ = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getZ()); - // Note: players need to be explicitly added to distance maps before they can be updated - this.nearbyPlayers.removePlayer(player); -+ // Paper start - use distance map to optimise tracker -+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) { -+ this.playerEntityTrackerTrackMaps[i].remove(player); -+ } -+ // Paper end - use distance map to optimise tracker - } - - void updateMaps(ServerPlayer player) { -@@ -189,6 +219,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - int chunkZ = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getZ()); - // Note: players need to be explicitly added to distance maps before they can be updated - this.nearbyPlayers.tickPlayer(player); -+ // Paper start - use distance map to optimise entity tracker -+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) { -+ com.destroystokyo.paper.util.misc.PlayerAreaMap trackMap = this.playerEntityTrackerTrackMaps[i]; -+ int trackRange = this.entityTrackerTrackRanges[i]; -+ -+ trackMap.update(player, chunkX, chunkZ, Math.min(trackRange, io.papermc.paper.chunk.system.ChunkSystem.getSendViewDistance(player))); -+ } -+ // Paper end - use distance map to optimise entity tracker - } - // Paper end - // Paper start -@@ -248,6 +286,48 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - // Paper start - this.nearbyPlayers = new io.papermc.paper.util.player.NearbyPlayers(this.level); - // Paper end -+ // Paper start - use distance map to optimise entity tracker -+ this.playerEntityTrackerTrackMaps = new com.destroystokyo.paper.util.misc.PlayerAreaMap[TRACKING_RANGE_TYPES.length]; -+ this.entityTrackerTrackRanges = new int[TRACKING_RANGE_TYPES.length]; -+ -+ org.spigotmc.SpigotWorldConfig spigotWorldConfig = this.level.spigotConfig; -+ -+ for (int ordinal = 0, len = TRACKING_RANGE_TYPES.length; ordinal < len; ++ordinal) { -+ org.spigotmc.TrackingRange.TrackingRangeType trackingRangeType = TRACKING_RANGE_TYPES[ordinal]; -+ int configuredSpigotValue; -+ switch (trackingRangeType) { -+ case PLAYER: -+ configuredSpigotValue = spigotWorldConfig.playerTrackingRange; -+ break; -+ case ANIMAL: -+ configuredSpigotValue = spigotWorldConfig.animalTrackingRange; -+ break; -+ case MONSTER: -+ configuredSpigotValue = spigotWorldConfig.monsterTrackingRange; -+ break; -+ case MISC: -+ configuredSpigotValue = spigotWorldConfig.miscTrackingRange; -+ break; -+ case OTHER: -+ configuredSpigotValue = spigotWorldConfig.otherTrackingRange; -+ break; -+ case ENDERDRAGON: -+ configuredSpigotValue = EntityType.ENDER_DRAGON.clientTrackingRange() * 16; -+ break; -+ case DISPLAY: -+ configuredSpigotValue = spigotWorldConfig.displayTrackingRange; -+ break; -+ default: -+ throw new IllegalStateException("Missing case for enum " + trackingRangeType); -+ } -+ configuredSpigotValue = convertSpigotRangeToVanilla(configuredSpigotValue); -+ -+ int trackRange = (configuredSpigotValue >>> 4) + ((configuredSpigotValue & 15) != 0 ? 1 : 0); -+ this.entityTrackerTrackRanges[ordinal] = trackRange; -+ -+ this.playerEntityTrackerTrackMaps[ordinal] = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets); -+ } -+ // Paper end - use distance map to optimise entity tracker - } - - // Paper start -@@ -821,17 +901,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - - public void move(ServerPlayer player) { -- ObjectIterator objectiterator = this.entityMap.values().iterator(); -- -- while (objectiterator.hasNext()) { -- ChunkMap.TrackedEntity playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next(); -- -- if (playerchunkmap_entitytracker.entity == player) { -- playerchunkmap_entitytracker.updatePlayers(this.level.players()); -- } else { -- playerchunkmap_entitytracker.updatePlayer(player); -- } -- } -+ // Paper - delay this logic for the entity tracker tick, no need to duplicate it - - SectionPos sectionposition = player.getLastSectionPos(); - SectionPos sectionposition1 = SectionPos.of((EntityAccess) player); -@@ -909,7 +979,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - - entity.tracker = playerchunkmap_entitytracker; // Paper - Fast access to tracker - this.entityMap.put(entity.getId(), playerchunkmap_entitytracker); -- playerchunkmap_entitytracker.updatePlayers(this.level.players()); -+ playerchunkmap_entitytracker.updatePlayers(entity.getPlayersInTrackRange()); // Paper - don't search all players - if (entity instanceof ServerPlayer) { - ServerPlayer entityplayer = (ServerPlayer) entity; - -@@ -951,9 +1021,38 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - entity.tracker = null; // Paper - We're no longer tracked - } - -+ // Paper start - optimised tracker -+ private final void processTrackQueue() { -+ this.level.timings.tracker1.startTiming(); -+ try { -+ for (TrackedEntity tracker : this.entityMap.values()) { -+ // update tracker entry -+ tracker.updatePlayers(tracker.entity.getPlayersInTrackRange()); -+ } -+ } finally { -+ this.level.timings.tracker1.stopTiming(); -+ } -+ -+ -+ this.level.timings.tracker2.startTiming(); -+ try { -+ for (TrackedEntity tracker : this.entityMap.values()) { -+ tracker.serverEntity.sendChanges(); -+ } -+ } finally { -+ this.level.timings.tracker2.stopTiming(); -+ } -+ } -+ // Paper end - optimised tracker -+ - protected void tick() { -+ // Paper start - optimized tracker -+ if (true) { -+ this.processTrackQueue(); -+ return; -+ } -+ // Paper end - optimized tracker - // Paper - rewrite chunk system -- - List list = Lists.newArrayList(); - List list1 = this.level.players(); - ObjectIterator objectiterator = this.entityMap.values().iterator(); -@@ -1108,6 +1207,42 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.lastSectionPos = SectionPos.of((EntityAccess) entity); - } - -+ // Paper start - use distance map to optimise tracker -+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet lastTrackerCandidates; -+ -+ final void updatePlayers(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newTrackerCandidates) { -+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet oldTrackerCandidates = this.lastTrackerCandidates; -+ this.lastTrackerCandidates = newTrackerCandidates; -+ -+ if (newTrackerCandidates != null) { -+ Object[] rawData = newTrackerCandidates.getBackingSet(); -+ for (int i = 0, len = rawData.length; i < len; ++i) { -+ Object raw = rawData[i]; -+ if (!(raw instanceof ServerPlayer)) { -+ continue; -+ } -+ ServerPlayer player = (ServerPlayer)raw; -+ this.updatePlayer(player); -+ } -+ } -+ -+ if (oldTrackerCandidates == newTrackerCandidates) { -+ // this is likely the case. -+ // means there has been no range changes, so we can just use the above for tracking. -+ return; -+ } -+ -+ // stuff could have been removed, so we need to check the trackedPlayers set -+ // for players that were removed -+ -+ for (ServerPlayerConnection conn : this.seenBy.toArray(new ServerPlayerConnection[0])) { // avoid CME -+ if (newTrackerCandidates == null || !newTrackerCandidates.contains(conn.getPlayer())) { -+ this.updatePlayer(conn.getPlayer()); -+ } -+ } -+ } -+ // Paper end - use distance map to optimise tracker -+ - public boolean equals(Object object) { - return object instanceof ChunkMap.TrackedEntity ? ((ChunkMap.TrackedEntity) object).entity.getId() == this.entity.getId() : false; - } -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 2519c08a45ce3febcf51ac308ad98ac0e2657cf3..3062e108ed80ace0f2caf03c70a8e48f276be450 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -63,6 +63,7 @@ import net.minecraft.network.syncher.EntityDataSerializers; - import net.minecraft.network.syncher.SyncedDataHolder; - import net.minecraft.network.syncher.SynchedEntityData; - import net.minecraft.resources.ResourceLocation; -+import io.papermc.paper.util.MCUtil; - import net.minecraft.server.MinecraftServer; - import net.minecraft.server.level.ServerEntity; - import net.minecraft.server.level.ServerLevel; -@@ -445,6 +446,38 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - return this.originWorld; - } - // Paper end - Entity origin API -+ // Paper start - optimise entity tracking -+ final org.spigotmc.TrackingRange.TrackingRangeType trackingRangeType = org.spigotmc.TrackingRange.getTrackingRangeType(this); -+ -+ public boolean isLegacyTrackingEntity = false; -+ -+ public final void setLegacyTrackingEntity(final boolean isLegacyTrackingEntity) { -+ this.isLegacyTrackingEntity = isLegacyTrackingEntity; -+ } -+ -+ public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet getPlayersInTrackRange() { -+ // determine highest range of passengers -+ if (this.passengers.isEmpty()) { -+ return ((ServerLevel)this.level).getChunkSource().chunkMap.playerEntityTrackerTrackMaps[this.trackingRangeType.ordinal()] -+ .getObjectsInRange(MCUtil.getCoordinateKey(this)); -+ } -+ Iterable passengers = this.getIndirectPassengers(); -+ net.minecraft.server.level.ChunkMap chunkMap = ((ServerLevel)this.level).getChunkSource().chunkMap; -+ org.spigotmc.TrackingRange.TrackingRangeType type = this.trackingRangeType; -+ int range = chunkMap.getEntityTrackerRange(type.ordinal()); -+ -+ for (Entity passenger : passengers) { -+ org.spigotmc.TrackingRange.TrackingRangeType passengerType = passenger.trackingRangeType; -+ int passengerRange = chunkMap.getEntityTrackerRange(passengerType.ordinal()); -+ if (passengerRange > range) { -+ type = passengerType; -+ range = passengerRange; -+ } -+ } -+ -+ return chunkMap.playerEntityTrackerTrackMaps[type.ordinal()].getObjectsInRange(MCUtil.getCoordinateKey(this)); -+ } -+ // Paper end - optimise entity tracking - public float getBukkitYaw() { - return this.yRot; - } -diff --git a/src/main/java/org/spigotmc/TrackingRange.java b/src/main/java/org/spigotmc/TrackingRange.java -index bb06f89a29f30144e7e2113e088a503db006a83c..e4425b242fe73d1fd2bd10c313aa16925432329f 100644 ---- a/src/main/java/org/spigotmc/TrackingRange.java -+++ b/src/main/java/org/spigotmc/TrackingRange.java -@@ -55,4 +55,48 @@ public class TrackingRange - return config.otherTrackingRange; - } - } -+ -+ // Paper start - optimise entity tracking -+ // copied from above, TODO check on update -+ public static TrackingRangeType getTrackingRangeType(Entity entity) -+ { -+ if (entity instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon) return TrackingRangeType.ENDERDRAGON; // Paper - enderdragon is exempt -+ if ( entity instanceof ServerPlayer ) -+ { -+ return TrackingRangeType.PLAYER; -+ // Paper start - Simplify and set water mobs to animal tracking range -+ } -+ switch (entity.activationType) { -+ case RAIDER: -+ case MONSTER: -+ case FLYING_MONSTER: -+ return TrackingRangeType.MONSTER; -+ case WATER: -+ case VILLAGER: -+ case ANIMAL: -+ return TrackingRangeType.ANIMAL; -+ case MISC: -+ } -+ if ( entity instanceof ItemFrame || entity instanceof Painting || entity instanceof ItemEntity || entity instanceof ExperienceOrb ) -+ // Paper end -+ { -+ return TrackingRangeType.MISC; -+ } else if (entity instanceof Display) { -+ return TrackingRangeType.DISPLAY; -+ } else -+ { -+ return TrackingRangeType.OTHER; -+ } -+ } -+ -+ public static enum TrackingRangeType { -+ PLAYER, -+ ANIMAL, -+ MONSTER, -+ MISC, -+ OTHER, -+ ENDERDRAGON, -+ DISPLAY; -+ } -+ // Paper end - optimise entity tracking - } diff --git a/patches/server/0005-Paper-Unapplied-Collision-optimisations.patch b/patches/server/0005-Paper-Unapplied-Collision-optimisations.patch deleted file mode 100644 index 4af6995..0000000 --- a/patches/server/0005-Paper-Unapplied-Collision-optimisations.patch +++ /dev/null @@ -1,4723 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Spottedleaf -Date: Mon, 4 May 2020 10:06:24 -0700 -Subject: [PATCH] Paper Unapplied - Collision optimisations - -The collision patch has been designed with the assumption that -most shapes are either a single AABB or an ArrayVoxelShape -(typical voxel bitset representation). Like previously, -single AABB shapes are treated as AABBs. Unlike previously, the -VoxelShape class has been changed to carry shape data that -ArrayVoxelShape would, except in a discrete manner rather -than abstracted away (not hidden behind DoubleList and -the poorly named DiscreteVoxelShape). - -VoxelShape now carries three important states: - 1. The voxel bitset + its sizes for the X, Y, and Z axis - 2. The voxel coordinates (represented as an array and an offset per axis) - 3. Single AABB representation, if possible - -Note that if the single AABB representation is present, -it is used instead of the voxel bitset representation as -the single AABB representation is a special case of the -voxel bitset representation and can be optimised as such. - -This effectively turns every VoxelShape instance, regardless of -actual class, into a typical voxel bitset representation. -This allows all VoxelShape operations to be optimised -for voxel bitset representations without dealing with the -abstraction and indirection that was imposed on VoxelShape -by Mojang. The patch now effectively optimises all VoxelShape -operations. Below is a list of some of the operations optimised: - - Shape merging/ORing - - Shape optimisation - - Occlusion checking - - Non-single AABB VoxelShape collisions/intersection - - Shape raytracing - - Empty VoxelShape testing - -This patch also includes optimisations for raytracing, -which mostly boil down to removing indirection caused by the -interface BlockGetter which allows chunk caching. - -diff --git a/src/main/java/io/papermc/paper/util/CachedLists.java b/src/main/java/io/papermc/paper/util/CachedLists.java -index be668387f65a633c6ac497fca632a4767a1bf3a2..e08f4e39db4ee3fed62e37364d17dcc5c5683504 100644 ---- a/src/main/java/io/papermc/paper/util/CachedLists.java -+++ b/src/main/java/io/papermc/paper/util/CachedLists.java -@@ -1,8 +1,57 @@ - package io.papermc.paper.util; - -+import net.minecraft.world.entity.Entity; -+import net.minecraft.world.phys.AABB; -+import org.bukkit.Bukkit; -+import org.bukkit.craftbukkit.util.UnsafeList; -+import java.util.List; -+ - public final class CachedLists { - -- public static void reset() { -+ // Paper start - optimise collisions -+ static final UnsafeList TEMP_COLLISION_LIST = new UnsafeList<>(1024); -+ static boolean tempCollisionListInUse; -+ -+ public static UnsafeList getTempCollisionList() { -+ if (!Bukkit.isPrimaryThread() || tempCollisionListInUse) { -+ return new UnsafeList<>(16); -+ } -+ tempCollisionListInUse = true; -+ return TEMP_COLLISION_LIST; -+ } -+ -+ public static void returnTempCollisionList(List list) { -+ if (list != TEMP_COLLISION_LIST) { -+ return; -+ } -+ ((UnsafeList)list).setSize(0); -+ tempCollisionListInUse = false; -+ } - -+ static final UnsafeList TEMP_GET_ENTITIES_LIST = new UnsafeList<>(1024); -+ static boolean tempGetEntitiesListInUse; -+ -+ public static UnsafeList getTempGetEntitiesList() { -+ if (!Bukkit.isPrimaryThread() || tempGetEntitiesListInUse) { -+ return new UnsafeList<>(16); -+ } -+ tempGetEntitiesListInUse = true; -+ return TEMP_GET_ENTITIES_LIST; -+ } -+ -+ public static void returnTempGetEntitiesList(List list) { -+ if (list != TEMP_GET_ENTITIES_LIST) { -+ return; -+ } -+ ((UnsafeList)list).setSize(0); -+ tempGetEntitiesListInUse = false; -+ } -+ // Paper end - optimise collisions -+ -+ public static void reset() { -+ // Paper start - optimise collisions -+ TEMP_COLLISION_LIST.completeReset(); -+ TEMP_GET_ENTITIES_LIST.completeReset(); -+ // Paper end - optimise collisions - } - } -diff --git a/src/main/java/io/papermc/paper/util/CollisionUtil.java b/src/main/java/io/papermc/paper/util/CollisionUtil.java -new file mode 100644 -index 0000000000000000000000000000000000000000..614a3de568ec1113f445dce37b46863f3cc1c741 ---- /dev/null -+++ b/src/main/java/io/papermc/paper/util/CollisionUtil.java -@@ -0,0 +1,1851 @@ -+package io.papermc.paper.util; -+ -+import io.papermc.paper.util.collisions.CachedShapeData; -+import it.unimi.dsi.fastutil.doubles.DoubleArrayList; -+import it.unimi.dsi.fastutil.doubles.DoubleList; -+import net.minecraft.core.BlockPos; -+import net.minecraft.core.Direction; -+import net.minecraft.server.level.ServerChunkCache; -+import net.minecraft.util.Mth; -+import net.minecraft.world.entity.Entity; -+import net.minecraft.world.item.Item; -+import net.minecraft.world.level.CollisionGetter; -+import net.minecraft.world.level.EntityGetter; -+import net.minecraft.world.level.Level; -+import net.minecraft.world.level.block.Blocks; -+import net.minecraft.world.level.block.state.BlockState; -+import net.minecraft.world.level.border.WorldBorder; -+import net.minecraft.world.level.chunk.ChunkAccess; -+import net.minecraft.world.level.chunk.status.ChunkStatus; -+import net.minecraft.world.level.chunk.LevelChunkSection; -+import net.minecraft.world.level.chunk.PalettedContainer; -+import net.minecraft.world.level.material.FluidState; -+import net.minecraft.world.phys.AABB; -+import net.minecraft.world.phys.Vec3; -+import net.minecraft.world.phys.shapes.ArrayVoxelShape; -+import net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape; -+import net.minecraft.world.phys.shapes.BooleanOp; -+import net.minecraft.world.phys.shapes.CollisionContext; -+import net.minecraft.world.phys.shapes.DiscreteVoxelShape; -+import net.minecraft.world.phys.shapes.EntityCollisionContext; -+import net.minecraft.world.phys.shapes.OffsetDoubleList; -+import net.minecraft.world.phys.shapes.Shapes; -+import net.minecraft.world.phys.shapes.VoxelShape; -+import java.util.Arrays; -+import java.util.List; -+import java.util.function.BiPredicate; -+import java.util.function.Predicate; -+ -+public final class CollisionUtil { -+ -+ public static final double COLLISION_EPSILON = 1.0E-7; -+ public static final DoubleArrayList ZERO_ONE = DoubleArrayList.wrap(new double[] { 0.0, 1.0 }); -+ -+ public static boolean isSpecialCollidingBlock(final net.minecraft.world.level.block.state.BlockBehaviour.BlockStateBase block) { -+ return block.hasLargeCollisionShape() || block.getBlock() == Blocks.MOVING_PISTON; -+ } -+ -+ public static boolean isEmpty(final AABB aabb) { -+ return (aabb.maxX - aabb.minX) < COLLISION_EPSILON || (aabb.maxY - aabb.minY) < COLLISION_EPSILON || (aabb.maxZ - aabb.minZ) < COLLISION_EPSILON; -+ } -+ -+ public static boolean isEmpty(final double minX, final double minY, final double minZ, -+ final double maxX, final double maxY, final double maxZ) { -+ return (maxX - minX) < COLLISION_EPSILON || (maxY - minY) < COLLISION_EPSILON || (maxZ - minZ) < COLLISION_EPSILON; -+ } -+ -+ public static AABB getBoxForChunk(final int chunkX, final int chunkZ) { -+ double x = (double)(chunkX << 4); -+ double z = (double)(chunkZ << 4); -+ // use a bounding box bigger than the chunk to prevent entities from entering it on move -+ return new AABB(x - 3*COLLISION_EPSILON, Double.NEGATIVE_INFINITY, z - 3*COLLISION_EPSILON, -+ x + (16.0 + 3*COLLISION_EPSILON), Double.POSITIVE_INFINITY, z + (16.0 + 3*COLLISION_EPSILON), false); -+ } -+ -+ /* -+ A couple of rules for VoxelShape collisions: -+ Two shapes only intersect if they are actually more than EPSILON units into each other. This also applies to movement -+ checks. -+ If the two shapes strictly collide, then the return value of a collide call will return a value in the opposite -+ direction of the source move. However, this value will not be greater in magnitude than EPSILON. Collision code -+ will automatically round it to 0. -+ */ -+ -+ public static boolean voxelShapeIntersect(final double minX1, final double minY1, final double minZ1, final double maxX1, -+ final double maxY1, final double maxZ1, final double minX2, final double minY2, -+ final double minZ2, final double maxX2, final double maxY2, final double maxZ2) { -+ return (minX1 - maxX2) < -COLLISION_EPSILON && (maxX1 - minX2) > COLLISION_EPSILON && -+ (minY1 - maxY2) < -COLLISION_EPSILON && (maxY1 - minY2) > COLLISION_EPSILON && -+ (minZ1 - maxZ2) < -COLLISION_EPSILON && (maxZ1 - minZ2) > COLLISION_EPSILON; -+ } -+ -+ public static boolean voxelShapeIntersect(final AABB box, final double minX, final double minY, final double minZ, -+ final double maxX, final double maxY, final double maxZ) { -+ return (box.minX - maxX) < -COLLISION_EPSILON && (box.maxX - minX) > COLLISION_EPSILON && -+ (box.minY - maxY) < -COLLISION_EPSILON && (box.maxY - minY) > COLLISION_EPSILON && -+ (box.minZ - maxZ) < -COLLISION_EPSILON && (box.maxZ - minZ) > COLLISION_EPSILON; -+ } -+ -+ public static boolean voxelShapeIntersect(final AABB box1, final AABB box2) { -+ return (box1.minX - box2.maxX) < -COLLISION_EPSILON && (box1.maxX - box2.minX) > COLLISION_EPSILON && -+ (box1.minY - box2.maxY) < -COLLISION_EPSILON && (box1.maxY - box2.minY) > COLLISION_EPSILON && -+ (box1.minZ - box2.maxZ) < -COLLISION_EPSILON && (box1.maxZ - box2.minZ) > COLLISION_EPSILON; -+ } -+ -+ // assume !isEmpty(target) && abs(source_move) >= COLLISION_EPSILON -+ public static double collideX(final AABB target, final AABB source, final double source_move) { -+ if ((source.minY - target.maxY) < -COLLISION_EPSILON && (source.maxY - target.minY) > COLLISION_EPSILON && -+ (source.minZ - target.maxZ) < -COLLISION_EPSILON && (source.maxZ - target.minZ) > COLLISION_EPSILON) { -+ if (source_move >= 0.0) { -+ final double max_move = target.minX - source.maxX; // < 0.0 if no strict collision -+ if (max_move < -COLLISION_EPSILON) { -+ return source_move; -+ } -+ return Math.min(max_move, source_move); -+ } else { -+ final double max_move = target.maxX - source.minX; // > 0.0 if no strict collision -+ if (max_move > COLLISION_EPSILON) { -+ return source_move; -+ } -+ return Math.max(max_move, source_move); -+ } -+ } -+ return source_move; -+ } -+ -+ // assume !isEmpty(target) && abs(source_move) >= COLLISION_EPSILON -+ public static double collideY(final AABB target, final AABB source, final double source_move) { -+ if ((source.minX - target.maxX) < -COLLISION_EPSILON && (source.maxX - target.minX) > COLLISION_EPSILON && -+ (source.minZ - target.maxZ) < -COLLISION_EPSILON && (source.maxZ - target.minZ) > COLLISION_EPSILON) { -+ if (source_move >= 0.0) { -+ final double max_move = target.minY - source.maxY; // < 0.0 if no strict collision -+ if (max_move < -COLLISION_EPSILON) { -+ return source_move; -+ } -+ return Math.min(max_move, source_move); -+ } else { -+ final double max_move = target.maxY - source.minY; // > 0.0 if no strict collision -+ if (max_move > COLLISION_EPSILON) { -+ return source_move; -+ } -+ return Math.max(max_move, source_move); -+ } -+ } -+ return source_move; -+ } -+ -+ // assume !isEmpty(target) && abs(source_move) >= COLLISION_EPSILON -+ public static double collideZ(final AABB target, final AABB source, final double source_move) { -+ if ((source.minX - target.maxX) < -COLLISION_EPSILON && (source.maxX - target.minX) > COLLISION_EPSILON && -+ (source.minY - target.maxY) < -COLLISION_EPSILON && (source.maxY - target.minY) > COLLISION_EPSILON) { -+ if (source_move >= 0.0) { -+ final double max_move = target.minZ - source.maxZ; // < 0.0 if no strict collision -+ if (max_move < -COLLISION_EPSILON) { -+ return source_move; -+ } -+ return Math.min(max_move, source_move); -+ } else { -+ final double max_move = target.maxZ - source.minZ; // > 0.0 if no strict collision -+ if (max_move > COLLISION_EPSILON) { -+ return source_move; -+ } -+ return Math.max(max_move, source_move); -+ } -+ } -+ return source_move; -+ } -+ -+ // startIndex and endIndex inclusive -+ // assumes indices are in range of array -+ private static int findFloor(final double[] values, final double value, int startIndex, int endIndex) { -+ do { -+ final int middle = (startIndex + endIndex) >>> 1; -+ final double middleVal = values[middle]; -+ -+ if (value < middleVal) { -+ endIndex = middle - 1; -+ } else { -+ startIndex = middle + 1; -+ } -+ } while (startIndex <= endIndex); -+ -+ return startIndex - 1; -+ } -+ -+ public static boolean voxelShapeIntersectNoEmpty(final VoxelShape voxel, final AABB aabb) { -+ if (voxel.isEmpty()) { -+ return false; -+ } -+ -+ // note: this function assumes that for any i in coords that coord[i + 1] - coord[i] > COLLISION_EPSILON is true -+ -+ // offsets that should be applied to coords -+ final double off_x = voxel.offsetX(); -+ final double off_y = voxel.offsetY(); -+ final double off_z = voxel.offsetZ(); -+ -+ final double[] coords_x = voxel.rootCoordinatesX(); -+ final double[] coords_y = voxel.rootCoordinatesY(); -+ final double[] coords_z = voxel.rootCoordinatesZ(); -+ -+ final CachedShapeData cached_shape_data = voxel.getCachedVoxelData(); -+ -+ // note: size = coords.length - 1 -+ final int size_x = cached_shape_data.sizeX(); -+ final int size_y = cached_shape_data.sizeY(); -+ final int size_z = cached_shape_data.sizeZ(); -+ -+ // note: voxel bitset with set index (x, y, z) indicates that -+ // an AABB(coords_x[x], coords_y[y], coords_z[z], coords_x[x + 1], coords_y[y + 1], coords_z[z + 1]) -+ // is collidable. this is the fundamental principle of operation for the voxel collision operation -+ -+ // note: we should be offsetting coords, but we can also just subtract from source as well - which is -+ // a win in terms of ops / simplicity (see findFloor, allows us to not modify coords for that) -+ // note: for intersection, one we find the floor of the min we can use that as the start index -+ // for the next check as source max >= source min -+ // note: we can fast check intersection on the two other axis by seeing if the min index is >= size, -+ // as this implies that coords[coords.length - 1] < source min -+ // we can also fast check by seeing if max index is < 0, as this implies that coords[0] > source max -+ -+ final int floor_min_x = Math.max( -+ 0, -+ findFloor(coords_x, (aabb.minX - off_x) + COLLISION_EPSILON, 0, size_x) -+ ); -+ if (floor_min_x >= size_x) { -+ // cannot intersect -+ return false; -+ } -+ -+ final int ceil_max_x = Math.min( -+ size_x, -+ findFloor(coords_x, (aabb.maxX - off_x) - COLLISION_EPSILON, floor_min_x, size_x) + 1 -+ ); -+ if (floor_min_x >= ceil_max_x) { -+ // cannot intersect -+ return false; -+ } -+ -+ final int floor_min_y = Math.max( -+ 0, -+ findFloor(coords_y, (aabb.minY - off_y) + COLLISION_EPSILON, 0, size_y) -+ ); -+ if (floor_min_y >= size_y) { -+ // cannot intersect -+ return false; -+ } -+ -+ final int ceil_max_y = Math.min( -+ size_y, -+ findFloor(coords_y, (aabb.maxY - off_y) - COLLISION_EPSILON, floor_min_y, size_y) + 1 -+ ); -+ if (floor_min_y >= ceil_max_y) { -+ // cannot intersect -+ return false; -+ } -+ -+ final int floor_min_z = Math.max( -+ 0, -+ findFloor(coords_z, (aabb.minZ - off_z) + COLLISION_EPSILON, 0, size_z) -+ ); -+ if (floor_min_z >= size_z) { -+ // cannot intersect -+ return false; -+ } -+ -+ final int ceil_max_z = Math.min( -+ size_z, -+ findFloor(coords_z, (aabb.maxZ - off_z) - COLLISION_EPSILON, floor_min_z, size_z) + 1 -+ ); -+ if (floor_min_z >= ceil_max_z) { -+ // cannot intersect -+ return false; -+ } -+ -+ final long[] bitset = cached_shape_data.voxelSet(); -+ -+ // check bitset to check if any shapes in range are full -+ -+ final int mul_x = size_y*size_z; -+ for (int curr_x = floor_min_x; curr_x < ceil_max_x; ++curr_x) { -+ for (int curr_y = floor_min_y; curr_y < ceil_max_y; ++curr_y) { -+ for (int curr_z = floor_min_z; curr_z < ceil_max_z; ++curr_z) { -+ final int index = curr_z + curr_y*size_z + curr_x*mul_x; -+ // note: JLS states long shift operators ANDS shift by 63 -+ if ((bitset[index >>> 6] & (1L << index)) != 0L) { -+ return true; -+ } -+ } -+ } -+ } -+ -+ return false; -+ } -+ -+ // assume !target.isEmpty() && abs(source_move) >= COLLISION_EPSILON -+ public static double collideX(final VoxelShape target, final AABB source, final double source_move) { -+ final AABB single_aabb = target.getSingleAABBRepresentation(); -+ if (single_aabb != null) { -+ return collideX(single_aabb, source, source_move); -+ } -+ // note: this function assumes that for any i in coords that coord[i + 1] - coord[i] > COLLISION_EPSILON is true -+ -+ // offsets that should be applied to coords -+ final double off_x = target.offsetX(); -+ final double off_y = target.offsetY(); -+ final double off_z = target.offsetZ(); -+ -+ final double[] coords_x = target.rootCoordinatesX(); -+ final double[] coords_y = target.rootCoordinatesY(); -+ final double[] coords_z = target.rootCoordinatesZ(); -+ -+ final CachedShapeData cached_shape_data = target.getCachedVoxelData(); -+ -+ // note: size = coords.length - 1 -+ final int size_x = cached_shape_data.sizeX(); -+ final int size_y = cached_shape_data.sizeY(); -+ final int size_z = cached_shape_data.sizeZ(); -+ -+ // note: voxel bitset with set index (x, y, z) indicates that -+ // an AABB(coords_x[x], coords_y[y], coords_z[z], coords_x[x + 1], coords_y[y + 1], coords_z[z + 1]) -+ // is collidable. this is the fundamental principle of operation for the voxel collision operation -+ -+ -+ // note: we should be offsetting coords, but we can also just subtract from source as well - which is -+ // a win in terms of ops / simplicity (see findFloor, allows us to not modify coords for that) -+ // note: for intersection, one we find the floor of the min we can use that as the start index -+ // for the next check as source max >= source min -+ // note: we can fast check intersection on the two other axis by seeing if the min index is >= size, -+ // as this implies that coords[coords.length - 1] < source min -+ // we can also fast check by seeing if max index is < 0, as this implies that coords[0] > source max -+ -+ final int floor_min_y = Math.max( -+ 0, -+ findFloor(coords_y, (source.minY - off_y) + COLLISION_EPSILON, 0, size_y) -+ ); -+ if (floor_min_y >= size_y) { -+ // cannot intersect -+ return source_move; -+ } -+ -+ final int ceil_max_y = Math.min( -+ size_y, -+ findFloor(coords_y, (source.maxY - off_y) - COLLISION_EPSILON, floor_min_y, size_y) + 1 -+ ); -+ if (floor_min_y >= ceil_max_y) { -+ // cannot intersect -+ return source_move; -+ } -+ -+ final int floor_min_z = Math.max( -+ 0, -+ findFloor(coords_z, (source.minZ - off_z) + COLLISION_EPSILON, 0, size_z) -+ ); -+ if (floor_min_z >= size_z) { -+ // cannot intersect -+ return source_move; -+ } -+ -+ final int ceil_max_z = Math.min( -+ size_z, -+ findFloor(coords_z, (source.maxZ - off_z) - COLLISION_EPSILON, floor_min_z, size_z) + 1 -+ ); -+ if (floor_min_z >= ceil_max_z) { -+ // cannot intersect -+ return source_move; -+ } -+ -+ // index = z + y*size_z + x*(size_z*size_y) -+ -+ final long[] bitset = cached_shape_data.voxelSet(); -+ -+ if (source_move > 0.0) { -+ final double source_max = source.maxX - off_x; -+ final int ceil_max_x = findFloor( -+ coords_x, source_max - COLLISION_EPSILON, 0, size_x -+ ) + 1; // add one, we are not interested in (coords[i] + COLLISION_EPSILON) < max -+ -+ // note: only the order of the first loop matters -+ -+ // note: we cannot collide with the face at index size on the collision axis for forward movement -+ -+ final int mul_x = size_y*size_z; -+ for (int curr_x = ceil_max_x; curr_x < size_x; ++curr_x) { -+ double max_dist = coords_x[curr_x] - source_max; -+ if (max_dist >= source_move) { -+ // if we reach here, then we will never have a case where -+ // coords[curr + n] - source_max < source_move, as coords[curr + n] < coords[curr + n + 1] -+ // thus, we can return immediately -+ -+ // this optimization is important since this loop is bounded by size, and _not_ by -+ // a calculated max index based off of source_move - so it would be possible to check -+ // the whole intersected shape for collisions when we didn't need to! -+ return source_move; -+ } -+ if (max_dist >= -COLLISION_EPSILON) { // only push out by up to COLLISION_EPSILON -+ max_dist = Math.min(max_dist, source_move); -+ } -+ for (int curr_y = floor_min_y; curr_y < ceil_max_y; ++curr_y) { -+ for (int curr_z = floor_min_z; curr_z < ceil_max_z; ++curr_z) { -+ final int index = curr_z + curr_y*size_z + curr_x*mul_x; -+ // note: JLS states long shift operators ANDS shift by 63 -+ if ((bitset[index >>> 6] & (1L << index)) != 0L) { -+ return max_dist; -+ } -+ } -+ } -+ } -+ -+ return source_move; -+ } else { -+ final double source_min = source.minX - off_x; -+ final int floor_min_x = findFloor( -+ coords_x, source_min + COLLISION_EPSILON, 0, size_x -+ ); -+ -+ // note: only the order of the first loop matters -+ -+ // note: we cannot collide with the face at index 0 on the collision axis for backwards movement -+ -+ // note: we offset the collision axis by - 1 for the voxel bitset index, but use + 1 for the -+ // coordinate index as the voxelset stores whether the shape is solid for [index, index + 1] -+ // thus, we need to use the voxel index i-1 if we want to check that the face at index i is solid -+ final int mul_x = size_y*size_z; -+ for (int curr_x = floor_min_x - 1; curr_x >= 0; --curr_x) { -+ double max_dist = coords_x[curr_x + 1] - source_min; -+ if (max_dist <= source_move) { -+ // if we reach here, then we will never have a case where -+ // coords[curr + n] - source_max > source_move, as coords[curr + n] > coords[curr + n - 1] -+ // thus, we can return immediately -+ -+ // this optimization is important since this loop is possibly bounded by size, and _not_ by -+ // a calculated max index based off of source_move - so it would be possible to check -+ // the whole intersected shape for collisions when we didn't need to! -+ return source_move; -+ } -+ if (max_dist <= COLLISION_EPSILON) { // only push out by up to COLLISION_EPSILON -+ max_dist = Math.max(max_dist, source_move); -+ } -+ for (int curr_y = floor_min_y; curr_y < ceil_max_y; ++curr_y) { -+ for (int curr_z = floor_min_z; curr_z < ceil_max_z; ++curr_z) { -+ final int index = curr_z + curr_y*size_z + curr_x*mul_x; -+ // note: JLS states long shift operators ANDS shift by 63 -+ if ((bitset[index >>> 6] & (1L << index)) != 0L) { -+ return max_dist; -+ } -+ } -+ } -+ } -+ -+ return source_move; -+ } -+ } -+ -+ public static double collideY(final VoxelShape target, final AABB source, final double source_move) { -+ final AABB single_aabb = target.getSingleAABBRepresentation(); -+ if (single_aabb != null) { -+ return collideY(single_aabb, source, source_move); -+ } -+ // note: this function assumes that for any i in coords that coord[i + 1] - coord[i] > COLLISION_EPSILON is true -+ -+ // offsets that should be applied to coords -+ final double off_x = target.offsetX(); -+ final double off_y = target.offsetY(); -+ final double off_z = target.offsetZ(); -+ -+ final double[] coords_x = target.rootCoordinatesX(); -+ final double[] coords_y = target.rootCoordinatesY(); -+ final double[] coords_z = target.rootCoordinatesZ(); -+ -+ final CachedShapeData cached_shape_data = target.getCachedVoxelData(); -+ -+ // note: size = coords.length - 1 -+ final int size_x = cached_shape_data.sizeX(); -+ final int size_y = cached_shape_data.sizeY(); -+ final int size_z = cached_shape_data.sizeZ(); -+ -+ // note: voxel bitset with set index (x, y, z) indicates that -+ // an AABB(coords_x[x], coords_y[y], coords_z[z], coords_x[x + 1], coords_y[y + 1], coords_z[z + 1]) -+ // is collidable. this is the fundamental principle of operation for the voxel collision operation -+ -+ -+ // note: we should be offsetting coords, but we can also just subtract from source as well - which is -+ // a win in terms of ops / simplicity (see findFloor, allows us to not modify coords for that) -+ // note: for intersection, one we find the floor of the min we can use that as the start index -+ // for the next check as source max >= source min -+ // note: we can fast check intersection on the two other axis by seeing if the min index is >= size, -+ // as this implies that coords[coords.length - 1] < source min -+ // we can also fast check by seeing if max index is < 0, as this implies that coords[0] > source max -+ -+ final int floor_min_x = Math.max( -+ 0, -+ findFloor(coords_x, (source.minX - off_x) + COLLISION_EPSILON, 0, size_x) -+ ); -+ if (floor_min_x >= size_x) { -+ // cannot intersect -+ return source_move; -+ } -+ -+ final int ceil_max_x = Math.min( -+ size_x, -+ findFloor(coords_x, (source.maxX - off_x) - COLLISION_EPSILON, floor_min_x, size_x) + 1 -+ ); -+ if (floor_min_x >= ceil_max_x) { -+ // cannot intersect -+ return source_move; -+ } -+ -+ final int floor_min_z = Math.max( -+ 0, -+ findFloor(coords_z, (source.minZ - off_z) + COLLISION_EPSILON, 0, size_z) -+ ); -+ if (floor_min_z >= size_z) { -+ // cannot intersect -+ return source_move; -+ } -+ -+ final int ceil_max_z = Math.min( -+ size_z, -+ findFloor(coords_z, (source.maxZ - off_z) - COLLISION_EPSILON, floor_min_z, size_z) + 1 -+ ); -+ if (floor_min_z >= ceil_max_z) { -+ // cannot intersect -+ return source_move; -+ } -+ -+ // index = z + y*size_z + x*(size_z*size_y) -+ -+ final long[] bitset = cached_shape_data.voxelSet(); -+ -+ if (source_move > 0.0) { -+ final double source_max = source.maxY - off_y; -+ final int ceil_max_y = findFloor( -+ coords_y, source_max - COLLISION_EPSILON, 0, size_y -+ ) + 1; // add one, we are not interested in (coords[i] + COLLISION_EPSILON) < max -+ -+ // note: only the order of the first loop matters -+ -+ // note: we cannot collide with the face at index size on the collision axis for forward movement -+ -+ final int mul_x = size_y*size_z; -+ for (int curr_y = ceil_max_y; curr_y < size_y; ++curr_y) { -+ double max_dist = coords_y[curr_y] - source_max; -+ if (max_dist >= source_move) { -+ // if we reach here, then we will never have a case where -+ // coords[curr + n] - source_max < source_move, as coords[curr + n] < coords[curr + n + 1] -+ // thus, we can return immediately -+ -+ // this optimization is important since this loop is bounded by size, and _not_ by -+ // a calculated max index based off of source_move - so it would be possible to check -+ // the whole intersected shape for collisions when we didn't need to! -+ return source_move; -+ } -+ if (max_dist >= -COLLISION_EPSILON) { // only push out by up to COLLISION_EPSILON -+ max_dist = Math.min(max_dist, source_move); -+ } -+ for (int curr_x = floor_min_x; curr_x < ceil_max_x; ++curr_x) { -+ for (int curr_z = floor_min_z; curr_z < ceil_max_z; ++curr_z) { -+ final int index = curr_z + curr_y*size_z + curr_x*mul_x; -+ // note: JLS states long shift operators ANDS shift by 63 -+ if ((bitset[index >>> 6] & (1L << index)) != 0L) { -+ return max_dist; -+ } -+ } -+ } -+ } -+ -+ return source_move; -+ } else { -+ final double source_min = source.minY - off_y; -+ final int floor_min_y = findFloor( -+ coords_y, source_min + COLLISION_EPSILON, 0, size_y -+ ); -+ -+ // note: only the order of the first loop matters -+ -+ // note: we cannot collide with the face at index 0 on the collision axis for backwards movement -+ -+ // note: we offset the collision axis by - 1 for the voxel bitset index, but use + 1 for the -+ // coordinate index as the voxelset stores whether the shape is solid for [index, index + 1] -+ // thus, we need to use the voxel index i-1 if we want to check that the face at index i is solid -+ final int mul_x = size_y*size_z; -+ for (int curr_y = floor_min_y - 1; curr_y >= 0; --curr_y) { -+ double max_dist = coords_y[curr_y + 1] - source_min; -+ if (max_dist <= source_move) { -+ // if we reach here, then we will never have a case where -+ // coords[curr + n] - source_max > source_move, as coords[curr + n] > coords[curr + n - 1] -+ // thus, we can return immediately -+ -+ // this optimization is important since this loop is possibly bounded by size, and _not_ by -+ // a calculated max index based off of source_move - so it would be possible to check -+ // the whole intersected shape for collisions when we didn't need to! -+ return source_move; -+ } -+ if (max_dist <= COLLISION_EPSILON) { // only push out by up to COLLISION_EPSILON -+ max_dist = Math.max(max_dist, source_move); -+ } -+ for (int curr_x = floor_min_x; curr_x < ceil_max_x; ++curr_x) { -+ for (int curr_z = floor_min_z; curr_z < ceil_max_z; ++curr_z) { -+ final int index = curr_z + curr_y*size_z + curr_x*mul_x; -+ // note: JLS states long shift operators ANDS shift by 63 -+ if ((bitset[index >>> 6] & (1L << index)) != 0L) { -+ return max_dist; -+ } -+ } -+ } -+ } -+ -+ return source_move; -+ } -+ } -+ -+ public static double collideZ(final VoxelShape target, final AABB source, final double source_move) { -+ final AABB single_aabb = target.getSingleAABBRepresentation(); -+ if (single_aabb != null) { -+ return collideZ(single_aabb, source, source_move); -+ } -+ // note: this function assumes that for any i in coords that coord[i + 1] - coord[i] > COLLISION_EPSILON is true -+ -+ // offsets that should be applied to coords -+ final double off_x = target.offsetX(); -+ final double off_y = target.offsetY(); -+ final double off_z = target.offsetZ(); -+ -+ final double[] coords_x = target.rootCoordinatesX(); -+ final double[] coords_y = target.rootCoordinatesY(); -+ final double[] coords_z = target.rootCoordinatesZ(); -+ -+ final CachedShapeData cached_shape_data = target.getCachedVoxelData(); -+ -+ // note: size = coords.length - 1 -+ final int size_x = cached_shape_data.sizeX(); -+ final int size_y = cached_shape_data.sizeY(); -+ final int size_z = cached_shape_data.sizeZ(); -+ -+ // note: voxel bitset with set index (x, y, z) indicates that -+ // an AABB(coords_x[x], coords_y[y], coords_z[z], coords_x[x + 1], coords_y[y + 1], coords_z[z + 1]) -+ // is collidable. this is the fundamental principle of operation for the voxel collision operation -+ -+ -+ // note: we should be offsetting coords, but we can also just subtract from source as well - which is -+ // a win in terms of ops / simplicity (see findFloor, allows us to not modify coords for that) -+ // note: for intersection, one we find the floor of the min we can use that as the start index -+ // for the next check as source max >= source min -+ // note: we can fast check intersection on the two other axis by seeing if the min index is >= size, -+ // as this implies that coords[coords.length - 1] < source min -+ // we can also fast check by seeing if max index is < 0, as this implies that coords[0] > source max -+ -+ final int floor_min_x = Math.max( -+ 0, -+ findFloor(coords_x, (source.minX - off_x) + COLLISION_EPSILON, 0, size_x) -+ ); -+ if (floor_min_x >= size_x) { -+ // cannot intersect -+ return source_move; -+ } -+ -+ final int ceil_max_x = Math.min( -+ size_x, -+ findFloor(coords_x, (source.maxX - off_x) - COLLISION_EPSILON, floor_min_x, size_x) + 1 -+ ); -+ if (floor_min_x >= ceil_max_x) { -+ // cannot intersect -+ return source_move; -+ } -+ -+ final int floor_min_y = Math.max( -+ 0, -+ findFloor(coords_y, (source.minY - off_y) + COLLISION_EPSILON, 0, size_y) -+ ); -+ if (floor_min_y >= size_y) { -+ // cannot intersect -+ return source_move; -+ } -+ -+ final int ceil_max_y = Math.min( -+ size_y, -+ findFloor(coords_y, (source.maxY - off_y) - COLLISION_EPSILON, floor_min_y, size_y) + 1 -+ ); -+ if (floor_min_y >= ceil_max_y) { -+ // cannot intersect -+ return source_move; -+ } -+ -+ // index = z + y*size_z + x*(size_z*size_y) -+ -+ final long[] bitset = cached_shape_data.voxelSet(); -+ -+ if (source_move > 0.0) { -+ final double source_max = source.maxZ - off_z; -+ final int ceil_max_z = findFloor( -+ coords_z, source_max - COLLISION_EPSILON, 0, size_z -+ ) + 1; // add one, we are not interested in (coords[i] + COLLISION_EPSILON) < max -+ -+ // note: only the order of the first loop matters -+ -+ // note: we cannot collide with the face at index size on the collision axis for forward movement -+ -+ final int mul_x = size_y*size_z; -+ for (int curr_z = ceil_max_z; curr_z < size_z; ++curr_z) { -+ double max_dist = coords_z[curr_z] - source_max; -+ if (max_dist >= source_move) { -+ // if we reach here, then we will never have a case where -+ // coords[curr + n] - source_max < source_move, as coords[curr + n] < coords[curr + n + 1] -+ // thus, we can return immediately -+ -+ // this optimization is important since this loop is bounded by size, and _not_ by -+ // a calculated max index based off of source_move - so it would be possible to check -+ // the whole intersected shape for collisions when we didn't need to! -+ return source_move; -+ } -+ if (max_dist >= -COLLISION_EPSILON) { // only push out by up to COLLISION_EPSILON -+ max_dist = Math.min(max_dist, source_move); -+ } -+ for (int curr_x = floor_min_x; curr_x < ceil_max_x; ++curr_x) { -+ for (int curr_y = floor_min_y; curr_y < ceil_max_y; ++curr_y) { -+ final int index = curr_z + curr_y*size_z + curr_x*mul_x; -+ // note: JLS states long shift operators ANDS shift by 63 -+ if ((bitset[index >>> 6] & (1L << index)) != 0L) { -+ return max_dist; -+ } -+ } -+ } -+ } -+ -+ return source_move; -+ } else { -+ final double source_min = source.minZ - off_z; -+ final int floor_min_z = findFloor( -+ coords_z, source_min + COLLISION_EPSILON, 0, size_z -+ ); -+ -+ // note: only the order of the first loop matters -+ -+ // note: we cannot collide with the face at index 0 on the collision axis for backwards movement -+ -+ // note: we offset the collision axis by - 1 for the voxel bitset index, but use + 1 for the -+ // coordinate index as the voxelset stores whether the shape is solid for [index, index + 1] -+ // thus, we need to use the voxel index i-1 if we want to check that the face at index i is solid -+ final int mul_x = size_y*size_z; -+ for (int curr_z = floor_min_z - 1; curr_z >= 0; --curr_z) { -+ double max_dist = coords_z[curr_z + 1] - source_min; -+ if (max_dist <= source_move) { -+ // if we reach here, then we will never have a case where -+ // coords[curr + n] - source_max > source_move, as coords[curr + n] > coords[curr + n - 1] -+ // thus, we can return immediately -+ -+ // this optimization is important since this loop is possibly bounded by size, and _not_ by -+ // a calculated max index based off of source_move - so it would be possible to check -+ // the whole intersected shape for collisions when we didn't need to! -+ return source_move; -+ } -+ if (max_dist <= COLLISION_EPSILON) { // only push out by up to COLLISION_EPSILON -+ max_dist = Math.max(max_dist, source_move); -+ } -+ for (int curr_x = floor_min_x; curr_x < ceil_max_x; ++curr_x) { -+ for (int curr_y = floor_min_y; curr_y < ceil_max_y; ++curr_y) { -+ final int index = curr_z + curr_y*size_z + curr_x*mul_x; -+ // note: JLS states long shift operators ANDS shift by 63 -+ if ((bitset[index >>> 6] & (1L << index)) != 0L) { -+ return max_dist; -+ } -+ } -+ } -+ } -+ -+ return source_move; -+ } -+ } -+ -+ // does not use epsilon -+ public static boolean strictlyContains(final VoxelShape voxel, final Vec3 point) { -+ return strictlyContains(voxel, point.x, point.y, point.z); -+ } -+ -+ // does not use epsilon -+ public static boolean strictlyContains(final VoxelShape voxel, double x, double y, double z) { -+ final AABB single_aabb = voxel.getSingleAABBRepresentation(); -+ if (single_aabb != null) { -+ return single_aabb.contains(x, y, z); -+ } -+ -+ if (voxel.isEmpty()) { -+ // bitset is clear, no point in searching -+ return false; -+ } -+ -+ // offset input -+ x -= voxel.offsetX(); -+ y -= voxel.offsetY(); -+ z -= voxel.offsetZ(); -+ -+ final double[] coords_x = voxel.rootCoordinatesX(); -+ final double[] coords_y = voxel.rootCoordinatesY(); -+ final double[] coords_z = voxel.rootCoordinatesZ(); -+ -+ final CachedShapeData cached_shape_data = voxel.getCachedVoxelData(); -+ -+ // note: size = coords.length - 1 -+ final int size_x = cached_shape_data.sizeX(); -+ final int size_y = cached_shape_data.sizeY(); -+ final int size_z = cached_shape_data.sizeZ(); -+ -+ // note: should mirror AABB#contains, which is that for any point X that X >= min and X < max. -+ // specifically, it cannot collide on the max bounds of the shape -+ -+ final int index_x = findFloor(coords_x, x, 0, size_x); -+ if (index_x < 0 || index_x >= size_x) { -+ return false; -+ } -+ -+ final int index_y = findFloor(coords_y, y, 0, size_y); -+ if (index_y < 0 || index_y >= size_y) { -+ return false; -+ } -+ -+ final int index_z = findFloor(coords_z, z, 0, size_z); -+ if (index_z < 0 || index_z >= size_z) { -+ return false; -+ } -+ -+ // index = z + y*size_z + x*(size_z*size_y) -+ -+ final int index = index_z + index_y*size_z + index_x*(size_z*size_y); -+ -+ final long[] bitset = cached_shape_data.voxelSet(); -+ -+ return (bitset[index >>> 6] & (1L << index)) != 0L; -+ } -+ -+ private static int makeBitset(final boolean ft, final boolean tf, final boolean tt) { -+ // idx ff -> 0 -+ // idx ft -> 1 -+ // idx tf -> 2 -+ // idx tt -> 3 -+ return ((ft ? 1 : 0) << 1) | ((tf ? 1 : 0) << 2) | ((tt ? 1 : 0) << 3); -+ } -+ -+ private static BitSetDiscreteVoxelShape merge(final CachedShapeData shapeDataFirst, final CachedShapeData shapeDataSecond, -+ final MergedVoxelCoordinateList mergedX, final MergedVoxelCoordinateList mergedY, -+ final MergedVoxelCoordinateList mergedZ, -+ final int booleanOp) { -+ final int sizeX = mergedX.voxels; -+ final int sizeY = mergedY.voxels; -+ final int sizeZ = mergedZ.voxels; -+ -+ final long[] s1Voxels = shapeDataFirst.voxelSet(); -+ final long[] s2Voxels = shapeDataSecond.voxelSet(); -+ -+ final int s1Mul1 = shapeDataFirst.sizeZ(); -+ final int s1Mul2 = s1Mul1 * shapeDataFirst.sizeY(); -+ -+ final int s2Mul1 = shapeDataSecond.sizeZ(); -+ final int s2Mul2 = s2Mul1 * shapeDataSecond.sizeY(); -+ -+ // note: indices may contain -1, but nothing > size -+ final BitSetDiscreteVoxelShape ret = new BitSetDiscreteVoxelShape(sizeX, sizeY, sizeZ); -+ -+ boolean empty = true; -+ -+ int mergedIdx = 0; -+ for (int idxX = 0; idxX < sizeX; ++idxX) { -+ final int s1x = mergedX.firstIndices[idxX]; -+ final int s2x = mergedX.secondIndices[idxX]; -+ boolean setX = false; -+ for (int idxY = 0; idxY < sizeY; ++idxY) { -+ final int s1y = mergedY.firstIndices[idxY]; -+ final int s2y = mergedY.secondIndices[idxY]; -+ boolean setY = false; -+ for (int idxZ = 0; idxZ < sizeZ; ++idxZ) { -+ final int s1z = mergedZ.firstIndices[idxZ]; -+ final int s2z = mergedZ.secondIndices[idxZ]; -+ -+ int idx; -+ -+ final int isS1Full = (s1x | s1y | s1z) < 0 ? 0 : (int)((s1Voxels[(idx = s1z + s1y*s1Mul1 + s1x*s1Mul2) >>> 6] >>> idx) & 1L); -+ final int isS2Full = (s2x | s2y | s2z) < 0 ? 0 : (int)((s2Voxels[(idx = s2z + s2y*s2Mul1 + s2x*s2Mul2) >>> 6] >>> idx) & 1L); -+ -+ // idx ff -> 0 -+ // idx ft -> 1 -+ // idx tf -> 2 -+ // idx tt -> 3 -+ -+ final boolean res = (booleanOp & (1 << (isS2Full | (isS1Full << 1)))) != 0; -+ setY |= res; -+ setX |= res; -+ -+ if (res) { -+ empty = false; -+ // inline and optimize fill operation -+ ret.zMin = Math.min(ret.zMin, idxZ); -+ ret.zMax = Math.max(ret.zMax, idxZ + 1); -+ ret.storage.set(mergedIdx); -+ } -+ -+ ++mergedIdx; -+ } -+ if (setY) { -+ ret.yMin = Math.min(ret.yMin, idxY); -+ ret.yMax = Math.max(ret.yMax, idxY + 1); -+ } -+ } -+ if (setX) { -+ ret.xMin = Math.min(ret.xMin, idxX); -+ ret.xMax = Math.max(ret.xMax, idxX + 1); -+ } -+ } -+ -+ return empty ? null : ret; -+ } -+ -+ private static boolean isMergeEmpty(final CachedShapeData shapeDataFirst, final CachedShapeData shapeDataSecond, -+ final MergedVoxelCoordinateList mergedX, final MergedVoxelCoordinateList mergedY, -+ final MergedVoxelCoordinateList mergedZ, -+ final int booleanOp) { -+ final int sizeX = mergedX.voxels; -+ final int sizeY = mergedY.voxels; -+ final int sizeZ = mergedZ.voxels; -+ -+ final long[] s1Voxels = shapeDataFirst.voxelSet(); -+ final long[] s2Voxels = shapeDataSecond.voxelSet(); -+ -+ final int s1Mul1 = shapeDataFirst.sizeZ(); -+ final int s1Mul2 = s1Mul1 * shapeDataFirst.sizeY(); -+ -+ final int s2Mul1 = shapeDataSecond.sizeZ(); -+ final int s2Mul2 = s2Mul1 * shapeDataSecond.sizeY(); -+ -+ // note: indices may contain -1, but nothing > size -+ for (int idxX = 0; idxX < sizeX; ++idxX) { -+ final int s1x = mergedX.firstIndices[idxX]; -+ final int s2x = mergedX.secondIndices[idxX]; -+ for (int idxY = 0; idxY < sizeY; ++idxY) { -+ final int s1y = mergedY.firstIndices[idxY]; -+ final int s2y = mergedY.secondIndices[idxY]; -+ for (int idxZ = 0; idxZ < sizeZ; ++idxZ) { -+ final int s1z = mergedZ.firstIndices[idxZ]; -+ final int s2z = mergedZ.secondIndices[idxZ]; -+ -+ int idx; -+ -+ final int isS1Full = (s1x | s1y | s1z) < 0 ? 0 : (int)((s1Voxels[(idx = s1z + s1y*s1Mul1 + s1x*s1Mul2) >>> 6] >>> idx) & 1L); -+ final int isS2Full = (s2x | s2y | s2z) < 0 ? 0 : (int)((s2Voxels[(idx = s2z + s2y*s2Mul1 + s2x*s2Mul2) >>> 6] >>> idx) & 1L); -+ -+ // idx ff -> 0 -+ // idx ft -> 1 -+ // idx tf -> 2 -+ // idx tt -> 3 -+ -+ final boolean res = (booleanOp & (1 << (isS2Full | (isS1Full << 1)))) != 0; -+ -+ if (res) { -+ return false; -+ } -+ } -+ } -+ } -+ -+ return true; -+ } -+ -+ public static VoxelShape joinOptimized(final VoxelShape first, final VoxelShape second, final BooleanOp operator) { -+ return joinUnoptimized(first, second, operator).optimize(); -+ } -+ -+ public static VoxelShape joinUnoptimized(final VoxelShape first, final VoxelShape second, final BooleanOp operator) { -+ final boolean ff = operator.apply(false, false); -+ if (ff) { -+ // technically, should be an infinite box but that's clearly an error -+ throw new UnsupportedOperationException("Ambiguous operator: (false, false) -> true"); -+ } -+ -+ final boolean tt = operator.apply(true, true); -+ -+ if (first == second) { -+ return tt ? first : Shapes.empty(); -+ } -+ -+ final boolean ft = operator.apply(false, true); -+ final boolean tf = operator.apply(true, false); -+ -+ if (first.isEmpty()) { -+ return ft ? second : Shapes.empty(); -+ } -+ if (second.isEmpty()) { -+ return tf ? first : Shapes.empty(); -+ } -+ -+ if (!tt) { -+ // try to check for no intersection, since tt = false -+ final AABB aabbF = first.getSingleAABBRepresentation(); -+ final AABB aabbS = second.getSingleAABBRepresentation(); -+ -+ final boolean intersect; -+ -+ final boolean hasAABBF = aabbF != null; -+ final boolean hasAABBS = aabbS != null; -+ if (hasAABBF | hasAABBS) { -+ if (hasAABBF & hasAABBS) { -+ intersect = voxelShapeIntersect(aabbF, aabbS); -+ } else if (hasAABBF) { -+ intersect = voxelShapeIntersectNoEmpty(second, aabbF); -+ } else { -+ intersect = voxelShapeIntersectNoEmpty(first, aabbS); -+ } -+ } else { -+ // expect cached bounds -+ intersect = voxelShapeIntersect(first.bounds(), second.bounds()); -+ } -+ -+ if (!intersect) { -+ if (!tf & !ft) { -+ return Shapes.empty(); -+ } -+ if (!tf | !ft) { -+ return tf ? first : second; -+ } -+ } -+ } -+ -+ final MergedVoxelCoordinateList mergedX = MergedVoxelCoordinateList.merge( -+ first.rootCoordinatesX(), first.offsetX(), -+ second.rootCoordinatesX(), second.offsetX(), -+ ft, tf -+ ); -+ if (mergedX == MergedVoxelCoordinateList.EMPTY) { -+ return Shapes.empty(); -+ } -+ final MergedVoxelCoordinateList mergedY = MergedVoxelCoordinateList.merge( -+ first.rootCoordinatesY(), first.offsetY(), -+ second.rootCoordinatesY(), second.offsetY(), -+ ft, tf -+ ); -+ if (mergedY == MergedVoxelCoordinateList.EMPTY) { -+ return Shapes.empty(); -+ } -+ final MergedVoxelCoordinateList mergedZ = MergedVoxelCoordinateList.merge( -+ first.rootCoordinatesZ(), first.offsetZ(), -+ second.rootCoordinatesZ(), second.offsetZ(), -+ ft, tf -+ ); -+ if (mergedZ == MergedVoxelCoordinateList.EMPTY) { -+ return Shapes.empty(); -+ } -+ -+ final CachedShapeData shapeDataFirst = first.getCachedVoxelData(); -+ final CachedShapeData shapeDataSecond = second.getCachedVoxelData(); -+ -+ final BitSetDiscreteVoxelShape mergedShape = merge( -+ shapeDataFirst, shapeDataSecond, -+ mergedX, mergedY, mergedZ, -+ makeBitset(ft, tf, tt) -+ ); -+ -+ if (mergedShape == null) { -+ return Shapes.empty(); -+ } -+ -+ return new ArrayVoxelShape( -+ mergedShape, mergedX.wrapCoords(), mergedY.wrapCoords(), mergedZ.wrapCoords() -+ ); -+ } -+ -+ public static boolean isJoinNonEmpty(final VoxelShape first, final VoxelShape second, final BooleanOp operator) { -+ final boolean ff = operator.apply(false, false); -+ if (ff) { -+ // technically, should be an infinite box but that's clearly an error -+ throw new UnsupportedOperationException("Ambiguous operator: (false, false) -> true"); -+ } -+ final boolean firstEmpty = first.isEmpty(); -+ final boolean secondEmpty = second.isEmpty(); -+ if (firstEmpty | secondEmpty) { -+ return operator.apply(!firstEmpty, !secondEmpty); -+ } -+ -+ final boolean tt = operator.apply(true, true); -+ -+ if (first == second) { -+ return tt; -+ } -+ -+ final boolean ft = operator.apply(false, true); -+ final boolean tf = operator.apply(true, false); -+ -+ // try to check intersection -+ final AABB aabbF = first.getSingleAABBRepresentation(); -+ final AABB aabbS = second.getSingleAABBRepresentation(); -+ -+ final boolean intersect; -+ -+ final boolean hasAABBF = aabbF != null; -+ final boolean hasAABBS = aabbS != null; -+ if (hasAABBF | hasAABBS) { -+ if (hasAABBF & hasAABBS) { -+ intersect = voxelShapeIntersect(aabbF, aabbS); -+ } else if (hasAABBF) { -+ intersect = voxelShapeIntersectNoEmpty(second, aabbF); -+ } else { -+ // hasAABBS -> true -+ intersect = voxelShapeIntersectNoEmpty(first, aabbS); -+ } -+ -+ if (!intersect) { -+ // is only non-empty if we take from first or second, as there is no overlap AND both shapes are non-empty -+ return tf | ft; -+ } else if (tt) { -+ // intersect = true && tt = true -> non-empty merged shape -+ return true; -+ } -+ } else { -+ // expect cached bounds -+ intersect = voxelShapeIntersect(first.bounds(), second.bounds()); -+ if (!intersect) { -+ // is only non-empty if we take from first or second, as there is no intersection -+ return tf | ft; -+ } -+ } -+ -+ final MergedVoxelCoordinateList mergedX = MergedVoxelCoordinateList.merge( -+ first.rootCoordinatesX(), first.offsetX(), -+ second.rootCoordinatesX(), second.offsetX(), -+ ft, tf -+ ); -+ if (mergedX == MergedVoxelCoordinateList.EMPTY) { -+ return false; -+ } -+ final MergedVoxelCoordinateList mergedY = MergedVoxelCoordinateList.merge( -+ first.rootCoordinatesY(), first.offsetY(), -+ second.rootCoordinatesY(), second.offsetY(), -+ ft, tf -+ ); -+ if (mergedY == MergedVoxelCoordinateList.EMPTY) { -+ return false; -+ } -+ final MergedVoxelCoordinateList mergedZ = MergedVoxelCoordinateList.merge( -+ first.rootCoordinatesZ(), first.offsetZ(), -+ second.rootCoordinatesZ(), second.offsetZ(), -+ ft, tf -+ ); -+ if (mergedZ == MergedVoxelCoordinateList.EMPTY) { -+ return false; -+ } -+ -+ final CachedShapeData shapeDataFirst = first.getCachedVoxelData(); -+ final CachedShapeData shapeDataSecond = second.getCachedVoxelData(); -+ -+ return !isMergeEmpty( -+ shapeDataFirst, shapeDataSecond, -+ mergedX, mergedY, mergedZ, -+ makeBitset(ft, tf, tt) -+ ); -+ } -+ -+ private static final class MergedVoxelCoordinateList { -+ -+ private static final int[][] SIMPLE_INDICES_CACHE = new int[64][]; -+ static { -+ for (int i = 0; i < SIMPLE_INDICES_CACHE.length; ++i) { -+ SIMPLE_INDICES_CACHE[i] = getIndices(i); -+ } -+ } -+ -+ private static final MergedVoxelCoordinateList EMPTY = new MergedVoxelCoordinateList( -+ new double[] { 0.0 }, 0.0, new int[0], new int[0], 0 -+ ); -+ -+ private static int[] getIndices(final int length) { -+ final int[] ret = new int[length]; -+ -+ for (int i = 1; i < length; ++i) { -+ ret[i] = i; -+ } -+ -+ return ret; -+ } -+ -+ // indices above voxel size are always set to -1 -+ public final double[] coordinates; -+ public final double coordinateOffset; -+ public final int[] firstIndices; -+ public final int[] secondIndices; -+ public final int voxels; -+ -+ private MergedVoxelCoordinateList(final double[] coordinates, final double coordinateOffset, -+ final int[] firstIndices, final int[] secondIndices, final int voxels) { -+ this.coordinates = coordinates; -+ this.coordinateOffset = coordinateOffset; -+ this.firstIndices = firstIndices; -+ this.secondIndices = secondIndices; -+ this.voxels = voxels; -+ } -+ -+ public DoubleList wrapCoords() { -+ if (this.coordinateOffset == 0.0) { -+ return DoubleArrayList.wrap(this.coordinates, this.voxels + 1); -+ } -+ return new OffsetDoubleList(DoubleArrayList.wrap(this.coordinates, this.voxels + 1), this.coordinateOffset); -+ } -+ -+ // assume coordinates.length > 1 -+ public static MergedVoxelCoordinateList getForSingle(final double[] coordinates, final double offset) { -+ final int voxels = coordinates.length - 1; -+ final int[] indices = voxels < SIMPLE_INDICES_CACHE.length ? SIMPLE_INDICES_CACHE[voxels] : getIndices(voxels); -+ -+ return new MergedVoxelCoordinateList(coordinates, offset, indices, indices, voxels); -+ } -+ -+ // assume coordinates.length > 1 -+ public static MergedVoxelCoordinateList merge(final double[] firstCoordinates, final double firstOffset, -+ final double[] secondCoordinates, final double secondOffset, -+ final boolean ft, final boolean tf) { -+ if (firstCoordinates == secondCoordinates && firstOffset == secondOffset) { -+ return getForSingle(firstCoordinates, firstOffset); -+ } -+ -+ final int firstCount = firstCoordinates.length; -+ final int secondCount = secondCoordinates.length; -+ -+ final int voxelsFirst = firstCount - 1; -+ final int voxelsSecond = secondCount - 1; -+ -+ final int maxCount = firstCount + secondCount; -+ -+ final double[] coordinates = new double[maxCount]; -+ final int[] firstIndices = new int[maxCount]; -+ final int[] secondIndices = new int[maxCount]; -+ -+ final boolean notTF = !tf; -+ final boolean notFT = !ft; -+ -+ int firstIndex = 0; -+ int secondIndex = 0; -+ int resultSize = 0; -+ -+ // note: operations on NaN are false -+ double last = Double.NaN; -+ -+ for (;;) { -+ final boolean noneLeftFirst = firstIndex >= firstCount; -+ final boolean noneLeftSecond = secondIndex >= secondCount; -+ -+ if ((noneLeftFirst & noneLeftSecond) | (noneLeftSecond & notTF) | (noneLeftFirst & notFT)) { -+ break; -+ } -+ -+ final boolean firstZero = firstIndex == 0; -+ final boolean secondZero = secondIndex == 0; -+ -+ final double select; -+ -+ if (noneLeftFirst) { -+ // noneLeftSecond -> false -+ // notFT -> false -+ select = secondCoordinates[secondIndex] + secondOffset; -+ ++secondIndex; -+ } else if (noneLeftSecond) { -+ // noneLeftFirst -> false -+ // notTF -> false -+ select = firstCoordinates[firstIndex] + firstOffset; -+ ++firstIndex; -+ } else { -+ // noneLeftFirst | noneLeftSecond -> false -+ // notTF -> ?? -+ // notFT -> ?? -+ final boolean breakFirst = notTF & secondZero; -+ final boolean breakSecond = notFT & firstZero; -+ -+ final double first = firstCoordinates[firstIndex] + firstOffset; -+ final double second = secondCoordinates[secondIndex] + secondOffset; -+ final boolean useFirst = first < (second + COLLISION_EPSILON); -+ final boolean cont = (useFirst & breakFirst) | (!useFirst & breakSecond); -+ -+ select = useFirst ? first : second; -+ firstIndex += useFirst ? 1 : 0; -+ secondIndex += 1 ^ (useFirst ? 1 : 0); -+ -+ if (cont) { -+ continue; -+ } -+ } -+ -+ int prevFirst = firstIndex - 1; -+ prevFirst = prevFirst >= voxelsFirst ? -1 : prevFirst; -+ int prevSecond = secondIndex - 1; -+ prevSecond = prevSecond >= voxelsSecond ? -1 : prevSecond; -+ -+ if (last >= (select - COLLISION_EPSILON)) { -+ // note: any operations on NaN is false -+ firstIndices[resultSize - 1] = prevFirst; -+ secondIndices[resultSize - 1] = prevSecond; -+ } else { -+ firstIndices[resultSize] = prevFirst; -+ secondIndices[resultSize] = prevSecond; -+ coordinates[resultSize] = select; -+ -+ ++resultSize; -+ last = select; -+ } -+ } -+ -+ return resultSize <= 1 ? EMPTY : new MergedVoxelCoordinateList(coordinates, 0.0, firstIndices, secondIndices, resultSize - 1); -+ } -+ } -+ -+ public static AABB offsetX(final AABB box, final double dx) { -+ return new AABB(box.minX + dx, box.minY, box.minZ, box.maxX + dx, box.maxY, box.maxZ, false); -+ } -+ -+ public static AABB offsetY(final AABB box, final double dy) { -+ return new AABB(box.minX, box.minY + dy, box.minZ, box.maxX, box.maxY + dy, box.maxZ, false); -+ } -+ -+ public static AABB offsetZ(final AABB box, final double dz) { -+ return new AABB(box.minX, box.minY, box.minZ + dz, box.maxX, box.maxY, box.maxZ + dz, false); -+ } -+ -+ public static AABB expandRight(final AABB box, final double dx) { // dx > 0.0 -+ return new AABB(box.minX, box.minY, box.minZ, box.maxX + dx, box.maxY, box.maxZ, false); -+ } -+ -+ public static AABB expandLeft(final AABB box, final double dx) { // dx < 0.0 -+ return new AABB(box.minX - dx, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ); -+ } -+ -+ public static AABB expandUpwards(final AABB box, final double dy) { // dy > 0.0 -+ return new AABB(box.minX, box.minY, box.minZ, box.maxX, box.maxY + dy, box.maxZ, false); -+ } -+ -+ public static AABB expandDownwards(final AABB box, final double dy) { // dy < 0.0 -+ return new AABB(box.minX, box.minY - dy, box.minZ, box.maxX, box.maxY, box.maxZ, false); -+ } -+ -+ public static AABB expandForwards(final AABB box, final double dz) { // dz > 0.0 -+ return new AABB(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ + dz, false); -+ } -+ -+ public static AABB expandBackwards(final AABB box, final double dz) { // dz < 0.0 -+ return new AABB(box.minX, box.minY, box.minZ - dz, box.maxX, box.maxY, box.maxZ, false); -+ } -+ -+ public static AABB cutRight(final AABB box, final double dx) { // dx > 0.0 -+ return new AABB(box.maxX, box.minY, box.minZ, box.maxX + dx, box.maxY, box.maxZ, false); -+ } -+ -+ public static AABB cutLeft(final AABB box, final double dx) { // dx < 0.0 -+ return new AABB(box.minX + dx, box.minY, box.minZ, box.minX, box.maxY, box.maxZ, false); -+ } -+ -+ public static AABB cutUpwards(final AABB box, final double dy) { // dy > 0.0 -+ return new AABB(box.minX, box.maxY, box.minZ, box.maxX, box.maxY + dy, box.maxZ, false); -+ } -+ -+ public static AABB cutDownwards(final AABB box, final double dy) { // dy < 0.0 -+ return new AABB(box.minX, box.minY + dy, box.minZ, box.maxX, box.minY, box.maxZ, false); -+ } -+ -+ public static AABB cutForwards(final AABB box, final double dz) { // dz > 0.0 -+ return new AABB(box.minX, box.minY, box.maxZ, box.maxX, box.maxY, box.maxZ + dz, false); -+ } -+ -+ public static AABB cutBackwards(final AABB box, final double dz) { // dz < 0.0 -+ return new AABB(box.minX, box.minY, box.minZ + dz, box.maxX, box.maxY, box.minZ, false); -+ } -+ -+ public static double performAABBCollisionsX(final AABB currentBoundingBox, double value, final List potentialCollisions) { -+ for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { -+ if (Math.abs(value) < COLLISION_EPSILON) { -+ return 0.0; -+ } -+ final AABB target = potentialCollisions.get(i); -+ value = collideX(target, currentBoundingBox, value); -+ } -+ -+ return value; -+ } -+ -+ public static double performAABBCollisionsY(final AABB currentBoundingBox, double value, final List potentialCollisions) { -+ for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { -+ if (Math.abs(value) < COLLISION_EPSILON) { -+ return 0.0; -+ } -+ final AABB target = potentialCollisions.get(i); -+ value = collideY(target, currentBoundingBox, value); -+ } -+ -+ return value; -+ } -+ -+ public static double performAABBCollisionsZ(final AABB currentBoundingBox, double value, final List potentialCollisions) { -+ for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { -+ if (Math.abs(value) < COLLISION_EPSILON) { -+ return 0.0; -+ } -+ final AABB target = potentialCollisions.get(i); -+ value = collideZ(target, currentBoundingBox, value); -+ } -+ -+ return value; -+ } -+ -+ public static double performVoxelCollisionsX(final AABB currentBoundingBox, double value, final List potentialCollisions) { -+ for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { -+ if (Math.abs(value) < COLLISION_EPSILON) { -+ return 0.0; -+ } -+ final VoxelShape target = potentialCollisions.get(i); -+ value = collideX(target, currentBoundingBox, value); -+ } -+ -+ return value; -+ } -+ -+ public static double performVoxelCollisionsY(final AABB currentBoundingBox, double value, final List potentialCollisions) { -+ for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { -+ if (Math.abs(value) < COLLISION_EPSILON) { -+ return 0.0; -+ } -+ final VoxelShape target = potentialCollisions.get(i); -+ value = collideY(target, currentBoundingBox, value); -+ } -+ -+ return value; -+ } -+ -+ public static double performVoxelCollisionsZ(final AABB currentBoundingBox, double value, final List potentialCollisions) { -+ for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { -+ if (Math.abs(value) < COLLISION_EPSILON) { -+ return 0.0; -+ } -+ final VoxelShape target = potentialCollisions.get(i); -+ value = collideZ(target, currentBoundingBox, value); -+ } -+ -+ return value; -+ } -+ -+ public static Vec3 performVoxelCollisions(final Vec3 moveVector, AABB axisalignedbb, final List potentialCollisions) { -+ double x = moveVector.x; -+ double y = moveVector.y; -+ double z = moveVector.z; -+ -+ if (y != 0.0) { -+ y = performVoxelCollisionsY(axisalignedbb, y, potentialCollisions); -+ if (y != 0.0) { -+ axisalignedbb = offsetY(axisalignedbb, y); -+ } -+ } -+ -+ final boolean xSmaller = Math.abs(x) < Math.abs(z); -+ -+ if (xSmaller && z != 0.0) { -+ z = performVoxelCollisionsZ(axisalignedbb, z, potentialCollisions); -+ if (z != 0.0) { -+ axisalignedbb = offsetZ(axisalignedbb, z); -+ } -+ } -+ -+ if (x != 0.0) { -+ x = performVoxelCollisionsX(axisalignedbb, x, potentialCollisions); -+ if (!xSmaller && x != 0.0) { -+ axisalignedbb = offsetX(axisalignedbb, x); -+ } -+ } -+ -+ if (!xSmaller && z != 0.0) { -+ z = performVoxelCollisionsZ(axisalignedbb, z, potentialCollisions); -+ } -+ -+ return new Vec3(x, y, z); -+ } -+ -+ public static Vec3 performAABBCollisions(final Vec3 moveVector, AABB axisalignedbb, final List potentialCollisions) { -+ double x = moveVector.x; -+ double y = moveVector.y; -+ double z = moveVector.z; -+ -+ if (y != 0.0) { -+ y = performAABBCollisionsY(axisalignedbb, y, potentialCollisions); -+ if (y != 0.0) { -+ axisalignedbb = offsetY(axisalignedbb, y); -+ } -+ } -+ -+ final boolean xSmaller = Math.abs(x) < Math.abs(z); -+ -+ if (xSmaller && z != 0.0) { -+ z = performAABBCollisionsZ(axisalignedbb, z, potentialCollisions); -+ if (z != 0.0) { -+ axisalignedbb = offsetZ(axisalignedbb, z); -+ } -+ } -+ -+ if (x != 0.0) { -+ x = performAABBCollisionsX(axisalignedbb, x, potentialCollisions); -+ if (!xSmaller && x != 0.0) { -+ axisalignedbb = offsetX(axisalignedbb, x); -+ } -+ } -+ -+ if (!xSmaller && z != 0.0) { -+ z = performAABBCollisionsZ(axisalignedbb, z, potentialCollisions); -+ } -+ -+ return new Vec3(x, y, z); -+ } -+ -+ public static Vec3 performCollisions(final Vec3 moveVector, AABB axisalignedbb, -+ final List voxels, -+ final List aabbs) { -+ if (voxels.isEmpty()) { -+ // fast track only AABBs -+ return performAABBCollisions(moveVector, axisalignedbb, aabbs); -+ } -+ -+ double x = moveVector.x; -+ double y = moveVector.y; -+ double z = moveVector.z; -+ -+ if (y != 0.0) { -+ y = performAABBCollisionsY(axisalignedbb, y, aabbs); -+ y = performVoxelCollisionsY(axisalignedbb, y, voxels); -+ if (y != 0.0) { -+ axisalignedbb = offsetY(axisalignedbb, y); -+ } -+ } -+ -+ final boolean xSmaller = Math.abs(x) < Math.abs(z); -+ -+ if (xSmaller && z != 0.0) { -+ z = performAABBCollisionsZ(axisalignedbb, z, aabbs); -+ z = performVoxelCollisionsZ(axisalignedbb, z, voxels); -+ if (z != 0.0) { -+ axisalignedbb = offsetZ(axisalignedbb, z); -+ } -+ } -+ -+ if (x != 0.0) { -+ x = performAABBCollisionsX(axisalignedbb, x, aabbs); -+ x = performVoxelCollisionsX(axisalignedbb, x, voxels); -+ if (!xSmaller && x != 0.0) { -+ axisalignedbb = offsetX(axisalignedbb, x); -+ } -+ } -+ -+ if (!xSmaller && z != 0.0) { -+ z = performAABBCollisionsZ(axisalignedbb, z, aabbs); -+ z = performVoxelCollisionsZ(axisalignedbb, z, voxels); -+ } -+ -+ return new Vec3(x, y, z); -+ } -+ -+ public static boolean isCollidingWithBorder(final WorldBorder worldborder, final AABB boundingBox) { -+ return isCollidingWithBorder(worldborder, boundingBox.minX, boundingBox.maxX, boundingBox.minZ, boundingBox.maxZ); -+ } -+ -+ public static boolean isCollidingWithBorder(final WorldBorder worldborder, final double boxMinX, final double boxMaxX, -+ final double boxMinZ, final double boxMaxZ) { -+ // border size is rounded like the collide voxel shape of the border -+ final double borderMinX = Math.floor(worldborder.getMinX()); // -X -+ final double borderMaxX = Math.ceil(worldborder.getMaxX()); // +X -+ -+ final double borderMinZ = Math.floor(worldborder.getMinZ()); // -Z -+ final double borderMaxZ = Math.ceil(worldborder.getMaxZ()); // +Z -+ -+ // inverted check for world border enclosing the specified box expanded by -EPSILON -+ return (borderMinX - boxMinX) > CollisionUtil.COLLISION_EPSILON || (borderMaxX - boxMaxX) < -CollisionUtil.COLLISION_EPSILON || -+ (borderMinZ - boxMinZ) > CollisionUtil.COLLISION_EPSILON || (borderMaxZ - boxMaxZ) < -CollisionUtil.COLLISION_EPSILON; -+ } -+ -+ /* Math.max/min specify that any NaN argument results in a NaN return, unlike these functions */ -+ private static double min(final double x, final double y) { -+ return x < y ? x : y; -+ } -+ -+ private static double max(final double x, final double y) { -+ return x > y ? x : y; -+ } -+ -+ public static final int COLLISION_FLAG_LOAD_CHUNKS = 1 << 0; -+ public static final int COLLISION_FLAG_COLLIDE_WITH_UNLOADED_CHUNKS = 1 << 1; -+ public static final int COLLISION_FLAG_CHECK_BORDER = 1 << 2; -+ public static final int COLLISION_FLAG_CHECK_ONLY = 1 << 3; -+ -+ public static boolean getCollisionsForBlocksOrWorldBorder(final Level world, final Entity entity, final AABB aabb, -+ final List intoVoxel, final List intoAABB, -+ final int collisionFlags, final BiPredicate predicate) { -+ final boolean checkOnly = (collisionFlags & COLLISION_FLAG_CHECK_ONLY) != 0; -+ boolean ret = false; -+ -+ if ((collisionFlags & COLLISION_FLAG_CHECK_BORDER) != 0) { -+ final WorldBorder worldBorder = world.getWorldBorder(); -+ if (CollisionUtil.isCollidingWithBorder(worldBorder, aabb) && entity != null && worldBorder.isInsideCloseToBorder(entity, aabb)) { -+ if (checkOnly) { -+ return true; -+ } else { -+ final VoxelShape borderShape = worldBorder.getCollisionShape(); -+ intoVoxel.add(borderShape); -+ ret = true; -+ } -+ } -+ } -+ -+ final int minSection = world.minSection; -+ -+ final int minBlockX = Mth.floor(aabb.minX - COLLISION_EPSILON) - 1; -+ final int maxBlockX = Mth.floor(aabb.maxX + COLLISION_EPSILON) + 1; -+ -+ final int minBlockY = Math.max((minSection << 4) - 1, Mth.floor(aabb.minY - COLLISION_EPSILON) - 1); -+ final int maxBlockY = Math.min((world.maxSection << 4) + 16, Mth.floor(aabb.maxY + COLLISION_EPSILON) + 1); -+ -+ final int minBlockZ = Mth.floor(aabb.minZ - COLLISION_EPSILON) - 1; -+ final int maxBlockZ = Mth.floor(aabb.maxZ + COLLISION_EPSILON) + 1; -+ -+ final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); -+ final CollisionContext collisionShape = new LazyEntityCollisionContext(entity); -+ -+ // special cases: -+ if (minBlockY > maxBlockY) { -+ // no point in checking -+ return ret; -+ } -+ -+ final int minChunkX = minBlockX >> 4; -+ final int maxChunkX = maxBlockX >> 4; -+ -+ final int minChunkY = minBlockY >> 4; -+ final int maxChunkY = maxBlockY >> 4; -+ -+ final int minChunkZ = minBlockZ >> 4; -+ final int maxChunkZ = maxBlockZ >> 4; -+ -+ final boolean loadChunks = (collisionFlags & COLLISION_FLAG_LOAD_CHUNKS) != 0; -+ final ServerChunkCache chunkSource = (ServerChunkCache)world.getChunkSource(); -+ -+ for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) { -+ for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) { -+ final ChunkAccess chunk = loadChunks ? chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, true) : chunkSource.getChunkAtIfLoadedImmediately(currChunkX, currChunkZ); -+ -+ if (chunk == null) { -+ if ((collisionFlags & COLLISION_FLAG_COLLIDE_WITH_UNLOADED_CHUNKS) != 0) { -+ if (checkOnly) { -+ return true; -+ } else { -+ intoAABB.add(getBoxForChunk(currChunkX, currChunkZ)); -+ ret = true; -+ } -+ } -+ continue; -+ } -+ -+ final LevelChunkSection[] sections = chunk.getSections(); -+ -+ // bound y -+ for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) { -+ final int sectionIdx = currChunkY - minSection; -+ if (sectionIdx < 0 || sectionIdx >= sections.length) { -+ continue; -+ } -+ final LevelChunkSection section = sections[sectionIdx]; -+ if (section == null || section.hasOnlyAir()) { -+ // empty -+ continue; -+ } -+ -+ final boolean hasSpecial = section.getSpecialCollidingBlocks() != 0; -+ final int sectionAdjust = !hasSpecial ? 1 : 0; -+ -+ final PalettedContainer blocks = section.states; -+ -+ final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) + sectionAdjust : 0; -+ final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) - sectionAdjust : 15; -+ final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) + sectionAdjust : 0; -+ final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) - sectionAdjust : 15; -+ final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) + sectionAdjust : 0; -+ final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) - sectionAdjust : 15; -+ -+ for (int currY = minYIterate; currY <= maxYIterate; ++currY) { -+ final int blockY = currY | (currChunkY << 4); -+ for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) { -+ final int blockZ = currZ | (currChunkZ << 4); -+ for (int currX = minXIterate; currX <= maxXIterate; ++currX) { -+ final int localBlockIndex = (currX) | (currZ << 4) | ((currY) << 8); -+ final int blockX = currX | (currChunkX << 4); -+ -+ final int edgeCount = hasSpecial ? ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) + -+ ((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) + -+ ((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0) : 0; -+ if (edgeCount == 3) { -+ continue; -+ } -+ -+ final BlockState blockData = blocks.get(localBlockIndex); -+ -+ if (blockData.emptyCollisionShape()) { -+ continue; -+ } -+ -+ if (edgeCount == 0 || ((edgeCount != 1 || blockData.hasLargeCollisionShape()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON))) { -+ VoxelShape blockCollision = blockData.getConstantCollisionShape(); -+ -+ if (blockCollision == null) { -+ mutablePos.set(blockX, blockY, blockZ); -+ blockCollision = blockData.getCollisionShape(world, mutablePos, collisionShape); -+ } -+ -+ AABB singleAABB = blockCollision.getSingleAABBRepresentation(); -+ if (singleAABB != null) { -+ singleAABB = singleAABB.move((double)blockX, (double)blockY, (double)blockZ); -+ if (!voxelShapeIntersect(aabb, singleAABB)) { -+ continue; -+ } -+ -+ if (predicate != null) { -+ mutablePos.set(blockX, blockY, blockZ); -+ if (!predicate.test(blockData, mutablePos)) { -+ continue; -+ } -+ } -+ -+ if (checkOnly) { -+ return true; -+ } else { -+ ret = true; -+ intoAABB.add(singleAABB); -+ continue; -+ } -+ } -+ -+ if (blockCollision.isEmpty()) { -+ continue; -+ } -+ -+ final VoxelShape blockCollisionOffset = blockCollision.move((double)blockX, (double)blockY, (double)blockZ); -+ -+ if (!voxelShapeIntersectNoEmpty(blockCollisionOffset, aabb)) { -+ continue; -+ } -+ -+ if (predicate != null) { -+ mutablePos.set(blockX, blockY, blockZ); -+ if (!predicate.test(blockData, mutablePos)) { -+ continue; -+ } -+ } -+ -+ if (checkOnly) { -+ return true; -+ } else { -+ ret = true; -+ intoVoxel.add(blockCollisionOffset); -+ continue; -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ -+ return ret; -+ } -+ -+ public static boolean getEntityHardCollisions(final CollisionGetter getter, final Entity entity, AABB aabb, -+ final List into, final int collisionFlags, final Predicate predicate) { -+ final boolean checkOnly = (collisionFlags & COLLISION_FLAG_CHECK_ONLY) != 0; -+ if (!(getter instanceof EntityGetter entityGetter)) { -+ return false; -+ } -+ -+ boolean ret = false; -+ -+ // to comply with vanilla intersection rules, expand by -epsilon so that we only get stuff we definitely collide with. -+ // Vanilla for hard collisions has this backwards, and they expand by +epsilon but this causes terrible problems -+ // specifically with boat collisions. -+ aabb = aabb.inflate(-COLLISION_EPSILON, -COLLISION_EPSILON, -COLLISION_EPSILON); -+ final List entities; -+ if (entity != null && entity.moonrise$isHardColliding()) { -+ entities = entityGetter.getEntities(entity, aabb, predicate); -+ } else { -+ entities = entityGetter.moonrise$getHardCollidingEntities(entity, aabb, predicate); -+ } -+ -+ for (int i = 0, len = entities.size(); i < len; ++i) { -+ final Entity otherEntity = entities.get(i); -+ -+ if (otherEntity.isSpectator()) { -+ continue; -+ } -+ -+ if ((entity == null && otherEntity.canBeCollidedWith()) || (entity != null && entity.canCollideWith(otherEntity))) { -+ if (checkOnly) { -+ return true; -+ } else { -+ into.add(otherEntity.getBoundingBox()); -+ ret = true; -+ } -+ } -+ } -+ -+ return ret; -+ } -+ -+ public static boolean getCollisions(final Level world, final Entity entity, final AABB aabb, -+ final List intoVoxel, final List intoAABB, final int collisionFlags, -+ final BiPredicate blockPredicate, -+ final Predicate entityPredicate) { -+ if ((collisionFlags & COLLISION_FLAG_CHECK_ONLY) != 0) { -+ return getCollisionsForBlocksOrWorldBorder(world, entity, aabb, intoVoxel, intoAABB, collisionFlags, blockPredicate) -+ || getEntityHardCollisions(world, entity, aabb, intoAABB, collisionFlags, entityPredicate); -+ } else { -+ return getCollisionsForBlocksOrWorldBorder(world, entity, aabb, intoVoxel, intoAABB, collisionFlags, blockPredicate) -+ | getEntityHardCollisions(world, entity, aabb, intoAABB, collisionFlags, entityPredicate); -+ } -+ } -+ -+ public static final class LazyEntityCollisionContext extends EntityCollisionContext { -+ -+ private CollisionContext delegate; -+ private boolean delegated; -+ -+ public LazyEntityCollisionContext(final Entity entity) { -+ super(false, 0.0, null, null, entity); -+ } -+ -+ public boolean isDelegated() { -+ final boolean delegated = this.delegated; -+ this.delegated = false; -+ return delegated; -+ } -+ -+ public CollisionContext getDelegate() { -+ this.delegated = true; -+ final Entity entity = this.getEntity(); -+ return this.delegate == null ? this.delegate = (entity == null ? CollisionContext.empty() : CollisionContext.of(entity)) : this.delegate; -+ } -+ -+ @Override -+ public boolean isDescending() { -+ return this.getDelegate().isDescending(); -+ } -+ -+ @Override -+ public boolean isAbove(final VoxelShape shape, final BlockPos pos, final boolean defaultValue) { -+ return this.getDelegate().isAbove(shape, pos, defaultValue); -+ } -+ -+ @Override -+ public boolean isHoldingItem(final Item item) { -+ return this.getDelegate().isHoldingItem(item); -+ } -+ -+ @Override -+ public boolean canStandOnFluid(final FluidState state, final FluidState fluidState) { -+ return this.getDelegate().canStandOnFluid(state, fluidState); -+ } -+ } -+ -+ private CollisionUtil() { -+ throw new RuntimeException(); -+ } -+} -diff --git a/src/main/java/io/papermc/paper/util/collisions/CachedShapeData.java b/src/main/java/io/papermc/paper/util/collisions/CachedShapeData.java -new file mode 100644 -index 0000000000000000000000000000000000000000..1cb96b09375770f92f3e494ce2f28d9ff8699581 ---- /dev/null -+++ b/src/main/java/io/papermc/paper/util/collisions/CachedShapeData.java -@@ -0,0 +1,10 @@ -+package io.papermc.paper.util.collisions; -+ -+public record CachedShapeData( -+ int sizeX, int sizeY, int sizeZ, -+ long[] voxelSet, -+ int minFullX, int minFullY, int minFullZ, -+ int maxFullX, int maxFullY, int maxFullZ, -+ boolean isEmpty, boolean hasSingleAABB -+) { -+} -diff --git a/src/main/java/io/papermc/paper/util/collisions/CachedToAABBs.java b/src/main/java/io/papermc/paper/util/collisions/CachedToAABBs.java -new file mode 100644 -index 0000000000000000000000000000000000000000..85c448a775f60ca4b4a4f2baf17487ef45bdd383 ---- /dev/null -+++ b/src/main/java/io/papermc/paper/util/collisions/CachedToAABBs.java -@@ -0,0 +1,39 @@ -+package io.papermc.paper.util.collisions; -+ -+import net.minecraft.world.phys.AABB; -+import java.util.ArrayList; -+import java.util.List; -+ -+public record CachedToAABBs( -+ List aabbs, -+ boolean isOffset, -+ double offX, double offY, double offZ -+) { -+ -+ public CachedToAABBs removeOffset() { -+ final List toOffset = this.aabbs; -+ final double offX = this.offX; -+ final double offY = this.offY; -+ final double offZ = this.offZ; -+ -+ final List ret = new ArrayList<>(toOffset.size()); -+ -+ for (int i = 0, len = toOffset.size(); i < len; ++i) { -+ ret.add(toOffset.get(i).move(offX, offY, offZ)); -+ } -+ -+ return new CachedToAABBs(ret, false, 0.0, 0.0, 0.0); -+ } -+ -+ public static CachedToAABBs offset(final CachedToAABBs cache, final double offX, final double offY, final double offZ) { -+ if (offX == 0.0 && offY == 0.0 && offZ == 0.0) { -+ return cache; -+ } -+ -+ final double resX = cache.offX + offX; -+ final double resY = cache.offY + offY; -+ final double resZ = cache.offZ + offZ; -+ -+ return new CachedToAABBs(cache.aabbs, true, resX, resY, resZ); -+ } -+} -diff --git a/src/main/java/io/papermc/paper/util/collisions/FlatBitsetUtil.java b/src/main/java/io/papermc/paper/util/collisions/FlatBitsetUtil.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ff9d2dad39dcc02b2371458b7b5f64c6090e8012 ---- /dev/null -+++ b/src/main/java/io/papermc/paper/util/collisions/FlatBitsetUtil.java -@@ -0,0 +1,109 @@ -+package io.papermc.paper.util.collisions; -+ -+import java.util.Objects; -+ -+public final class FlatBitsetUtil { -+ -+ private static final int LOG2_LONG = 6; -+ private static final long ALL_SET = -1L; -+ private static final int BITS_PER_LONG = Long.SIZE; -+ -+ // from inclusive -+ // to exclusive -+ public static int firstSet(final long[] bitset, final int from, final int to) { -+ if ((from | to | (to - from)) < 0) { -+ throw new IndexOutOfBoundsException(); -+ } -+ -+ int bitsetIdx = from >>> LOG2_LONG; -+ int bitIdx = from & ~(BITS_PER_LONG - 1); -+ -+ long tmp = bitset[bitsetIdx] & (ALL_SET << from); -+ for (;;) { -+ if (tmp != 0L) { -+ final int ret = bitIdx | Long.numberOfTrailingZeros(tmp); -+ return ret >= to ? -1 : ret; -+ } -+ -+ bitIdx += BITS_PER_LONG; -+ -+ if (bitIdx >= to) { -+ return -1; -+ } -+ -+ tmp = bitset[++bitsetIdx]; -+ } -+ } -+ -+ // from inclusive -+ // to exclusive -+ public static int firstClear(final long[] bitset, final int from, final int to) { -+ if ((from | to | (to - from)) < 0) { -+ throw new IndexOutOfBoundsException(); -+ } -+ // like firstSet, but invert the bitset -+ -+ int bitsetIdx = from >>> LOG2_LONG; -+ int bitIdx = from & ~(BITS_PER_LONG - 1); -+ -+ long tmp = (~bitset[bitsetIdx]) & (ALL_SET << from); -+ for (;;) { -+ if (tmp != 0L) { -+ final int ret = bitIdx | Long.numberOfTrailingZeros(tmp); -+ return ret >= to ? -1 : ret; -+ } -+ -+ bitIdx += BITS_PER_LONG; -+ -+ if (bitIdx >= to) { -+ return -1; -+ } -+ -+ tmp = ~bitset[++bitsetIdx]; -+ } -+ } -+ -+ // from inclusive -+ // to exclusive -+ public static void clearRange(final long[] bitset, final int from, int to) { -+ if ((from | to | (to - from)) < 0) { -+ throw new IndexOutOfBoundsException(); -+ } -+ -+ if (from == to) { -+ return; -+ } -+ -+ --to; -+ -+ final int fromBitsetIdx = from >>> LOG2_LONG; -+ final int toBitsetIdx = to >>> LOG2_LONG; -+ -+ final long keepFirst = ~(ALL_SET << from); -+ final long keepLast = ~(ALL_SET >>> ((BITS_PER_LONG - 1) ^ to)); -+ -+ Objects.checkFromToIndex(fromBitsetIdx, toBitsetIdx, bitset.length); -+ -+ if (fromBitsetIdx == toBitsetIdx) { -+ // special case: need to keep both first and last -+ bitset[fromBitsetIdx] &= (keepFirst | keepLast); -+ } else { -+ bitset[fromBitsetIdx] &= keepFirst; -+ -+ for (int i = fromBitsetIdx + 1; i < toBitsetIdx; ++i) { -+ bitset[i] = 0L; -+ } -+ -+ bitset[toBitsetIdx] &= keepLast; -+ } -+ } -+ -+ // from inclusive -+ // to exclusive -+ public static boolean isRangeSet(final long[] bitset, final int from, final int to) { -+ return firstClear(bitset, from, to) == -1; -+ } -+ -+ -+ private FlatBitsetUtil() {} -+} -diff --git a/src/main/java/io/papermc/paper/util/collisions/MergedORCache.java b/src/main/java/io/papermc/paper/util/collisions/MergedORCache.java -new file mode 100644 -index 0000000000000000000000000000000000000000..1f42bdfdb052056e62a939ab0d1944f8a064fe6c ---- /dev/null -+++ b/src/main/java/io/papermc/paper/util/collisions/MergedORCache.java -@@ -0,0 +1,10 @@ -+package io.papermc.paper.util.collisions; -+ -+import net.minecraft.world.phys.shapes.VoxelShape; -+ -+public record MergedORCache( -+ VoxelShape key, -+ VoxelShape result -+) { -+ -+} -diff --git a/src/main/java/net/minecraft/core/Direction.java b/src/main/java/net/minecraft/core/Direction.java -index 03c45ee77276462818a6f774b5945b25924aa3f0..ab289a6ca85459e03acb2089c6b9e931caa9c873 100644 ---- a/src/main/java/net/minecraft/core/Direction.java -+++ b/src/main/java/net/minecraft/core/Direction.java -@@ -60,6 +60,21 @@ public enum Direction implements StringRepresentable { - private final int adjY; - private final int adjZ; - // Paper end - Perf: Inline shift direction fields -+ // Paper start - optimise collisions -+ private static final int RANDOM_OFFSET = 2017601568; -+ private Direction opposite; -+ static { -+ for (final Direction direction : VALUES) { -+ direction.opposite = from3DDataValue(direction.oppositeIndex); -+ } -+ } -+ -+ private final int id = it.unimi.dsi.fastutil.HashCommon.murmurHash3(it.unimi.dsi.fastutil.HashCommon.murmurHash3(this.ordinal() + RANDOM_OFFSET) + RANDOM_OFFSET); -+ -+ public final int uniqueId() { -+ return this.id; -+ } -+ // Paper end - optimise collisions - - private Direction( - final int id, -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 89ed20e9c629cf39a24c7a0ce5c4fee41fc64fd5..2636b018cdd12e4d7f00b775257585ab8eb91171 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -513,7 +513,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - int k2 = i2 / (i * 2 + 1); - - blockposition1 = PlayerRespawnLogic.getOverworldRespawnPos(world, basePos.getX() + j2 - i, basePos.getZ() + k2 - i); -- if (blockposition1 != null && world.noCollision(this, axisalignedbb.move(blockposition1.getBottomCenter()))) { -+ if (blockposition1 != null && world.noCollision(this, axisalignedbb.move(blockposition1.getBottomCenter()), true)) { // Paper - make sure this loads chunks, we default to NOT loading now - return blockposition1; - } - } -@@ -521,7 +521,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - blockposition1 = basePos; - } - -- while (!world.noCollision(this, axisalignedbb.move(blockposition1.getBottomCenter())) && blockposition1.getY() < world.getMaxBuildHeight() - 1) { -+ while (!world.noCollision(this, axisalignedbb.move(blockposition1.getBottomCenter()), true) && blockposition1.getY() < world.getMaxBuildHeight() - 1) { // Paper - make sure this loads chunks, we default to NOT loading now - blockposition1 = blockposition1.above(); - } - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 3062e108ed80ace0f2caf03c70a8e48f276be450..07feecd51ac3f5465027f0d26be53255c913b684 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1250,9 +1250,44 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - float f = this.getBlockSpeedFactor(); - - this.setDeltaMovement(this.getDeltaMovement().multiply((double) f, 1.0D, (double) f)); -- if (this.level().getBlockStatesIfLoaded(this.getBoundingBox().deflate(1.0E-6D)).noneMatch((iblockdata2) -> { -- return iblockdata2.is(BlockTags.FIRE) || iblockdata2.is(Blocks.LAVA); -- })) { -+ // Paper start - remove expensive streams from here -+ boolean noneMatch = true; -+ AABB fireSearchBox = this.getBoundingBox().deflate(1.0E-6D); -+ { -+ int minX = Mth.floor(fireSearchBox.minX); -+ int minY = Mth.floor(fireSearchBox.minY); -+ int minZ = Mth.floor(fireSearchBox.minZ); -+ int maxX = Mth.floor(fireSearchBox.maxX); -+ int maxY = Mth.floor(fireSearchBox.maxY); -+ int maxZ = Mth.floor(fireSearchBox.maxZ); -+ fire_search_loop: -+ for (int fz = minZ; fz <= maxZ; ++fz) { -+ for (int fx = minX; fx <= maxX; ++fx) { -+ for (int fy = minY; fy <= maxY; ++fy) { -+ net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)this.level.getChunkIfLoadedImmediately(fx >> 4, fz >> 4); -+ if (chunk == null) { -+ // Vanilla rets an empty stream if all the chunks are not loaded, so noneMatch will be true -+ // even if we're in lava/fire -+ noneMatch = true; -+ break fire_search_loop; -+ } -+ if (!noneMatch) { -+ // don't do get type, we already know we're in fire - we just need to check the chunks -+ // loaded state -+ continue; -+ } -+ -+ BlockState type = chunk.getBlockStateFinal(fx, fy, fz); -+ if (type.is(BlockTags.FIRE) || type.is(Blocks.LAVA)) { -+ noneMatch = false; -+ // can't break, we need to retain vanilla behavior by ensuring ALL chunks are loaded -+ } -+ } -+ } -+ } -+ } -+ if (noneMatch) { -+ // Paper end - remove expensive streams from here - if (this.remainingFireTicks <= 0) { - this.setRemainingFireTicks(-this.getFireImmuneTicks()); - } -@@ -1425,6 +1460,92 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - - private Vec3 collide(Vec3 movement) { -+ // Paper start - optimise collisions -+ // Sakura - This is temporary workaround until this patch is updated by Paper. -+ if (this.maxUpStep() != 0.0f) { -+ return this.vanilla_collide(movement); -+ } else { -+ return this.paper_collide(movement); -+ } -+ } -+ private Vec3 paper_collide(Vec3 movement) { -+ final boolean xZero = movement.x == 0.0; -+ final boolean yZero = movement.y == 0.0; -+ final boolean zZero = movement.z == 0.0; -+ if (xZero & yZero & zZero) { -+ return movement; -+ } -+ -+ final Level world = this.level; -+ final AABB currBoundingBox = this.getBoundingBox(); -+ -+ if (io.papermc.paper.util.CollisionUtil.isEmpty(currBoundingBox)) { -+ return movement; -+ } -+ -+ final List potentialCollisionsBB = new java.util.ArrayList<>(); -+ final List potentialCollisionsVoxel = new java.util.ArrayList<>(); -+ final double stepHeight = (double) this.maxUpStep(); -+ final AABB collisionBox; -+ final boolean onGround = this.onGround; -+ -+ if (xZero & zZero) { -+ if (movement.y > 0.0) { -+ collisionBox = io.papermc.paper.util.CollisionUtil.cutUpwards(currBoundingBox, movement.y); -+ } else { -+ collisionBox = io.papermc.paper.util.CollisionUtil.cutDownwards(currBoundingBox, movement.y); -+ } -+ } else { -+ // note: xZero == false or zZero == false -+ if (stepHeight > 0.0 && (onGround || (movement.y < 0.0))) { -+ // don't bother getting the collisions if we don't need them. -+ if (movement.y <= 0.0) { -+ collisionBox = io.papermc.paper.util.CollisionUtil.expandUpwards(currBoundingBox.expandTowards(movement.x, movement.y, movement.z), stepHeight); -+ } else { -+ collisionBox = currBoundingBox.expandTowards(movement.x, Math.max(stepHeight, movement.y), movement.z); -+ } -+ } else { -+ collisionBox = currBoundingBox.expandTowards(movement.x, movement.y, movement.z); -+ } -+ } -+ -+ io.papermc.paper.util.CollisionUtil.getCollisions( -+ world, this, collisionBox, potentialCollisionsVoxel, potentialCollisionsBB, -+ io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_CHECK_BORDER, -+ null, null -+ ); -+ -+ if (potentialCollisionsVoxel.isEmpty() && potentialCollisionsBB.isEmpty()) { -+ return movement; -+ } -+ -+ final Vec3 limitedMoveVector = io.papermc.paper.util.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB); -+ -+ if (stepHeight > 0.0 -+ && (onGround || (limitedMoveVector.y != movement.y && movement.y < 0.0)) -+ && (limitedMoveVector.x != movement.x || limitedMoveVector.z != movement.z)) { -+ Vec3 vec3d2 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(movement.x, stepHeight, movement.z), currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB); -+ final Vec3 vec3d3 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(0.0, stepHeight, 0.0), currBoundingBox.expandTowards(movement.x, 0.0, movement.z), potentialCollisionsVoxel, potentialCollisionsBB); -+ -+ if (vec3d3.y < stepHeight) { -+ final Vec3 vec3d4 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(movement.x, 0.0D, movement.z), currBoundingBox.move(vec3d3), potentialCollisionsVoxel, potentialCollisionsBB).add(vec3d3); -+ -+ if (vec3d4.horizontalDistanceSqr() > vec3d2.horizontalDistanceSqr()) { -+ vec3d2 = vec3d4; -+ } -+ } -+ -+ if (vec3d2.horizontalDistanceSqr() > limitedMoveVector.horizontalDistanceSqr()) { -+ return vec3d2.add(io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(0.0D, -vec3d2.y + movement.y, 0.0D), currBoundingBox.move(vec3d2), potentialCollisionsVoxel, potentialCollisionsBB)); -+ } -+ -+ return limitedMoveVector; -+ } else { -+ return limitedMoveVector; -+ } -+ } -+ private Vec3 vanilla_collide(Vec3 movement) { -+ // Paper end - optimise collisions - AABB axisalignedbb = this.getBoundingBox(); - List list = this.level().getEntityCollisions(this, axisalignedbb.expandTowards(movement)); - Vec3 vec3d1 = movement.lengthSqr() == 0.0D ? movement : Entity.collideBoundingBox(this, movement, axisalignedbb, this.level(), list); -@@ -2781,11 +2902,70 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - float f = this.dimensions.width() * 0.8F; - AABB axisalignedbb = AABB.ofSize(this.getEyePosition(), (double) f, 1.0E-6D, (double) f); - -- return BlockPos.betweenClosedStream(axisalignedbb).anyMatch((blockposition) -> { -- BlockState iblockdata = this.level().getBlockState(blockposition); -+ // Paper start - optimise collisions -+ if (io.papermc.paper.util.CollisionUtil.isEmpty(axisalignedbb)) { -+ return false; -+ } -+ -+ final BlockPos.MutableBlockPos tempPos = new BlockPos.MutableBlockPos(); -+ -+ final int minX = Mth.floor(axisalignedbb.minX); -+ final int minY = Mth.floor(axisalignedbb.minY); -+ final int minZ = Mth.floor(axisalignedbb.minZ); -+ final int maxX = Mth.floor(axisalignedbb.maxX); -+ final int maxY = Mth.floor(axisalignedbb.maxY); -+ final int maxZ = Mth.floor(axisalignedbb.maxZ); -+ -+ final net.minecraft.server.level.ServerChunkCache chunkProvider = (net.minecraft.server.level.ServerChunkCache)this.level.getChunkSource(); -+ -+ long lastChunkKey = ChunkPos.INVALID_CHUNK_POS; -+ net.minecraft.world.level.chunk.LevelChunk lastChunk = null; -+ for (int fz = minZ; fz <= maxZ; ++fz) { -+ tempPos.setZ(fz); -+ for (int fx = minX; fx <= maxX; ++fx) { -+ final int newChunkX = fx >> 4; -+ final int newChunkZ = fz >> 4; -+ final net.minecraft.world.level.chunk.LevelChunk chunk = lastChunkKey == (lastChunkKey = io.papermc.paper.util.CoordinateUtils.getChunkKey(newChunkX, newChunkZ)) ? -+ lastChunk : (lastChunk = chunkProvider.getChunkAtIfLoadedImmediately(newChunkX, newChunkZ)); -+ tempPos.setX(fx); -+ if (chunk == null) { -+ continue; -+ } -+ for (int fy = minY; fy <= maxY; ++fy) { -+ tempPos.setY(fy); - -- return !iblockdata.isAir() && iblockdata.isSuffocating(this.level(), blockposition) && Shapes.joinIsNotEmpty(iblockdata.getCollisionShape(this.level(), blockposition).move((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ()), Shapes.create(axisalignedbb), BooleanOp.AND); -- }); -+ final BlockState state = chunk.getBlockState(tempPos); -+ -+ if (state.emptyCollisionShape() || !state.isSuffocating(this.level, tempPos)) { -+ continue; -+ } -+ -+ // Yes, it does not use the Entity context stuff. -+ final VoxelShape collisionShape = state.getCollisionShape(this.level, tempPos); -+ -+ if (collisionShape.isEmpty()) { -+ continue; -+ } -+ -+ final AABB toCollide = axisalignedbb.move(-(double)fx, -(double)fy, -(double)fz); -+ -+ final AABB singleAABB = collisionShape.getSingleAABBRepresentation(); -+ if (singleAABB != null) { -+ if (io.papermc.paper.util.CollisionUtil.voxelShapeIntersect(singleAABB, toCollide)) { -+ return true; -+ } -+ continue; -+ } -+ -+ if (io.papermc.paper.util.CollisionUtil.voxelShapeIntersectNoEmpty(collisionShape, toCollide)) { -+ return true; -+ } -+ continue; -+ } -+ } -+ } -+ // Paper end - optimise collisions -+ return false; - } - } - -diff --git a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -index 2f398750bfee5758ad8b1367b6fc14364e4de776..13c02f0b95c3f2913d7a1b0c8877d00566b3cb00 100644 ---- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -@@ -359,7 +359,7 @@ public class ArmorStand extends LivingEntity { - @Override - protected void pushEntities() { - if (!this.level().paperConfig().entities.armorStands.doCollisionEntityLookups) return; // Paper - Option to prevent armor stands from doing entity lookups -- List list = this.level().getEntities((Entity) this, this.getBoundingBox(), ArmorStand.RIDABLE_MINECARTS); -+ List list = this.level().getEntitiesOfClass(AbstractMinecart.class, this.getBoundingBox(), ArmorStand.RIDABLE_MINECARTS); // Paper - optimise collisions - Iterator iterator = list.iterator(); - - while (iterator.hasNext()) { -diff --git a/src/main/java/net/minecraft/world/entity/monster/Spider.java b/src/main/java/net/minecraft/world/entity/monster/Spider.java -index e675f1e3e5b6f9e1aa0d928ebb9abe76458edb38..fa0316e9d2a4cf213982994dc8bf310299cca984 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Spider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Spider.java -@@ -82,7 +82,7 @@ public class Spider extends Monster { - public void tick() { - super.tick(); - if (!this.level().isClientSide) { -- this.setClimbing(this.horizontalCollision && (this.level().paperConfig().entities.behavior.allowSpiderWorldBorderClimbing)); // Paper - Add config option for spider worldborder climbing -+ this.setClimbing(this.horizontalCollision && (this.level().paperConfig().entities.behavior.allowSpiderWorldBorderClimbing || !io.papermc.paper.util.CollisionUtil.isCollidingWithBorder(this.level().getWorldBorder(), this.getBoundingBox().inflate(io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)))); // Paper - Add config option for spider worldborder climbing & Inflate by +EPSILON as collision will just barely place us outside border - } - - } -diff --git a/src/main/java/net/minecraft/world/level/BlockCollisions.java b/src/main/java/net/minecraft/world/level/BlockCollisions.java -index 1c10835b59aaefa3a65ff64f784620bdc54ddcdc..aa63b460c63336be8eee16842c1b32bc96062a36 100644 ---- a/src/main/java/net/minecraft/world/level/BlockCollisions.java -+++ b/src/main/java/net/minecraft/world/level/BlockCollisions.java -@@ -80,7 +80,7 @@ public class BlockCollisions extends AbstractIterator { - && (l != 2 || blockState.is(Blocks.MOVING_PISTON))) { - VoxelShape voxelShape = blockState.getCollisionShape(this.collisionGetter, this.pos, this.context); - if (voxelShape == Shapes.block()) { -- if (this.box.intersects((double)i, (double)j, (double)k, (double)i + 1.0, (double)j + 1.0, (double)k + 1.0)) { -+ if (io.papermc.paper.util.CollisionUtil.voxelShapeIntersect(this.box, i, j, k, i + 1.0, j + 1.0, k + 1.0)) { // Paper - keep vanilla behavior for voxelshape intersection - See comment in CollisionUtil - return this.resultProvider.apply(this.pos, voxelShape.move((double)i, (double)j, (double)k)); - } - } else { -diff --git a/src/main/java/net/minecraft/world/level/ClipContext.java b/src/main/java/net/minecraft/world/level/ClipContext.java -index 3fa2964b979053ecbefc946c7fe76828de86d8f1..2634ba06147c936b0fae08a190c7820c832e6b23 100644 ---- a/src/main/java/net/minecraft/world/level/ClipContext.java -+++ b/src/main/java/net/minecraft/world/level/ClipContext.java -@@ -17,8 +17,8 @@ public class ClipContext { - - private final Vec3 from; - private final Vec3 to; -- private final ClipContext.Block block; -- private final ClipContext.Fluid fluid; -+ public final ClipContext.Block block; // Paper - optimise collisions - public -+ public final ClipContext.Fluid fluid; // Paper - optimise collisions - public - private final CollisionContext collisionContext; - - public ClipContext(Vec3 start, Vec3 end, ClipContext.Block shapeType, ClipContext.Fluid fluidHandling, Entity entity) { -diff --git a/src/main/java/net/minecraft/world/level/CollisionGetter.java b/src/main/java/net/minecraft/world/level/CollisionGetter.java -index e57cb7fe53e915d24246e44c7f49971f5b2ab2cf..eafe09db4db7f10d0e97110f4cc6469d7d7099b7 100644 ---- a/src/main/java/net/minecraft/world/level/CollisionGetter.java -+++ b/src/main/java/net/minecraft/world/level/CollisionGetter.java -@@ -35,6 +35,12 @@ public interface CollisionGetter extends BlockGetter { - return this.isUnobstructed(entity, Shapes.create(entity.getBoundingBox())); - } - -+ // Paper start - optimise collisions -+ default boolean noCollision(Entity entity, AABB box, boolean loadChunks) { -+ return this.noCollision(entity, box); -+ } -+ // Paper end - optimise collisions -+ - default boolean noCollision(AABB box) { - return this.noCollision(null, box); - } -diff --git a/src/main/java/net/minecraft/world/level/EntityGetter.java b/src/main/java/net/minecraft/world/level/EntityGetter.java -index 70c2017400168d4fef3c14462798edcfed58d4bf..206a0ac730e254ca2aba3cc2deca500727c59af4 100644 ---- a/src/main/java/net/minecraft/world/level/EntityGetter.java -+++ b/src/main/java/net/minecraft/world/level/EntityGetter.java -@@ -41,20 +41,36 @@ public interface EntityGetter extends ca.spottedleaf.moonrise.patches.chunk_syst - // Paper end - rewrite chunk system - - default boolean isUnobstructed(@Nullable Entity except, VoxelShape shape) { -+ // Paper start - optimise collisions - if (shape.isEmpty()) { -- return true; -- } else { -- for (Entity entity : this.getEntities(except, shape.bounds())) { -- if (!entity.isRemoved() -- && entity.blocksBuilding -- && (except == null || !entity.isPassengerOfSameVehicle(except)) -- && Shapes.joinIsNotEmpty(shape, Shapes.create(entity.getBoundingBox()), BooleanOp.AND)) { -- return false; -+ return false; -+ } -+ -+ final AABB singleAABB = shape.getSingleAABBRepresentation(); -+ final List entities = this.getEntities( -+ except, -+ singleAABB == null ? shape.bounds() : singleAABB.inflate(-io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON, -io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON, -io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) -+ ); -+ -+ for (int i = 0, len = entities.size(); i < len; ++i) { -+ final Entity otherEntity = entities.get(i); -+ -+ if (otherEntity.isRemoved() || !otherEntity.blocksBuilding || (except != null && otherEntity.isPassengerOfSameVehicle(except))) { -+ continue; -+ } -+ -+ if (singleAABB == null) { -+ final AABB entityBB = otherEntity.getBoundingBox(); -+ if (io.papermc.paper.util.CollisionUtil.isEmpty(entityBB) || !io.papermc.paper.util.CollisionUtil.voxelShapeIntersectNoEmpty(shape, entityBB)) { -+ continue; - } - } - -- return true; -+ return false; - } -+ -+ return true; -+ // Paper end - optimise collisions - } - - default List getEntitiesOfClass(Class entityClass, AABB box) { -@@ -62,23 +78,41 @@ public interface EntityGetter extends ca.spottedleaf.moonrise.patches.chunk_syst - } - - default List getEntityCollisions(@Nullable Entity entity, AABB box) { -- if (box.getSize() < 1.0E-7) { -- return List.of(); -+ // Paper start - optimise collisions -+ // first behavior change is to correctly check for empty AABB -+ if (io.papermc.paper.util.CollisionUtil.isEmpty(box)) { -+ // reduce indirection by always returning type with same class -+ return new java.util.ArrayList<>(); -+ } -+ -+ // to comply with vanilla intersection rules, expand by -epsilon so that we only get stuff we definitely collide with. -+ // Vanilla for hard collisions has this backwards, and they expand by +epsilon but this causes terrible problems -+ // specifically with boat collisions. -+ box = box.inflate(-io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON, -io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON, -io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON); -+ -+ final List entities; -+ if (entity != null && entity.moonrise$isHardColliding()) { -+ entities = this.getEntities(entity, box, null); - } else { -- Predicate predicate = entity == null ? EntitySelector.CAN_BE_COLLIDED_WITH : EntitySelector.NO_SPECTATORS.and(entity::canCollideWith); -- List list = this.getEntities(entity, box.inflate(1.0E-7), predicate); -- if (list.isEmpty()) { -- return List.of(); -- } else { -- Builder builder = ImmutableList.builderWithExpectedSize(list.size()); -- -- for (Entity entity2 : list) { -- builder.add(Shapes.create(entity2.getBoundingBox())); -- } -+ entities = this.moonrise$getHardCollidingEntities(entity, box, null); -+ } -+ -+ final List ret = new java.util.ArrayList<>(Math.min(25, entities.size())); - -- return builder.build(); -+ for (int i = 0, len = entities.size(); i < len; ++i) { -+ final Entity otherEntity = entities.get(i); -+ -+ if (otherEntity.isSpectator()) { -+ continue; -+ } -+ -+ if ((entity == null && otherEntity.canBeCollidedWith()) || (entity != null && entity.canCollideWith(otherEntity))) { -+ ret.add(Shapes.create(otherEntity.getBoundingBox())); - } - } -+ -+ return ret; -+ // Paper end - optimise collisions - } - - // Paper start - Affects Spawning API -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index e345fbcf798c0b663780d206ce337233e28dfc4e..11a002ec4482716d35ed04f2e24c96e8f7fe6f64 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -349,6 +349,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - this.tileLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.tileMaxTickTime); - this.entityLookup = new ca.spottedleaf.moonrise.patches.chunk_system.level.entity.dfl.DefaultEntityLookup(this); // Paper - rewrite chunk system - this.chunkPacketBlockController = this.paperConfig().anticheat.antiXray.enabled ? new com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) : com.destroystokyo.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray -+ // Paper start - optimise collisions -+ this.minSection = io.papermc.paper.util.WorldUtil.getMinSection(this); -+ this.maxSection = io.papermc.paper.util.WorldUtil.getMaxSection(this); -+ // Paper end - optimise collisions - } - - // Paper start - Cancel hit for vanished players -@@ -390,6 +394,366 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - return true; - } - // Paper end - Cancel hit for vanished players -+ // Paper start - optimise collisions -+ public final int minSection; -+ public final int maxSection; -+ -+ @Override -+ public final boolean isUnobstructed(final Entity entity) { -+ final AABB boundingBox = entity.getBoundingBox(); -+ if (io.papermc.paper.util.CollisionUtil.isEmpty(boundingBox)) { -+ return false; -+ } -+ -+ final List entities = this.getEntities( -+ entity, -+ boundingBox.inflate(-io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON, -io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON, -io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON), -+ null -+ ); -+ -+ for (int i = 0, len = entities.size(); i < len; ++i) { -+ final Entity otherEntity = entities.get(i); -+ -+ if (otherEntity.isSpectator() || otherEntity.isRemoved() || !otherEntity.blocksBuilding || otherEntity.isPassengerOfSameVehicle(entity)) { -+ continue; -+ } -+ -+ return false; -+ } -+ -+ return true; -+ } -+ -+ private static net.minecraft.world.phys.BlockHitResult miss(final ClipContext clipContext) { -+ final Vec3 to = clipContext.getTo(); -+ final Vec3 from = clipContext.getFrom(); -+ -+ return net.minecraft.world.phys.BlockHitResult.miss(to, Direction.getNearest(from.x - to.x, from.y - to.y, from.z - to.z), BlockPos.containing(to.x, to.y, to.z)); -+ } -+ -+ private static final FluidState AIR_FLUIDSTATE = Fluids.EMPTY.defaultFluidState(); -+ -+ private static net.minecraft.world.phys.BlockHitResult fastClip(final Vec3 from, final Vec3 to, final Level level, -+ final ClipContext clipContext) { -+ final double adjX = io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON * (from.x - to.x); -+ final double adjY = io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON * (from.y - to.y); -+ final double adjZ = io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON * (from.z - to.z); -+ -+ if (adjX == 0.0 && adjY == 0.0 && adjZ == 0.0) { -+ return miss(clipContext); -+ } -+ -+ final double toXAdj = to.x - adjX; -+ final double toYAdj = to.y - adjY; -+ final double toZAdj = to.z - adjZ; -+ final double fromXAdj = from.x + adjX; -+ final double fromYAdj = from.y + adjY; -+ final double fromZAdj = from.z + adjZ; -+ -+ int currX = Mth.floor(fromXAdj); -+ int currY = Mth.floor(fromYAdj); -+ int currZ = Mth.floor(fromZAdj); -+ -+ final BlockPos.MutableBlockPos currPos = new BlockPos.MutableBlockPos(); -+ -+ final double diffX = toXAdj - fromXAdj; -+ final double diffY = toYAdj - fromYAdj; -+ final double diffZ = toZAdj - fromZAdj; -+ -+ final double dxDouble = Math.signum(diffX); -+ final double dyDouble = Math.signum(diffY); -+ final double dzDouble = Math.signum(diffZ); -+ -+ final int dx = (int)dxDouble; -+ final int dy = (int)dyDouble; -+ final int dz = (int)dzDouble; -+ -+ final double normalizedDiffX = diffX == 0.0 ? Double.MAX_VALUE : dxDouble / diffX; -+ final double normalizedDiffY = diffY == 0.0 ? Double.MAX_VALUE : dyDouble / diffY; -+ final double normalizedDiffZ = diffZ == 0.0 ? Double.MAX_VALUE : dzDouble / diffZ; -+ -+ double normalizedCurrX = normalizedDiffX * (diffX > 0.0 ? (1.0 - Mth.frac(fromXAdj)) : Mth.frac(fromXAdj)); -+ double normalizedCurrY = normalizedDiffY * (diffY > 0.0 ? (1.0 - Mth.frac(fromYAdj)) : Mth.frac(fromYAdj)); -+ double normalizedCurrZ = normalizedDiffZ * (diffZ > 0.0 ? (1.0 - Mth.frac(fromZAdj)) : Mth.frac(fromZAdj)); -+ -+ net.minecraft.world.level.chunk.LevelChunkSection[] lastChunk = null; -+ net.minecraft.world.level.chunk.PalettedContainer lastSection = null; -+ int lastChunkX = Integer.MIN_VALUE; -+ int lastChunkY = Integer.MIN_VALUE; -+ int lastChunkZ = Integer.MIN_VALUE; -+ -+ final int minSection = level.minSection; -+ final net.minecraft.server.level.ServerChunkCache chunkProvider = (net.minecraft.server.level.ServerChunkCache)level.getChunkSource(); -+ -+ for (;;) { -+ currPos.set(currX, currY, currZ); -+ -+ final int newChunkX = currX >> 4; -+ final int newChunkY = currY >> 4; -+ final int newChunkZ = currZ >> 4; -+ -+ final int chunkDiff = ((newChunkX ^ lastChunkX) | (newChunkZ ^ lastChunkZ)); -+ final int chunkYDiff = newChunkY ^ lastChunkY; -+ -+ if ((chunkDiff | chunkYDiff) != 0) { -+ if (chunkDiff != 0) { -+ LevelChunk chunk = chunkProvider.getChunkAtIfLoadedImmediately(newChunkX, newChunkZ); -+ lastChunk = chunk == null ? null : chunk.getSections(); // diff: don't load chunks for this -+ } -+ final int sectionY = newChunkY - minSection; -+ lastSection = lastChunk != null && sectionY >= 0 && sectionY < lastChunk.length ? lastChunk[sectionY].states : null; -+ -+ lastChunkX = newChunkX; -+ lastChunkY = newChunkY; -+ lastChunkZ = newChunkZ; -+ } -+ -+ final BlockState blockState; -+ if (lastSection != null && !(blockState = lastSection.get((currX & 15) | ((currZ & 15) << 4) | ((currY & 15) << (4+4)))).isAir()) { -+ final net.minecraft.world.phys.shapes.VoxelShape blockCollision = clipContext.getBlockShape(blockState, level, currPos); -+ -+ final net.minecraft.world.phys.BlockHitResult blockHit = blockCollision.isEmpty() ? null : level.clipWithInteractionOverride(from, to, currPos, blockCollision, blockState); -+ -+ final net.minecraft.world.phys.shapes.VoxelShape fluidCollision; -+ final FluidState fluidState; -+ if (clipContext.fluid != ClipContext.Fluid.NONE && (fluidState = blockState.getFluidState()) != AIR_FLUIDSTATE) { -+ fluidCollision = clipContext.getFluidShape(fluidState, level, currPos); -+ -+ final net.minecraft.world.phys.BlockHitResult fluidHit = fluidCollision.clip(from, to, currPos); -+ -+ if (fluidHit != null) { -+ if (blockHit == null) { -+ return fluidHit; -+ } -+ -+ return from.distanceToSqr(blockHit.getLocation()) <= from.distanceToSqr(fluidHit.getLocation()) ? blockHit : fluidHit; -+ } -+ } -+ -+ if (blockHit != null) { -+ return blockHit; -+ } -+ } // else: usually fall here -+ -+ if (normalizedCurrX > 1.0 && normalizedCurrY > 1.0 && normalizedCurrZ > 1.0) { -+ return miss(clipContext); -+ } -+ -+ // inc the smallest normalized coordinate -+ -+ if (normalizedCurrX < normalizedCurrY) { -+ if (normalizedCurrX < normalizedCurrZ) { -+ currX += dx; -+ normalizedCurrX += normalizedDiffX; -+ } else { -+ // x < y && x >= z <--> z < y && z <= x -+ currZ += dz; -+ normalizedCurrZ += normalizedDiffZ; -+ } -+ } else if (normalizedCurrY < normalizedCurrZ) { -+ // y <= x && y < z -+ currY += dy; -+ normalizedCurrY += normalizedDiffY; -+ } else { -+ // y <= x && z <= y <--> z <= y && z <= x -+ currZ += dz; -+ normalizedCurrZ += normalizedDiffZ; -+ } -+ } -+ } -+ -+ @Override -+ public final net.minecraft.world.phys.BlockHitResult clip(final ClipContext clipContext) { -+ // can only do this in this class, as not everything that implements BlockGetter can retrieve chunks -+ return fastClip(clipContext.getFrom(), clipContext.getTo(), this, clipContext); -+ } -+ -+ @Override -+ public final boolean noCollision(final Entity entity, final AABB box, final boolean loadChunks) { -+ int flags = io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_CHECK_ONLY; -+ if (entity != null) { -+ flags |= io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_CHECK_BORDER; -+ } -+ if (loadChunks) { -+ flags |= io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_LOAD_CHUNKS; -+ } -+ if (io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, entity, box, null, null, flags, null)) { -+ return false; -+ } -+ -+ return !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, entity, box, null, flags, null); -+ } -+ -+ @Override -+ public final boolean collidesWithSuffocatingBlock(final Entity entity, final AABB box) { -+ return io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, entity, box, null, null, -+ io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_CHECK_ONLY, -+ (final BlockState state, final BlockPos pos) -> { -+ return state.isSuffocating(Level.this, pos); -+ } -+ ); -+ } -+ -+ private static net.minecraft.world.phys.shapes.VoxelShape inflateAABBToVoxel(final AABB aabb, final double x, final double y, final double z) { -+ return net.minecraft.world.phys.shapes.Shapes.create( -+ aabb.minX - x, -+ aabb.minY - y, -+ aabb.minZ - z, -+ -+ aabb.maxX + x, -+ aabb.maxY + y, -+ aabb.maxZ + z -+ ); -+ } -+ -+ @Override -+ public final java.util.Optional findFreePosition(final Entity entity, final net.minecraft.world.phys.shapes.VoxelShape boundsShape, final Vec3 fromPosition, -+ final double rangeX, final double rangeY, final double rangeZ) { -+ if (boundsShape.isEmpty()) { -+ return java.util.Optional.empty(); -+ } -+ -+ final double expandByX = rangeX * 0.5; -+ final double expandByY = rangeY * 0.5; -+ final double expandByZ = rangeZ * 0.5; -+ -+ // note: it is useless to look at shapes outside of range / 2.0 -+ final AABB collectionVolume = boundsShape.bounds().inflate(expandByX, expandByY, expandByZ); -+ -+ final List aabbs = new java.util.ArrayList<>(); -+ final List voxels = new java.util.ArrayList<>(); -+ -+ io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder( -+ this, entity, collectionVolume, voxels, aabbs, -+ io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_CHECK_BORDER, -+ null -+ ); -+ -+ // push voxels into aabbs -+ for (int i = 0, len = voxels.size(); i < len; ++i) { -+ aabbs.addAll(voxels.get(i).toAabbs()); -+ } -+ -+ // expand AABBs -+ final net.minecraft.world.phys.shapes.VoxelShape first = aabbs.isEmpty() ? net.minecraft.world.phys.shapes.Shapes.empty() : inflateAABBToVoxel(aabbs.get(0), expandByX, expandByY, expandByZ); -+ final net.minecraft.world.phys.shapes.VoxelShape[] rest = new net.minecraft.world.phys.shapes.VoxelShape[Math.max(0, aabbs.size() - 1)]; -+ -+ for (int i = 1, len = aabbs.size(); i < len; ++i) { -+ rest[i - 1] = inflateAABBToVoxel(aabbs.get(i), expandByX, expandByY, expandByZ); -+ } -+ -+ // use optimized implementation of ORing the shapes together -+ final net.minecraft.world.phys.shapes.VoxelShape joined = net.minecraft.world.phys.shapes.Shapes.or(first, rest); -+ -+ // find free space -+ // can use unoptimized join here (instead of join()), as closestPointTo uses toAabbs() -+ final net.minecraft.world.phys.shapes.VoxelShape freeSpace = net.minecraft.world.phys.shapes.Shapes.joinUnoptimized( -+ boundsShape, joined, net.minecraft.world.phys.shapes.BooleanOp.ONLY_FIRST -+ ); -+ -+ return freeSpace.closestPointTo(fromPosition); -+ } -+ -+ @Override -+ public final java.util.Optional findSupportingBlock(final Entity entity, final AABB aabb) { -+ final int minBlockX = Mth.floor(aabb.minX - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) - 1; -+ final int maxBlockX = Mth.floor(aabb.maxX + io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) + 1; -+ -+ final int minBlockY = Mth.floor(aabb.minY - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) - 1; -+ final int maxBlockY = Mth.floor(aabb.maxY + io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) + 1; -+ -+ final int minBlockZ = Mth.floor(aabb.minZ - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) - 1; -+ final int maxBlockZ = Mth.floor(aabb.maxZ + io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) + 1; -+ -+ io.papermc.paper.util.CollisionUtil.LazyEntityCollisionContext collisionContext = null; -+ -+ final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); -+ BlockPos selected = null; -+ double selectedDistance = Double.MAX_VALUE; -+ -+ final Vec3 entityPos = entity.position(); -+ -+ LevelChunk lastChunk = null; -+ int lastChunkX = Integer.MIN_VALUE; -+ int lastChunkZ = Integer.MIN_VALUE; -+ -+ final net.minecraft.server.level.ServerChunkCache chunkProvider = (net.minecraft.server.level.ServerChunkCache)this.getChunkSource(); -+ -+ for (int currZ = minBlockZ; currZ <= maxBlockZ; ++currZ) { -+ pos.setZ(currZ); -+ for (int currX = minBlockX; currX <= maxBlockX; ++currX) { -+ pos.setX(currX); -+ -+ final int newChunkX = currX >> 4; -+ final int newChunkZ = currZ >> 4; -+ -+ final int chunkDiff = ((newChunkX ^ lastChunkX) | (newChunkZ ^ lastChunkZ)); -+ -+ if (chunkDiff != 0) { -+ lastChunk = chunkProvider.getChunkAtIfLoadedImmediately(newChunkX, newChunkZ); -+ } -+ -+ if (lastChunk == null) { -+ continue; -+ } -+ for (int currY = minBlockY; currY <= maxBlockY; ++currY) { -+ int edgeCount = ((currX == minBlockX || currX == maxBlockX) ? 1 : 0) + -+ ((currY == minBlockY || currY == maxBlockY) ? 1 : 0) + -+ ((currZ == minBlockZ || currZ == maxBlockZ) ? 1 : 0); -+ if (edgeCount == 3) { -+ continue; -+ } -+ -+ pos.setY(currY); -+ -+ final double distance = pos.distToCenterSqr(entityPos); -+ if (distance > selectedDistance || (distance == selectedDistance && selected.compareTo(pos) >= 0)) { -+ continue; -+ } -+ -+ final BlockState state = lastChunk.getBlockState(currX, currY, currZ); -+ if (state.emptyCollisionShape()) { -+ continue; -+ } -+ -+ if ((edgeCount != 1 || state.hasLargeCollisionShape()) && (edgeCount != 2 || state.getBlock() == Blocks.MOVING_PISTON)) { -+ if (collisionContext == null) { -+ collisionContext = new io.papermc.paper.util.CollisionUtil.LazyEntityCollisionContext(entity); -+ } -+ final net.minecraft.world.phys.shapes.VoxelShape blockCollision = state.getCollisionShape(lastChunk, pos, collisionContext); -+ if (blockCollision.isEmpty()) { -+ continue; -+ } -+ -+ // avoid VoxelShape#move by shifting the entity collision shape instead -+ final AABB shiftedAABB = aabb.move(-(double)currX, -(double)currY, -(double)currZ); -+ -+ final AABB singleAABB = blockCollision.getSingleAABBRepresentation(); -+ if (singleAABB != null) { -+ if (!io.papermc.paper.util.CollisionUtil.voxelShapeIntersect(singleAABB, shiftedAABB)) { -+ continue; -+ } -+ -+ selected = pos.immutable(); -+ selectedDistance = distance; -+ continue; -+ } -+ -+ if (!io.papermc.paper.util.CollisionUtil.voxelShapeIntersectNoEmpty(blockCollision, shiftedAABB)) { -+ continue; -+ } -+ -+ selected = pos.immutable(); -+ selectedDistance = distance; -+ continue; -+ } -+ } -+ } -+ } -+ -+ return java.util.Optional.ofNullable(selected); -+ } -+ // Paper end - optimise collisions - @Override - public boolean isClientSide() { - return this.isClientSide; -@@ -1002,7 +1366,17 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - @Override - public boolean noCollision(@Nullable Entity entity, AABB box) { - if (entity instanceof net.minecraft.world.entity.decoration.ArmorStand && !entity.level().paperConfig().entities.armorStands.doCollisionEntityLookups) return false; -- return LevelAccessor.super.noCollision(entity, box); -+ // Paper start - optimise collisions -+ int flags = io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_CHECK_ONLY; -+ if (entity != null) { -+ flags |= io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_CHECK_BORDER; -+ } -+ if (io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, entity, box, null, null, flags, null)) { -+ return false; -+ } -+ -+ return !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, entity, box, null, flags, null); -+ // Paper end - optimise collisions - } - // Paper end - Option to prevent armor stands from doing entity lookups - -diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java -index 45704653310efe9cb755a644674b54b8722c2c84..2c356d2c40fe52672f108b2dbb89b7a27616e4d1 100644 ---- a/src/main/java/net/minecraft/world/level/block/Block.java -+++ b/src/main/java/net/minecraft/world/level/block/Block.java -@@ -279,7 +279,7 @@ public class Block extends BlockBehaviour implements ItemLike { - } - - public static boolean isShapeFullBlock(VoxelShape shape) { -- return (Boolean) Block.SHAPE_FULL_BLOCK_CACHE.getUnchecked(shape); -+ return shape.isFullBlock(); // Paper - optimise collisions - } - - public void animateTick(BlockState state, Level world, BlockPos pos, RandomSource random) {} -diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java -index a768b07dae4bf75b68e3bc1d3de4b68fc7d23842..cff9e93778c5625a801eab9438856b9cb248ba7e 100644 ---- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java -+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java -@@ -833,6 +833,10 @@ public abstract class BlockBehaviour implements FeatureElement { - this.spawnTerrainParticles = blockbase_info.spawnTerrainParticles; - this.instrument = blockbase_info.instrument; - this.replaceable = blockbase_info.replaceable; -+ // Paper start - optimise collisions -+ this.id1 = it.unimi.dsi.fastutil.HashCommon.murmurHash3(it.unimi.dsi.fastutil.HashCommon.murmurHash3(ID_GENERATOR.getAndIncrement() + RANDOM_OFFSET) + RANDOM_OFFSET); -+ this.id2 = it.unimi.dsi.fastutil.HashCommon.murmurHash3(it.unimi.dsi.fastutil.HashCommon.murmurHash3(ID_GENERATOR.getAndIncrement() + RANDOM_OFFSET) + RANDOM_OFFSET); -+ // Paper end - optimise collisions - } - // Paper start - Perf: impl cached craft block data, lazy load to fix issue with loading at the wrong time - private org.bukkit.craftbukkit.block.data.CraftBlockData cachedCraftBlockData; -@@ -869,6 +873,52 @@ public abstract class BlockBehaviour implements FeatureElement { - return this.shapeExceedsCube; - } - // Paper end -+ // Paper start - optimise collisions -+ private static final int RANDOM_OFFSET = 704237939; -+ private static final Direction[] DIRECTIONS_CACHED = Direction.values(); -+ private static final java.util.concurrent.atomic.AtomicInteger ID_GENERATOR = new java.util.concurrent.atomic.AtomicInteger(); -+ private final int id1, id2; -+ private boolean occludesFullBlock; -+ private boolean emptyCollisionShape; -+ private VoxelShape constantCollisionShape; -+ private AABB constantAABBCollision; -+ private static void initCaches(final VoxelShape shape) { -+ shape.isFullBlock(); -+ shape.occludesFullBlock(); -+ shape.toAabbs(); -+ if (!shape.isEmpty()) { -+ shape.bounds(); -+ } -+ } -+ -+ public final boolean hasCache() { -+ return this.cache != null; -+ } -+ -+ public final boolean occludesFullBlock() { -+ return this.occludesFullBlock; -+ } -+ -+ public final boolean emptyCollisionShape() { -+ return this.emptyCollisionShape; -+ } -+ -+ public final int uniqueId1() { -+ return this.id1; -+ } -+ -+ public final int uniqueId2() { -+ return this.id2; -+ } -+ -+ public final VoxelShape getConstantCollisionShape() { -+ return this.constantCollisionShape; -+ } -+ -+ public final AABB getConstantCollisionAABB() { -+ return this.constantAABBCollision; -+ } -+ // Paper end - optimise collisions - - public void initCache() { - this.fluidState = ((Block) this.owner).getFluidState(this.asState()); -@@ -883,6 +933,39 @@ public abstract class BlockBehaviour implements FeatureElement { - this.isConditionallyFullOpaque = this.canOcclude & this.useShapeForLightOcclusion; - this.opacityIfCached = this.cache == null || this.isConditionallyFullOpaque ? -1 : this.cache.lightBlock; - // Paper end - rewrite chunk system -+ // Paper start - optimise collisions -+ if (this.cache != null) { -+ final VoxelShape collisionShape = this.cache.collisionShape; -+ try { -+ this.constantCollisionShape = this.getCollisionShape(null, null, null); -+ this.constantAABBCollision = this.constantCollisionShape == null ? null : this.constantCollisionShape.getSingleAABBRepresentation(); -+ } catch (final Throwable throwable) { -+ this.constantCollisionShape = null; -+ this.constantAABBCollision = null; -+ } -+ this.occludesFullBlock = collisionShape.occludesFullBlock(); -+ this.emptyCollisionShape = collisionShape.isEmpty(); -+ // init caches -+ initCaches(collisionShape); -+ if (collisionShape != Shapes.empty() && collisionShape != Shapes.block()) { -+ for (final Direction direction : DIRECTIONS_CACHED) { -+ // initialise the directional face shape cache as well -+ final VoxelShape shape = Shapes.getFaceShape(collisionShape, direction); -+ initCaches(shape); -+ } -+ } -+ if (this.cache.occlusionShapes != null) { -+ for (final VoxelShape shape : this.cache.occlusionShapes) { -+ initCaches(shape); -+ } -+ } -+ } else { -+ this.occludesFullBlock = false; -+ this.emptyCollisionShape = false; -+ this.constantCollisionShape = null; -+ this.constantAABBCollision = null; -+ } -+ // Paper end - optimise collisions - } - - public Block getBlock() { -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -index f2e11bff414b521295bde721e01ae2166a6a3fd6..c7b4c4463b26445964ac1fa51e66e35798ea4c41 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -@@ -25,6 +25,22 @@ public class LevelChunkSection { - public final PalettedContainer states; - // CraftBukkit start - read/write - private PalettedContainer> biomes; -+ // Paper start - optimise collisions -+ private int specialCollidingBlocks; -+ -+ private void updateBlockCallback(final int x, final int y, final int z, final BlockState oldState, final BlockState newState) { -+ if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(newState)) { -+ ++this.specialCollidingBlocks; -+ } -+ if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(oldState)) { -+ --this.specialCollidingBlocks; -+ } -+ } -+ -+ public final int getSpecialCollidingBlocks() { -+ return this.specialCollidingBlocks; -+ } -+ // Paper end - optimise collisions - - public LevelChunkSection(PalettedContainer datapaletteblock, PalettedContainer> palettedcontainerro) { - // CraftBukkit end -@@ -61,8 +77,8 @@ public class LevelChunkSection { - return this.setBlockState(x, y, z, state, true); - } - -- public BlockState setBlockState(int x, int y, int z, BlockState state, boolean lock) { -- BlockState iblockdata1; -+ public BlockState setBlockState(int x, int y, int z, BlockState state, boolean lock) { // Paper - state -> new state -+ BlockState iblockdata1; // Paper - iblockdata1 -> oldState - - if (lock) { - iblockdata1 = (BlockState) this.states.getAndSet(x, y, z, state); -@@ -95,6 +111,7 @@ public class LevelChunkSection { - ++this.tickingFluidCount; - } - -+ this.updateBlockCallback(x, y, z, iblockdata1, state); // Paper - optimise collisions - return iblockdata1; - } - -@@ -120,6 +137,7 @@ public class LevelChunkSection { - public int nonEmptyBlockCount; - public int tickingBlockCount; - public int tickingFluidCount; -+ public int specialCollidingBlocks; // Paper - optimise collisions - - a(final LevelChunkSection chunksection) {} - -@@ -140,6 +158,11 @@ public class LevelChunkSection { - } - } - -+ // Paper start - optimise collisions -+ if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(iblockdata)) { -+ this.specialCollidingBlocks += i; -+ } -+ // Paper end - optimise collisions - } - } - -@@ -149,6 +172,7 @@ public class LevelChunkSection { - this.nonEmptyBlockCount = (short) a0.nonEmptyBlockCount; - this.tickingBlockCount = (short) a0.tickingBlockCount; - this.tickingFluidCount = (short) a0.tickingFluidCount; -+ this.specialCollidingBlocks = a0.specialCollidingBlocks; // Paper - optimise collisions - } - - public PalettedContainer getStates() { -diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -index 1c0712295695727ee9c4d430d4157b8e17cbd71f..c2943d892b067b3f1fb3b93301a092e912d71f08 100644 ---- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -+++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -@@ -240,6 +240,17 @@ public abstract class FlowingFluid extends Fluid { - } - - private boolean canPassThroughWall(Direction face, BlockGetter world, BlockPos pos, BlockState state, BlockPos fromPos, BlockState fromState) { -+ // Paper start - optimise collisions -+ if (state.emptyCollisionShape() & fromState.emptyCollisionShape()) { -+ // don't even try to cache simple cases -+ return true; -+ } -+ -+ if (state.occludesFullBlock() | fromState.occludesFullBlock()) { -+ // don't even try to cache simple cases -+ return false; -+ } -+ // Paper end - optimise collisions - Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap; - - if (!state.getBlock().hasDynamicShape() && !fromState.getBlock().hasDynamicShape()) { -diff --git a/src/main/java/net/minecraft/world/phys/AABB.java b/src/main/java/net/minecraft/world/phys/AABB.java -index c8f7c43134e7c51ce8af5b3c1a28c11db67715a2..e4e99fb9f1670dbc1c032dd834c4bfb3c9d4a8ca 100644 ---- a/src/main/java/net/minecraft/world/phys/AABB.java -+++ b/src/main/java/net/minecraft/world/phys/AABB.java -@@ -26,6 +26,17 @@ public class AABB { - this.maxZ = Math.max(z1, z2); - } - -+ // Paper start -+ public AABB(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, boolean dummy) { -+ this.minX = minX; -+ this.minY = minY; -+ this.minZ = minZ; -+ this.maxX = maxX; -+ this.maxY = maxY; -+ this.maxZ = maxZ; -+ } -+ // Paper end -+ - public AABB(BlockPos pos) { - this((double)pos.getX(), (double)pos.getY(), (double)pos.getZ(), (double)(pos.getX() + 1), (double)(pos.getY() + 1), (double)(pos.getZ() + 1)); - } -@@ -326,7 +337,7 @@ public class AABB { - } - - @Nullable -- private static Direction getDirection( -+ public static Direction getDirection( // Paper - optimise collisions - public - AABB box, Vec3 intersectingVector, double[] traceDistanceResult, @Nullable Direction approachDirection, double deltaX, double deltaY, double deltaZ - ) { - if (deltaX > 1.0E-7) { -diff --git a/src/main/java/net/minecraft/world/phys/shapes/ArrayVoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/ArrayVoxelShape.java -index 4fee67f7214b464b9e09862778e3ef187fcb8b72..462fe8ce538f2bd9c7ba10f521514e6f6a23c868 100644 ---- a/src/main/java/net/minecraft/world/phys/shapes/ArrayVoxelShape.java -+++ b/src/main/java/net/minecraft/world/phys/shapes/ArrayVoxelShape.java -@@ -20,7 +20,7 @@ public class ArrayVoxelShape extends VoxelShape { - ); - } - -- ArrayVoxelShape(DiscreteVoxelShape shape, DoubleList xPoints, DoubleList yPoints, DoubleList zPoints) { -+ public ArrayVoxelShape(DiscreteVoxelShape shape, DoubleList xPoints, DoubleList yPoints, DoubleList zPoints) { // Paper - optimise collisions - public - super(shape); - int i = shape.getXSize() + 1; - int j = shape.getYSize() + 1; -@@ -34,6 +34,7 @@ public class ArrayVoxelShape extends VoxelShape { - new IllegalArgumentException("Lengths of point arrays must be consistent with the size of the VoxelShape.") - ); - } -+ this.initCache(); // Paper - optimise collisions - } - - @Override -@@ -49,4 +50,5 @@ public class ArrayVoxelShape extends VoxelShape { - throw new IllegalArgumentException(); - } - } -+ - } -diff --git a/src/main/java/net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape.java -index e8f3307727e7e3da9a7629cafc6e1ee53790b75d..a71421947b161697d43bd8602e2af95aa272ed3f 100644 ---- a/src/main/java/net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape.java -+++ b/src/main/java/net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape.java -@@ -4,13 +4,13 @@ import java.util.BitSet; - import net.minecraft.core.Direction; - - public final class BitSetDiscreteVoxelShape extends DiscreteVoxelShape { -- private final BitSet storage; -- private int xMin; -- private int yMin; -- private int zMin; -- private int xMax; -- private int yMax; -- private int zMax; -+ public final BitSet storage; // Paper - optimise collisions - public -+ public int xMin; // Paper - optimise collisions - public -+ public int yMin; // Paper - optimise collisions - public -+ public int zMin; // Paper - optimise collisions - public -+ public int xMax; // Paper - optimise collisions - public -+ public int yMax; // Paper - optimise collisions - public -+ public int zMax; // Paper - optimise collisions - public - - public BitSetDiscreteVoxelShape(int sizeX, int sizeY, int sizeZ) { - super(sizeX, sizeY, sizeZ); -@@ -151,45 +151,106 @@ public final class BitSetDiscreteVoxelShape extends DiscreteVoxelShape { - } - - protected static void forAllBoxes(DiscreteVoxelShape voxelSet, DiscreteVoxelShape.IntLineConsumer callback, boolean coalesce) { -- BitSetDiscreteVoxelShape bitSetDiscreteVoxelShape = new BitSetDiscreteVoxelShape(voxelSet); -+ // Paper start - optimise collisions -+ // called with the shape of a VoxelShape, so we can expect the cache to exist -+ final io.papermc.paper.util.collisions.CachedShapeData cache = voxelSet.getOrCreateCachedShapeData(); -+ -+ final int sizeX = cache.sizeX(); -+ final int sizeY = cache.sizeY(); -+ final int sizeZ = cache.sizeZ(); -+ -+ int indexX; -+ int indexY = 0; -+ int indexZ; -+ -+ int incY = sizeZ; -+ int incX = sizeZ*sizeY; -+ -+ long[] bitset = cache.voxelSet(); -+ -+ // index = z + y*size_z + x*(size_z*size_y) -+ -+ if (!coalesce) { -+ // due to the odd selection of loop order (which does affect behavior, unfortunately) we can't simply -+ // increment an index in the Z loop, and have to perform this trash (keeping track of 3 counters) to avoid -+ // the multiplication -+ for (int y = 0; y < sizeY; ++y, indexY += incY) { -+ indexX = indexY; -+ for (int x = 0; x < sizeX; ++x, indexX += incX) { -+ indexZ = indexX; -+ for (int z = 0; z < sizeZ; ++z, ++indexZ) { -+ if ((bitset[indexZ >>> 6] & (1L << indexZ)) != 0L) { -+ callback.consume(x, y, z, x + 1, y + 1, z + 1); -+ } -+ } -+ } -+ } -+ } else { -+ // same notes about loop order as the above -+ // this branch is actually important to optimise, as it affects uncached toAabbs() (which affects optimize()) - -- for (int i = 0; i < bitSetDiscreteVoxelShape.ySize; i++) { -- for (int j = 0; j < bitSetDiscreteVoxelShape.xSize; j++) { -- int k = -1; -+ // only clone when we may write to it -+ bitset = bitset.clone(); - -- for (int l = 0; l <= bitSetDiscreteVoxelShape.zSize; l++) { -- if (bitSetDiscreteVoxelShape.isFullWide(j, i, l)) { -- if (coalesce) { -- if (k == -1) { -- k = l; -- } -- } else { -- callback.consume(j, i, l, j + 1, i + 1, l + 1); -+ for (int y = 0; y < sizeY; ++y, indexY += incY) { -+ indexX = indexY; -+ for (int x = 0; x < sizeX; ++x, indexX += incX) { -+ for (int zIdx = indexX, endIndex = indexX + sizeZ; zIdx < endIndex;) { -+ final int firstSetZ = io.papermc.paper.util.collisions.FlatBitsetUtil.firstSet(bitset, zIdx, endIndex); -+ -+ if (firstSetZ == -1) { -+ break; -+ } -+ -+ int lastSetZ = io.papermc.paper.util.collisions.FlatBitsetUtil.firstClear(bitset, firstSetZ, endIndex); -+ if (lastSetZ == -1) { -+ lastSetZ = endIndex; - } -- } else if (k != -1) { -- int m = j; -- int n = i; -- bitSetDiscreteVoxelShape.clearZStrip(k, l, j, i); -- -- while (bitSetDiscreteVoxelShape.isZStripFull(k, l, m + 1, i)) { -- bitSetDiscreteVoxelShape.clearZStrip(k, l, m + 1, i); -- m++; -+ -+ io.papermc.paper.util.collisions.FlatBitsetUtil.clearRange(bitset, firstSetZ, lastSetZ); -+ -+ // try to merge neighbouring on the X axis -+ int endX = x + 1; // exclusive -+ for (int neighbourIdxStart = firstSetZ + incX, neighbourIdxEnd = lastSetZ + incX; -+ endX < sizeX && io.papermc.paper.util.collisions.FlatBitsetUtil.isRangeSet(bitset, neighbourIdxStart, neighbourIdxEnd); -+ neighbourIdxStart += incX, neighbourIdxEnd += incX) { -+ -+ ++endX; -+ io.papermc.paper.util.collisions.FlatBitsetUtil.clearRange(bitset, neighbourIdxStart, neighbourIdxEnd); - } - -- while (bitSetDiscreteVoxelShape.isXZRectangleFull(j, m + 1, k, l, n + 1)) { -- for (int o = j; o <= m; o++) { -- bitSetDiscreteVoxelShape.clearZStrip(k, l, o, n + 1); -+ // try to merge neighbouring on the Y axis -+ -+ int endY; // exclusive -+ int firstSetZY, lastSetZY; -+ y_merge: -+ for (endY = y + 1, firstSetZY = firstSetZ + incY, lastSetZY = lastSetZ + incY; endY < sizeY; -+ firstSetZY += incY, lastSetZY += incY) { -+ -+ // test the whole XZ range -+ for (int testX = x, start = firstSetZY, end = lastSetZY; testX < endX; -+ ++testX, start += incX, end += incX) { -+ if (!io.papermc.paper.util.collisions.FlatBitsetUtil.isRangeSet(bitset, start, end)) { -+ break y_merge; -+ } - } - -- n++; -+ ++endY; -+ -+ // passed, so we can clear it -+ for (int testX = x, start = firstSetZY, end = lastSetZY; testX < endX; -+ ++testX, start += incX, end += incX) { -+ io.papermc.paper.util.collisions.FlatBitsetUtil.clearRange(bitset, start, end); -+ } - } - -- callback.consume(j, i, k, m + 1, n + 1, l); -- k = -1; -+ callback.consume(x, y, firstSetZ - indexX, endX, endY, lastSetZ - indexX); -+ zIdx = lastSetZ; - } - } - } - } -+ // Paper end - optimise collisions - } - - private boolean isZStripFull(int z1, int z2, int x, int y) { -diff --git a/src/main/java/net/minecraft/world/phys/shapes/CubeVoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/CubeVoxelShape.java -index d812949c7329ae2696b38dc792fa011ba87decb9..8b0f0daa8fe9d200ff7f62b521dd40b72a556644 100644 ---- a/src/main/java/net/minecraft/world/phys/shapes/CubeVoxelShape.java -+++ b/src/main/java/net/minecraft/world/phys/shapes/CubeVoxelShape.java -@@ -7,6 +7,7 @@ import net.minecraft.util.Mth; - public final class CubeVoxelShape extends VoxelShape { - protected CubeVoxelShape(DiscreteVoxelShape voxels) { - super(voxels); -+ this.initCache(); // Paper - optimise collisions - } - - @Override -diff --git a/src/main/java/net/minecraft/world/phys/shapes/DiscreteVoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/DiscreteVoxelShape.java -index 01693ba050b12b9debcdaefceeff9cbcd503b369..a7af766f1c28f2c9ca2a430808ac4c07fe24df68 100644 ---- a/src/main/java/net/minecraft/world/phys/shapes/DiscreteVoxelShape.java -+++ b/src/main/java/net/minecraft/world/phys/shapes/DiscreteVoxelShape.java -@@ -9,6 +9,71 @@ public abstract class DiscreteVoxelShape { - protected final int ySize; - protected final int zSize; - -+ // Paper start - optimise collisions -+ private io.papermc.paper.util.collisions.CachedShapeData cachedShapeData; -+ -+ public final io.papermc.paper.util.collisions.CachedShapeData getOrCreateCachedShapeData() { -+ if (this.cachedShapeData != null) { -+ return this.cachedShapeData; -+ } -+ -+ final DiscreteVoxelShape discreteVoxelShape = (DiscreteVoxelShape)(Object)this; -+ -+ final int sizeX = discreteVoxelShape.getXSize(); -+ final int sizeY = discreteVoxelShape.getYSize(); -+ final int sizeZ = discreteVoxelShape.getZSize(); -+ -+ final int maxIndex = sizeX * sizeY * sizeZ; // exclusive -+ -+ final int longsRequired = (maxIndex + (Long.SIZE - 1)) >>> 6; -+ long[] voxelSet; -+ -+ final boolean isEmpty = discreteVoxelShape.isEmpty(); -+ -+ if (discreteVoxelShape instanceof BitSetDiscreteVoxelShape bitsetShape) { -+ voxelSet = bitsetShape.storage.toLongArray(); -+ if (voxelSet.length < longsRequired) { -+ // happens when the later long values are 0L, so we need to resize -+ voxelSet = java.util.Arrays.copyOf(voxelSet, longsRequired); -+ } -+ } else { -+ voxelSet = new long[longsRequired]; -+ if (!isEmpty) { -+ final int mulX = sizeZ * sizeY; -+ for (int x = 0; x < sizeX; ++x) { -+ for (int y = 0; y < sizeY; ++y) { -+ for (int z = 0; z < sizeZ; ++z) { -+ if (discreteVoxelShape.isFull(x, y, z)) { -+ // index = z + y*size_z + x*(size_z*size_y) -+ final int index = z + y * sizeZ + x * mulX; -+ -+ voxelSet[index >>> 6] |= 1L << index; -+ } -+ } -+ } -+ } -+ } -+ } -+ -+ final boolean hasSingleAABB = sizeX == 1 && sizeY == 1 && sizeZ == 1 && !isEmpty && discreteVoxelShape.isFull(0, 0, 0); -+ -+ final int minFullX = discreteVoxelShape.firstFull(Direction.Axis.X); -+ final int minFullY = discreteVoxelShape.firstFull(Direction.Axis.Y); -+ final int minFullZ = discreteVoxelShape.firstFull(Direction.Axis.Z); -+ -+ final int maxFullX = discreteVoxelShape.lastFull(Direction.Axis.X); -+ final int maxFullY = discreteVoxelShape.lastFull(Direction.Axis.Y); -+ final int maxFullZ = discreteVoxelShape.lastFull(Direction.Axis.Z); -+ -+ return this.cachedShapeData = new io.papermc.paper.util.collisions.CachedShapeData( -+ sizeX, sizeY, sizeZ, voxelSet, -+ minFullX, minFullY, minFullZ, -+ maxFullX, maxFullY, maxFullZ, -+ isEmpty, hasSingleAABB -+ ); -+ } -+ // Paper end - optimise collisions -+ - protected DiscreteVoxelShape(int sizeX, int sizeY, int sizeZ) { - if (sizeX >= 0 && sizeY >= 0 && sizeZ >= 0) { - this.xSize = sizeX; -diff --git a/src/main/java/net/minecraft/world/phys/shapes/OffsetDoubleList.java b/src/main/java/net/minecraft/world/phys/shapes/OffsetDoubleList.java -index 7ec02a7849437a18860aa0df7d9ddd71b2447d4c..5e45e49ab09344cb95736f4124b1c6e002ef5b82 100644 ---- a/src/main/java/net/minecraft/world/phys/shapes/OffsetDoubleList.java -+++ b/src/main/java/net/minecraft/world/phys/shapes/OffsetDoubleList.java -@@ -4,8 +4,8 @@ import it.unimi.dsi.fastutil.doubles.AbstractDoubleList; - import it.unimi.dsi.fastutil.doubles.DoubleList; - - public class OffsetDoubleList extends AbstractDoubleList { -- private final DoubleList delegate; -- private final double offset; -+ public final DoubleList delegate; // Paper - optimise collisions - public -+ public final double offset; // Paper - optimise collisions - public - - public OffsetDoubleList(DoubleList oldList, double offset) { - this.delegate = oldList; -diff --git a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java -index 86df4ef44d0a5107ee929dfd40d8ccb0779e8bfc..fbf1a559aefe444410b63a773374e011e4964e16 100644 ---- a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java -+++ b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java -@@ -16,9 +16,15 @@ public final class Shapes { - public static final double EPSILON = 1.0E-7; - public static final double BIG_EPSILON = 1.0E-6; - private static final VoxelShape BLOCK = Util.make(() -> { -- DiscreteVoxelShape discreteVoxelShape = new BitSetDiscreteVoxelShape(1, 1, 1); -- discreteVoxelShape.fill(0, 0, 0); -- return new CubeVoxelShape(discreteVoxelShape); -+ // Paper start - optimise collisions - force arrayvoxelshape -+ final DiscreteVoxelShape shape = new BitSetDiscreteVoxelShape(1, 1, 1); -+ shape.fill(0, 0, 0); -+ -+ return new ArrayVoxelShape( -+ shape, -+ io.papermc.paper.util.CollisionUtil.ZERO_ONE, io.papermc.paper.util.CollisionUtil.ZERO_ONE, io.papermc.paper.util.CollisionUtil.ZERO_ONE -+ ); -+ // Paper end - optimise collisions - force arrayvoxelshape - }); - public static final VoxelShape INFINITY = box( - Double.NEGATIVE_INFINITY, -@@ -35,6 +41,30 @@ public final class Shapes { - new DoubleArrayList(new double[]{0.0}) - ); - -+ // Paper start - optimise collisions - force arrayvoxelshape -+ private static final DoubleArrayList[] PARTS_BY_BITS = new DoubleArrayList[] { -+ DoubleArrayList.wrap(generateCubeParts(1 << 0)), -+ DoubleArrayList.wrap(generateCubeParts(1 << 1)), -+ DoubleArrayList.wrap(generateCubeParts(1 << 2)), -+ DoubleArrayList.wrap(generateCubeParts(1 << 3)) -+ }; -+ -+ private static double[] generateCubeParts(final int parts) { -+ // note: parts is a power of two, so we do not need to worry about loss of precision here -+ // note: parts is from [2^0, 2^3] -+ final double inc = 1.0 / (double)parts; -+ -+ final double[] ret = new double[parts + 1]; -+ double val = 0.0; -+ for (int i = 0; i <= parts; ++i) { -+ ret[i] = val; -+ val += inc; -+ } -+ -+ return ret; -+ } -+ // Paper end - optimise collisions - force arrayvoxelshape -+ - public static VoxelShape empty() { - return EMPTY; - } -@@ -53,35 +83,39 @@ public final class Shapes { - - public static VoxelShape create(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { - if (!(maxX - minX < 1.0E-7) && !(maxY - minY < 1.0E-7) && !(maxZ - minZ < 1.0E-7)) { -- int i = findBits(minX, maxX); -- int j = findBits(minY, maxY); -- int k = findBits(minZ, maxZ); -- if (i < 0 || j < 0 || k < 0) { -- return new ArrayVoxelShape( -- BLOCK.shape, -- DoubleArrayList.wrap(new double[]{minX, maxX}), -- DoubleArrayList.wrap(new double[]{minY, maxY}), -- DoubleArrayList.wrap(new double[]{minZ, maxZ}) -- ); -- } else if (i == 0 && j == 0 && k == 0) { -- return block(); -+ // Paper start - optimise collisions -+ // force ArrayVoxelShape in every case -+ final int bitsX = findBits(minX, maxX); -+ final int bitsY = findBits(minY, maxY); -+ final int bitsZ = findBits(minZ, maxZ); -+ if (bitsX >= 0 && bitsY >= 0 && bitsZ >= 0) { -+ if (bitsX == 0 && bitsY == 0 && bitsZ == 0) { -+ return BLOCK; -+ } else { -+ final int sizeX = 1 << bitsX; -+ final int sizeY = 1 << bitsY; -+ final int sizeZ = 1 << bitsZ; -+ final BitSetDiscreteVoxelShape shape = BitSetDiscreteVoxelShape.withFilledBounds( -+ sizeX, sizeY, sizeZ, -+ (int)Math.round(minX * (double)sizeX), (int)Math.round(minY * (double)sizeY), (int)Math.round(minZ * (double)sizeZ), -+ (int)Math.round(maxX * (double)sizeX), (int)Math.round(maxY * (double)sizeY), (int)Math.round(maxZ * (double)sizeZ) -+ ); -+ return new ArrayVoxelShape( -+ shape, -+ PARTS_BY_BITS[bitsX], -+ PARTS_BY_BITS[bitsY], -+ PARTS_BY_BITS[bitsZ] -+ ); -+ } - } else { -- int l = 1 << i; -- int m = 1 << j; -- int n = 1 << k; -- BitSetDiscreteVoxelShape bitSetDiscreteVoxelShape = BitSetDiscreteVoxelShape.withFilledBounds( -- l, -- m, -- n, -- (int)Math.round(minX * (double)l), -- (int)Math.round(minY * (double)m), -- (int)Math.round(minZ * (double)n), -- (int)Math.round(maxX * (double)l), -- (int)Math.round(maxY * (double)m), -- (int)Math.round(maxZ * (double)n) -+ return new ArrayVoxelShape( -+ BLOCK.shape, -+ minX == 0.0 && maxX == 1.0 ? io.papermc.paper.util.CollisionUtil.ZERO_ONE : DoubleArrayList.wrap(new double[] { minX, maxX }), -+ minY == 0.0 && maxY == 1.0 ? io.papermc.paper.util.CollisionUtil.ZERO_ONE : DoubleArrayList.wrap(new double[] { minY, maxY }), -+ minZ == 0.0 && maxZ == 1.0 ? io.papermc.paper.util.CollisionUtil.ZERO_ONE : DoubleArrayList.wrap(new double[] { minZ, maxZ }) - ); -- return new CubeVoxelShape(bitSetDiscreteVoxelShape); - } -+ // Paper end - optimise collisions - } else { - return empty(); - } -@@ -120,79 +154,53 @@ public final class Shapes { - } - - public static VoxelShape or(VoxelShape first, VoxelShape... others) { -- return Arrays.stream(others).reduce(first, Shapes::or); -+ // Paper start - optimise collisions -+ int size = others.length; -+ if (size == 0) { -+ return first; -+ } -+ -+ // reduce complexity of joins by splitting the merges -+ -+ // add extra slot for first shape -+ ++size; -+ final VoxelShape[] tmp = Arrays.copyOf(others, size); -+ // insert first shape -+ tmp[size - 1] = first; -+ -+ while (size > 1) { -+ int newSize = 0; -+ for (int i = 0; i < size; i += 2) { -+ final int next = i + 1; -+ if (next >= size) { -+ // nothing to merge with, so leave it for next iteration -+ tmp[newSize++] = tmp[i]; -+ break; -+ } else { -+ // merge with adjacent -+ final VoxelShape one = tmp[i]; -+ final VoxelShape second = tmp[next]; -+ -+ tmp[newSize++] = Shapes.or(one, second); -+ } -+ } -+ size = newSize; -+ } -+ -+ return tmp[0]; -+ // Paper end - optimise collisions - } - - public static VoxelShape join(VoxelShape first, VoxelShape second, BooleanOp function) { -- return joinUnoptimized(first, second, function).optimize(); -+ return io.papermc.paper.util.CollisionUtil.joinOptimized(first, second, function); // Paper - optimise collisions - } - - public static VoxelShape joinUnoptimized(VoxelShape one, VoxelShape two, BooleanOp function) { -- if (function.apply(false, false)) { -- throw (IllegalArgumentException)Util.pauseInIde(new IllegalArgumentException()); -- } else if (one == two) { -- return function.apply(true, true) ? one : empty(); -- } else { -- boolean bl = function.apply(true, false); -- boolean bl2 = function.apply(false, true); -- if (one.isEmpty()) { -- return bl2 ? two : empty(); -- } else if (two.isEmpty()) { -- return bl ? one : empty(); -- } else { -- IndexMerger indexMerger = createIndexMerger(1, one.getCoords(Direction.Axis.X), two.getCoords(Direction.Axis.X), bl, bl2); -- IndexMerger indexMerger2 = createIndexMerger(indexMerger.size() - 1, one.getCoords(Direction.Axis.Y), two.getCoords(Direction.Axis.Y), bl, bl2); -- IndexMerger indexMerger3 = createIndexMerger( -- (indexMerger.size() - 1) * (indexMerger2.size() - 1), one.getCoords(Direction.Axis.Z), two.getCoords(Direction.Axis.Z), bl, bl2 -- ); -- BitSetDiscreteVoxelShape bitSetDiscreteVoxelShape = BitSetDiscreteVoxelShape.join( -- one.shape, two.shape, indexMerger, indexMerger2, indexMerger3, function -- ); -- return (VoxelShape)(indexMerger instanceof DiscreteCubeMerger -- && indexMerger2 instanceof DiscreteCubeMerger -- && indexMerger3 instanceof DiscreteCubeMerger -- ? new CubeVoxelShape(bitSetDiscreteVoxelShape) -- : new ArrayVoxelShape(bitSetDiscreteVoxelShape, indexMerger.getList(), indexMerger2.getList(), indexMerger3.getList())); -- } -- } -+ return io.papermc.paper.util.CollisionUtil.joinUnoptimized(one, two, function); // Paper - optimise collisions - } - - public static boolean joinIsNotEmpty(VoxelShape shape1, VoxelShape shape2, BooleanOp predicate) { -- if (predicate.apply(false, false)) { -- throw (IllegalArgumentException)Util.pauseInIde(new IllegalArgumentException()); -- } else { -- boolean bl = shape1.isEmpty(); -- boolean bl2 = shape2.isEmpty(); -- if (!bl && !bl2) { -- if (shape1 == shape2) { -- return predicate.apply(true, true); -- } else { -- boolean bl3 = predicate.apply(true, false); -- boolean bl4 = predicate.apply(false, true); -- -- for (Direction.Axis axis : AxisCycle.AXIS_VALUES) { -- if (shape1.max(axis) < shape2.min(axis) - 1.0E-7) { -- return bl3 || bl4; -- } -- -- if (shape2.max(axis) < shape1.min(axis) - 1.0E-7) { -- return bl3 || bl4; -- } -- } -- -- IndexMerger indexMerger = createIndexMerger(1, shape1.getCoords(Direction.Axis.X), shape2.getCoords(Direction.Axis.X), bl3, bl4); -- IndexMerger indexMerger2 = createIndexMerger( -- indexMerger.size() - 1, shape1.getCoords(Direction.Axis.Y), shape2.getCoords(Direction.Axis.Y), bl3, bl4 -- ); -- IndexMerger indexMerger3 = createIndexMerger( -- (indexMerger.size() - 1) * (indexMerger2.size() - 1), shape1.getCoords(Direction.Axis.Z), shape2.getCoords(Direction.Axis.Z), bl3, bl4 -- ); -- return joinIsNotEmpty(indexMerger, indexMerger2, indexMerger3, shape1.shape, shape2.shape, predicate); -- } -- } else { -- return predicate.apply(!bl, !bl2); -- } -- } -+ return io.papermc.paper.util.CollisionUtil.isJoinNonEmpty(shape1, shape2, predicate); // Paper - optimise collisions - } - - private static boolean joinIsNotEmpty( -@@ -220,69 +228,119 @@ public final class Shapes { - } - - public static boolean blockOccudes(VoxelShape shape, VoxelShape neighbor, Direction direction) { -- if (shape == block() && neighbor == block()) { -+ // Paper start - optimise collisions -+ final boolean firstBlock = shape == BLOCK; -+ final boolean secondBlock = neighbor == BLOCK; -+ -+ if (firstBlock & secondBlock) { - return true; -- } else if (neighbor.isEmpty()) { -+ } -+ -+ if (shape.isEmpty() | neighbor.isEmpty()) { -+ return false; -+ } -+ -+ // we optimise getOpposite, so we can use it -+ // secondly, use our cache to retrieve sliced shape -+ final VoxelShape newFirst = shape.getFaceShapeClamped(direction); -+ if (newFirst.isEmpty()) { - return false; -- } else { -- Direction.Axis axis = direction.getAxis(); -- Direction.AxisDirection axisDirection = direction.getAxisDirection(); -- VoxelShape voxelShape = axisDirection == Direction.AxisDirection.POSITIVE ? shape : neighbor; -- VoxelShape voxelShape2 = axisDirection == Direction.AxisDirection.POSITIVE ? neighbor : shape; -- BooleanOp booleanOp = axisDirection == Direction.AxisDirection.POSITIVE ? BooleanOp.ONLY_FIRST : BooleanOp.ONLY_SECOND; -- return DoubleMath.fuzzyEquals(voxelShape.max(axis), 1.0, 1.0E-7) -- && DoubleMath.fuzzyEquals(voxelShape2.min(axis), 0.0, 1.0E-7) -- && !joinIsNotEmpty(new SliceShape(voxelShape, axis, voxelShape.shape.getSize(axis) - 1), new SliceShape(voxelShape2, axis, 0), booleanOp); - } -+ final VoxelShape newSecond = neighbor.getFaceShapeClamped(direction.getOpposite()); -+ if (newSecond.isEmpty()) { -+ return false; -+ } -+ -+ return !joinIsNotEmpty(newFirst, newSecond, BooleanOp.ONLY_FIRST); -+ // Paper end - optimise collisions - } - - public static VoxelShape getFaceShape(VoxelShape shape, Direction direction) { -- if (shape == block()) { -- return block(); -- } else { -- Direction.Axis axis = direction.getAxis(); -- boolean bl; -- int i; -- if (direction.getAxisDirection() == Direction.AxisDirection.POSITIVE) { -- bl = DoubleMath.fuzzyEquals(shape.max(axis), 1.0, 1.0E-7); -- i = shape.shape.getSize(axis) - 1; -- } else { -- bl = DoubleMath.fuzzyEquals(shape.min(axis), 0.0, 1.0E-7); -- i = 0; -- } -+ return shape.getFaceShapeClamped(direction); // Paper - optimise collisions -+ } - -- return (VoxelShape)(!bl ? empty() : new SliceShape(shape, axis, i)); -- } -+ // Paper start - optimise collisions -+ private static boolean mergedMayOccludeBlock(final VoxelShape shape1, final VoxelShape shape2) { -+ // if the combined bounds of the two shapes cannot occlude, then neither can the merged -+ final AABB bounds1 = shape1.bounds(); -+ final AABB bounds2 = shape2.bounds(); -+ -+ final double minX = Math.min(bounds1.minX, bounds2.minX); -+ final double minY = Math.min(bounds1.minY, bounds2.minY); -+ final double minZ = Math.min(bounds1.minZ, bounds2.minZ); -+ -+ final double maxX = Math.max(bounds1.maxX, bounds2.maxX); -+ final double maxY = Math.max(bounds1.maxY, bounds2.maxY); -+ final double maxZ = Math.max(bounds1.maxZ, bounds2.maxZ); -+ -+ return (minX <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && maxX >= (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) && -+ (minY <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && maxY >= (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) && -+ (minZ <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && maxZ >= (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)); - } -+ // Paper end - optimise collisions - - public static boolean mergedFaceOccludes(VoxelShape one, VoxelShape two, Direction direction) { -- if (one != block() && two != block()) { -- Direction.Axis axis = direction.getAxis(); -- Direction.AxisDirection axisDirection = direction.getAxisDirection(); -- VoxelShape voxelShape = axisDirection == Direction.AxisDirection.POSITIVE ? one : two; -- VoxelShape voxelShape2 = axisDirection == Direction.AxisDirection.POSITIVE ? two : one; -- if (!DoubleMath.fuzzyEquals(voxelShape.max(axis), 1.0, 1.0E-7)) { -- voxelShape = empty(); -- } -+ // Paper start - optimise collisions -+ // see if any of the shapes on their own occludes, only if cached -+ if (one.occludesFullBlockIfCached() || two.occludesFullBlockIfCached()) { -+ return true; -+ } - -- if (!DoubleMath.fuzzyEquals(voxelShape2.min(axis), 0.0, 1.0E-7)) { -- voxelShape2 = empty(); -- } -+ if (one.isEmpty() & two.isEmpty()) { -+ return false; -+ } - -- return !joinIsNotEmpty( -- block(), -- joinUnoptimized(new SliceShape(voxelShape, axis, voxelShape.shape.getSize(axis) - 1), new SliceShape(voxelShape2, axis, 0), BooleanOp.OR), -- BooleanOp.ONLY_FIRST -- ); -- } else { -+ // we optimise getOpposite, so we can use it -+ // secondly, use our cache to retrieve sliced shape -+ final VoxelShape newFirst = one.getFaceShapeClamped(direction); -+ final VoxelShape newSecond = two.getFaceShapeClamped(direction.getOpposite()); -+ -+ // see if any of the shapes on their own occludes, only if cached -+ if (newFirst.occludesFullBlockIfCached() || newSecond.occludesFullBlockIfCached()) { - return true; - } -+ -+ final boolean firstEmpty = newFirst.isEmpty(); -+ final boolean secondEmpty = newSecond.isEmpty(); -+ -+ if (firstEmpty & secondEmpty) { -+ return false; -+ } -+ -+ if (firstEmpty | secondEmpty) { -+ return secondEmpty ? newFirst.occludesFullBlock() : newSecond.occludesFullBlock(); -+ } -+ -+ if (newFirst == newSecond) { -+ return newFirst.occludesFullBlock(); -+ } -+ -+ return mergedMayOccludeBlock(newFirst, newSecond) && newFirst.orUnoptimized(newSecond).occludesFullBlock(); -+ // Paper end - optimise collisions - } - - public static boolean faceShapeOccludes(VoxelShape one, VoxelShape two) { -- return one == block() -- || two == block() -- || (!one.isEmpty() || !two.isEmpty()) && !joinIsNotEmpty(block(), joinUnoptimized(one, two, BooleanOp.OR), BooleanOp.ONLY_FIRST); -+ // Paper start - optimise collisions -+ if (one.occludesFullBlockIfCached() || two.occludesFullBlockIfCached()) { -+ return true; -+ } -+ -+ final boolean s1Empty = one.isEmpty(); -+ final boolean s2Empty = two.isEmpty(); -+ if (s1Empty & s2Empty) { -+ return false; -+ } -+ -+ if (s1Empty | s2Empty) { -+ return s2Empty ? one.occludesFullBlock() : two.occludesFullBlock(); -+ } -+ -+ if (one == two) { -+ return one.occludesFullBlock(); -+ } -+ -+ return mergedMayOccludeBlock(one, two) && (one.orUnoptimized(two)).occludesFullBlock(); -+ // Paper end - optimise collisions - } - - @VisibleForTesting -diff --git a/src/main/java/net/minecraft/world/phys/shapes/SliceShape.java b/src/main/java/net/minecraft/world/phys/shapes/SliceShape.java -index b07f1c58e00d232e7c83e6df3499e4b677645609..b114bdd955d9ae1cd8c9701ad213da46ceb2b49a 100644 ---- a/src/main/java/net/minecraft/world/phys/shapes/SliceShape.java -+++ b/src/main/java/net/minecraft/world/phys/shapes/SliceShape.java -@@ -12,6 +12,7 @@ public class SliceShape extends VoxelShape { - super(makeSlice(shape.shape, axis, sliceWidth)); - this.delegate = shape; - this.axis = axis; -+ this.initCache(); // Paper - optimise collisions - } - - private static DiscreteVoxelShape makeSlice(DiscreteVoxelShape voxelSet, Direction.Axis axis, int sliceWidth) { -diff --git a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java -index d440003ca4403511a964f61bcf67ac2cd75c5359..001e2e6ed4b8b63a23dc0013882674834812a961 100644 ---- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java -+++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java -@@ -16,37 +16,438 @@ import net.minecraft.world.phys.BlockHitResult; - import net.minecraft.world.phys.Vec3; - - public abstract class VoxelShape { -- protected final DiscreteVoxelShape shape; -+ public final DiscreteVoxelShape shape; // Paper - optimise collisions - public - @Nullable - private VoxelShape[] faces; - -- protected VoxelShape(DiscreteVoxelShape voxels) { -+ // Paper start - optimise collisions -+ private double offsetX; -+ private double offsetY; -+ private double offsetZ; -+ @Nullable private AABB singleAABBRepresentation; -+ private double[] rootCoordinatesX; -+ private double[] rootCoordinatesY; -+ private double[] rootCoordinatesZ; -+ -+ private io.papermc.paper.util.collisions.CachedShapeData cachedShapeData; -+ private boolean isEmpty; -+ -+ private io.papermc.paper.util.collisions.CachedToAABBs cachedToAABBs; -+ private AABB cachedBounds; -+ -+ private Boolean isFullBlock; -+ -+ private Boolean occludesFullBlock; -+ -+ // must be power of two -+ private static final int MERGED_CACHE_SIZE = 16; -+ -+ private io.papermc.paper.util.collisions.MergedORCache[] mergedORCache; -+ -+ public final double offsetX() { -+ return this.offsetX; -+ } -+ -+ public final double offsetY() { -+ return this.offsetY; -+ } -+ -+ public final double offsetZ() { -+ return this.offsetZ; -+ } -+ -+ public final AABB getSingleAABBRepresentation() { -+ return this.singleAABBRepresentation; -+ } -+ -+ public final double[] rootCoordinatesX() { -+ return this.rootCoordinatesX; -+ } -+ -+ public final double[] rootCoordinatesY() { -+ return this.rootCoordinatesY; -+ } -+ -+ public final double[] rootCoordinatesZ() { -+ return this.rootCoordinatesZ; -+ } -+ -+ private static double[] extractRawArray(final DoubleList list) { -+ if (list instanceof it.unimi.dsi.fastutil.doubles.DoubleArrayList rawList) { -+ final double[] raw = rawList.elements(); -+ final int expected = rawList.size(); -+ if (raw.length == expected) { -+ return raw; -+ } else { -+ return java.util.Arrays.copyOf(raw, expected); -+ } -+ } else { -+ return list.toDoubleArray(); -+ } -+ } -+ -+ public final void initCache() { -+ this.cachedShapeData = this.shape.getOrCreateCachedShapeData(); -+ this.isEmpty = this.cachedShapeData.isEmpty(); -+ -+ final DoubleList xList = this.getCoords(Direction.Axis.X); -+ final DoubleList yList = this.getCoords(Direction.Axis.Y); -+ final DoubleList zList = this.getCoords(Direction.Axis.Z); -+ -+ if (xList instanceof OffsetDoubleList offsetDoubleList) { -+ this.offsetX = offsetDoubleList.offset; -+ this.rootCoordinatesX = extractRawArray(offsetDoubleList.delegate); -+ } else { -+ this.rootCoordinatesX = extractRawArray(xList); -+ } -+ -+ if (yList instanceof OffsetDoubleList offsetDoubleList) { -+ this.offsetY = offsetDoubleList.offset; -+ this.rootCoordinatesY = extractRawArray(offsetDoubleList.delegate); -+ } else { -+ this.rootCoordinatesY = extractRawArray(yList); -+ } -+ -+ if (zList instanceof OffsetDoubleList offsetDoubleList) { -+ this.offsetZ = offsetDoubleList.offset; -+ this.rootCoordinatesZ = extractRawArray(offsetDoubleList.delegate); -+ } else { -+ this.rootCoordinatesZ = extractRawArray(zList); -+ } -+ -+ if (this.cachedShapeData.hasSingleAABB()) { -+ this.singleAABBRepresentation = new AABB( -+ this.rootCoordinatesX[0] + this.offsetX, this.rootCoordinatesY[0] + this.offsetY, this.rootCoordinatesZ[0] + this.offsetZ, -+ this.rootCoordinatesX[1] + this.offsetX, this.rootCoordinatesY[1] + this.offsetY, this.rootCoordinatesZ[1] + this.offsetZ -+ ); -+ this.cachedBounds = this.singleAABBRepresentation; -+ } -+ } -+ -+ public final io.papermc.paper.util.collisions.CachedShapeData getCachedVoxelData() { -+ return this.cachedShapeData; -+ } -+ -+ private VoxelShape[] faceShapeClampedCache; -+ -+ public final VoxelShape getFaceShapeClamped(final Direction direction) { -+ if (this.isEmpty) { -+ return (VoxelShape)(Object)this; -+ } -+ if ((VoxelShape)(Object)this == Shapes.block()) { -+ return (VoxelShape)(Object)this; -+ } -+ -+ VoxelShape[] cache = this.faceShapeClampedCache; -+ if (cache != null) { -+ final VoxelShape ret = cache[direction.ordinal()]; -+ if (ret != null) { -+ return ret; -+ } -+ } -+ -+ -+ if (cache == null) { -+ this.faceShapeClampedCache = cache = new VoxelShape[6]; -+ } -+ -+ final Direction.Axis axis = direction.getAxis(); -+ -+ final VoxelShape ret; -+ -+ if (direction.getAxisDirection() == Direction.AxisDirection.POSITIVE) { -+ if (DoubleMath.fuzzyEquals(this.max(axis), 1.0, io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) { -+ ret = tryForceBlock(new SliceShape((VoxelShape)(Object)this, axis, this.shape.getSize(axis) - 1)); -+ } else { -+ ret = Shapes.empty(); -+ } -+ } else { -+ if (DoubleMath.fuzzyEquals(this.min(axis), 0.0, io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) { -+ ret = tryForceBlock(new SliceShape((VoxelShape)(Object)this, axis, 0)); -+ } else { -+ ret = Shapes.empty(); -+ } -+ } -+ -+ cache[direction.ordinal()] = ret; -+ -+ return ret; -+ } -+ -+ private static VoxelShape tryForceBlock(final VoxelShape other) { -+ if (other == Shapes.block()) { -+ return other; -+ } -+ -+ final AABB otherAABB = other.getSingleAABBRepresentation(); -+ if (otherAABB == null) { -+ return other; -+ } -+ -+ if (Shapes.block().getSingleAABBRepresentation().equals(otherAABB)) { -+ return Shapes.block(); -+ } -+ -+ return other; -+ } -+ -+ private boolean computeOccludesFullBlock() { -+ if (this.isEmpty) { -+ this.occludesFullBlock = Boolean.FALSE; -+ return false; -+ } -+ -+ if (this.isFullBlock()) { -+ this.occludesFullBlock = Boolean.TRUE; -+ return true; -+ } -+ -+ final AABB singleAABB = this.singleAABBRepresentation; -+ if (singleAABB != null) { -+ // check if the bounding box encloses the full cube -+ final boolean ret = -+ (singleAABB.minY <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && singleAABB.maxY >= (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) && -+ (singleAABB.minX <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && singleAABB.maxX >= (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) && -+ (singleAABB.minZ <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && singleAABB.maxZ >= (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)); -+ this.occludesFullBlock = Boolean.valueOf(ret); -+ return ret; -+ } -+ -+ final boolean ret = !Shapes.joinIsNotEmpty(Shapes.block(), ((VoxelShape)(Object)this), BooleanOp.ONLY_FIRST); -+ this.occludesFullBlock = Boolean.valueOf(ret); -+ return ret; -+ } -+ -+ public final boolean occludesFullBlock() { -+ final Boolean ret = this.occludesFullBlock; -+ if (ret != null) { -+ return ret.booleanValue(); -+ } -+ -+ return this.computeOccludesFullBlock(); -+ } -+ -+ public final boolean occludesFullBlockIfCached() { -+ final Boolean ret = this.occludesFullBlock; -+ return ret != null ? ret.booleanValue() : false; -+ } -+ -+ private static int hash(final VoxelShape key) { -+ return it.unimi.dsi.fastutil.HashCommon.mix(System.identityHashCode(key)); -+ } -+ -+ public final VoxelShape orUnoptimized(final VoxelShape other) { -+ // don't cache simple cases -+ if (((VoxelShape)(Object)this) == other) { -+ return other; -+ } -+ -+ if (this.isEmpty) { -+ return other; -+ } -+ -+ if (other.isEmpty()) { -+ return (VoxelShape)(Object)this; -+ } -+ -+ // try this cache first -+ final int thisCacheKey = hash(other) & (MERGED_CACHE_SIZE - 1); -+ final io.papermc.paper.util.collisions.MergedORCache cached = this.mergedORCache == null ? null : this.mergedORCache[thisCacheKey]; -+ if (cached != null && cached.key() == other) { -+ return cached.result(); -+ } -+ -+ // try other cache -+ final int otherCacheKey = hash(this) & (MERGED_CACHE_SIZE - 1); -+ final io.papermc.paper.util.collisions.MergedORCache otherCache = other.mergedORCache == null ? null : other.mergedORCache[otherCacheKey]; -+ if (otherCache != null && otherCache.key() == this) { -+ return otherCache.result(); -+ } -+ -+ // note: unsure if joinUnoptimized(1, 2, OR) == joinUnoptimized(2, 1, OR) for all cases -+ final VoxelShape result = Shapes.joinUnoptimized(this, other, BooleanOp.OR); -+ -+ if (cached != null && otherCache == null) { -+ // try to use second cache instead of replacing an entry in this cache -+ if (other.mergedORCache == null) { -+ other.mergedORCache = new io.papermc.paper.util.collisions.MergedORCache[MERGED_CACHE_SIZE]; -+ } -+ other.mergedORCache[otherCacheKey] = new io.papermc.paper.util.collisions.MergedORCache(this, result); -+ } else { -+ // line is not occupied or other cache line is full -+ // always bias to replace this cache, as this cache is the first we check -+ if (this.mergedORCache == null) { -+ this.mergedORCache = new io.papermc.paper.util.collisions.MergedORCache[MERGED_CACHE_SIZE]; -+ } -+ this.mergedORCache[thisCacheKey] = new io.papermc.paper.util.collisions.MergedORCache(other, result); -+ } -+ -+ return result; -+ } -+ -+ private boolean computeFullBlock() { -+ Boolean ret; -+ if (this.isEmpty) { -+ ret = Boolean.FALSE; -+ } else if ((VoxelShape)(Object)this == Shapes.block()) { -+ ret = Boolean.TRUE; -+ } else { -+ final AABB singleAABB = this.singleAABBRepresentation; -+ if (singleAABB == null) { -+ final io.papermc.paper.util.collisions.CachedShapeData shapeData = this.cachedShapeData; -+ final int sMinX = shapeData.minFullX(); -+ final int sMinY = shapeData.minFullY(); -+ final int sMinZ = shapeData.minFullZ(); -+ -+ final int sMaxX = shapeData.maxFullX(); -+ final int sMaxY = shapeData.maxFullY(); -+ final int sMaxZ = shapeData.maxFullZ(); -+ -+ if (Math.abs(this.rootCoordinatesX[sMinX] + this.offsetX) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && -+ Math.abs(this.rootCoordinatesY[sMinY] + this.offsetY) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && -+ Math.abs(this.rootCoordinatesZ[sMinZ] + this.offsetZ) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && -+ -+ Math.abs(1.0 - (this.rootCoordinatesX[sMaxX] + this.offsetX)) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && -+ Math.abs(1.0 - (this.rootCoordinatesY[sMaxY] + this.offsetY)) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && -+ Math.abs(1.0 - (this.rootCoordinatesZ[sMaxZ] + this.offsetZ)) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) { -+ -+ // index = z + y*sizeZ + x*(sizeZ*sizeY) -+ -+ final int sizeY = shapeData.sizeY(); -+ final int sizeZ = shapeData.sizeZ(); -+ -+ final long[] bitset = shapeData.voxelSet(); -+ -+ ret = Boolean.TRUE; -+ -+ check_full: -+ for (int x = sMinX; x < sMaxX; ++x) { -+ for (int y = sMinY; y < sMaxY; ++y) { -+ final int baseIndex = y*sizeZ + x*(sizeZ*sizeY); -+ if (!io.papermc.paper.util.collisions.FlatBitsetUtil.isRangeSet(bitset, baseIndex + sMinZ, baseIndex + sMaxZ)) { -+ ret = Boolean.FALSE; -+ break check_full; -+ } -+ } -+ } -+ } else { -+ ret = Boolean.FALSE; -+ } -+ } else { -+ ret = Boolean.valueOf( -+ Math.abs(singleAABB.minX) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && -+ Math.abs(singleAABB.minY) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && -+ Math.abs(singleAABB.minZ) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && -+ -+ Math.abs(1.0 - singleAABB.maxX) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && -+ Math.abs(1.0 - singleAABB.maxY) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && -+ Math.abs(1.0 - singleAABB.maxZ) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON -+ ); -+ } -+ } -+ -+ this.isFullBlock = ret; -+ -+ return ret.booleanValue(); -+ } -+ -+ public boolean isFullBlock() { -+ final Boolean ret = this.isFullBlock; -+ -+ if (ret != null) { -+ return ret.booleanValue(); -+ } -+ -+ return this.computeFullBlock(); -+ } -+ // Paper end - optimise collisions -+ -+ protected VoxelShape(DiscreteVoxelShape voxels) { // Paper - protected - this.shape = voxels; - } - - public double min(Direction.Axis axis) { -- int i = this.shape.firstFull(axis); -- return i >= this.shape.getSize(axis) ? Double.POSITIVE_INFINITY : this.get(axis, i); -+ // Paper start - optimise collisions -+ final io.papermc.paper.util.collisions.CachedShapeData shapeData = this.cachedShapeData; -+ switch (axis) { -+ case X: { -+ final int idx = shapeData.minFullX(); -+ return idx >= shapeData.sizeX() ? Double.POSITIVE_INFINITY : (this.rootCoordinatesX[idx] + this.offsetX); -+ } -+ case Y: { -+ final int idx = shapeData.minFullY(); -+ return idx >= shapeData.sizeY() ? Double.POSITIVE_INFINITY : (this.rootCoordinatesY[idx] + this.offsetY); -+ } -+ case Z: { -+ final int idx = shapeData.minFullZ(); -+ return idx >= shapeData.sizeZ() ? Double.POSITIVE_INFINITY : (this.rootCoordinatesZ[idx] + this.offsetZ); -+ } -+ default: { -+ // should never get here -+ return Double.POSITIVE_INFINITY; -+ } -+ } -+ // Paper end - optimise collisions - } - - public double max(Direction.Axis axis) { -- int i = this.shape.lastFull(axis); -- return i <= 0 ? Double.NEGATIVE_INFINITY : this.get(axis, i); -+ // Paper start - optimise collisions -+ final io.papermc.paper.util.collisions.CachedShapeData shapeData = this.cachedShapeData; -+ switch (axis) { -+ case X: { -+ final int idx = shapeData.maxFullX(); -+ return idx <= 0 ? Double.NEGATIVE_INFINITY : (this.rootCoordinatesX[idx] + this.offsetX); -+ } -+ case Y: { -+ final int idx = shapeData.maxFullY(); -+ return idx <= 0 ? Double.NEGATIVE_INFINITY : (this.rootCoordinatesY[idx] + this.offsetY); -+ } -+ case Z: { -+ final int idx = shapeData.maxFullZ(); -+ return idx <= 0 ? Double.NEGATIVE_INFINITY : (this.rootCoordinatesZ[idx] + this.offsetZ); -+ } -+ default: { -+ // should never get here -+ return Double.NEGATIVE_INFINITY; -+ } -+ } -+ // Paper end - optimise collisions - } - - public AABB bounds() { -- if (this.isEmpty()) { -- throw (UnsupportedOperationException)Util.pauseInIde(new UnsupportedOperationException("No bounds for empty shape.")); -- } else { -- return new AABB( -- this.min(Direction.Axis.X), -- this.min(Direction.Axis.Y), -- this.min(Direction.Axis.Z), -- this.max(Direction.Axis.X), -- this.max(Direction.Axis.Y), -- this.max(Direction.Axis.Z) -- ); -+ // Paper start - optimise collisions -+ if (this.isEmpty) { -+ throw Util.pauseInIde(new UnsupportedOperationException("No bounds for empty shape.")); -+ } -+ AABB cached = this.cachedBounds; -+ if (cached != null) { -+ return cached; - } -+ -+ final io.papermc.paper.util.collisions.CachedShapeData shapeData = this.cachedShapeData; -+ -+ final double[] coordsX = this.rootCoordinatesX; -+ final double[] coordsY = this.rootCoordinatesY; -+ final double[] coordsZ = this.rootCoordinatesZ; -+ -+ final double offX = this.offsetX; -+ final double offY = this.offsetY; -+ final double offZ = this.offsetZ; -+ -+ // note: if not empty, then there is one full AABB so no bounds checks are needed on the minFull/maxFull indices -+ cached = new AABB( -+ coordsX[shapeData.minFullX()] + offX, -+ coordsY[shapeData.minFullY()] + offY, -+ coordsZ[shapeData.minFullZ()] + offZ, -+ -+ coordsX[shapeData.maxFullX()] + offX, -+ coordsY[shapeData.maxFullY()] + offY, -+ coordsZ[shapeData.maxFullZ()] + offZ -+ ); -+ -+ this.cachedBounds = cached; -+ return cached; -+ // Paper end - optimise collisions - } - - public VoxelShape singleEncompassing() { -@@ -69,28 +470,106 @@ public abstract class VoxelShape { - public abstract DoubleList getCoords(Direction.Axis axis); - - public boolean isEmpty() { -- return this.shape.isEmpty(); -+ return this.isEmpty; // Paper - optimise collisions -+ } -+ -+ // Paper start - optimise collisions -+ private static DoubleList offsetList(final DoubleList src, final double by) { -+ if (src instanceof OffsetDoubleList offsetDoubleList) { -+ return new OffsetDoubleList(offsetDoubleList.delegate, by + offsetDoubleList.offset); -+ } -+ return new OffsetDoubleList(src, by); - } -+ // Paper end - optimise collisions - - public VoxelShape move(double x, double y, double z) { -- return (VoxelShape)(this.isEmpty() -- ? Shapes.empty() -- : new ArrayVoxelShape( -+ // Paper start - optimise collisions -+ if (this.isEmpty) { -+ return Shapes.empty(); -+ } -+ -+ final ArrayVoxelShape ret = new ArrayVoxelShape( - this.shape, -- new OffsetDoubleList(this.getCoords(Direction.Axis.X), x), -- new OffsetDoubleList(this.getCoords(Direction.Axis.Y), y), -- new OffsetDoubleList(this.getCoords(Direction.Axis.Z), z) -- )); -+ offsetList(this.getCoords(Direction.Axis.X), x), -+ offsetList(this.getCoords(Direction.Axis.Y), y), -+ offsetList(this.getCoords(Direction.Axis.Z), z) -+ ); -+ -+ final io.papermc.paper.util.collisions.CachedToAABBs cachedToAABBs = this.cachedToAABBs; -+ if (cachedToAABBs != null) { -+ ((VoxelShape)ret).cachedToAABBs = io.papermc.paper.util.collisions.CachedToAABBs.offset(cachedToAABBs, x, y, z); -+ } -+ -+ return ret; -+ // Paper end - optimise collisions - } - - public VoxelShape optimize() { -- VoxelShape[] voxelShapes = new VoxelShape[]{Shapes.empty()}; -- this.forAllBoxes( -- (minX, minY, minZ, maxX, maxY, maxZ) -> voxelShapes[0] = Shapes.joinUnoptimized( -- voxelShapes[0], Shapes.box(minX, minY, minZ, maxX, maxY, maxZ), BooleanOp.OR -- ) -- ); -- return voxelShapes[0]; -+ // Paper start - optimise collisions -+ // Optimise merge strategy to increase the number of simple joins, and additionally forward the toAabbs cache -+ // to result -+ if (this.isEmpty) { -+ return Shapes.empty(); -+ } -+ -+ if (this.singleAABBRepresentation != null) { -+ // note: the isFullBlock() is fuzzy, and Shapes.create() is also fuzzy which would return block() -+ return this.isFullBlock() ? Shapes.block() : this; -+ } -+ -+ final List aabbs = this.toAabbs(); -+ -+ if (aabbs.size() == 1) { -+ final AABB singleAABB = aabbs.get(0); -+ final VoxelShape ret = Shapes.create(singleAABB); -+ -+ // forward AABB cache -+ if (ret.cachedToAABBs == null) { -+ ret.cachedToAABBs = this.cachedToAABBs; -+ } -+ -+ return ret; -+ } else { -+ // reduce complexity of joins by splitting the merges (old complexity: n^2, new: nlogn) -+ -+ // set up flat array so that this merge is done in-place -+ final VoxelShape[] tmp = new VoxelShape[aabbs.size()]; -+ -+ // initialise as unmerged -+ for (int i = 0, len = aabbs.size(); i < len; ++i) { -+ tmp[i] = Shapes.create(aabbs.get(i)); -+ } -+ -+ int size = aabbs.size(); -+ while (size > 1) { -+ int newSize = 0; -+ for (int i = 0; i < size; i += 2) { -+ final int next = i + 1; -+ if (next >= size) { -+ // nothing to merge with, so leave it for next iteration -+ tmp[newSize++] = tmp[i]; -+ break; -+ } else { -+ // merge with adjacent -+ final VoxelShape first = tmp[i]; -+ final VoxelShape second = tmp[next]; -+ -+ tmp[newSize++] = Shapes.joinUnoptimized(first, second, BooleanOp.OR); -+ } -+ } -+ size = newSize; -+ } -+ -+ final VoxelShape ret = tmp[0]; -+ -+ // forward AABB cache -+ if (ret.cachedToAABBs == null) { -+ ret.cachedToAABBs = this.cachedToAABBs; -+ } -+ -+ return ret; -+ } -+ // Paper end - optimise collisions - } - - public void forAllEdges(Shapes.DoubleLineConsumer consumer) { -@@ -126,10 +605,43 @@ public abstract class VoxelShape { - ); - } - -+ // Paper start - optimise collisions -+ private List toAabbsUncached() { -+ final List ret = new java.util.ArrayList<>(); -+ if (this.singleAABBRepresentation != null) { -+ ret.add(this.singleAABBRepresentation); -+ } else { -+ this.forAllBoxes((minX, minY, minZ, maxX, maxY, maxZ) -> { -+ ret.add(new AABB(minX, minY, minZ, maxX, maxY, maxZ)); -+ }); -+ } -+ -+ // cache result -+ this.cachedToAABBs = new io.papermc.paper.util.collisions.CachedToAABBs(ret, false, 0.0, 0.0, 0.0); -+ -+ return ret; -+ } -+ // Paper end - optimise collisions -+ - public List toAabbs() { -- List list = Lists.newArrayList(); -- this.forAllBoxes((x1, y1, z1, x2, y2, z2) -> list.add(new AABB(x1, y1, z1, x2, y2, z2))); -- return list; -+ // Paper start - optimise collisions -+ io.papermc.paper.util.collisions.CachedToAABBs cachedToAABBs = this.cachedToAABBs; -+ if (cachedToAABBs != null) { -+ if (!cachedToAABBs.isOffset()) { -+ return cachedToAABBs.aabbs(); -+ } -+ -+ // all we need to do is offset the cache -+ cachedToAABBs = cachedToAABBs.removeOffset(); -+ // update cache -+ this.cachedToAABBs = cachedToAABBs; -+ -+ return cachedToAABBs.aabbs(); -+ } -+ -+ // make new cache -+ return this.toAabbsUncached(); -+ // Paper end - optimise collisions - } - - public double min(Direction.Axis axis, double from, double to) { -@@ -154,43 +666,85 @@ public abstract class VoxelShape { - return Mth.binarySearch(0, this.shape.getSize(axis) + 1, i -> coord < this.get(axis, i)) - 1; - } - -+ // Paper start - optimise collisions -+ /** -+ * Copy of AABB#clip but for one AABB -+ */ -+ private static BlockHitResult clip(final AABB aabb, final Vec3 from, final Vec3 to, final BlockPos offset) { -+ final double[] minDistanceArr = new double[] { 1.0 }; -+ final double diffX = to.x - from.x; -+ final double diffY = to.y - from.y; -+ final double diffZ = to.z - from.z; -+ -+ final Direction direction = AABB.getDirection(aabb.move(offset), from, minDistanceArr, null, diffX, diffY, diffZ); -+ -+ if (direction == null) { -+ return null; -+ } -+ -+ final double minDistance = minDistanceArr[0]; -+ return new BlockHitResult(from.add(minDistance * diffX, minDistance * diffY, minDistance * diffZ), direction, offset, false); -+ } -+ // Paper end - optimise collisions -+ - @Nullable - public BlockHitResult clip(Vec3 start, Vec3 end, BlockPos pos) { -- if (this.isEmpty()) { -+ // Paper start - optimise collisions -+ if (this.isEmpty) { - return null; -- } else { -- Vec3 vec3 = end.subtract(start); -- if (vec3.lengthSqr() < 1.0E-7) { -- return null; -- } else { -- Vec3 vec32 = start.add(vec3.scale(0.001)); -- return this.shape -- .isFullWide( -- this.findIndex(Direction.Axis.X, vec32.x - (double)pos.getX()), -- this.findIndex(Direction.Axis.Y, vec32.y - (double)pos.getY()), -- this.findIndex(Direction.Axis.Z, vec32.z - (double)pos.getZ()) -- ) -- ? new BlockHitResult(vec32, Direction.getNearest(vec3.x, vec3.y, vec3.z).getOpposite(), pos, true) -- : AABB.clip(this.toAabbs(), start, end, pos); -+ } -+ -+ final Vec3 directionOpposite = end.subtract(start); -+ if (directionOpposite.lengthSqr() < io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) { -+ return null; -+ } -+ -+ final Vec3 fromBehind = start.add(directionOpposite.scale(0.001)); -+ final double fromBehindOffsetX = fromBehind.x - (double)pos.getX(); -+ final double fromBehindOffsetY = fromBehind.y - (double)pos.getY(); -+ final double fromBehindOffsetZ = fromBehind.z - (double)pos.getZ(); -+ -+ final AABB singleAABB = this.singleAABBRepresentation; -+ if (singleAABB != null) { -+ if (singleAABB.contains(fromBehindOffsetX, fromBehindOffsetY, fromBehindOffsetZ)) { -+ return new BlockHitResult(fromBehind, Direction.getNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), pos, true); - } -+ return clip(singleAABB, start, end, pos); - } -+ -+ if (io.papermc.paper.util.CollisionUtil.strictlyContains(this, fromBehindOffsetX, fromBehindOffsetY, fromBehindOffsetZ)) { -+ return new BlockHitResult(fromBehind, Direction.getNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), pos, true); -+ } -+ -+ return AABB.clip(this.toAabbs(), start, end, pos); -+ // Paper end - optimise collisions - } - - public Optional closestPointTo(Vec3 target) { -- if (this.isEmpty()) { -+ // Paper start - optimise collisions -+ if (this.isEmpty) { - return Optional.empty(); -- } else { -- Vec3[] vec3s = new Vec3[1]; -- this.forAllBoxes((minX, minY, minZ, maxX, maxY, maxZ) -> { -- double d = Mth.clamp(target.x(), minX, maxX); -- double e = Mth.clamp(target.y(), minY, maxY); -- double f = Mth.clamp(target.z(), minZ, maxZ); -- if (vec3s[0] == null || target.distanceToSqr(d, e, f) < target.distanceToSqr(vec3s[0])) { -- vec3s[0] = new Vec3(d, e, f); -- } -- }); -- return Optional.of(vec3s[0]); - } -+ -+ Vec3 ret = null; -+ double retDistance = Double.MAX_VALUE; -+ -+ final List aabbs = this.toAabbs(); -+ for (int i = 0, len = aabbs.size(); i < len; ++i) { -+ final AABB aabb = aabbs.get(i); -+ final double x = Mth.clamp(target.x, aabb.minX, aabb.maxX); -+ final double y = Mth.clamp(target.y, aabb.minY, aabb.maxY); -+ final double z = Mth.clamp(target.z, aabb.minZ, aabb.maxZ); -+ -+ double dist = target.distanceToSqr(x, y, z); -+ if (dist < retDistance) { -+ ret = new Vec3(x, y, z); -+ retDistance = dist; -+ } -+ } -+ -+ return Optional.ofNullable(ret); -+ // Paper end - optimise collisions - } - - public VoxelShape getFaceShape(Direction facing) { -@@ -227,7 +781,28 @@ public abstract class VoxelShape { - } - - public double collide(Direction.Axis axis, AABB box, double maxDist) { -- return this.collideX(AxisCycle.between(axis, Direction.Axis.X), box, maxDist); -+ // Paper start - optimise collisions -+ if (this.isEmpty) { -+ return maxDist; -+ } -+ if (Math.abs(maxDist) < io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) { -+ return 0.0; -+ } -+ switch (axis) { -+ case X: { -+ return io.papermc.paper.util.CollisionUtil.collideX(this, box, maxDist); -+ } -+ case Y: { -+ return io.papermc.paper.util.CollisionUtil.collideY(this, box, maxDist); -+ } -+ case Z: { -+ return io.papermc.paper.util.CollisionUtil.collideZ(this, box, maxDist); -+ } -+ default: { -+ throw new RuntimeException("Unknown axis: " + axis); -+ } -+ } -+ // Paper end - optimise collisions - } - - protected double collideX(AxisCycle axisCycle, AABB box, double maxDist) { diff --git a/patches/server/0008-Visibility-API-and-Command.patch b/patches/server/0005-Visibility-API-and-Command.patch similarity index 95% rename from patches/server/0008-Visibility-API-and-Command.patch rename to patches/server/0005-Visibility-API-and-Command.patch index 3507222..30033c2 100644 --- a/patches/server/0008-Visibility-API-and-Command.patch +++ b/patches/server/0005-Visibility-API-and-Command.patch @@ -255,10 +255,10 @@ index 1a37654aff9a9c86c9f7af10a1cf721371f0c5ec..82644b34a77dc5e5af38260b7b07b3ec public ClientboundSectionBlocksUpdatePacket(SectionPos sectionPos, ShortSet positions, LevelChunkSection section) { this.sectionPos = sectionPos; diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 3f4997b642c9de1982a500d3a6ff07a9ca59da10..524ca2d1719ac52ae287f6c4c56ca9d024f084eb 100644 +index 517a9b8c67eba6569aabea6ffb9260619540c3ea..9f21eec9547b15b19f6dca4a7e894219abf5350b 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1748,6 +1748,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop entity instanceof Player); +@@ -550,6 +550,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } } - // Paper end - rewrite chunk system + // Paper end - optimise entity tracker + // Sakura start - visibility api and command + public boolean isPrimedTNT; + public boolean isFallingBlock; @@ -520,19 +520,19 @@ index 42bd2d9a1528b6210e4dfb56233062fd97c9743b..28a15f612239614c6c56974da5855ce3 public PrimedTnt(Level world, double x, double y, double z, @Nullable LivingEntity igniter) { diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 439f1e69cc330aa590311eb4e308e539815ad0d5..05a4b69fbaf36269bea3a050022a54d28211c285 100644 +index beb94ab8f9e3709cd92d4dda59fa756921bd6ce3..5e1e50a5c8cc380d0c23fd14e0da54da3d750834 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -269,6 +269,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - return this.getChunkSource().getChunk(chunkX, chunkZ, leastStatus, false); +@@ -653,6 +653,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + return java.util.Optional.ofNullable(selected); } - // Paper end - rewrite chunk system + // Paper end - optimise collisions + public final it.unimi.dsi.fastutil.longs.Long2IntMap minimalTNT = new it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap(); // Sakura - visibility api protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, Supplier sakuraWorldConfigCreator, java.util.concurrent.Executor executor) { // Sakura - sakura configuration files// Paper - create paper world config & Anti-Xray this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 7eea190ce8a62960ecc42ff56a4ef71b754184fb..a280b9c9c1451f72758b30b9c514c889c846c88c 100644 +index d01b45a48d412e3cb591acee101730704574448a..6fb40ff3198e72314ade642102e11b7e007782e2 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -545,6 +545,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player { diff --git a/patches/server/0006-Paper-Unapplied-Actually-optimise-explosions.patch b/patches/server/0006-Paper-Unapplied-Actually-optimise-explosions.patch deleted file mode 100644 index 45b76cc..0000000 --- a/patches/server/0006-Paper-Unapplied-Actually-optimise-explosions.patch +++ /dev/null @@ -1,521 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Spottedleaf -Date: Tue, 12 Sep 2023 06:50:16 -0700 -Subject: [PATCH] Paper Unapplied - Actually optimise explosions - -The vast majority of blocks an explosion of power ~4 tries -to destroy are duplicates. The core of the block destroying -part of this patch is to cache the block state, resistance, and -whether it should explode - as those will not change. - -The other part of this patch is to optimise the visibility -percentage calculation. The new visibility calculation takes -advantage of the block caching already done by the explosion logic. -It continues to update the cache as the visibility calculation -uses many rays which can overlap significantly. - -Effectively, the patch uses a lot of caching to eliminate -redundant operations. - -Performance benchmarking explosions is challenging, as it varies -depending on the power, the number of nearby entities, and the -nearby terrain. This means that no benchmark can cover all the cases. -I decided to test a giant block of TNT, as that's where the optimisations -would be needed the most. - -I tested using a 50x10x50 block of TNT above ground -and determined the following: - -Vanilla time per explosion: 2.27ms -Lithium time per explosion: 1.07ms -This patch time per explosion: 0.45ms - -The results indicate that this logic is 5 times faster than Vanilla -and 2.3 times faster than Lithium. - -diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index bff83fe413c7baef4ba56a3270ea4463a58c792f..7aa9ddb1d61ffb7da3f867e5a5bd04e3432b5621 100644 ---- a/src/main/java/net/minecraft/world/level/Explosion.java -+++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -113,6 +113,271 @@ public class Explosion { - this.yield = this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F; // CraftBukkit - } - -+ // Paper start - optimise collisions -+ private static final double[] CACHED_RAYS; -+ static { -+ final it.unimi.dsi.fastutil.doubles.DoubleArrayList rayCoords = new it.unimi.dsi.fastutil.doubles.DoubleArrayList(); -+ -+ for (int x = 0; x <= 15; ++x) { -+ for (int y = 0; y <= 15; ++y) { -+ for (int z = 0; z <= 15; ++z) { -+ if ((x == 0 || x == 15) || (y == 0 || y == 15) || (z == 0 || z == 15)) { -+ double xDir = (double)((float)x / 15.0F * 2.0F - 1.0F); -+ double yDir = (double)((float)y / 15.0F * 2.0F - 1.0F); -+ double zDir = (double)((float)z / 15.0F * 2.0F - 1.0F); -+ -+ double mag = Math.sqrt( -+ xDir * xDir + yDir * yDir + zDir * zDir -+ ); -+ -+ rayCoords.add((xDir / mag) * (double)0.3F); -+ rayCoords.add((yDir / mag) * (double)0.3F); -+ rayCoords.add((zDir / mag) * (double)0.3F); -+ } -+ } -+ } -+ } -+ -+ CACHED_RAYS = rayCoords.toDoubleArray(); -+ } -+ -+ private static final int CHUNK_CACHE_SHIFT = 2; -+ private static final int CHUNK_CACHE_MASK = (1 << CHUNK_CACHE_SHIFT) - 1; -+ private static final int CHUNK_CACHE_WIDTH = 1 << CHUNK_CACHE_SHIFT; -+ -+ private static final int BLOCK_EXPLOSION_CACHE_SHIFT = 3; -+ private static final int BLOCK_EXPLOSION_CACHE_MASK = (1 << BLOCK_EXPLOSION_CACHE_SHIFT) - 1; -+ private static final int BLOCK_EXPLOSION_CACHE_WIDTH = 1 << BLOCK_EXPLOSION_CACHE_SHIFT; -+ -+ // resistance = (res + 0.3F) * 0.3F; -+ // so for resistance = 0, we need res = -0.3F -+ private static final Float ZERO_RESISTANCE = Float.valueOf(-0.3f); -+ private it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap blockCache = null; -+ -+ public static final class ExplosionBlockCache { -+ -+ public final long key; -+ public final BlockPos immutablePos; -+ public final BlockState blockState; -+ public final FluidState fluidState; -+ public final float resistance; -+ public final boolean outOfWorld; -+ public Boolean shouldExplode; // null -> not called yet -+ public net.minecraft.world.phys.shapes.VoxelShape cachedCollisionShape; -+ -+ public ExplosionBlockCache(long key, BlockPos immutablePos, BlockState blockState, FluidState fluidState, float resistance, -+ boolean outOfWorld) { -+ this.key = key; -+ this.immutablePos = immutablePos; -+ this.blockState = blockState; -+ this.fluidState = fluidState; -+ this.resistance = resistance; -+ this.outOfWorld = outOfWorld; -+ } -+ } -+ -+ private long[] chunkPosCache = null; -+ private net.minecraft.world.level.chunk.LevelChunk[] chunkCache = null; -+ -+ private ExplosionBlockCache getOrCacheExplosionBlock(final int x, final int y, final int z, -+ final long key, final boolean calculateResistance) { -+ ExplosionBlockCache ret = this.blockCache.get(key); -+ if (ret != null) { -+ return ret; -+ } -+ -+ BlockPos pos = new BlockPos(x, y, z); -+ -+ if (!this.level.isInWorldBounds(pos)) { -+ ret = new ExplosionBlockCache(key, pos, null, null, 0.0f, true); -+ } else { -+ net.minecraft.world.level.chunk.LevelChunk chunk; -+ long chunkKey = io.papermc.paper.util.CoordinateUtils.getChunkKey(x >> 4, z >> 4); -+ int chunkCacheKey = ((x >> 4) & CHUNK_CACHE_MASK) | (((z >> 4) << CHUNK_CACHE_SHIFT) & (CHUNK_CACHE_MASK << CHUNK_CACHE_SHIFT)); -+ if (this.chunkPosCache[chunkCacheKey] == chunkKey) { -+ chunk = this.chunkCache[chunkCacheKey]; -+ } else { -+ this.chunkPosCache[chunkCacheKey] = chunkKey; -+ this.chunkCache[chunkCacheKey] = chunk = this.level.getChunk(x >> 4, z >> 4); -+ } -+ -+ BlockState blockState = chunk.getBlockStateFinal(x, y, z); -+ FluidState fluidState = blockState.getFluidState(); -+ -+ Optional resistance = !calculateResistance ? Optional.empty() : this.damageCalculator.getBlockExplosionResistance((Explosion)(Object)this, this.level, pos, blockState, fluidState); -+ -+ ret = new ExplosionBlockCache( -+ key, pos, blockState, fluidState, -+ (resistance.orElse(ZERO_RESISTANCE).floatValue() + 0.3f) * 0.3f, -+ false -+ ); -+ } -+ -+ this.blockCache.put(key, ret); -+ -+ return ret; -+ } -+ -+ private boolean clipsAnything(final Vec3 from, final Vec3 to, -+ final io.papermc.paper.util.CollisionUtil.LazyEntityCollisionContext context, -+ final ExplosionBlockCache[] blockCache, -+ final BlockPos.MutableBlockPos currPos) { -+ // assume that context.delegated = false -+ final double adjX = io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON * (from.x - to.x); -+ final double adjY = io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON * (from.y - to.y); -+ final double adjZ = io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON * (from.z - to.z); -+ -+ if (adjX == 0.0 && adjY == 0.0 && adjZ == 0.0) { -+ return false; -+ } -+ -+ final double toXAdj = to.x - adjX; -+ final double toYAdj = to.y - adjY; -+ final double toZAdj = to.z - adjZ; -+ final double fromXAdj = from.x + adjX; -+ final double fromYAdj = from.y + adjY; -+ final double fromZAdj = from.z + adjZ; -+ -+ int currX = Mth.floor(fromXAdj); -+ int currY = Mth.floor(fromYAdj); -+ int currZ = Mth.floor(fromZAdj); -+ -+ final double diffX = toXAdj - fromXAdj; -+ final double diffY = toYAdj - fromYAdj; -+ final double diffZ = toZAdj - fromZAdj; -+ -+ final double dxDouble = Math.signum(diffX); -+ final double dyDouble = Math.signum(diffY); -+ final double dzDouble = Math.signum(diffZ); -+ -+ final int dx = (int)dxDouble; -+ final int dy = (int)dyDouble; -+ final int dz = (int)dzDouble; -+ -+ final double normalizedDiffX = diffX == 0.0 ? Double.MAX_VALUE : dxDouble / diffX; -+ final double normalizedDiffY = diffY == 0.0 ? Double.MAX_VALUE : dyDouble / diffY; -+ final double normalizedDiffZ = diffZ == 0.0 ? Double.MAX_VALUE : dzDouble / diffZ; -+ -+ double normalizedCurrX = normalizedDiffX * (diffX > 0.0 ? (1.0 - Mth.frac(fromXAdj)) : Mth.frac(fromXAdj)); -+ double normalizedCurrY = normalizedDiffY * (diffY > 0.0 ? (1.0 - Mth.frac(fromYAdj)) : Mth.frac(fromYAdj)); -+ double normalizedCurrZ = normalizedDiffZ * (diffZ > 0.0 ? (1.0 - Mth.frac(fromZAdj)) : Mth.frac(fromZAdj)); -+ -+ for (;;) { -+ currPos.set(currX, currY, currZ); -+ -+ // ClipContext.Block.COLLIDER -> BlockBehaviour.BlockStateBase::getCollisionShape -+ // ClipContext.Fluid.NONE -> ignore fluids -+ -+ // read block from cache -+ final long key = BlockPos.asLong(currX, currY, currZ); -+ -+ final int cacheKey = -+ (currX & BLOCK_EXPLOSION_CACHE_MASK) | -+ (currY & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT) | -+ (currZ & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT + BLOCK_EXPLOSION_CACHE_SHIFT); -+ ExplosionBlockCache cachedBlock = blockCache[cacheKey]; -+ if (cachedBlock == null || cachedBlock.key != key) { -+ blockCache[cacheKey] = cachedBlock = this.getOrCacheExplosionBlock(currX, currY, currZ, key, false); -+ } -+ -+ final BlockState blockState = cachedBlock.blockState; -+ if (blockState != null && !blockState.emptyCollisionShape()) { -+ net.minecraft.world.phys.shapes.VoxelShape collision = cachedBlock.cachedCollisionShape; -+ if (collision == null) { -+ collision = blockState.getConstantCollisionShape(); -+ if (collision == null) { -+ collision = blockState.getCollisionShape(this.level, currPos, context); -+ if (!context.isDelegated()) { -+ // if it was not delegated during this call, assume that for any future ones it will not be delegated -+ // again, and cache the result -+ cachedBlock.cachedCollisionShape = collision; -+ } -+ } else { -+ cachedBlock.cachedCollisionShape = collision; -+ } -+ } -+ -+ if (!collision.isEmpty() && collision.clip(from, to, currPos) != null) { -+ return true; -+ } -+ } -+ -+ if (normalizedCurrX > 1.0 && normalizedCurrY > 1.0 && normalizedCurrZ > 1.0) { -+ return false; -+ } -+ -+ // inc the smallest normalized coordinate -+ -+ if (normalizedCurrX < normalizedCurrY) { -+ if (normalizedCurrX < normalizedCurrZ) { -+ currX += dx; -+ normalizedCurrX += normalizedDiffX; -+ } else { -+ // x < y && x >= z <--> z < y && z <= x -+ currZ += dz; -+ normalizedCurrZ += normalizedDiffZ; -+ } -+ } else if (normalizedCurrY < normalizedCurrZ) { -+ // y <= x && y < z -+ currY += dy; -+ normalizedCurrY += normalizedDiffY; -+ } else { -+ // y <= x && z <= y <--> z <= y && z <= x -+ currZ += dz; -+ normalizedCurrZ += normalizedDiffZ; -+ } -+ } -+ } -+ -+ private float getSeenFraction(final Vec3 source, final Entity target, -+ final ExplosionBlockCache[] blockCache, -+ final BlockPos.MutableBlockPos blockPos) { -+ final AABB boundingBox = target.getBoundingBox(); -+ final double diffX = boundingBox.maxX - boundingBox.minX; -+ final double diffY = boundingBox.maxY - boundingBox.minY; -+ final double diffZ = boundingBox.maxZ - boundingBox.minZ; -+ -+ final double incX = 1.0 / (diffX * 2.0 + 1.0); -+ final double incY = 1.0 / (diffY * 2.0 + 1.0); -+ final double incZ = 1.0 / (diffZ * 2.0 + 1.0); -+ -+ if (incX < 0.0 || incY < 0.0 || incZ < 0.0) { -+ return 0.0f; -+ } -+ -+ final double offX = (1.0 - Math.floor(1.0 / incX) * incX) * 0.5 + boundingBox.minX; -+ final double offY = boundingBox.minY; -+ final double offZ = (1.0 - Math.floor(1.0 / incZ) * incZ) * 0.5 + boundingBox.minZ; -+ -+ final io.papermc.paper.util.CollisionUtil.LazyEntityCollisionContext context = new io.papermc.paper.util.CollisionUtil.LazyEntityCollisionContext(target); -+ -+ int totalRays = 0; -+ int missedRays = 0; -+ -+ for (double dx = 0.0; dx <= 1.0; dx += incX) { -+ final double fromX = Math.fma(dx, diffX, offX); -+ for (double dy = 0.0; dy <= 1.0; dy += incY) { -+ final double fromY = Math.fma(dy, diffY, offY); -+ for (double dz = 0.0; dz <= 1.0; dz += incZ) { -+ ++totalRays; -+ -+ final Vec3 from = new Vec3( -+ fromX, -+ fromY, -+ Math.fma(dz, diffZ, offZ) -+ ); -+ -+ if (!this.clipsAnything(from, source, context, blockCache, blockPos)) { -+ ++missedRays; -+ } -+ } -+ } -+ } -+ -+ return (float)missedRays / (float)totalRays; -+ } -+ // Paper end - optimise collisions -+ - private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity entity) { - return (ExplosionDamageCalculator) (entity == null ? Explosion.EXPLOSION_DAMAGE_CALCULATOR : new EntityBasedExplosionDamageCalculator(entity)); - } -@@ -173,40 +438,88 @@ public class Explosion { - int i; - int j; - -- for (int k = 0; k < 16; ++k) { -- for (i = 0; i < 16; ++i) { -- for (j = 0; j < 16; ++j) { -- if (k == 0 || k == 15 || i == 0 || i == 15 || j == 0 || j == 15) { -- double d0 = (double) ((float) k / 15.0F * 2.0F - 1.0F); -- double d1 = (double) ((float) i / 15.0F * 2.0F - 1.0F); -- double d2 = (double) ((float) j / 15.0F * 2.0F - 1.0F); -- double d3 = Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2); -- -- d0 /= d3; -- d1 /= d3; -- d2 /= d3; -+ // Paper start - optimise explosions -+ this.blockCache = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(); -+ -+ this.chunkPosCache = new long[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH]; -+ java.util.Arrays.fill(this.chunkPosCache, ChunkPos.INVALID_CHUNK_POS); -+ -+ this.chunkCache = new net.minecraft.world.level.chunk.LevelChunk[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH]; -+ -+ final ExplosionBlockCache[] blockCache = new ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH]; -+ // use initial cache value that is most likely to be used: the source position -+ final ExplosionBlockCache initialCache; -+ { -+ final int blockX = Mth.floor(this.x); -+ final int blockY = Mth.floor(this.y); -+ final int blockZ = Mth.floor(this.z); -+ -+ final long key = BlockPos.asLong(blockX, blockY, blockZ); -+ -+ initialCache = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true); -+ } -+ // only ~1/3rd of the loop iterations in vanilla will result in a ray, as it is iterating the perimeter of -+ // a 16x16x16 cube -+ // we can cache the rays and their normals as well, so that we eliminate the excess iterations / checks and -+ // calculations in one go -+ // additional aggressive caching of block retrieval is very significant, as at low power (i.e tnt) most -+ // block retrievals are not unique -+ for (int ray = 0, len = CACHED_RAYS.length; ray < len;) { -+ { -+ { -+ { -+ ExplosionBlockCache cachedBlock = initialCache; -+ -+ double d0 = CACHED_RAYS[ray]; -+ double d1 = CACHED_RAYS[ray + 1]; -+ double d2 = CACHED_RAYS[ray + 2]; -+ ray += 3; -+ // Paper end - optimise explosions - float f = this.radius * (0.7F + this.level.random.nextFloat() * 0.6F); - double d4 = this.x; - double d5 = this.y; - double d6 = this.z; - - for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) { -- BlockPos blockposition = BlockPos.containing(d4, d5, d6); -- BlockState iblockdata = this.level.getBlockState(blockposition); -- if (!iblockdata.isDestroyable()) continue; // Paper - Protect Bedrock and End Portal/Frames from being destroyed -- FluidState fluid = iblockdata.getFluidState(); // Paper - Perf: Optimize call to getFluid for explosions -+ // Paper start - optimise explosions -+ final int blockX = Mth.floor(d4); -+ final int blockY = Mth.floor(d5); -+ final int blockZ = Mth.floor(d6); -+ -+ final long key = BlockPos.asLong(blockX, blockY, blockZ); -+ -+ if (cachedBlock.key != key) { -+ final int cacheKey = -+ (blockX & BLOCK_EXPLOSION_CACHE_MASK) | -+ (blockY & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT) | -+ (blockZ & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT + BLOCK_EXPLOSION_CACHE_SHIFT); -+ cachedBlock = blockCache[cacheKey]; -+ if (cachedBlock == null || cachedBlock.key != key) { -+ blockCache[cacheKey] = cachedBlock = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true); -+ } -+ } - -- if (!this.level.isInWorldBounds(blockposition)) { -+ if (cachedBlock.outOfWorld) { - break; - } - -- Optional optional = this.damageCalculator.getBlockExplosionResistance(this, this.level, blockposition, iblockdata, fluid); -+ BlockPos blockposition = cachedBlock.immutablePos; -+ BlockState iblockdata = cachedBlock.blockState; -+ // Paper end - optimise explosions - -- if (optional.isPresent()) { -- f -= ((Float) optional.get() + 0.3F) * 0.3F; -- } -+ if (!iblockdata.isDestroyable()) continue; // Paper -+ // Paper - optimise explosions - -- if (f > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, blockposition, iblockdata, f)) { -+ f -= cachedBlock.resistance; // Paper - optimise explosions -+ -+ if (f > 0.0F && cachedBlock.shouldExplode == null) { // Paper - optimise explosions -+ // Paper start - optimise explosions -+ // note: we expect shouldBlockExplode to be pure with respect to power, as Vanilla currently is. -+ // basically, it is unused, which allows us to cache the result -+ final boolean shouldExplode = this.damageCalculator.shouldBlockExplode(this, this.level, cachedBlock.immutablePos, cachedBlock.blockState, f); -+ cachedBlock.shouldExplode = shouldExplode ? Boolean.TRUE : Boolean.FALSE; -+ if (shouldExplode && (this.fire || !cachedBlock.blockState.isAir())) { -+ // Paper end - optimise explosions - set.add(blockposition); - // Paper start - prevent headless pistons from forming - if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowHeadlessPistons && iblockdata.getBlock() == Blocks.MOVING_PISTON) { -@@ -217,11 +530,12 @@ public class Explosion { - } - } - // Paper end - prevent headless pistons from forming -+ } // Paper - optimise explosions - } - -- d4 += d0 * 0.30000001192092896D; -- d5 += d1 * 0.30000001192092896D; -- d6 += d2 * 0.30000001192092896D; -+ d4 += d0; // Paper - optimise explosions -+ d5 += d1; // Paper - optimise explosions -+ d6 += d2; // Paper - optimise explosions - } - } - } -@@ -241,6 +555,8 @@ public class Explosion { - Vec3 vec3d = new Vec3(this.x, this.y, this.z); - Iterator iterator = list.iterator(); - -+ final BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(); // Paper - optimise explosions -+ - while (iterator.hasNext()) { - Entity entity = (Entity) iterator.next(); - -@@ -276,11 +592,11 @@ public class Explosion { - for (EnderDragonPart entityComplexPart : ((EnderDragon) entity).subEntities) { - // Calculate damage separately for each EntityComplexPart - if (list.contains(entityComplexPart)) { -- entityComplexPart.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity)); -+ entityComplexPart.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entityComplexPart, getSeenFraction(vec3d, entityComplexPart, blockCache, blockPos))); // Paper - actually optimise explosions and use the right entity to calculate the damage - } - } - } else { -- entity.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity)); -+ entity.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, getSeenFraction(vec3d, entity, blockCache, blockPos))); // Paper - actually optimise explosions - } - - if (entity.lastDamageCancelled) { // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Skip entity if damage event was cancelled -@@ -289,7 +605,7 @@ public class Explosion { - // CraftBukkit end - } - -- double d12 = (1.0D - d7) * this.getBlockDensity(vec3d, entity) * (double) this.damageCalculator.getKnockbackMultiplier(entity); // Paper - Optimize explosions -+ double d12 = (1.0D - d7) * this.getBlockDensity(vec3d, entity, blockCache, blockPos) * (double) this.damageCalculator.getKnockbackMultiplier(entity); // Paper - Optimize explosions - double d13; - - if (entity instanceof LivingEntity) { -@@ -328,6 +644,9 @@ public class Explosion { - } - } - -+ this.blockCache = null; // Paper - optimise explosions -+ this.chunkPosCache = null; // Paper - optimise explosions -+ this.chunkCache = null; // Paper - optimise explosions - } - - public void finalizeExplosion(boolean particles) { -@@ -547,14 +866,14 @@ public class Explosion { - private BlockInteraction() {} - } - // Paper start - Optimize explosions -- private float getBlockDensity(Vec3 vec3d, Entity entity) { -+ private float getBlockDensity(Vec3 vec3d, Entity entity, ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { // Paper - optimise explosions - if (!this.level.paperConfig().environment.optimizeExplosions) { -- return getSeenPercent(vec3d, entity); -+ return this.getSeenFraction(vec3d, entity, blockCache, blockPos); // Paper - optimise explosions - } - CacheKey key = new CacheKey(this, entity.getBoundingBox()); - Float blockDensity = this.level.explosionDensityCache.get(key); - if (blockDensity == null) { -- blockDensity = getSeenPercent(vec3d, entity); -+ blockDensity = this.getSeenFraction(vec3d, entity, blockCache, blockPos); // Paper - optimise explosions; - this.level.explosionDensityCache.put(key, blockDensity); - } - -diff --git a/src/main/java/net/minecraft/world/level/ExplosionDamageCalculator.java b/src/main/java/net/minecraft/world/level/ExplosionDamageCalculator.java -index 0ef9b402d129b072134688c06719a56328581158..1eb259b48bcab6172c15546744eea410c6a3e1fe 100644 ---- a/src/main/java/net/minecraft/world/level/ExplosionDamageCalculator.java -+++ b/src/main/java/net/minecraft/world/level/ExplosionDamageCalculator.java -@@ -26,11 +26,17 @@ public class ExplosionDamageCalculator { - return 1.0F; - } - -+ @io.papermc.paper.annotation.DoNotUse @Deprecated // Paper - public float getEntityDamageAmount(Explosion explosion, Entity entity) { -+ // Paper start - actually optimise explosions -+ return this.getEntityDamageAmount(explosion, entity, Explosion.getSeenPercent(explosion.center(), entity)); -+ } -+ public float getEntityDamageAmount(Explosion explosion, Entity entity, double seenPercent) { -+ // Paper end - actually optimise explosions - float f = explosion.radius() * 2.0F; - Vec3 vec3 = explosion.center(); - double d = Math.sqrt(entity.distanceToSqr(vec3)) / (double)f; -- double e = (1.0 - d) * (double)Explosion.getSeenPercent(vec3, entity); -+ double e = (1.0 - d) * seenPercent; // Paper - actually optimise explosions - return (float)((e * e + e) / 2.0 * 7.0 * (double)f + 1.0); - } - } diff --git a/patches/server/0010-Reduce-deltaMovement-Allocations.patch b/patches/server/0006-Reduce-deltaMovement-Allocations.patch similarity index 90% rename from patches/server/0010-Reduce-deltaMovement-Allocations.patch rename to patches/server/0006-Reduce-deltaMovement-Allocations.patch index 7193cdb..53a3c8e 100644 --- a/patches/server/0010-Reduce-deltaMovement-Allocations.patch +++ b/patches/server/0006-Reduce-deltaMovement-Allocations.patch @@ -5,19 +5,19 @@ Subject: [PATCH] Reduce deltaMovement Allocations diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 198ad71c934dd48f8a55927a3f15b6d8fa398903..3fcc7eb45a1729a31697788870c7375e9c1f7303 100644 +index a04c417602c928e09b856d4ac73562a18a4995e4..03f87fe754c46bd3d726ecbf9948c224d07759bc 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1253,7 +1253,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1276,7 +1276,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.tryCheckInsideBlocks(); float f = this.getBlockSpeedFactor(); - this.setDeltaMovement(this.getDeltaMovement().multiply((double) f, 1.0D, (double) f)); + this.multiplyDeltaMovement((double) f, 1.0D, (double) f); // Sakura - reduce movement allocations - // Paper start - remove expensive streams from here - boolean noneMatch = true; - AABB fireSearchBox = this.getBoundingBox().deflate(1.0E-6D); -@@ -1871,7 +1871,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + if (this.level().getBlockStatesIfLoaded(this.getBoundingBox().deflate(1.0E-6D)).noneMatch((iblockdata2) -> { + return iblockdata2.is(BlockTags.FIRE) || iblockdata2.is(Blocks.LAVA); + })) { +@@ -1821,7 +1821,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess double d0 = this.getGravity(); if (d0 != 0.0D) { @@ -26,7 +26,7 @@ index 198ad71c934dd48f8a55927a3f15b6d8fa398903..3fcc7eb45a1729a31697788870c7375e } } -@@ -2171,6 +2171,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2121,6 +2121,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess public void moveTo(double x, double y, double z, float yaw, float pitch) { // Paper start - Fix Entity Teleportation and cancel velocity if teleported if (!preserveMotion) { @@ -34,7 +34,7 @@ index 198ad71c934dd48f8a55927a3f15b6d8fa398903..3fcc7eb45a1729a31697788870c7375e this.deltaMovement = Vec3.ZERO; } else { this.preserveMotion = false; -@@ -3600,29 +3601,33 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -3548,29 +3549,33 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public void onAboveBubbleCol(boolean drag) { @@ -76,7 +76,7 @@ index 198ad71c934dd48f8a55927a3f15b6d8fa398903..3fcc7eb45a1729a31697788870c7375e this.resetFallDistance(); } -@@ -4560,16 +4565,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4511,16 +4516,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess vec3d = vec3d.normalize(); } @@ -99,7 +99,7 @@ index 198ad71c934dd48f8a55927a3f15b6d8fa398903..3fcc7eb45a1729a31697788870c7375e } this.fluidHeight.put(tag, d1); -@@ -4640,11 +4648,53 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4591,12 +4599,54 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return this.chunkPosition; } @@ -149,11 +149,12 @@ index 198ad71c934dd48f8a55927a3f15b6d8fa398903..3fcc7eb45a1729a31697788870c7375e } public void setDeltaMovement(Vec3 velocity) { + synchronized (this.posLock) { // Paper + this.movementDirty = false; this.deltaMovement = velocity; + } // Paper } - -@@ -4653,7 +4703,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4606,7 +4656,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public void setDeltaMovement(double x, double y, double z) { @@ -205,10 +206,10 @@ index 28a15f612239614c6c56974da5855ce3bfd7c89e..d14128dd5d63e93a3d4aec76b0ee8d0e int i = this.getFuse() - 1; diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 3d5131e51fb924778c392267699fe2c11bf2f40f..3a35dcdfd639240363891f412beeef47b8b41e06 100644 +index a248d859cbce48f4a34c4771a7acffc17d7edc84..798b840af2dc5f58e6df55426a1802d79ff5d6cb 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -619,17 +619,23 @@ public class Explosion { +@@ -595,17 +595,23 @@ public class Explosion { d8 *= d13; d9 *= d13; d10 *= d13; @@ -235,10 +236,10 @@ index 3d5131e51fb924778c392267699fe2c11bf2f40f..3a35dcdfd639240363891f412beeef47 Player entityhuman = (Player) entity; diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java -index 2c356d2c40fe52672f108b2dbb89b7a27616e4d1..472f64bb1fc2ebf7e89e4e90d780732de3c85aef 100644 +index a7108b2be0746aa1f0e574d8c6f5ffad6d369835..6daca887dd4cc0683a4d066aa164bcd6dc2fc575 100644 --- a/src/main/java/net/minecraft/world/level/block/Block.java +++ b/src/main/java/net/minecraft/world/level/block/Block.java -@@ -460,7 +460,7 @@ public class Block extends BlockBehaviour implements ItemLike { +@@ -461,7 +461,7 @@ public class Block extends BlockBehaviour implements ItemLike { } public void updateEntityAfterFallOn(BlockGetter world, Entity entity) { diff --git a/patches/server/0011-Optional-Force-Position-Updates.patch b/patches/server/0007-Optional-Force-Position-Updates.patch similarity index 100% rename from patches/server/0011-Optional-Force-Position-Updates.patch rename to patches/server/0007-Optional-Force-Position-Updates.patch diff --git a/patches/server/0012-Load-Chunks-on-Movement.patch b/patches/server/0008-Load-Chunks-on-Movement.patch similarity index 51% rename from patches/server/0012-Load-Chunks-on-Movement.patch rename to patches/server/0008-Load-Chunks-on-Movement.patch index 53b7294..eaad591 100644 --- a/patches/server/0012-Load-Chunks-on-Movement.patch +++ b/patches/server/0008-Load-Chunks-on-Movement.patch @@ -4,143 +4,145 @@ Date: Sat, 11 Sep 2021 19:19:41 +0100 Subject: [PATCH] Load Chunks on Movement -diff --git a/src/main/java/io/papermc/paper/util/CollisionUtil.java b/src/main/java/io/papermc/paper/util/CollisionUtil.java -index 614a3de568ec1113f445dce37b46863f3cc1c741..f941c4bbf94f80dc3db3153297713a41c6c8ca48 100644 ---- a/src/main/java/io/papermc/paper/util/CollisionUtil.java -+++ b/src/main/java/io/papermc/paper/util/CollisionUtil.java -@@ -1569,6 +1569,7 @@ public final class CollisionUtil { +diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java +index 3cbd02085cc3e2ddb15458faea4b553868cff39a..740dc010cfd42941a6f9a2b2c543973429fdbfa3 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java ++++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java +@@ -1574,6 +1574,7 @@ public final class CollisionUtil { public static final int COLLISION_FLAG_COLLIDE_WITH_UNLOADED_CHUNKS = 1 << 1; public static final int COLLISION_FLAG_CHECK_BORDER = 1 << 2; public static final int COLLISION_FLAG_CHECK_ONLY = 1 << 3; -+ public static final int COLLISION_FLAG_ADD_TICKET = 1 << 4; // Sakura ++ public static final int COLLISION_FLAG_ADD_TICKET = 1 << 4; // Sakura - load chunks on movement - public static boolean getCollisionsForBlocksOrWorldBorder(final Level world, final Entity entity, final AABB aabb, - final List intoVoxel, final List intoAABB, -@@ -1619,11 +1620,21 @@ public final class CollisionUtil { + public static boolean getCollisionsForBlocksOrWorldBorder(final net.minecraft.world.level.Level world, final net.minecraft.world.entity.Entity entity, final net.minecraft.world.phys.AABB aabb, + final java.util.List intoVoxel, final java.util.List intoAABB, +@@ -1624,6 +1625,7 @@ public final class CollisionUtil { final int maxChunkZ = maxBlockZ >> 4; final boolean loadChunks = (collisionFlags & COLLISION_FLAG_LOAD_CHUNKS) != 0; + final boolean addTicket = (collisionFlags & COLLISION_FLAG_ADD_TICKET) != 0; // Sakura - final ServerChunkCache chunkSource = (ServerChunkCache)world.getChunkSource(); + final net.minecraft.world.level.chunk.ChunkSource chunkSource = world.getChunkSource(); for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) { - for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) { - final ChunkAccess chunk = loadChunks ? chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, true) : chunkSource.getChunkAtIfLoadedImmediately(currChunkX, currChunkZ); -+ // Sakura start - keep chunks loaded on movement -+ if (addTicket && chunk instanceof net.minecraft.world.level.chunk.LevelChunk levelChunk && levelChunk.movementLoadTicketRequiresUpdate()) { -+ chunkSource.chunkMap.getDistanceManager().getChunkHolderManager().addTicketAtLevel(net.minecraft.server.level.TicketType.ENTITY_MOVEMENT, currChunkX, currChunkZ, 33, CoordinateUtils.getChunkKey(currChunkX, currChunkZ)); -+ // This is known to work, uncomment if any issues -+ // var pos = new net.minecraft.world.level.ChunkPos(currChunkX, currChunkZ); -+ // chunkSource.addTicketAtLevel(net.minecraft.server.level.TicketType.ENTITY_MOVEMENT, pos, 33, pos.toLong()); -+ levelChunk.updatedMovementLoadTicket(); -+ } -+ // Sakura end +@@ -1642,6 +1644,14 @@ public final class CollisionUtil { + continue; + } - if (chunk == null) { - if ((collisionFlags & COLLISION_FLAG_COLLIDE_WITH_UNLOADED_CHUNKS) != 0) { ++ // Sakura start - load chunks on movement ++ if (addTicket && chunk.movementTicketNeedsUpdate() && chunkSource instanceof net.minecraft.server.level.ServerChunkCache chunkCache) { ++ final long chunkKey = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(currChunkX, currChunkZ); ++ chunkCache.chunkMap.getDistanceManager().getChunkHolderManager().addTicketAtLevel(net.minecraft.server.level.TicketType.ENTITY_MOVEMENT, currChunkX, currChunkZ, 33, chunkKey); ++ chunk.updatedMovementTicket(); ++ } ++ // Sakura end - load chunks on movement ++ + final net.minecraft.world.level.chunk.LevelChunkSection[] sections = chunk.getSections(); + + // bound y diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java -index f56e5c0f53f9b52a9247b9be9265b949494fc924..d0fc85480cc88aa2d04a03b0d3637fee41f003a4 100644 +index f56e5c0f53f9b52a9247b9be9265b949494fc924..5a480f36e709a5e9fea2cbb8ccc0a14d1e863f4c 100644 --- a/src/main/java/net/minecraft/server/level/TicketType.java +++ b/src/main/java/net/minecraft/server/level/TicketType.java @@ -25,6 +25,7 @@ public class TicketType { public static final TicketType UNKNOWN = TicketType.create("unknown", Comparator.comparingLong(ChunkPos::toLong), 1); public static final TicketType PLUGIN = TicketType.create("plugin", (a, b) -> 0); // CraftBukkit public static final TicketType PLUGIN_TICKET = TicketType.create("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit -+ public static final TicketType ENTITY_MOVEMENT = create("entity_movement", Long::compareTo, 10 * 20); // Sakura ++ public static final TicketType ENTITY_MOVEMENT = create("entity_movement", Long::compareTo, 10 * 20); // Sakura - load chunks on movement public static TicketType create(String name, Comparator argumentComparator) { return new TicketType<>(name, argumentComparator, 0L); diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 3fcc7eb45a1729a31697788870c7375e9c1f7303..117f36743e1fa8a468528d44f0e16e6f7283880e 100644 +index 03f87fe754c46bd3d726ecbf9948c224d07759bc..8cbbe924f56e2e3d3c0e48e14e3b9176e3ef714f 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -566,6 +566,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -554,6 +554,20 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess public boolean isPrimedTNT; public boolean isFallingBlock; // Sakura end - visibility api and command -+ // Sakura start - load chunks on cannon entity movement ++ // Sakura start - load chunks on movement + protected boolean loadChunks = false; + + private int getExtraCollisionFlags() { + int flags = 0; + + if (this.loadChunks) { -+ flags |= io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_LOAD_CHUNKS | io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_ADD_TICKET; ++ flags |= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_LOAD_CHUNKS; ++ flags |= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_ADD_TICKET; + } + + return flags; + } -+ // Sakura end - load chunks on cannon entity movement ++ // Sakura end - load chunks on movement public Entity(EntityType type, Level world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); -@@ -1515,7 +1528,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1502,7 +1516,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - io.papermc.paper.util.CollisionUtil.getCollisions( - world, this, collisionBox, potentialCollisionsVoxel, potentialCollisionsBB, -- io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_CHECK_BORDER, -+ io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_CHECK_BORDER | this.getExtraCollisionFlags(), // Sakura + ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.getCollisions( + world, (Entity)(Object)this, collisionBox, potentialCollisionsVoxel, potentialCollisionsBB, +- ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER, ++ ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER | this.getExtraCollisionFlags(), // Sakura - load chunks on movement null, null ); -@@ -4969,12 +4982,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4924,12 +4938,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @Override public boolean shouldBeSaved() { - return this.removalReason != null && !this.removalReason.shouldSave() ? false : (this.isPassenger() ? false : !this.isVehicle() || !((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)this).moonrise$hasAnyPlayerPassengers()); // Paper - rewrite chunk system -+ return this.removalReason != null && !this.removalReason.shouldSave() ? false : (this.loadChunks || this.isPassenger() ? false : !this.isVehicle() || !((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)this).moonrise$hasAnyPlayerPassengers()); // Sakura - used to determine whether a chunk should unload // Paper - rewrite chunk system ++ return this.removalReason != null && !this.removalReason.shouldSave() ? false : (this.loadChunks || this.isPassenger() ? false : !this.isVehicle() || !((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)this).moonrise$hasAnyPlayerPassengers()); // Sakura - load chunks on movement; used to determine whether a chunk should unload // Paper - rewrite chunk system } @Override public boolean isAlwaysTicking() { - return false; -+ return this.loadChunks; // Sakura - always tick in chunks ++ return this.loadChunks; // Sakura - load chunks on movement; always tick in unloaded & lazy chunks } public boolean mayInteract(Level world, BlockPos pos) { diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index 0b84c21749d942851d5126b795e4b609070ed31f..3feffddfb537451d88e316f39ee28a6fa22921aa 100644 +index 0b84c21749d942851d5126b795e4b609070ed31f..c030d4e3e059227995c2f9a3a1c58d78d29ec41c 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -79,6 +79,7 @@ public class FallingBlockEntity extends Entity { this.dropItem = true; this.fallDamageMax = 40; this.isFallingBlock = true; // Sakura -+ this.loadChunks = world.sakuraConfig().cannons.loadChunks; // Sakura - falling blocks load chunks ++ this.loadChunks = world.sakuraConfig().cannons.loadChunks; // Sakura - load chunks on movement } public FallingBlockEntity(Level world, double x, double y, double z, BlockState block) { diff --git a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -index 2eb69c7d7843a7b58479049633b838212d003c10..bcee8b6254618dc7f444992ec8cdfe53b12458a7 100644 +index 2eb69c7d7843a7b58479049633b838212d003c10..eeb3ef7a3f86a062e39e6978e97ab7bd71dfa574 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java @@ -58,6 +58,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { super(type, world); this.blocksBuilding = true; this.isPrimedTNT = true; // Sakura -+ this.loadChunks = world.sakuraConfig().cannons.loadChunks; // Sakura - tnt load chunks ++ this.loadChunks = world.sakuraConfig().cannons.loadChunks; // Sakura - load chunks on movement } public PrimedTnt(Level world, double x, double y, double z, @Nullable LivingEntity igniter) { -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 d388fbcbff63928f0e9140c02400a63ba8f19d9c..40471b340a9f92a4b60e19285ebf9d58a4857a60 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -127,6 +127,17 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p - return this.postProcessingDone; - } - // Paper end - rewrite chunk system -+ // Sakura start +diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java +index 75c8125e20b70433fe9d143a3193d821043327c3..77071ef825951cbdcbad79f4496add9dc499e856 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java +@@ -140,6 +140,17 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom + private final int minSection; + private final int maxSection; + // Paper end - get block chunk optimisation ++ // Sakura start - load chunks on movement + private long lastMovementLoadTicket = 0; + -+ public boolean movementLoadTicketRequiresUpdate() { ++ public final boolean movementTicketNeedsUpdate() { + return net.minecraft.server.MinecraftServer.currentTickLong - this.lastMovementLoadTicket >= 100; + } + -+ public void updatedMovementLoadTicket() { ++ public final void updatedMovementTicket() { + this.lastMovementLoadTicket = net.minecraft.server.MinecraftServer.currentTickLong; + } -+ // Sakura end ++ // Sakura end - load chunks on movement - public LevelChunk(ServerLevel world, ProtoChunk protoChunk, @Nullable LevelChunk.PostLoadProcessor entityLoader) { - this(world, protoChunk.getPos(), protoChunk.getUpgradeData(), protoChunk.unpackBlockTicks(), protoChunk.unpackFluidTicks(), protoChunk.getInhabitedTime(), protoChunk.getSections(), entityLoader, protoChunk.getBlendingData()); + public ChunkAccess(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor heightLimitView, Registry biomeRegistry, long inhabitedTime, @Nullable LevelChunkSection[] sectionArray, @Nullable BlendingData blendingData) { + this.locX = pos.x; this.locZ = pos.z; // Paper - reduce need for field lookups diff --git a/patches/server/0009-Optimise-rayTracing.patch b/patches/server/0009-Optimise-rayTracing.patch deleted file mode 100644 index 8ba37b7..0000000 --- a/patches/server/0009-Optimise-rayTracing.patch +++ /dev/null @@ -1,134 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik -Date: Tue, 1 Mar 2022 18:40:09 +0000 -Subject: [PATCH] Optimise rayTracing - - -diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 7aa9ddb1d61ffb7da3f867e5a5bd04e3432b5621..3d5131e51fb924778c392267699fe2c11bf2f40f 100644 ---- a/src/main/java/net/minecraft/world/level/Explosion.java -+++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -297,7 +297,7 @@ public class Explosion { - } - } - -- if (!collision.isEmpty() && collision.clip(from, to, currPos) != null) { -+ if (!collision.isEmpty() && collision.clipDirect(from, to, currPos)) { // Sakura - optimise block clipping - return true; - } - } -diff --git a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java -index 001e2e6ed4b8b63a23dc0013882674834812a961..138fd28437fb4923773b8e6ba9bb53c3ce540e0d 100644 ---- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java -+++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java -@@ -720,6 +720,110 @@ public abstract class VoxelShape { - // Paper end - optimise collisions - } - -+ // Sakura start -+ // As of 1.20.2 paper has their own version of the pufferfish patch that this patch expanded on. -+ // A bit this patch is now obsolete such as simple AABB clipping. -+ // We will still use our method when a detailed hit result isn't required. -+ public boolean clipDirect(Vec3 start, Vec3 end, BlockPos pos) { -+ if (this.isEmpty) { -+ return false; -+ } -+ -+ double vec3_x = end.x - start.x; -+ double vec3_y = end.y - start.y; -+ double vec3_z = end.z - start.z; -+ double vec3_lengthSqr = (vec3_x * vec3_x) + (vec3_y * vec3_y) + (vec3_z * vec3_z); -+ -+ if (vec3_lengthSqr < 1.0E-7D) { -+ return false; -+ } -+ -+ AABB singleAABB = this.singleAABBRepresentation; -+ //noinspection ConstantValue -+ if (singleAABB != null) { -+ return clipWithBBDirect(singleAABB, vec3_x, vec3_y, vec3_z, start, pos); -+ } -+ -+ return clipWithBBsDirect(vec3_x, vec3_y, vec3_z, start, pos); -+ } -+ -+ protected boolean clipWithBBDirect(AABB single, double deltaX, double deltaY, double deltaZ, Vec3 from, BlockPos pos) { -+ double posX = pos.getX(); -+ double posY = pos.getY(); -+ double posZ = pos.getZ(); -+ -+ return clipPointBB(single, from, posX, posY, posZ, deltaX, deltaY, deltaZ) -+ || clipInsideDirectBB(single, deltaX, deltaY, deltaZ, from, pos); -+ } -+ -+ protected boolean clipWithBBsDirect(double deltaX, double deltaY, double deltaZ, Vec3 from, BlockPos pos) { -+ double posX = pos.getX(); -+ double posY = pos.getY(); -+ double posZ = pos.getZ(); -+ -+ for (AABB bb : toAabbs()) { // err -+ if (clipPointBB(bb, from, posX, posY, posZ, deltaX, deltaY, deltaZ)) { -+ return true; -+ } -+ } -+ -+ return clipInsideDirectBBs(deltaX, deltaY, deltaZ, from, pos); -+ } -+ -+ @SuppressWarnings("SuspiciousNameCombination") -+ protected static boolean clipPointBB(AABB box, Vec3 p, double posX, double posY, double posZ, double deltaX, double deltaY, double deltaZ) { -+ double minX = box.minX + posX; -+ double minY = box.minY + posY; -+ double minZ = box.minZ + posZ; -+ double maxX = box.maxX + posX; -+ double maxY = box.maxY + posY; -+ double maxZ = box.maxZ + posZ; -+ -+ // todo: this could be simplified by using the centre of the bb -+ // if the bb dimensions are not the same then either scale or subtract from the result. -+ double closestX = deltaX > 1.0E-7D ? minX : maxX; -+ double closestY = deltaY > 1.0E-7D ? minY : maxY; -+ double closestZ = deltaZ > 1.0E-7D ? minZ : maxZ; -+ -+ return clipPoint(deltaX, deltaY, deltaZ, closestX, minY, maxY, minZ, maxZ, p.x, p.y, p.z) -+ || clipPoint(deltaY, deltaZ, deltaX, closestY, minZ, maxZ, minX, maxX, p.y, p.z, p.x) -+ || clipPoint(deltaZ, deltaX, deltaY, closestZ, minX, maxX, minY, maxY, p.z, p.x, p.y); -+ } -+ -+ private static boolean clipPoint(double deltaX, double deltaY, double deltaZ, double begin, double minX, double maxX, double minZ, double maxZ, double startX, double startY, double startZ) { -+ double d = (begin - startX) / deltaX; -+ double e = startY + d * deltaY; -+ double f = startZ + d * deltaZ; -+ return (d > 0.0D && d < 1.0) && (minX - 1.0E-7D < e && maxX + 1.0E-7D > e) && (minZ - 1.0E-7D < f && maxZ + 1.0E-7D > f); -+ } -+ -+ // Absolutely horrendous code that takes a toll on all clip misses. -+ // This cannot be removed to maintain edge cases caused by this code existing in vanilla. -+ protected boolean clipInsideDirectBB(AABB single, double vec3_x, double vec3_y, double vec3_z, Vec3 start, BlockPos pos) { -+ double fromBehindX = start.x + (vec3_x * 0.001D); -+ double fromBehindY = start.y + (vec3_y * 0.001D); -+ double fromBehindZ = start.z + (vec3_z * 0.001D); -+ -+ double fromBehindOffsetX = fromBehindX - (double) pos.getX(); -+ double fromBehindOffsetY = fromBehindY - (double) pos.getY(); -+ double fromBehindOffsetZ = fromBehindZ - (double) pos.getZ(); -+ -+ return single.contains(fromBehindOffsetX, fromBehindOffsetY, fromBehindOffsetZ); -+ } -+ -+ protected boolean clipInsideDirectBBs(double vec3_x, double vec3_y, double vec3_z, Vec3 start, BlockPos pos) { -+ double fromBehindX = start.x + (vec3_x * 0.001D); -+ double fromBehindY = start.y + (vec3_y * 0.001D); -+ double fromBehindZ = start.z + (vec3_z * 0.001D); -+ -+ int indexX = this.findIndex(Direction.Axis.X, fromBehindX - (double)pos.getX()); -+ int indexY = this.findIndex(Direction.Axis.Y, fromBehindY - (double)pos.getY()); -+ int indexZ = this.findIndex(Direction.Axis.Z, fromBehindZ - (double)pos.getZ()); -+ -+ return this.shape.isFullWide(indexX, indexY, indexZ); -+ } -+ // Sakura end -+ - public Optional closestPointTo(Vec3 target) { - // Paper start - optimise collisions - if (this.isEmpty) { diff --git a/patches/server/0013-TPS-Graph-Command.patch b/patches/server/0009-TPS-Graph-Command.patch similarity index 97% rename from patches/server/0013-TPS-Graph-Command.patch rename to patches/server/0009-TPS-Graph-Command.patch index 13aaf6f..f7cc72b 100644 --- a/patches/server/0013-TPS-Graph-Command.patch +++ b/patches/server/0009-TPS-Graph-Command.patch @@ -418,10 +418,10 @@ index 0000000000000000000000000000000000000000..6903863ad293a335a8ed1aeaa06fccb4 + +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 524ca2d1719ac52ae287f6c4c56ca9d024f084eb..041e42c25e536a960a2e59114d2761c54520002e 100644 +index 9f21eec9547b15b19f6dca4a7e894219abf5350b..564278107e38b2a1adb03dab4c92a6ed20fd1d4e 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1153,6 +1153,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop SORT_EXPLOSION_RAYS = java.util.Comparator.comparingDouble(vec -> { -+ double sign = Math.signum(vec[0]); -+ double dir = (sign - 1) / 2; -+ return sign + 8 + vec[2] * dir; -+ }); -+ + private static double[] sortExplosionRays(final it.unimi.dsi.fastutil.doubles.DoubleArrayList rayCoords) { + List explosionRays = new ArrayList<>(); + @@ -38,7 +41,11 @@ index 3a35dcdfd639240363891f412beeef47b8b41e06..b6364b02142a71741bdeb5df3da58dcb + } + + rayCoords.clear(); -+ explosionRays.sort(SORT_EXPLOSION_RAYS); ++ explosionRays.sort(java.util.Comparator.comparingDouble(vec -> { ++ double sign = Math.signum(vec[0]); ++ double dir = (sign - 1) / 2; ++ return sign + 8 + vec[2] * dir; ++ })); + + double[] rays = new double[explosionRays.size() * 3]; + for (int i = 0; i < explosionRays.size() * 3; i++) { @@ -47,31 +54,13 @@ index 3a35dcdfd639240363891f412beeef47b8b41e06..b6364b02142a71741bdeb5df3da58dcb + return rays; + } + // Sakura end - optimise paper explosions - // Paper start - optimise collisions - private static final double[] CACHED_RAYS; - static { -@@ -138,7 +173,7 @@ public class Explosion { - } - } -- CACHED_RAYS = rayCoords.toDoubleArray(); -+ CACHED_RAYS = sortExplosionRays(rayCoords); // Sakura - optimise paper explosions - } + public static DamageSource getDefaultDamageSource(Level world, @Nullable Entity source) { + return world.damageSources().explosion(source, Explosion.getIndirectSourceEntityInternal(source)); +@@ -438,6 +471,16 @@ public class Explosion { + int j; - private static final int CHUNK_CACHE_SHIFT = 2; -@@ -432,7 +467,7 @@ public class Explosion { - } - // CraftBukkit end - this.level.gameEvent(this.source, (Holder) GameEvent.EXPLODE, new Vec3(this.x, this.y, this.z)); -- Set set = Sets.newHashSet(); -+ // Sakura - moved into searchForBlocks - boolean flag = true; - - int i; -@@ -458,6 +493,17 @@ public class Explosion { - - initialCache = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true); - } + // Paper start - collision optimisations + // Sakura start - optimise paper explosions + if (initialCache.resistance <= (this.radius * 1.3f) && this.interactsWithBlocks()) { + this.searchForBlocks(blockCache, initialCache); @@ -80,16 +69,15 @@ index 3a35dcdfd639240363891f412beeef47b8b41e06..b6364b02142a71741bdeb5df3da58dcb + this.clearBlockCache(); + } + -+ protected final void searchForBlocks(final ExplosionBlockCache[] blockCache, final ExplosionBlockCache initialCache) { -+ Set set = Sets.newHashSet(); ++ protected final void searchForBlocks(final ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache, final ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache initialCache) { + // Sakura end - optimise paper explosions - // only ~1/3rd of the loop iterations in vanilla will result in a ray, as it is iterating the perimeter of - // a 16x16x16 cube - // we can cache the rays and their normals as well, so that we eliminate the excess iterations / checks and -@@ -543,23 +589,62 @@ public class Explosion { - } + for (int ray = 0, len = CACHED_RAYS.length; ray < len;) { + ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache cachedBlock = initialCache; - this.toBlow.addAll(set); +@@ -509,25 +552,64 @@ public class Explosion { + } + + // Paper - optimise collisions - float f2 = this.radius * 2.0F; + // Sakura start - optimise paper explosions + } @@ -106,16 +94,17 @@ index 3a35dcdfd639240363891f412beeef47b8b41e06..b6364b02142a71741bdeb5df3da58dcb int j1 = Mth.floor(this.z - (double) f2 - 1.0D); int k1 = Mth.floor(this.z + (double) f2 + 1.0D); - List list = this.level.getEntities(this.source, new AABB((double) i, (double) l, (double) j1, (double) j, (double) i1, (double) k1), (com.google.common.base.Predicate) entity -> entity.isAlive() && !entity.isSpectator()); // Paper - Fix lag from explosions processing dead entities -- Vec3 vec3d = new Vec3(this.x, this.y, this.z); -- Iterator iterator = list.iterator(); + return new AABB((double) i, (double) l, (double) j1, (double) j, (double) i1, (double) k1); + } - -+ protected final void locateAndImpactEntities(final ExplosionBlockCache[] blockCache) { -+ float f2 = this.radius * 2.0F; + -+ Vec3 vec3d = new Vec3(this.x, this.y, this.z); - final BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(); // Paper - optimise explosions ++ protected final void locateAndImpactEntities(final ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache) { ++ float f2 = this.radius * 2.0F; + Vec3 vec3d = new Vec3(this.x, this.y, this.z); +- Iterator iterator = list.iterator(); + + // Paper start - optimise collisions + final BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(); + // Paper end - optimise collisions - while (iterator.hasNext()) { - Entity entity = (Entity) iterator.next(); @@ -128,7 +117,7 @@ index 3a35dcdfd639240363891f412beeef47b8b41e06..b6364b02142a71741bdeb5df3da58dcb + int maxChunkY = Mth.clamp(Mth.floor(this.y + f2) >> 4, minSection, maxSection); + int minChunkZ = Mth.floor(this.z - f2) >> 4; + int maxChunkZ = Mth.floor(this.z + f2) >> 4; -+ + + ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup entityLookup = this.level.moonrise$getEntityLookup(); + for (int chunkX = minChunkX; chunkX <= maxChunkX; ++chunkX) { + for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; ++chunkZ) { @@ -142,7 +131,7 @@ index 3a35dcdfd639240363891f412beeef47b8b41e06..b6364b02142a71741bdeb5df3da58dcb + } + } + -+ protected final void impactEntities(final ExplosionBlockCache[] blockCache, final BlockPos.MutableBlockPos blockPos, float f2, Vec3 vec3d, Entity[] entities) { ++ protected final void impactEntities(ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos, float f2, Vec3 vec3d, Entity[] entities) { + for (int i = 0; i < entities.length; i++) { + Entity entity = entities[i]; + if (entity == null) break; // end of entity section @@ -150,14 +139,14 @@ index 3a35dcdfd639240363891f412beeef47b8b41e06..b6364b02142a71741bdeb5df3da58dcb + if (entity != entities[i]) i--; // entities can be removed mid-explosion + } + } - -+ protected final void impactEntity(final ExplosionBlockCache[] blockCache, final BlockPos.MutableBlockPos blockPos, float f2, Vec3 vec3d, Entity entity) { ++ ++ protected final void impactEntity(ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos, float f2, Vec3 vec3d, Entity entity) { + if (entity.isAlive() && !entity.isSpectator()) { // Paper - Fix lag from explosions processing dead entities + // Sakura end - optimise paper explosions if (!entity.ignoreExplosion(this)) { double d7 = Math.sqrt(entity.distanceToSqr(vec3d)) / (double) f2; -@@ -583,24 +668,27 @@ public class Explosion { +@@ -552,7 +634,7 @@ public class Explosion { // - Damaging EntityEnderDragon does nothing // - EntityEnderDragon hitbock always covers the other parts and is therefore always present if (entity instanceof EnderDragonPart) { @@ -166,22 +155,19 @@ index 3a35dcdfd639240363891f412beeef47b8b41e06..b6364b02142a71741bdeb5df3da58dcb } entity.lastDamageCancelled = false; +@@ -560,9 +642,10 @@ public class Explosion { + seenFraction = (double)this.getBlockDensity(vec3d, entity, blockCache, blockPos); // Paper - optimise collisions if (entity instanceof EnderDragon) { -+ // Sakura start - optimise paper explosions -+ AABB bounds = this.getExplosionBounds(f2); ++ AABB bounds = this.getExplosionBounds(f2); // Sakura - optimise paper explosions for (EnderDragonPart entityComplexPart : ((EnderDragon) entity).subEntities) { // Calculate damage separately for each EntityComplexPart - if (list.contains(entityComplexPart)) { -- entityComplexPart.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entityComplexPart, getSeenFraction(vec3d, entityComplexPart, blockCache, blockPos))); // Paper - actually optimise explosions and use the right entity to calculate the damage -+ if (entityComplexPart.getBoundingBox().intersects(bounds)) { -+ entityComplexPart.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entityComplexPart, getBlockDensity(vec3d, entityComplexPart, blockCache, blockPos))); // Paper - actually optimise explosions and use the right entity to calculate the damage ++ if (entityComplexPart.getBoundingBox().intersects(bounds)) { // Sakura - optimise paper explosions + entityComplexPart.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity)); } } - } else { -- entity.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, getSeenFraction(vec3d, entity, blockCache, blockPos))); // Paper - actually optimise explosions -+ entity.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, getBlockDensity(vec3d, entity, blockCache, blockPos))); // Paper - actually optimise explosions -+ // Sakura end - optimise paper explosions +@@ -576,7 +659,7 @@ public class Explosion { } if (entity.lastDamageCancelled) { // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Skip entity if damage event was cancelled @@ -189,17 +175,16 @@ index 3a35dcdfd639240363891f412beeef47b8b41e06..b6364b02142a71741bdeb5df3da58dcb + return; // Sakura - optimise paper explosions } // CraftBukkit end - } -@@ -648,8 +736,12 @@ public class Explosion { - } + } else { seenFraction = (double)this.getBlockDensity(vec3d, entity, blockCache, blockPos); } // Paper - optimise collisions +@@ -625,6 +708,11 @@ public class Explosion { } } -+ // Sakura start - optimise paper explosions } ++ // Sakura start - optimise paper explosions + } - ++ + protected void clearBlockCache() { + // Sakura end - optimise paper explosions - this.blockCache = null; // Paper - optimise explosions - this.chunkPosCache = null; // Paper - optimise explosions - this.chunkCache = null; // Paper - optimise explosions + // Paper start - optimise collisions + this.blockCache = null; + this.chunkPosCache = null; diff --git a/patches/server/0020-Store-Entity-Data-State.patch b/patches/server/0016-Store-Entity-Data-State.patch similarity index 83% rename from patches/server/0020-Store-Entity-Data-State.patch rename to patches/server/0016-Store-Entity-Data-State.patch index 259944d..3bdde27 100644 --- a/patches/server/0020-Store-Entity-Data-State.patch +++ b/patches/server/0016-Store-Entity-Data-State.patch @@ -52,26 +52,26 @@ index 0000000000000000000000000000000000000000..c9f2c5ae57878283e8c8bc3847fe63b9 + +} diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 71e8a4f30afac822cc0da9620b904a02c201e452..6b90cfe63dce4a56033c8ecc9e68895fe3d77390 100644 +index 05d78fec35caee04241bb33808afb2d2d09f999c..1fa332238999fd45b20bc2be2d6284329da909b6 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -579,6 +579,25 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -568,6 +568,25 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return flags; } - // Sakura end - load chunks on cannon entity movement + // Sakura end - load chunks on movement + // Sakura start - store entity data/state -+ private @Nullable me.samsuik.sakura.entity.EntityState entityState = null; ++ private me.samsuik.sakura.entity.EntityState entityState = null; + + public final Vec3 stuckSpeedMultiplier() { -+ return stuckSpeedMultiplier; ++ return this.stuckSpeedMultiplier; + } + + public final void storeEntityState() { -+ entityState = me.samsuik.sakura.entity.EntityState.of(this); ++ this.entityState = me.samsuik.sakura.entity.EntityState.of(this); + } + -+ public final @Nullable me.samsuik.sakura.entity.EntityState entityState() { -+ return entityState; ++ public final me.samsuik.sakura.entity.EntityState entityState() { ++ return this.entityState; + } + + public final boolean compareState(Entity to) { @@ -82,10 +82,10 @@ index 71e8a4f30afac822cc0da9620b904a02c201e452..6b90cfe63dce4a56033c8ecc9e68895f public Entity(EntityType type, Level world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 05a4b69fbaf36269bea3a050022a54d28211c285..9afc93364fd1176f0dedced6042abc2df938228e 100644 +index 5e1e50a5c8cc380d0c23fd14e0da54da3d750834..4c3c6a04155fa4a0ea81beaa5bf2a2f53ab5dbbc 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -1358,6 +1358,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl +@@ -1389,6 +1389,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl public void guardEntityTick(Consumer tickConsumer, T entity) { try { diff --git a/patches/server/0021-Merge-Cannon-Entities.patch b/patches/server/0017-Merge-Cannon-Entities.patch similarity index 91% rename from patches/server/0021-Merge-Cannon-Entities.patch rename to patches/server/0017-Merge-Cannon-Entities.patch index c1166fb..8378bf0 100644 --- a/patches/server/0021-Merge-Cannon-Entities.patch +++ b/patches/server/0017-Merge-Cannon-Entities.patch @@ -169,54 +169,54 @@ index 0000000000000000000000000000000000000000..e63935c17e213bf60571d120ad9ce311 + +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 041e42c25e536a960a2e59114d2761c54520002e..5b366c69a28e3806d5507ad029e27231f8ef7c5c 100644 +index 564278107e38b2a1adb03dab4c92a6ed20fd1d4e..0a6a39672859aa4b70dacafc168250941f8d96d8 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1752,6 +1752,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { if (!entity.isRemoved()) { if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed -@@ -732,6 +733,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. +@@ -766,6 +767,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. entity.stopRiding(); } -+ // Sakura start ++ // Sakura start - merge cannon entities + Entity previous = previousEntity[0]; + if (entity.isMergeableType(previous) && entity.tryMergeInto(previous)) { + return; + } else { + previousEntity[0] = entity; + } -+ // Sakura end ++ // Sakura end - merge cannon entities + gameprofilerfiller.push("tick"); this.guardEntityTick(this::tickNonPassenger, entity); gameprofilerfiller.pop(); diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 6b90cfe63dce4a56033c8ecc9e68895fe3d77390..190e0022cb45545d8a76f0f7819b6f2be0b64307 100644 +index 1fa332238999fd45b20bc2be2d6284329da909b6..16b8881696f88f0a36bee78630df53f87578fed0 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -598,6 +598,116 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -587,6 +587,116 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return to.entityState() != null && to.entityState().isCurrentState(this); } // Sakura end - store entity data/state -+ // Sakura start - cannon entity merging ++ // Sakura start - merge cannon entities + // List of merged entities, should be naturally sorted (oldest -> youngest) + private final List mergeList = new java.util.ArrayList<>(1); + private @Nullable me.samsuik.sakura.entity.merge.SpawnPositionData originData = null; @@ -325,11 +325,11 @@ index 6b90cfe63dce4a56033c8ecc9e68895fe3d77390..190e0022cb45545d8a76f0f7819b6f2b + bukkitEntity = entity.bukkitEntity; + } + } -+ // Sakura end - cannon entity merging ++ // Sakura end - merge cannon entities public Entity(EntityType type, Level world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); -@@ -647,6 +757,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -636,6 +746,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.entityData = datawatcher_a.build(); this.setPos(0.0D, 0.0D, 0.0D); this.eyeHeight = this.dimensions.eyeHeight(); @@ -337,7 +337,7 @@ index 6b90cfe63dce4a56033c8ecc9e68895fe3d77390..190e0022cb45545d8a76f0f7819b6f2b } public boolean isColliding(BlockPos pos, BlockState state) { -@@ -2643,6 +2754,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2594,6 +2705,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess nbttagcompound.putBoolean("Paper.FreezeLock", true); } // Paper end @@ -349,7 +349,7 @@ index 6b90cfe63dce4a56033c8ecc9e68895fe3d77390..190e0022cb45545d8a76f0f7819b6f2b return nbttagcompound; } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Saving entity NBT"); -@@ -2790,6 +2906,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2741,6 +2857,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess freezeLocked = nbt.getBoolean("Paper.FreezeLock"); } // Paper end @@ -361,7 +361,7 @@ index 6b90cfe63dce4a56033c8ecc9e68895fe3d77390..190e0022cb45545d8a76f0f7819b6f2b } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Loading entity NBT"); -@@ -4961,6 +5082,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4917,6 +5038,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess // Paper end - rewrite chunk system CraftEventFactory.callEntityRemoveEvent(this, cause); // CraftBukkit end @@ -374,28 +374,28 @@ index 6b90cfe63dce4a56033c8ecc9e68895fe3d77390..190e0022cb45545d8a76f0f7819b6f2b if (this.removalReason == null) { this.removalReason = entity_removalreason; diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index 79c2ea81444c0ac9598f7fe334055326958a4f30..fa95135e3af7fdd610cad2230cc39b547cda0a8a 100644 +index 0458d965ca266606f922bf1b03b729810a3948a4..d15d4ddc99ddc2c37efc4e0b43ab51c0abc915a9 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -137,6 +137,59 @@ public class FallingBlockEntity extends Entity { return !this.isRemoved(); } -+ // Sakura start - cannon entity merging ++ // Sakura start - merge cannon entities + @Override -+ public boolean isMergeableType(@Nullable Entity previous) { ++ public final boolean isMergeableType(@Nullable Entity previous) { + return previous == null || !isRemoved() && !previous.isRemoved(); + } + + @Override -+ public boolean isSafeToMergeInto(Entity entity) { ++ public final boolean isSafeToMergeInto(Entity entity) { + return entity instanceof FallingBlockEntity fbe + && fbe.blockState.equals(blockState) + && fbe.time - 1 == time; // todo: special case in case on spawn isn't used + } + + @Override -+ protected boolean respawnMerged() { ++ protected final boolean respawnMerged() { + if (stacked <= 1) return false; + + while (stacked-- >= 1) { @@ -423,7 +423,7 @@ index 79c2ea81444c0ac9598f7fe334055326958a4f30..fa95135e3af7fdd610cad2230cc39b54 + } + + @Nullable -+ public ItemEntity spawnAtLocation(ItemLike item) { ++ public ItemEntity spawnAtLocation(ItemLike item) { // may be overridden by plugins + ItemEntity itemEntity = null; + + while (stacked-- >= 1) { @@ -432,7 +432,7 @@ index 79c2ea81444c0ac9598f7fe334055326958a4f30..fa95135e3af7fdd610cad2230cc39b54 + + return itemEntity; + } -+ // Sakura end ++ // Sakura end - merge cannon entities + @Override protected double getDefaultGravity() { @@ -446,21 +446,21 @@ index 79c2ea81444c0ac9598f7fe334055326958a4f30..fa95135e3af7fdd610cad2230cc39b54 ((ServerLevel) this.level()).getChunkSource().chunkMap.broadcast(this, new ClientboundBlockUpdatePacket(blockposition, this.level().getBlockState(blockposition))); this.discard(EntityRemoveEvent.Cause.DESPAWN); diff --git a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -index ac9781762a987b4d0121f69b9e873d169dfc24a0..71f01c2cb7182764ef4c527b8e84e162ebfd761c 100644 +index af9bd6822f60ee46ff5cc173bc8bd76d13020a80..6aaaed5899393046ff31cc65edf81ae27edf1319 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java @@ -90,6 +90,44 @@ public class PrimedTnt extends Entity implements TraceableEntity { return !this.isRemoved(); } -+ // Sakura start - cannon entity merging ++ // Sakura start - merge cannon entities + @Override -+ public boolean isMergeableType(@Nullable Entity previous) { ++ public final boolean isMergeableType(@Nullable Entity previous) { + return previous == null || !isRemoved() && !previous.isRemoved(); + } + + @Override -+ public boolean isSafeToMergeInto(Entity entity) { ++ public final boolean isSafeToMergeInto(Entity entity) { + return entity instanceof PrimedTnt tnt + && tnt.getFuse() + 1 == getFuse() + // required to prevent issues with powdered snow @@ -469,7 +469,7 @@ index ac9781762a987b4d0121f69b9e873d169dfc24a0..71f01c2cb7182764ef4c527b8e84e162 + } + + @Override -+ protected boolean respawnMerged() { ++ protected final boolean respawnMerged() { + if (stacked <= 1) return false; + + PrimedTnt tnt = new PrimedTnt(EntityType.TNT, level()); @@ -489,7 +489,7 @@ index ac9781762a987b4d0121f69b9e873d169dfc24a0..71f01c2cb7182764ef4c527b8e84e162 + + return true; + } -+ // Sakura end ++ // Sakura end - merge cannon entities + @Override protected double getDefaultGravity() { @@ -503,10 +503,10 @@ index ac9781762a987b4d0121f69b9e873d169dfc24a0..71f01c2cb7182764ef4c527b8e84e162 this.explode(); } diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 9afc93364fd1176f0dedced6042abc2df938228e..66af21d21f42e796ae49a2ac8a4f7e2ef9fad99c 100644 +index 4c3c6a04155fa4a0ea81beaa5bf2a2f53ab5dbbc..5ab7e3335448a262f892a3a7232179b19d91d78d 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -220,6 +220,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl +@@ -221,6 +221,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl // Paper start - rewrite chunk system private ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup entityLookup; diff --git a/patches/server/0022-Replace-explosion-density-cache.patch b/patches/server/0018-Replace-explosion-density-cache.patch similarity index 91% rename from patches/server/0022-Replace-explosion-density-cache.patch rename to patches/server/0018-Replace-explosion-density-cache.patch index a6aebd4..05c1ab8 100644 --- a/patches/server/0022-Replace-explosion-density-cache.patch +++ b/patches/server/0018-Replace-explosion-density-cache.patch @@ -129,22 +129,22 @@ index 0000000000000000000000000000000000000000..d7e24638f07f243502004970ab4ce646 + } +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 5b366c69a28e3806d5507ad029e27231f8ef7c5c..2df0f8a92174be654acb27785ff1f4880dcf3362 100644 +index 0a6a39672859aa4b70dacafc168250941f8d96d8..4ee6b6177df48701bd3521e3489797e62a85511f 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1753,6 +1753,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop blocks = new ObjectArrayList<>(1); @@ -26,13 +27,13 @@ index c5668c12a20628e1f3b216590488b432ad9a0adf..576b7260016b770a62ed9a291dd20bf7 + + return true; + } -+ // Sakura end - optimise protected explosions ++ // Sakura end - optimise explosion protected regions - private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity entity) { - return (ExplosionDamageCalculator) (entity == null ? Explosion.EXPLOSION_DAMAGE_CALCULATOR : new EntityBasedExplosionDamageCalculator(entity)); -@@ -508,7 +523,7 @@ public class Explosion { - initialCache = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true); - } + public static DamageSource getDefaultDamageSource(Level world, @Nullable Entity source) { + return world.damageSources().explosion(source, Explosion.getIndirectSourceEntityInternal(source)); +@@ -486,7 +502,7 @@ public class Explosion { + + // Paper start - collision optimisations // Sakura start - optimise paper explosions - if (initialCache.resistance <= (this.radius * 1.3f) && this.interactsWithBlocks()) { + if (initialCache.resistance <= (this.radius * 1.3f) && this.interactsWithBlocks() && this.isRegionUnprotected()) { // Sakura - optimise protected explosions diff --git a/patches/server/0024-Specialised-Explosions.patch b/patches/server/0020-Specialised-Explosions.patch similarity index 84% rename from patches/server/0024-Specialised-Explosions.patch rename to patches/server/0020-Specialised-Explosions.patch index e841b4b..1fbb4ff 100644 --- a/patches/server/0024-Specialised-Explosions.patch +++ b/patches/server/0020-Specialised-Explosions.patch @@ -23,12 +23,13 @@ index c21e00812f1aaa1279834a0562d360d6b89e146c..442119e7c4670582556b067dfc03e39a if (index >= 0) { diff --git a/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java b/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java new file mode 100644 -index 0000000000000000000000000000000000000000..55bf4424d8e995ea7384f88f43465f90ae3c6704 +index 0000000000000000000000000000000000000000..2dc4704b50dc9ca1e918c0869a29e333db5575d2 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java -@@ -0,0 +1,200 @@ +@@ -0,0 +1,201 @@ +package me.samsuik.sakura.explosion.special; + ++import ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache; +import io.papermc.paper.util.WorldUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; @@ -229,13 +230,14 @@ index 0000000000000000000000000000000000000000..55bf4424d8e995ea7384f88f43465f90 +} diff --git a/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java b/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java new file mode 100644 -index 0000000000000000000000000000000000000000..3888c76dc16b76ae214aeb7265e8b0e9d600b863 +index 0000000000000000000000000000000000000000..c7c373fa24ee28f4bcd278816346171d0cdd35f2 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java -@@ -0,0 +1,191 @@ +@@ -0,0 +1,189 @@ +package me.samsuik.sakura.explosion.special; + +import ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet; ++import ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import me.samsuik.sakura.entity.EntityState; +import net.minecraft.core.BlockPos; @@ -291,14 +293,11 @@ index 0000000000000000000000000000000000000000..3888c76dc16b76ae214aeb7265e8b0e9 + + private void midExplosion(Vec3 previousMomentum, boolean lastCycle) { + if (this.wrapped < FOUND_ALL_BLOCKS) { -+ final int blockX = Mth.floor(this.x); -+ final int blockY = Mth.floor(this.y); -+ final int blockZ = Mth.floor(this.z); -+ -+ final long key = BlockPos.asLong(blockX, blockY, blockZ); -+ -+ final ExplosionBlockCache initialCache = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true); -+ ++ int blockX = Mth.floor(this.x); ++ int blockY = Mth.floor(this.y); ++ int blockZ = Mth.floor(this.z); ++ long key = BlockPos.asLong(blockX, blockY, blockZ); ++ ExplosionBlockCache initialCache = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true); + if (initialCache.resistance <= (this.radius() * 1.3f) && this.interactsWithBlocks() && this.isRegionUnprotected()) { // Sakura - optimise protected explosions + this.searchForBlocks(this.recentBlockCache, initialCache); + } @@ -425,10 +424,10 @@ index 0000000000000000000000000000000000000000..3888c76dc16b76ae214aeb7265e8b0e9 + } +} diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 0f49093fce5e4eab4e8912d407edca04f0e124a4..82ff83fd00b976d41d1d5b30ad34ede9af1a7e5b 100644 +index d58b95383f4bf5358f5d1c6d33c5539e77dd3798..5f09b0a94e6b1087c9207c333aa5595281a57985 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1702,6 +1702,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. +@@ -1771,6 +1771,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. explosion.clearToBlow(); } @@ -441,7 +440,7 @@ index 0f49093fce5e4eab4e8912d407edca04f0e124a4..82ff83fd00b976d41d1d5b30ad34ede9 Iterator iterator = this.players.iterator(); while (iterator.hasNext()) { -@@ -1712,7 +1718,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. +@@ -1781,7 +1787,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. } } @@ -451,16 +450,13 @@ index 0f49093fce5e4eab4e8912d407edca04f0e124a4..82ff83fd00b976d41d1d5b30ad34ede9 @Override diff --git a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -index 71f01c2cb7182764ef4c527b8e84e162ebfd761c..a86ca8e7c66605260d8932b489d5d4fe8c74ab64 100644 +index 6aaaed5899393046ff31cc65edf81ae27edf1319..170eff4c5b76d037e269f973f14a1f301c205ffd 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -104,28 +104,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { - && (tnt.entityState().fallDistance() == fallDistance - || tnt.entityState().fallDistance() > 2.5f && fallDistance > 2.5f); - } -- -- @Override -- protected boolean respawnMerged() { +@@ -107,23 +107,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { + + @Override + protected final boolean respawnMerged() { - if (stacked <= 1) return false; - - PrimedTnt tnt = new PrimedTnt(EntityType.TNT, level()); @@ -478,14 +474,12 @@ index 71f01c2cb7182764ef4c527b8e84e162ebfd761c..a86ca8e7c66605260d8932b489d5d4fe - this.tick(); - } - -- return true; -- } -+ // Sakura - specialised explosions - // Sakura end - - @Override ++ // Sakura - specialised explosions + return true; + } + // Sakura end - merge cannon entities diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 576b7260016b770a62ed9a291dd20bf7063897cd..a376d53dba483b1da96bb468877a958f187a8680 100644 +index 5bf07349621d058739f12b9463bba47e9abfffd7..546c56d3465ca9c2358f1008d9db96f5fcdd6672 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java @@ -57,9 +57,11 @@ public class Explosion { @@ -503,22 +497,34 @@ index 576b7260016b770a62ed9a291dd20bf7063897cd..a376d53dba483b1da96bb468877a958f @Nullable public final Entity source; private final float radius; -@@ -187,7 +189,7 @@ public class Explosion { +@@ -114,10 +116,10 @@ public class Explosion { // resistance = (res + 0.3F) * 0.3F; // so for resistance = 0, we need res = -0.3F private static final Float ZERO_RESISTANCE = Float.valueOf(-0.3f); -- private it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap blockCache = null; -+ protected it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap blockCache = null; // Sakura - private -> protected - - public static final class ExplosionBlockCache { - -@@ -214,7 +216,29 @@ public class Explosion { +- private it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap blockCache = null; ++ protected it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap blockCache = null; // Sakura - private -> protected private long[] chunkPosCache = null; private net.minecraft.world.level.chunk.LevelChunk[] chunkCache = null; +- private ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache getOrCacheExplosionBlock(final int x, final int y, final int z, ++ protected final ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache getOrCacheExplosionBlock(final int x, final int y, final int z, // Sakura - private -> protected + final long key, final boolean calculateResistance) { + ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache ret = this.blockCache.get(key); + if (ret != null) { +@@ -215,7 +217,7 @@ public class Explosion { + (currZ & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT + BLOCK_EXPLOSION_CACHE_SHIFT); + ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache cachedBlock = blockCache[cacheKey]; + if (cachedBlock == null || cachedBlock.key != key) { +- blockCache[cacheKey] = cachedBlock = this.getOrCacheExplosionBlock(currX, currY, currZ, key, false); ++ blockCache[cacheKey] = cachedBlock = this.getOrCacheExplosionBlock(currX, currY, currZ, key, this.level.sakuraConfig().cannons.explosion.useBlockCacheAcrossExplosions); // Sakura - specialised explosions + } -- private ExplosionBlockCache getOrCacheExplosionBlock(final int x, final int y, final int z, + final BlockState blockState = cachedBlock.blockState; +@@ -369,6 +371,28 @@ public class Explosion { + return true; + } + // Sakura end - optimise explosion protected regions + // Sakura start - specialised explosions -+ protected final ExplosionBlockCache[] createBlockCache() { ++ protected final ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] createBlockCache() { + this.blockCache = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(); + + this.chunkPosCache = new long[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH]; @@ -526,12 +532,12 @@ index 576b7260016b770a62ed9a291dd20bf7063897cd..a376d53dba483b1da96bb468877a958f + + this.chunkCache = new net.minecraft.world.level.chunk.LevelChunk[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH]; + -+ return new ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH]; ++ return new ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH]; + } + + protected final void markBlocksInCacheAsExplodable(List blocks) { + for (BlockPos blow : blocks) { -+ ExplosionBlockCache cache = this.blockCache.get(blow.asLong()); ++ ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache cache = this.blockCache.get(blow.asLong()); + // May be null if the blockCache is cleared then retrieved from the recent block cache + if (cache != null) { + cache.shouldExplode = null; @@ -539,23 +545,14 @@ index 576b7260016b770a62ed9a291dd20bf7063897cd..a376d53dba483b1da96bb468877a958f + } + } + // Sakura end - specialised explosions -+ protected final ExplosionBlockCache getOrCacheExplosionBlock(final int x, final int y, final int z, // Sakura - private -> protected - final long key, final boolean calculateResistance) { - ExplosionBlockCache ret = this.blockCache.get(key); - if (ret != null) { -@@ -312,7 +336,7 @@ public class Explosion { - (currZ & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT + BLOCK_EXPLOSION_CACHE_SHIFT); - ExplosionBlockCache cachedBlock = blockCache[cacheKey]; - if (cachedBlock == null || cachedBlock.key != key) { -- blockCache[cacheKey] = cachedBlock = this.getOrCacheExplosionBlock(currX, currY, currZ, key, false); -+ blockCache[cacheKey] = cachedBlock = this.getOrCacheExplosionBlock(currX, currY, currZ, key, this.level.sakuraConfig().cannons.explosion.useBlockCacheAcrossExplosions); // Sakura - specialised explosions - } - final BlockState blockState = cachedBlock.blockState; -@@ -503,14 +527,7 @@ public class Explosion { - int j; + public static DamageSource getDefaultDamageSource(Level world, @Nullable Entity source) { + return world.damageSources().explosion(source, Explosion.getIndirectSourceEntityInternal(source)); +@@ -472,16 +496,7 @@ public class Explosion { + // CraftBukkit end + this.level.gameEvent(this.source, (Holder) GameEvent.EXPLODE, new Vec3(this.x, this.y, this.z)); - // Paper start - optimise explosions +- // Paper start - collision optimisations - this.blockCache = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(); - - this.chunkPosCache = new long[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH]; @@ -563,25 +560,26 @@ index 576b7260016b770a62ed9a291dd20bf7063897cd..a376d53dba483b1da96bb468877a958f - - this.chunkCache = new net.minecraft.world.level.chunk.LevelChunk[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH]; - -- final ExplosionBlockCache[] blockCache = new ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH]; -+ final ExplosionBlockCache[] blockCache = this.createBlockCache(); +- final ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache = new ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH]; +- ++ final ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache = this.createBlockCache(); // Sakura - specialised explosions // use initial cache value that is most likely to be used: the source position - final ExplosionBlockCache initialCache; + final ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache initialCache; { -@@ -998,7 +1015,7 @@ public class Explosion { +@@ -972,7 +987,7 @@ public class Explosion { private BlockInteraction() {} } // Paper start - Optimize explosions -- private float getBlockDensity(Vec3 vec3d, Entity entity, ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { // Paper - optimise explosions -+ protected final float getBlockDensity(Vec3 vec3d, Entity entity, ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { // Sakura - private -> protected // Paper - optimise explosions +- private float getBlockDensity(Vec3 vec3d, Entity entity, ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { // Paper - optimise collisions ++ protected final float getBlockDensity(Vec3 vec3d, Entity entity, ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { // Sakura - private -> protected // Paper - optimise collisions // Sakura start - replace density cache float blockDensity = this.level.densityCache.getDensity(vec3d, entity); if (blockDensity == me.samsuik.sakura.explosion.density.BlockDensityCache.UNKNOWN_DENSITY) { diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 1d822411f6cef0137eeb6383f404500642d3a731..459d8b792c40caaebe38cfcf87533b325e76ceea 100644 +index c33826219edf60a1349583d1dc7d0008ef4037b4..64e134138b1e1311239fa19da2c8cda26e1ebc84 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -1451,7 +1451,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl +@@ -1480,7 +1480,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl } Explosion.BlockInteraction explosion_effect1 = explosion_effect; diff --git a/patches/server/0025-Optimise-Fast-Movement.patch b/patches/server/0021-Optimise-cannon-entity-movement.patch similarity index 66% rename from patches/server/0025-Optimise-Fast-Movement.patch rename to patches/server/0021-Optimise-cannon-entity-movement.patch index 8c8db7e..5059742 100644 --- a/patches/server/0025-Optimise-Fast-Movement.patch +++ b/patches/server/0021-Optimise-cannon-entity-movement.patch @@ -1,18 +1,18 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Samsuik <40902469+Samsuik@users.noreply.github.com> Date: Fri, 13 Oct 2023 14:36:19 +0100 -Subject: [PATCH] Optimise Fast Movement +Subject: [PATCH] Optimise cannon entity movement diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 190e0022cb45545d8a76f0f7819b6f2be0b64307..348714eb8d74cf917daa5586f0b4962351a96102 100644 +index 16b8881696f88f0a36bee78630df53f87578fed0..a3483b79f2a2859a137c22fcb3fec5bc74835d82 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1245,6 +1245,95 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - return this.onGround; +@@ -1258,6 +1258,94 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + return this.moveStartZ; } - -+ // Sakura start - stripped back movement method for basic entities + // Paper end - detailed watchdog information ++ // Sakura start - optimise cannon entity movement; stripped back movement method + public void moveBasic(MoverType movementType, Vec3 movement) { + io.papermc.paper.util.TickThread.ensureTickThread("Cannot move an entity off-main"); + @@ -38,8 +38,8 @@ index 190e0022cb45545d8a76f0f7819b6f2be0b64307..348714eb8d74cf917daa5586f0b49623 + double d0 = vec3d1.lengthSqr(); + + if (d0 > 1.0E-7D) { -+ // NOTE: if there are any blocks in the future that rely on fall distance make sure this is correct. -+ // The only block I am aware of is powdered snow that has a special case for falling blocks. ++ // NOTE: Every minecraft update check if there are any new blocks that make sure of fallDistance. ++ // As of 1.21 the only block is powdered snow which returns a solid collision for falling blocks. + if (this.fallDistance != 0.0F && d0 >= 1.0D && !this.isFallingBlock) { + BlockHitResult movingobjectpositionblock = this.level().clip(new ClipContext(this.position(), this.position().add(vec3d1), ClipContext.Block.FALLDAMAGE_RESETTING, ClipContext.Fluid.WATER, this)); + @@ -99,32 +99,31 @@ index 190e0022cb45545d8a76f0f7819b6f2be0b64307..348714eb8d74cf917daa5586f0b49623 + } + } + } -+ // Sakura end -+ ++ // Sakura end - optimise cannon entity movement; stripped back movement method + public void move(MoverType movementType, Vec3 movement) { final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity - if (this.noPhysics) { -@@ -1606,6 +1695,95 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1602,6 +1690,95 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return offsetFactor; } -+ // Sakura start ++ // Sakura start - optimise cannon entity movement + private Vec3 collideScan(Vec3 movement) { + if (movement.x == 0.0 && movement.y == 0.0 && movement.z == 0.0) { + return movement; + } + -+ final boolean scan = movement.lengthSqr() >= 12.0; -+ final List potentialCollisionsBB = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(4); -+ final List potentialCollisionsVoxel = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(1); -+ final AABB currBoundingBox = getBoundingBox(); ++ boolean scan = movement.lengthSqr() >= 12.0; ++ List potentialCollisionsBB = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(4); ++ List potentialCollisionsVoxel = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(1); ++ AABB currBoundingBox = getBoundingBox(); + + if (scan) { -+ return scanAndCollide(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB); ++ return this.scanAndCollide(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB); + } else { -+ final AABB bb = currBoundingBox.expandTowards(movement.x, movement.y, movement.z); -+ collectCollisions(bb, potentialCollisionsVoxel, potentialCollisionsBB); -+ return io.papermc.paper.util.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB); ++ AABB bb = currBoundingBox.expandTowards(movement.x, movement.y, movement.z); ++ this.collectCollisions(bb, potentialCollisionsVoxel, potentialCollisionsBB); ++ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB); + } + } + @@ -133,34 +132,34 @@ index 190e0022cb45545d8a76f0f7819b6f2be0b64307..348714eb8d74cf917daa5586f0b49623 + double y = movement.y; + double z = movement.z; + -+ final boolean xSmaller = Math.abs(x) < Math.abs(z); ++ boolean xSmaller = Math.abs(x) < Math.abs(z); + + if (y != 0.0) { -+ y = scanY(currBoundingBox, y, voxelList, bbList); ++ y = this.scanY(currBoundingBox, y, voxelList, bbList); + + if (y != 0.0) { -+ currBoundingBox = io.papermc.paper.util.CollisionUtil.offsetY(currBoundingBox, y); ++ currBoundingBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.offsetY(currBoundingBox, y); + } + } + + if (xSmaller && z != 0.0) { -+ z = scanZ(currBoundingBox, z, voxelList, bbList); ++ z = this.scanZ(currBoundingBox, z, voxelList, bbList); + + if (z != 0.0) { -+ currBoundingBox = io.papermc.paper.util.CollisionUtil.offsetZ(currBoundingBox, z); ++ currBoundingBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.offsetZ(currBoundingBox, z); + } + } + + if (x != 0.0) { -+ x = scanX(currBoundingBox, x, voxelList, bbList); ++ x = this.scanX(currBoundingBox, x, voxelList, bbList); + + if (x != 0.0) { -+ currBoundingBox = io.papermc.paper.util.CollisionUtil.offsetX(currBoundingBox, x); ++ currBoundingBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.offsetX(currBoundingBox, x); + } + } + + if (!xSmaller && z != 0.0) { -+ z = scanZ(currBoundingBox, z, voxelList, bbList); ++ z = this.scanZ(currBoundingBox, z, voxelList, bbList); + } + + return new Vec3(x, y, z); @@ -168,40 +167,40 @@ index 190e0022cb45545d8a76f0f7819b6f2be0b64307..348714eb8d74cf917daa5586f0b49623 + + private void collectCollisions(AABB collisionBox, List voxelList, List bbList) { + // Copied from the collide method below -+ io.papermc.paper.util.CollisionUtil.getCollisions( -+ level, this, collisionBox, voxelList, bbList, -+ io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_CHECK_BORDER | this.getExtraCollisionFlags(), // Sakura ++ ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.getCollisions( ++ this.level, this, collisionBox, voxelList, bbList, ++ ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER | this.getExtraCollisionFlags(), // Sakura - load chunks on movement + null, null + ); + } + + private double scanX(AABB currBoundingBox, double x, List voxelList, List bbList) { + AABB scanBox = currBoundingBox.expandTowards(x, 0.0, 0.0); -+ collectCollisions(scanBox, voxelList, bbList); -+ x = io.papermc.paper.util.CollisionUtil.performAABBCollisionsX(currBoundingBox, x, bbList); -+ return io.papermc.paper.util.CollisionUtil.performVoxelCollisionsX(currBoundingBox, x, voxelList); ++ this.collectCollisions(scanBox, voxelList, bbList); ++ x = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performAABBCollisionsX(currBoundingBox, x, bbList); ++ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performVoxelCollisionsX(currBoundingBox, x, voxelList); + } + + private double scanY(AABB currBoundingBox, double y, List voxelList, List bbList) { + AABB scanBox = currBoundingBox.expandTowards(0.0, y, 0.0); -+ collectCollisions(scanBox, voxelList, bbList); -+ y = io.papermc.paper.util.CollisionUtil.performAABBCollisionsY(currBoundingBox, y, bbList); -+ return io.papermc.paper.util.CollisionUtil.performVoxelCollisionsY(currBoundingBox, y, voxelList); ++ this.collectCollisions(scanBox, voxelList, bbList); ++ y = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performAABBCollisionsY(currBoundingBox, y, bbList); ++ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performVoxelCollisionsY(currBoundingBox, y, voxelList); + } + + private double scanZ(AABB currBoundingBox, double z, List voxelList, List bbList) { + AABB scanBox = currBoundingBox.expandTowards(0.0, 0.0, z); -+ collectCollisions(scanBox, voxelList, bbList); -+ z = io.papermc.paper.util.CollisionUtil.performAABBCollisionsZ(currBoundingBox, z, bbList); -+ return io.papermc.paper.util.CollisionUtil.performVoxelCollisionsZ(currBoundingBox, z, voxelList); ++ this.collectCollisions(scanBox, voxelList, bbList); ++ z = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performAABBCollisionsZ(currBoundingBox, z, bbList); ++ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performVoxelCollisionsZ(currBoundingBox, z, voxelList); + } -+ // Sakura end ++ // Sakura end - optimise cannon entity movement + private Vec3 collide(Vec3 movement) { // Paper start - optimise collisions - // Sakura - This is temporary workaround until this patch is updated by Paper. + final boolean xZero = movement.x == 0.0; diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index fa95135e3af7fdd610cad2230cc39b547cda0a8a..533b7db0adc9aa9677106f807a992bc09ba60b28 100644 +index d15d4ddc99ddc2c37efc4e0b43ab51c0abc915a9..22034ec50d32a120ef5b591b76af9eb46d20c87e 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -204,7 +204,7 @@ public class FallingBlockEntity extends Entity { @@ -209,20 +208,20 @@ index fa95135e3af7fdd610cad2230cc39b547cda0a8a..533b7db0adc9aa9677106f807a992bc0 ++this.time; this.applyGravity(); - this.move(MoverType.SELF, this.getDeltaMovement()); -+ this.moveBasic(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise simple entity movement ++ this.moveBasic(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise cannon entity movement // Paper start - Configurable falling blocks height nerf if (this.level().paperConfig().fixes.fallingBlockHeightNerf.test(v -> this.getY() > v)) { if (this.dropItem && this.level().getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) { diff --git a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -index a86ca8e7c66605260d8932b489d5d4fe8c74ab64..96878f577174b0ccef7c063153a0787ec3e97177 100644 +index 170eff4c5b76d037e269f973f14a1f301c205ffd..5a655606fd2dbc625696a0dc4331acd08d6dba88 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -117,7 +117,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -122,7 +122,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { if (this.level().spigotConfig.maxTntTicksPerTick > 0 && ++this.level().spigotConfig.currentPrimedTnt > this.level().spigotConfig.maxTntTicksPerTick) { return; } // Spigot this.handlePortal(); this.applyGravity(); - this.move(MoverType.SELF, this.getDeltaMovement()); -+ this.moveBasic(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise simple entity movement ++ this.moveBasic(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise cannon entity movement // Paper start - Configurable TNT height nerf if (this.level().paperConfig().fixes.tntEntityHeightNerf.test(v -> this.getY() > v)) { this.discard(EntityRemoveEvent.Cause.OUT_OF_WORLD); diff --git a/patches/server/0026-isPushedByFluid-API.patch b/patches/server/0022-Entity-pushed-by-fluid-API.patch similarity index 68% rename from patches/server/0026-isPushedByFluid-API.patch rename to patches/server/0022-Entity-pushed-by-fluid-API.patch index f887b8b..962ec59 100644 --- a/patches/server/0026-isPushedByFluid-API.patch +++ b/patches/server/0022-Entity-pushed-by-fluid-API.patch @@ -1,49 +1,49 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Samsuik <40902469+Samsuik@users.noreply.github.com> Date: Fri, 13 Oct 2023 20:02:04 +0100 -Subject: [PATCH] isPushedByFluid API +Subject: [PATCH] Entity pushed by fluid API diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 348714eb8d74cf917daa5586f0b4962351a96102..6066121c7bd432228b0503c8200d2dbe891bce29 100644 +index a3483b79f2a2859a137c22fcb3fec5bc74835d82..526d54e3dc8473c6b1b67a9253b006cc27419ab2 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -708,6 +708,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -697,6 +697,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } } - // Sakura end - cannon entity merging + // Sakura end - merge cannon entities + public boolean pushedByFluid = true; // Sakura - entity pushed by fluid api public Entity(EntityType type, Level world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); -@@ -4329,7 +4330,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4277,7 +4278,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public boolean isPushedByFluid() { - return true; -+ return pushedByFluid; // Sakura ++ return this.pushedByFluid; // Sakura - entity pushed by fluid api } public static double getViewScale() { diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -index 2cde808bfa797256409879505ba205a71f381981..f06b71b62bef13c9de519fd2758d107241904a1c 100644 +index 2cde808bfa797256409879505ba205a71f381981..38e06c99478669e4fd247923bc6cd00bab835f54 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -203,6 +203,18 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { return this.entity.isInWater(); } -+ // Sakura start ++ // Sakura start - entity pushed by fluid api + @Override + public boolean isPushedByFluid() { -+ return getHandle().isPushedByFluid(); ++ return this.getHandle().isPushedByFluid(); + } + + @Override + public void setPushedByFluid(boolean push) { -+ getHandle().pushedByFluid = push; ++ this.getHandle().pushedByFluid = push; + } -+ // Sakura end ++ // Sakura end - entity pushed by fluid api + @Override public World getWorld() { diff --git a/patches/server/0027-Cannon-Mechanics.patch b/patches/server/0023-Cannon-Mechanics.patch similarity index 81% rename from patches/server/0027-Cannon-Mechanics.patch rename to patches/server/0023-Cannon-Mechanics.patch index 15a9e9e..832aa06 100644 --- a/patches/server/0027-Cannon-Mechanics.patch +++ b/patches/server/0023-Cannon-Mechanics.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Cannon Mechanics diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index 533b7db0adc9aa9677106f807a992bc09ba60b28..ab68447f818c0dca777c25c04ac959e5f2b80132 100644 +index 22034ec50d32a120ef5b591b76af9eb46d20c87e..ad7aa58ee5966c5d56ff8bf8070697d039d4f503 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -72,6 +72,7 @@ public class FallingBlockEntity extends Entity { @@ -19,7 +19,7 @@ index 533b7db0adc9aa9677106f807a992bc09ba60b28..ab68447f818c0dca777c25c04ac959e5 @@ -80,6 +81,7 @@ public class FallingBlockEntity extends Entity { this.fallDamageMax = 40; this.isFallingBlock = true; // Sakura - this.loadChunks = world.sakuraConfig().cannons.loadChunks; // Sakura - falling blocks load chunks + this.loadChunks = world.sakuraConfig().cannons.loadChunks; // Sakura - load chunks on movement + this.heightParity = world.sakuraConfig().cannons.mechanics.fallingBlockParity; // Sakura - configure cannon mechanics } @@ -27,39 +27,39 @@ index 533b7db0adc9aa9677106f807a992bc09ba60b28..ab68447f818c0dca777c25c04ac959e5 @@ -189,6 +191,12 @@ public class FallingBlockEntity extends Entity { return itemEntity; } - // Sakura end -+ // Sakura start + // Sakura end - merge cannon entities ++ // Sakura start - configure cannon mechanics + @Override + public final double getEyeY() { -+ return heightParity ? getY() : super.getEyeY(); ++ return this.heightParity ? this.getY() : super.getEyeY(); + } -+ // Sakura end ++ // Sakura end - configure cannon mechanics @Override protected double getDefaultGravity() { diff --git a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -index 96878f577174b0ccef7c063153a0787ec3e97177..ffb453616a5b5664c5b5a8a6410f3096d6c7f4e0 100644 +index 5a655606fd2dbc625696a0dc4331acd08d6dba88..92662134c56540f6215edf3c772fffd9262fbcd0 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java @@ -72,6 +72,12 @@ public class PrimedTnt extends Entity implements TraceableEntity { this.yo = y; this.zo = z; this.owner = igniter; -+ // Sakura start ++ // Sakura start - configure cannon mechanics + switch (world.sakuraConfig().cannons.mechanics.tntSpread) { -+ case NONE -> multiplyDeltaMovement(0, 0, 0); -+ case Y -> multiplyDeltaMovement(0, 1, 0); ++ case NONE -> this.multiplyDeltaMovement(0, 0, 0); ++ case Y -> this.multiplyDeltaMovement(0, 1, 0); + } -+ // Sakura end ++ // Sakura end - configure cannon mechanics } @Override -@@ -255,7 +261,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -260,7 +266,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { // Paper start - Option to prevent TNT from moving in water @Override public boolean isPushedByFluid() { - return !level().paperConfig().fixes.preventTntFromMovingInWater && super.isPushedByFluid(); -+ return !level().paperConfig().fixes.preventTntFromMovingInWater && level().sakuraConfig().cannons.mechanics.tntFlowsInWater && super.isPushedByFluid(); // Sakura - convenience ++ return !level().paperConfig().fixes.preventTntFromMovingInWater && level().sakuraConfig().cannons.mechanics.tntFlowsInWater && super.isPushedByFluid(); // Sakura - configure cannon mechanics } // Paper end - Option to prevent TNT from moving in water } diff --git a/patches/server/0028-Cache-MovingBlockEntity-collision-shape.patch b/patches/server/0024-Cache-MovingBlockEntity-collision-shape.patch similarity index 100% rename from patches/server/0028-Cache-MovingBlockEntity-collision-shape.patch rename to patches/server/0024-Cache-MovingBlockEntity-collision-shape.patch diff --git a/patches/server/0029-Optimise-TNT-fluid-state-and-pushing.patch b/patches/server/0025-Optimise-TNT-fluid-state.patch similarity index 74% rename from patches/server/0029-Optimise-TNT-fluid-state-and-pushing.patch rename to patches/server/0025-Optimise-TNT-fluid-state.patch index 9d80af9..86e246e 100644 --- a/patches/server/0029-Optimise-TNT-fluid-state-and-pushing.patch +++ b/patches/server/0025-Optimise-TNT-fluid-state.patch @@ -1,14 +1,14 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Samsuik <40902469+Samsuik@users.noreply.github.com> Date: Mon, 16 Oct 2023 22:57:55 +0100 -Subject: [PATCH] Optimise TNT fluid state and pushing +Subject: [PATCH] Optimise TNT fluid state diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 6066121c7bd432228b0503c8200d2dbe891bce29..0946d71f8ef41f42db038c2efd527a319f55e069 100644 +index 526d54e3dc8473c6b1b67a9253b006cc27419ab2..729bf74fb06fe4fadeecbd0d879e35ecf33990c1 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -2296,7 +2296,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2246,7 +2246,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return this.isInWater() || flag; } @@ -18,14 +18,15 @@ index 6066121c7bd432228b0503c8200d2dbe891bce29..0946d71f8ef41f42db038c2efd527a31 if (entity instanceof Boat entityboat) { diff --git a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -index ffb453616a5b5664c5b5a8a6410f3096d6c7f4e0..1bebee482d0a5ca1a56474660888be01d257b231 100644 +index 92662134c56540f6215edf3c772fffd9262fbcd0..8ffa6ea08c49c3481bf8717eda3f8fa3672706eb 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -112,6 +112,19 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -117,6 +117,20 @@ public class PrimedTnt extends Entity implements TraceableEntity { + return true; } - // Sakura - specialised explosions - // Sakura end -+ // Sakura start + // Sakura end - merge cannon entities ++ // Sakura start - optimise tnt fluid state ++ @Override + protected boolean updateInWaterStateAndDoFluidPushing() { + if (this.isPushedByFluid()) { + return super.updateInWaterStateAndDoFluidPushing(); @@ -37,7 +38,7 @@ index ffb453616a5b5664c5b5a8a6410f3096d6c7f4e0..1bebee482d0a5ca1a56474660888be01 + return this.isInWater(); + } + } -+ // Sakura end ++ // Sakura end - optimise tnt fluid state @Override protected double getDefaultGravity() { diff --git a/patches/server/0030-Add-maxSearch-to-getEntities.patch b/patches/server/0026-Add-maxSearch-to-getEntities.patch similarity index 93% rename from patches/server/0030-Add-maxSearch-to-getEntities.patch rename to patches/server/0026-Add-maxSearch-to-getEntities.patch index 0b52bef..f40b3b7 100644 --- a/patches/server/0030-Add-maxSearch-to-getEntities.patch +++ b/patches/server/0026-Add-maxSearch-to-getEntities.patch @@ -62,10 +62,10 @@ index c140e3a73712d2123bc9ed09e9bab25e48c4420e..ee58a65582800be827c8f386ec2d50a2 if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { continue; diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java -index f6a3eb3d1bb070bcc74133818682571d520d9894..e7d660a3adf8152b859be28b851088b6e9af8a96 100644 +index a346435abac725b4e024acf4a1589a51caac8d69..8045f1abc38022e7a6d70de91ce79c1ca805a1e9 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java -@@ -765,8 +765,15 @@ public abstract class EntityLookup implements LevelEntityGetter { +@@ -795,8 +795,15 @@ public abstract class EntityLookup implements LevelEntityGetter { } } @@ -81,7 +81,7 @@ index f6a3eb3d1bb070bcc74133818682571d520d9894..e7d660a3adf8152b859be28b851088b6 final int minChunkX = (Mth.floor(box.minX) - 2) >> 4; final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4; final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4; -@@ -798,7 +805,7 @@ public abstract class EntityLookup implements LevelEntityGetter { +@@ -828,7 +835,7 @@ public abstract class EntityLookup implements LevelEntityGetter { continue; } @@ -90,7 +90,7 @@ index f6a3eb3d1bb070bcc74133818682571d520d9894..e7d660a3adf8152b859be28b851088b6 return; } } -@@ -1041,4 +1048,4 @@ public abstract class EntityLookup implements LevelEntityGetter { +@@ -1074,4 +1081,4 @@ public abstract class EntityLookup implements LevelEntityGetter { @Override public void onRemove(final Entity.RemovalReason reason) {} } @@ -98,10 +98,10 @@ index f6a3eb3d1bb070bcc74133818682571d520d9894..e7d660a3adf8152b859be28b851088b6 \ No newline at end of file +} diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 459d8b792c40caaebe38cfcf87533b325e76ceea..71f64a9f894ba719581d04c40d8dc13c2c32b9ba 100644 +index 64e134138b1e1311239fa19da2c8cda26e1ebc84..ccf8867882f37a55368d2f44a2eddb2c31a20413 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -1598,10 +1598,18 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl +@@ -1627,10 +1627,18 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl this.getEntities(filter, box, predicate, result, Integer.MAX_VALUE); } @@ -121,7 +121,7 @@ index 459d8b792c40caaebe38cfcf87533b325e76ceea..71f64a9f894ba719581d04c40d8dc13c this.getProfiler().incrementCounter("getEntities"); if (entityTypeTest instanceof net.minecraft.world.entity.EntityType byType) { -@@ -1616,7 +1624,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl +@@ -1645,7 +1653,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl if (entityTypeTest == null) { if (maxCount != Integer.MAX_VALUE) { diff --git a/patches/server/0031-Optimise-LivingEntity-pushEntities.patch b/patches/server/0027-Optimise-LivingEntity-pushEntities.patch similarity index 90% rename from patches/server/0031-Optimise-LivingEntity-pushEntities.patch rename to patches/server/0027-Optimise-LivingEntity-pushEntities.patch index 73ce316..2390f0a 100644 --- a/patches/server/0031-Optimise-LivingEntity-pushEntities.patch +++ b/patches/server/0027-Optimise-LivingEntity-pushEntities.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Optimise LivingEntity#pushEntities diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index e980c8c356b30d25e2fc5a73b91ad2c6edd4fe05..cdfcd8e3e7e357151b8a1d5ed8b051d6cf550fc1 100644 +index fe435d4a387bb28be6831cec0c8bb0a7c8b603a4..e39732b16d0b8d47759b8fc282e4a9a158991104 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -3574,7 +3574,12 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3629,7 +3629,12 @@ public abstract class LivingEntity extends Entity implements Attackable { return; } // Paper end - don't run getEntities if we're not going to use its result diff --git a/patches/server/0032-Despawn-falling-blocks-inside-moving-pistons.patch b/patches/server/0028-Despawn-falling-blocks-inside-moving-pistons.patch similarity index 93% rename from patches/server/0032-Despawn-falling-blocks-inside-moving-pistons.patch rename to patches/server/0028-Despawn-falling-blocks-inside-moving-pistons.patch index 7cc0b78..99800c1 100644 --- a/patches/server/0032-Despawn-falling-blocks-inside-moving-pistons.patch +++ b/patches/server/0028-Despawn-falling-blocks-inside-moving-pistons.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Despawn falling blocks inside moving pistons diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index ab68447f818c0dca777c25c04ac959e5f2b80132..3e861f3546fee1dc4129abf3b72cf57a593db91c 100644 +index ad7aa58ee5966c5d56ff8bf8070697d039d4f503..dbc16b755f86917362d90f8801086a46c2140ceb 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -239,7 +239,7 @@ public class FallingBlockEntity extends Entity { diff --git a/patches/server/0033-Configure-Entity-Knockback.patch b/patches/server/0029-Configure-Entity-Knockback.patch similarity index 80% rename from patches/server/0033-Configure-Entity-Knockback.patch rename to patches/server/0029-Configure-Entity-Knockback.patch index 7588394..294db34 100644 --- a/patches/server/0033-Configure-Entity-Knockback.patch +++ b/patches/server/0029-Configure-Entity-Knockback.patch @@ -5,19 +5,19 @@ Subject: [PATCH] Configure Entity Knockback diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index cdfcd8e3e7e357151b8a1d5ed8b051d6cf550fc1..1ac872c7cc0330d11d995849513b12a7adbe3126 100644 +index e39732b16d0b8d47759b8fc282e4a9a158991104..64b55ad9544867ad0423aec368ff878d3ab8168f 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1545,7 +1545,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - d1 = source.getSourcePosition().z() - this.getZ(); +@@ -1570,7 +1570,7 @@ public abstract class LivingEntity extends Entity implements Attackable { } + // Paper end - Check distance in entity interactions - this.knockback(0.4000000059604645D, d0, d1, entity1, entity1 == null ? io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.DAMAGE : io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // CraftBukkit // Paper - knockback events + this.knockback((float) this.level().sakuraConfig().players.knockback.baseKnockback, d0, d1, entity1, entity1 == null ? io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.DAMAGE : io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // CraftBukkit // Paper - knockback events // Sakura - configure entity knockback if (!flag) { this.indicateDamage(d0, d1); } -@@ -1599,7 +1599,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1636,7 +1636,7 @@ public abstract class LivingEntity extends Entity implements Attackable { } protected void blockedByShield(LivingEntity target) { @@ -26,7 +26,7 @@ index cdfcd8e3e7e357151b8a1d5ed8b051d6cf550fc1..1ac872c7cc0330d11d995849513b12a7 } private boolean checkTotemDeathProtection(DamageSource source) { -@@ -1918,7 +1918,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1955,7 +1955,7 @@ public abstract class LivingEntity extends Entity implements Attackable { } public void knockback(double d0, double d1, double d2, @Nullable Entity attacker, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause cause) { // Paper - knockback events @@ -35,7 +35,7 @@ index cdfcd8e3e7e357151b8a1d5ed8b051d6cf550fc1..1ac872c7cc0330d11d995849513b12a7 if (true || d0 > 0.0D) { // CraftBukkit - Call event even when force is 0 //this.hasImpulse = true; // CraftBukkit - Move down -@@ -1929,9 +1929,21 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1966,9 +1966,21 @@ public abstract class LivingEntity extends Entity implements Attackable { } Vec3 vec3d1 = (new Vec3(d1, 0.0D, d2)).normalize().scale(d0); @@ -59,10 +59,10 @@ index cdfcd8e3e7e357151b8a1d5ed8b051d6cf550fc1..1ac872c7cc0330d11d995849513b12a7 io.papermc.paper.event.entity.EntityKnockbackEvent event = CraftEventFactory.callEntityKnockbackEvent((org.bukkit.craftbukkit.entity.CraftLivingEntity) this.getBukkitEntity(), attacker, cause, d0, diff); // Paper end - knockback events diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index b352a8d299a13026d8e2c8e3405eb7f40b289d38..852ceb5aa9dc92981f0d9d012cff04177f88e606 100644 +index 8b2b495f34b1ae96901ed8f2ca2d8d753b4eb2c8..4c5779112c5e7b880627ecc446a295eeeadb396e 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -196,6 +196,7 @@ public abstract class Player extends LivingEntity { +@@ -197,6 +197,7 @@ public abstract class Player extends LivingEntity { private int currentImpulseContextResetGraceTime; public boolean affectsSpawning = true; // Paper - Affects Spawning API public net.kyori.adventure.util.TriState flyingFallDamage = net.kyori.adventure.util.TriState.NOT_SET; // Paper - flying fall damage @@ -70,7 +70,7 @@ index b352a8d299a13026d8e2c8e3405eb7f40b289d38..852ceb5aa9dc92981f0d9d012cff0417 // CraftBukkit start public boolean fauxSleeping; -@@ -1303,7 +1304,7 @@ public abstract class Player extends LivingEntity { +@@ -1304,7 +1305,7 @@ public abstract class Player extends LivingEntity { boolean flag = f2 > 0.9F; boolean flag1; @@ -79,12 +79,12 @@ index b352a8d299a13026d8e2c8e3405eb7f40b289d38..852ceb5aa9dc92981f0d9d012cff0417 sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_KNOCKBACK, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility flag1 = true; } else { -@@ -1346,7 +1347,21 @@ public abstract class Player extends LivingEntity { +@@ -1347,7 +1348,21 @@ public abstract class Player extends LivingEntity { float f5 = this.getKnockback(target, damagesource) + (flag1 ? 1.0F : 0.0F); if (f5 > 0.0F) { - if (target instanceof LivingEntity) { -+ // Sakura start - configure extra sprinting knockback ++ // Sakura start - configure entity knockback; extra sprinting knockback + long millis = System.currentTimeMillis(); + long sinceLastKnockback = millis - this.lastSprintKnockback; + if (flag1) { // attackHasExtraKnockback @@ -98,19 +98,19 @@ index b352a8d299a13026d8e2c8e3405eb7f40b289d38..852ceb5aa9dc92981f0d9d012cff0417 + if (f5 == 0.0f) { + // required + } else if (target instanceof LivingEntity) { -+ // Sakura end - configure extra sprinting knockback ++ // Sakura end - configure entity knockback; extra sprinting knockback LivingEntity entityliving1 = (LivingEntity) target; entityliving1.knockback((double) (f5 * 0.5F), (double) Mth.sin(this.getYRot() * 0.017453292F), (double) (-Mth.cos(this.getYRot() * 0.017453292F)), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // Paper - knockback events -@@ -1376,7 +1391,7 @@ public abstract class Player extends LivingEntity { - - // CraftBukkit start - Only apply knockback if the damage hits - if (entityliving2.hurt(this.damageSources().playerAttack(this).sweep().critical(flag2), f7)) { // Paper - add critical damage API -- entityliving2.knockback(0.4000000059604645D, (double) Mth.sin(this.getYRot() * 0.017453292F), (double) (-Mth.cos(this.getYRot() * 0.017453292F)), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.SWEEP_ATTACK); // CraftBukkit // Paper - knockback events -+ entityliving2.knockback((float) this.level().sakuraConfig().players.knockback.sweepingEdgeKnockback, (double) Mth.sin(this.getYRot() * 0.017453292F), (double) (-Mth.cos(this.getYRot() * 0.017453292F)), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.SWEEP_ATTACK); // Sakura - configure entity knockback // CraftBukkit // Paper - knockback events +@@ -1380,7 +1395,7 @@ public abstract class Player extends LivingEntity { + continue; } // CraftBukkit end +- entityliving2.knockback(0.4000000059604645D, (double) Mth.sin(this.getYRot() * 0.017453292F), (double) (-Mth.cos(this.getYRot() * 0.017453292F)), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.SWEEP_ATTACK); // CraftBukkit // Paper - knockback events ++ entityliving2.knockback((float) this.level().sakuraConfig().players.knockback.sweepingEdgeKnockback, (double) Mth.sin(this.getYRot() * 0.017453292F), (double) (-Mth.cos(this.getYRot() * 0.017453292F)), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.SWEEP_ATTACK); // Sakura - configure entity knockback // CraftBukkit // Paper - knockback events + // entityliving2.hurt(damagesource, f7); // CraftBukkit - moved up Level world = this.level(); + diff --git a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java index 1223c5d23d0ea6aed068bdf0f5725e2ad49fc82c..43a3ac5858ffb2007e6f63ec0f3bc5a9971dce14 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java diff --git a/patches/server/0034-Explosion-Durable-Blocks.patch b/patches/server/0030-Explosion-Durable-Blocks.patch similarity index 89% rename from patches/server/0034-Explosion-Durable-Blocks.patch rename to patches/server/0030-Explosion-Durable-Blocks.patch index 0f95e41..119affc 100644 --- a/patches/server/0034-Explosion-Durable-Blocks.patch +++ b/patches/server/0030-Explosion-Durable-Blocks.patch @@ -74,12 +74,12 @@ index 0000000000000000000000000000000000000000..c58e52f7cc012babf4235e405e5fb501 + +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 2df0f8a92174be654acb27785ff1f4880dcf3362..6ca79a23d7be043166a421aab881c55ef1238a4c 100644 +index 4ee6b6177df48701bd3521e3489797e62a85511f..7a400d332fb4d9df7210fe4efabcc308cf37b296 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1754,6 +1754,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop resistance = !calculateResistance ? Optional.empty() : this.damageCalculator.getBlockExplosionResistance((Explosion)(Object)this, this.level, pos, blockState, fluidState); + Optional resistance = !calculateResistance ? Optional.empty() : this.calculateBlockResistance(blockState, fluidState, pos); // Sakura - explosion durable blocks - ret = new ExplosionBlockCache( + ret = new ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache( key, pos, blockState, fluidState, -@@ -277,6 +277,21 @@ public class Explosion { +@@ -158,6 +158,21 @@ public class Explosion { return ret; } @@ -156,9 +156,9 @@ index a376d53dba483b1da96bb468877a958f187a8680..f87b7a27c1d735ccb836f13dcb17156c + // Sakura end - explosion durable blocks + private boolean clipsAnything(final Vec3 from, final Vec3 to, - final io.papermc.paper.util.CollisionUtil.LazyEntityCollisionContext context, - final ExplosionBlockCache[] blockCache, -@@ -868,6 +883,16 @@ public class Explosion { + final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.LazyEntityCollisionContext context, + final ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache, +@@ -840,6 +855,16 @@ public class Explosion { // CraftBukkit start - TNTPrimeEvent BlockState iblockdata = this.level.getBlockState(blockposition); Block block = iblockdata.getBlock(); @@ -176,10 +176,10 @@ index a376d53dba483b1da96bb468877a958f187a8680..f87b7a27c1d735ccb836f13dcb17156c Entity sourceEntity = this.source == null ? null : this.source; BlockPos sourceBlock = sourceEntity == null ? BlockPos.containing(this.x, this.y, this.z) : null; diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 71f64a9f894ba719581d04c40d8dc13c2c32b9ba..566e65aa124feb2f02e6770729ff49c53f33e34a 100644 +index ccf8867882f37a55368d2f44a2eddb2c31a20413..70c4ef672dd5a8bcf8c7b011cc3ead2533012d79 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -222,6 +222,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl +@@ -223,6 +223,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl private ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup entityLookup; public final me.samsuik.sakura.entity.merge.MergeHistory mergeHistory = new me.samsuik.sakura.entity.merge.MergeHistory(); // Sakura - cannon entity merging public final me.samsuik.sakura.explosion.density.BlockDensityCache densityCache = new me.samsuik.sakura.explosion.density.BlockDensityCache(); // Sakura - explosion density cache diff --git a/patches/server/0035-Destroy-Waterlogged-Blocks.patch b/patches/server/0031-Destroy-Waterlogged-Blocks.patch similarity index 89% rename from patches/server/0035-Destroy-Waterlogged-Blocks.patch rename to patches/server/0031-Destroy-Waterlogged-Blocks.patch index 57a333d..5eb1125 100644 --- a/patches/server/0035-Destroy-Waterlogged-Blocks.patch +++ b/patches/server/0031-Destroy-Waterlogged-Blocks.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Destroy Waterlogged Blocks diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index f87b7a27c1d735ccb836f13dcb17156cbaac0231..210ab5feebd3f1554d8dc7191ab092937b218f5d 100644 +index 6495c02d089426b8ee3726f072b5d1662af3cdd5..5639f04b29b46d07e64b5e8b3292666aa0899f62 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -285,7 +285,11 @@ public class Explosion { +@@ -166,7 +166,11 @@ public class Explosion { if (material != null && material.resistance() >= 0.0f && (this.level.sakuraConfig().cannons.explosion.allowNonTntBreakingDurableBlocks || this.source instanceof net.minecraft.world.entity.item.PrimedTnt)) { return Optional.of(material.resistance()); diff --git a/patches/server/0036-Cache-Vanillia-and-Eigen-Redstone.patch b/patches/server/0032-Cache-Vanillia-and-Eigen-Redstone.patch similarity index 97% rename from patches/server/0036-Cache-Vanillia-and-Eigen-Redstone.patch rename to patches/server/0032-Cache-Vanillia-and-Eigen-Redstone.patch index 45d2056..70f12b7 100644 --- a/patches/server/0036-Cache-Vanillia-and-Eigen-Redstone.patch +++ b/patches/server/0032-Cache-Vanillia-and-Eigen-Redstone.patch @@ -343,11 +343,11 @@ index 0000000000000000000000000000000000000000..4f5af6e241b0194ed982144fd9320315 + +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 6ca79a23d7be043166a421aab881c55ef1238a4c..1c875d1ba3ead33fc3f9f369fc89bcbeb82c6844 100644 +index 7a400d332fb4d9df7210fe4efabcc308cf37b296..788e24171d0d57bbd6b57a51c194a38611d5dbf6 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1755,6 +1755,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop potentialCollisions) { + public static net.minecraft.world.phys.Vec3 performAABBCollisions(final net.minecraft.world.phys.Vec3 moveVector, net.minecraft.world.phys.AABB axisalignedbb, final java.util.List potentialCollisions) { ++ // Sakura start - physics version api + return performAABBCollisions(moveVector, axisalignedbb, potentialCollisions, null); + } -+ public static Vec3 performAABBCollisions(final Vec3 moveVector, -+ AABB axisalignedbb, -+ final List potentialCollisions, -+ final me.samsuik.sakura.physics.PhysicsVersion physics) { ++ public static net.minecraft.world.phys.Vec3 performAABBCollisions(final net.minecraft.world.phys.Vec3 moveVector, ++ net.minecraft.world.phys.AABB axisalignedbb, ++ final java.util.List potentialCollisions, ++ final me.samsuik.sakura.physics.PhysicsVersion physics) { + // Sakura end - physics version api double x = moveVector.x; double y = moveVector.y; double z = moveVector.z; -@@ -1469,7 +1477,10 @@ public final class CollisionUtil { +@@ -1474,7 +1482,10 @@ public final class CollisionUtil { } } @@ -36,26 +35,26 @@ index f941c4bbf94f80dc3db3153297713a41c6c8ca48..d8d98cd2290f4af61a45a1150f8230a2 if (xSmaller && z != 0.0) { z = performAABBCollisionsZ(axisalignedbb, z, potentialCollisions); -@@ -1495,9 +1506,17 @@ public final class CollisionUtil { - public static Vec3 performCollisions(final Vec3 moveVector, AABB axisalignedbb, - final List voxels, - final List aabbs) { +@@ -1500,9 +1511,17 @@ public final class CollisionUtil { + public static net.minecraft.world.phys.Vec3 performCollisions(final net.minecraft.world.phys.Vec3 moveVector, net.minecraft.world.phys.AABB axisalignedbb, + final java.util.List voxels, + final java.util.List aabbs) { + // Sakura start - physics version api + return performCollisions(moveVector, axisalignedbb, voxels, aabbs, null); + } -+ public static Vec3 performCollisions(final Vec3 moveVector, AABB axisalignedbb, -+ final List voxels, -+ final List aabbs, -+ final me.samsuik.sakura.physics.PhysicsVersion physics) { ++ public static net.minecraft.world.phys.Vec3 performCollisions(final net.minecraft.world.phys.Vec3 moveVector, net.minecraft.world.phys.AABB axisalignedbb, ++ final java.util.List voxels, ++ final java.util.List aabbs, ++ final me.samsuik.sakura.physics.PhysicsVersion physics) { ++ // Sakura end - physics version api if (voxels.isEmpty()) { // fast track only AABBs - return performAABBCollisions(moveVector, axisalignedbb, aabbs); -+ return performAABBCollisions(moveVector, axisalignedbb, aabbs, physics); -+ // Sakura end - physics version api ++ return performAABBCollisions(moveVector, axisalignedbb, aabbs, physics); // Sakura - physics version api } double x = moveVector.x; -@@ -1512,7 +1531,10 @@ public final class CollisionUtil { +@@ -1517,7 +1536,10 @@ public final class CollisionUtil { } } @@ -68,10 +67,10 @@ index f941c4bbf94f80dc3db3153297713a41c6c8ca48..d8d98cd2290f4af61a45a1150f8230a2 if (xSmaller && z != 0.0) { z = performAABBCollisionsZ(axisalignedbb, z, aabbs); diff --git a/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java b/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java -index 55bf4424d8e995ea7384f88f43465f90ae3c6704..eaef186a6f8857e442e33087f323ea878f5799b1 100644 +index 2dc4704b50dc9ca1e918c0869a29e333db5575d2..0644ebc5292de4924ba675a34a645f9be607635e 100644 --- a/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java +++ b/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java -@@ -172,9 +172,15 @@ public abstract class SpecialisedExplosion extends Explosion { +@@ -173,9 +173,15 @@ public abstract class SpecialisedExplosion extends Explosion { if (distanceFromBottom <= 1.0) { double x = entity.getX() - pos.x; @@ -89,10 +88,10 @@ index 55bf4424d8e995ea7384f88f43465f90ae3c6704..eaef186a6f8857e442e33087f323ea87 if (distance != 0.0D) { x /= distance; diff --git a/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java b/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java -index 3888c76dc16b76ae214aeb7265e8b0e9d600b863..31700b381d495c1d1273ab45e914fdb788e712c8 100644 +index c7c373fa24ee28f4bcd278816346171d0cdd35f2..3924dbd0007eb078ba016f7b1d84f6555b81261f 100644 --- a/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java +++ b/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java -@@ -37,6 +37,13 @@ public final class TntExplosion extends SpecialisedExplosion { +@@ -38,6 +38,13 @@ public final class TntExplosion extends SpecialisedExplosion { this.bounds = new AABB(x, y, z, x, y, z); } @@ -107,7 +106,7 @@ index 3888c76dc16b76ae214aeb7265e8b0e9d600b863..31700b381d495c1d1273ab45e914fdb7 protected void startExplosion() { for (int i = this.calculateExplosionPotential() - 1; i >= 0; --i) { diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 0946d71f8ef41f42db038c2efd527a319f55e069..2b94b7bf6cc132a54596ad71c8e2fa801e2eba77 100644 +index 729bf74fb06fe4fadeecbd0d879e35ecf33990c1..183204d645f294a74049c5cf1501250b0552839a 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -385,7 +385,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @@ -115,13 +114,13 @@ index 0946d71f8ef41f42db038c2efd527a319f55e069..2b94b7bf6cc132a54596ad71c8e2fa80 private long pistonDeltasGameTime; private EntityDimensions dimensions; - private float eyeHeight; -+ protected float eyeHeight; // Sakura - physics version ++ protected float eyeHeight; // Sakura - private -> protected public boolean isInPowderSnow; public boolean wasInPowderSnow; public boolean wasOnFire; -@@ -709,6 +709,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -698,6 +698,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } - // Sakura end - cannon entity merging + // Sakura end - merge cannon entities public boolean pushedByFluid = true; // Sakura - entity pushed by fluid api + // Sakura start - physics version api + protected me.samsuik.sakura.physics.PhysicsVersion physics = me.samsuik.sakura.physics.PhysicsVersion.LATEST; @@ -133,16 +132,16 @@ index 0946d71f8ef41f42db038c2efd527a319f55e069..2b94b7bf6cc132a54596ad71c8e2fa80 public Entity(EntityType type, Level world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); -@@ -1216,7 +1223,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1205,7 +1212,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } protected void checkSupportingBlock(boolean onGround, @Nullable Vec3 movement) { - if (onGround) { -+ if (onGround && this.physics.afterOrEqual(1_20_0)) { // Sakura - physics version ++ if (onGround && this.physics.afterOrEqual(1_20_0)) { // Sakura - physics version api AABB axisalignedbb = this.getBoundingBox(); AABB axisalignedbb1 = new AABB(axisalignedbb.minX, axisalignedbb.minY - 1.0E-6D, axisalignedbb.minZ, axisalignedbb.maxX, axisalignedbb.minY, axisalignedbb.maxZ); Optional optional = this.level.findSupportingBlock(this, axisalignedbb1); -@@ -1253,7 +1260,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1266,7 +1273,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess if (this.noPhysics) { this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z); } else { @@ -151,20 +150,20 @@ index 0946d71f8ef41f42db038c2efd527a319f55e069..2b94b7bf6cc132a54596ad71c8e2fa80 movement = this.limitPistonMovement(movement); if (movement.equals(Vec3.ZERO)) { return; -@@ -1271,10 +1278,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1284,10 +1291,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess Vec3 vec3d1 = this.collideScan(movement); double d0 = vec3d1.lengthSqr(); - if (d0 > 1.0E-7D) { + if (d0 > 1.0E-7D || this.physics.before(1_14_0)) { // Sakura - physics version api - // NOTE: if there are any blocks in the future that rely on fall distance make sure this is correct. - // The only block I am aware of is powdered snow that has a special case for falling blocks. + // NOTE: Every minecraft update check if there are any new blocks that make sure of fallDistance. + // As of 1.21 the only block is powdered snow which returns a solid collision for falling blocks. - if (this.fallDistance != 0.0F && d0 >= 1.0D && !this.isFallingBlock) { + if (this.fallDistance != 0.0F && d0 >= 1.0D && !this.isFallingBlock && this.physics.afterOrEqual(1_18_2)) { // Sakura - physics version api BlockHitResult movingobjectpositionblock = this.level().clip(new ClipContext(this.position(), this.position().add(vec3d1), ClipContext.Block.FALLDAMAGE_RESETTING, ClipContext.Fluid.WATER, this)); if (movingobjectpositionblock.getType() != HitResult.Type.MISS) { -@@ -1310,6 +1317,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1323,6 +1330,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess if (this.horizontalCollision) { Vec3 vec3d2 = this.getDeltaMovement(); @@ -172,12 +171,11 @@ index 0946d71f8ef41f42db038c2efd527a319f55e069..2b94b7bf6cc132a54596ad71c8e2fa80 + if (flag && flag1 && this.physics.isWithin(1_14_0, 1_18_1)) { + flag = false; + } -+ // Sakura end -+ ++ // Sakura end - physics version api this.setDeltaMovement(flag ? 0.0D : vec3d2.x, vec3d2.y, flag1 ? 0.0D : vec3d2.z); } -@@ -1341,7 +1354,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1364,7 +1376,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z); } else { this.wasOnFire = this.isOnFire(); @@ -186,7 +184,7 @@ index 0946d71f8ef41f42db038c2efd527a319f55e069..2b94b7bf6cc132a54596ad71c8e2fa80 this.activatedTick = Math.max(this.activatedTick, MinecraftServer.currentTick + 20); // Paper this.activatedImmunityTick = Math.max(this.activatedImmunityTick, MinecraftServer.currentTick + 20); // Paper movement = this.limitPistonMovement(movement); -@@ -1368,8 +1381,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1391,8 +1403,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess Vec3 vec3d1 = this.collide(movement); double d0 = vec3d1.lengthSqr(); @@ -197,7 +195,7 @@ index 0946d71f8ef41f42db038c2efd527a319f55e069..2b94b7bf6cc132a54596ad71c8e2fa80 BlockHitResult movingobjectpositionblock = this.level().clip(new ClipContext(this.position(), this.position().add(vec3d1), ClipContext.Block.FALLDAMAGE_RESETTING, ClipContext.Fluid.WATER, this)); if (movingobjectpositionblock.getType() != HitResult.Type.MISS) { -@@ -1405,6 +1418,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1428,6 +1440,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess if (this.horizontalCollision) { Vec3 vec3d2 = this.getDeltaMovement(); @@ -205,65 +203,64 @@ index 0946d71f8ef41f42db038c2efd527a319f55e069..2b94b7bf6cc132a54596ad71c8e2fa80 + if (flag && flag1 && this.physics.isWithin(1_14_0, 1_18_1)) { + flag = false; + } -+ // Sakura end -+ ++ // Sakura end - physics version api this.setDeltaMovement(flag ? 0.0D : vec3d2.x, vec3d2.y, flag1 ? 0.0D : vec3d2.z); } -@@ -1712,7 +1731,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1707,7 +1724,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } else { - final AABB bb = currBoundingBox.expandTowards(movement.x, movement.y, movement.z); - collectCollisions(bb, potentialCollisionsVoxel, potentialCollisionsBB); -- return io.papermc.paper.util.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB); -+ return io.papermc.paper.util.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB, this.physics); // Sakura - physics version api + AABB bb = currBoundingBox.expandTowards(movement.x, movement.y, movement.z); + this.collectCollisions(bb, potentialCollisionsVoxel, potentialCollisionsBB); +- return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB); ++ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB, this.physics); // Sakura - physics version api } } -@@ -1721,7 +1740,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1716,7 +1733,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess double y = movement.y; double z = movement.z; -- final boolean xSmaller = Math.abs(x) < Math.abs(z); +- boolean xSmaller = Math.abs(x) < Math.abs(z); + // Sakura start - physics version api -+ final boolean xSmaller = this.physics == null || this.physics.afterOrEqual(1_14_0) ? Math.abs(x) < Math.abs(z) ++ boolean xSmaller = this.physics == null || this.physics.afterOrEqual(1_14_0) ? Math.abs(x) < Math.abs(z) + : this.physics.isLegacy() && Math.abs(x) > Math.abs(z); + // Sakura end - physics version api if (y != 0.0) { - y = scanY(currBoundingBox, y, voxelList, bbList); -@@ -1845,7 +1867,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + y = this.scanY(currBoundingBox, y, voxelList, bbList); +@@ -1832,7 +1852,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return movement; } -- final Vec3 limitedMoveVector = io.papermc.paper.util.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB); -+ final Vec3 limitedMoveVector = io.papermc.paper.util.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB, this.physics); // Sakura - physics version api +- final Vec3 limitedMoveVector = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB); ++ final Vec3 limitedMoveVector = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB, this.physics); // Sakura - physics version api if (stepHeight > 0.0 && (onGround || (limitedMoveVector.y != movement.y && movement.y < 0.0)) -@@ -2033,8 +2055,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1983,8 +2003,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess protected void checkInsideBlocks() { AABB axisalignedbb = this.getBoundingBox(); - BlockPos blockposition = BlockPos.containing(axisalignedbb.minX + 1.0E-7D, axisalignedbb.minY + 1.0E-7D, axisalignedbb.minZ + 1.0E-7D); - BlockPos blockposition1 = BlockPos.containing(axisalignedbb.maxX - 1.0E-7D, axisalignedbb.maxY - 1.0E-7D, axisalignedbb.maxZ - 1.0E-7D); -+ // Sakura start - physics version ++ // Sakura start - physics version api + double offset = this.physics.afterOrEqual(1_19_3) ? 1.0E-7D : 0.001D; + BlockPos blockposition = BlockPos.containing(axisalignedbb.minX + offset, axisalignedbb.minY + offset, axisalignedbb.minZ + offset); + BlockPos blockposition1 = BlockPos.containing(axisalignedbb.maxX - offset, axisalignedbb.maxY - offset, axisalignedbb.maxZ - offset); -+ // Sakura end ++ // Sakura end - physics version api if (this.level().hasChunksAt(blockposition, blockposition1)) { BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(); diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index abf41ee186fd7d8e0963112b886d833496c2fb1a..c1de583f8336e18a591ebf4a0f8bf92ac8c5735f 100644 +index 4ad24f238343213dcba66914eab27091002361a0..0fdf97cd8d13b29db25348fc59d98391fce3346d 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -94,6 +94,8 @@ public class FallingBlockEntity extends Entity { this.yo = y; this.zo = z; this.setStartPos(this.blockPosition()); -+ this.physics = world.localConfig().config(this.blockPosition()).physicsVersion; // Sakura -+ this.eyeHeight = this.physics.isLegacy() ? 0.49f : this.eyeHeight; // Sakura ++ this.physics = world.localConfig().config(this.blockPosition()).physicsVersion; // Sakura - physics version api ++ this.eyeHeight = this.physics.isLegacy() ? 0.49f : this.eyeHeight; // Sakura - physics version api } public static FallingBlockEntity fall(Level world, BlockPos pos, BlockState state) { @@ -276,18 +273,14 @@ index abf41ee186fd7d8e0963112b886d833496c2fb1a..c1de583f8336e18a591ebf4a0f8bf92a + if (entityfallingblock.physics.afterOrEqual(1_18_2)) { + world.setBlock(blockposition, iblockdata.getFluidState().createLegacyBlock(), 3); + } -+ // Sakura end ++ // Sakura end - physics version api world.addFreshEntity(entityfallingblock, spawnReason); // CraftBukkit return entityfallingblock; } -@@ -194,13 +200,50 @@ public class FallingBlockEntity extends Entity { - // Sakura start - @Override - public final double getEyeY() { -- return heightParity ? getY() : super.getEyeY(); -+ return heightParity ? this.getY() : super.getEyeY(); +@@ -197,10 +203,47 @@ public class FallingBlockEntity extends Entity { + return this.heightParity ? this.getY() : super.getEyeY(); } - // Sakura end + // Sakura end - configure cannon mechanics + // Sakura start - physics version api + @Override + public final double distanceToSqr(Vec3 vector) { @@ -319,7 +312,7 @@ index abf41ee186fd7d8e0963112b886d833496c2fb1a..c1de583f8336e18a591ebf4a0f8bf92a + this.level().removeBlock(blockposition, false); + } else { + if (blockstate.is(block)) { -+ ((ServerLevel) level()).getChunkSource().blockChanged(blockposition); ++ ((ServerLevel) this.level()).getChunkSource().blockChanged(blockposition); + } + this.discard(EntityRemoveEvent.Cause.DESPAWN); + } @@ -344,7 +337,7 @@ index abf41ee186fd7d8e0963112b886d833496c2fb1a..c1de583f8336e18a591ebf4a0f8bf92a + // Sakura end - physics version api ++this.time; this.applyGravity(); - this.moveBasic(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise simple entity movement + this.moveBasic(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise cannon entity movement @@ -224,8 +272,15 @@ public class FallingBlockEntity extends Entity { } // Paper end - Configurable falling blocks height nerf @@ -382,8 +375,8 @@ index abf41ee186fd7d8e0963112b886d833496c2fb1a..c1de583f8336e18a591ebf4a0f8bf92a - this.scaleDeltaMovement(0.98D); // Sakura - reduce movement allocations + // Sakura start - physics version api -+ if (physics.afterOrEqual(1_12_0)) { -+ double drag = physics.before(1_14_0) ? 0.98F : 0.98D; ++ if (this.physics.afterOrEqual(1_12_0)) { ++ double drag = this.physics.before(1_14_0) ? 0.98F : 0.98D; + this.scaleDeltaMovement(drag); // Sakura - reduce movement allocations + } + // Sakura end - physics version api @@ -391,27 +384,27 @@ index abf41ee186fd7d8e0963112b886d833496c2fb1a..c1de583f8336e18a591ebf4a0f8bf92a } diff --git a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -index a5b7f3bf42e43a9b3673e8725c5f20f7a3e7dbe6..cdba6fd4c79919b9f1dcb41b8df1dca8563a6174 100644 +index 5de1bbd92e235c882c700a00200fea8daf730cf7..7247e77417264d1e9ccf23dfb7e8ec9cb38ac08a 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java @@ -78,6 +78,13 @@ public class PrimedTnt extends Entity implements TraceableEntity { - case Y -> multiplyDeltaMovement(0, 1, 0); + case Y -> this.multiplyDeltaMovement(0, 1, 0); } - // Sakura end + // Sakura end - configure cannon mechanics + // Sakura start - physics version api + this.physics = world.localConfig().config(this.blockPosition()).physicsVersion; + this.eyeHeight = this.physics.isLegacy() ? 0.49f : this.eyeHeight; + if (this.physics.isLegacy()) { -+ multiplyDeltaMovement(0, 1, 0); ++ this.multiplyDeltaMovement(0.0, 1.0, 0.0); + } + // Sakura end - physics version api } @Override -@@ -125,10 +132,26 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -131,10 +138,26 @@ public class PrimedTnt extends Entity implements TraceableEntity { } } - // Sakura end + // Sakura end - optimise tnt fluid state + // Sakura start - physics version api + @Override + public final double getEyeY() { @@ -436,7 +429,7 @@ index a5b7f3bf42e43a9b3673e8725c5f20f7a3e7dbe6..cdba6fd4c79919b9f1dcb41b8df1dca8 } @Override -@@ -143,15 +166,19 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -149,15 +172,19 @@ public class PrimedTnt extends Entity implements TraceableEntity { return; } // Paper end - Configurable TNT height nerf @@ -459,7 +452,7 @@ index a5b7f3bf42e43a9b3673e8725c5f20f7a3e7dbe6..cdba6fd4c79919b9f1dcb41b8df1dca8 // CraftBukkit start - Need to reverse the order of the explosion and the entity death so we have a location for the event // this.discard(); this.respawnMerged(); // Sakura -@@ -204,7 +231,10 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -210,7 +237,10 @@ public class PrimedTnt extends Entity implements TraceableEntity { ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent((org.bukkit.entity.Explosive)this.getBukkitEntity()); if (!event.isCancelled()) { @@ -467,21 +460,21 @@ index a5b7f3bf42e43a9b3673e8725c5f20f7a3e7dbe6..cdba6fd4c79919b9f1dcb41b8df1dca8 + // Sakura start - physics version api + double pos = this.physics.before(1_10_0) ? this.getY() + (double) 0.49f : this.getY(0.0625D); + this.level().explode(this, Explosion.getDefaultDamageSource(this.level(), this), this.usedPortal ? PrimedTnt.USED_PORTAL_DAMAGE_CALCULATOR : null, this.getX(), pos, this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.TNT); -+ // Sakura end ++ // Sakura end - physics version api } // CraftBukkit end } -@@ -274,7 +304,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -280,7 +310,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { // Paper start - Option to prevent TNT from moving in water @Override public boolean isPushedByFluid() { -- return !level().paperConfig().fixes.preventTntFromMovingInWater && level().sakuraConfig().cannons.mechanics.tntFlowsInWater && super.isPushedByFluid(); // Sakura - convenience -+ return !level().paperConfig().fixes.preventTntFromMovingInWater && !this.physics.isLegacy() && level().sakuraConfig().cannons.mechanics.tntFlowsInWater && super.isPushedByFluid(); // Sakura - physics version // Sakura - convenience +- return !level().paperConfig().fixes.preventTntFromMovingInWater && level().sakuraConfig().cannons.mechanics.tntFlowsInWater && super.isPushedByFluid(); // Sakura - configure cannon mechanics ++ return !level().paperConfig().fixes.preventTntFromMovingInWater && !this.physics.isLegacy() && level().sakuraConfig().cannons.mechanics.tntFlowsInWater && super.isPushedByFluid(); // Sakura - physics version api // Sakura - configure cannon mechanics } // Paper end - Option to prevent TNT from moving in water } diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index b35a51c8e51103260f9d250e318af8a00c21533f..77fcdc8aa11df094e4ac2185bf16d0b1a8b4aacb 100644 +index 6e029387500807f38305520b77ff0433eb055719..665e96abe709587bdff15eab78bf9fd88b8ce9dc 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java @@ -77,6 +77,7 @@ public class Explosion { @@ -490,17 +483,17 @@ index b35a51c8e51103260f9d250e318af8a00c21533f..77fcdc8aa11df094e4ac2185bf16d0b1 private final boolean consistentRadius; // Sakura - consistent explosion radius + protected final me.samsuik.sakura.physics.PhysicsVersion physics; // Sakura - physics version api - public static DamageSource getDefaultDamageSource(Level world, @Nullable Entity source) { - return world.damageSources().explosion(source, Explosion.getIndirectSourceEntityInternal(source)); -@@ -115,6 +116,7 @@ public class Explosion { + // Paper start - optimise collisions + private static final double[] CACHED_RAYS; +@@ -451,6 +452,7 @@ public class Explosion { this.explosionSound = soundEvent; this.yield = this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F; // CraftBukkit this.consistentRadius = world.localConfig().config(BlockPos.containing(x, y, z)).consistentRadius; // Sakura - consistent explosion radius + this.physics = entity != null ? entity.physics() : world.localConfig().config(BlockPos.containing(x, y, z)).physicsVersion; // Sakura - physics version api } - // Sakura start - optimise paper explosions -@@ -507,8 +509,12 @@ public class Explosion { + private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity entity) { +@@ -482,8 +484,12 @@ public class Explosion { final float density = entity.level().densityCache.getKnownDensity(vec3d1); if (density != me.samsuik.sakura.explosion.density.BlockDensityCache.UNKNOWN_DENSITY) { hitResult = density != 0.0f ? net.minecraft.world.phys.HitResult.Type.MISS : net.minecraft.world.phys.HitResult.Type.BLOCK; @@ -514,22 +507,22 @@ index b35a51c8e51103260f9d250e318af8a00c21533f..77fcdc8aa11df094e4ac2185bf16d0b1 } if (hitResult == HitResult.Type.MISS) { // Sakura end - replace density cache -@@ -613,6 +619,14 @@ public class Explosion { +@@ -579,6 +585,14 @@ public class Explosion { } if (cachedBlock.outOfWorld) { + // Sakura start - physics version api + if (this.physics.before(1_17_0)) { -+ d4 += d0; -+ d5 += d1; -+ d6 += d2; ++ currX += incX; ++ currY += incY; ++ currZ += incZ; + continue; + } + // Sakura end - physics version api break; } - -@@ -717,9 +731,15 @@ public class Explosion { + // Paper end - collision optimisations +@@ -681,9 +695,15 @@ public class Explosion { if (d7 <= 1.0D) { double d8 = entity.getX() - this.x; @@ -546,7 +539,7 @@ index b35a51c8e51103260f9d250e318af8a00c21533f..77fcdc8aa11df094e4ac2185bf16d0b1 if (d11 != 0.0D) { d8 /= d11; -@@ -1050,7 +1070,7 @@ public class Explosion { +@@ -1022,7 +1042,7 @@ public class Explosion { // Sakura start - replace density cache float blockDensity = this.level.densityCache.getDensity(vec3d, entity); if (blockDensity == me.samsuik.sakura.explosion.density.BlockDensityCache.UNKNOWN_DENSITY) { @@ -555,12 +548,12 @@ index b35a51c8e51103260f9d250e318af8a00c21533f..77fcdc8aa11df094e4ac2185bf16d0b1 this.level.densityCache.putDensity(vec3d, entity, blockDensity); // Sakura end - replace density cache } -@@ -1058,6 +1078,17 @@ public class Explosion { +@@ -1030,6 +1050,17 @@ public class Explosion { return blockDensity; } + // Sakura start - physics version api -+ private float getSeenPercent(Vec3 vec3d, Entity entity, ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { ++ private float getSeenPercent(Vec3 vec3d, Entity entity, ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { + if (this.physics.afterOrEqual(1_16_0)) { + // Papers impl is untouched, intended to be used as a fast path. + return this.getSeenFraction(vec3d, entity, blockCache, blockPos); @@ -574,10 +567,10 @@ index b35a51c8e51103260f9d250e318af8a00c21533f..77fcdc8aa11df094e4ac2185bf16d0b1 private final Level world; private final double posX, posY, posZ; diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 7a079b432092f70bac657520a0c71d0a0b4ccdb0..8a8ea38bb3e88e27fcd7e9f9f4c604956120b1d3 100644 +index c06061867ab2e4ac3f2c041adfac052c13e36707..e505b00d29f805b6d2d61a27e77ae8b5b52c2bb1 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -195,6 +195,205 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl +@@ -196,6 +196,205 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl public CraftWorld getWorld() { return this.world; } @@ -784,7 +777,7 @@ index 7a079b432092f70bac657520a0c71d0a0b4ccdb0..8a8ea38bb3e88e27fcd7e9f9f4c60495 public CraftServer getCraftServer() { return (CraftServer) Bukkit.getServer(); diff --git a/src/main/java/net/minecraft/world/level/block/FallingBlock.java b/src/main/java/net/minecraft/world/level/block/FallingBlock.java -index 1ea1232a5ba3e48eef3a139d6487c9a190155ebd..71364fe94cfeefa07fac3ee6359f7abd9bb58106 100644 +index 1ea1232a5ba3e48eef3a139d6487c9a190155ebd..4cf5147c8b089c3fa5476a05501fae0d9afaf341 100644 --- a/src/main/java/net/minecraft/world/level/block/FallingBlock.java +++ b/src/main/java/net/minecraft/world/level/block/FallingBlock.java @@ -35,6 +35,15 @@ public abstract class FallingBlock extends Block implements Fallable { @@ -798,13 +791,13 @@ index 1ea1232a5ba3e48eef3a139d6487c9a190155ebd..71364fe94cfeefa07fac3ee6359f7abd + world.scheduleTick(pos, this, this.getDelayAfterPlace()); + } + } -+ // Sakura end ++ // Sakura end - physics version api + @Override protected void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { if (isFree(world.getBlockState(pos.below())) && pos.getY() >= world.getMinBuildHeight()) { diff --git a/src/main/java/net/minecraft/world/level/block/FenceGateBlock.java b/src/main/java/net/minecraft/world/level/block/FenceGateBlock.java -index 26ed17e433cbafbbf788231f27f296f08048adfe..1e28d5175bbb4dc2d4c1c3080825e797b574fc06 100644 +index 26ed17e433cbafbbf788231f27f296f08048adfe..40176223df11efd9cb63d2a9abc425fefa860d3f 100644 --- a/src/main/java/net/minecraft/world/level/block/FenceGateBlock.java +++ b/src/main/java/net/minecraft/world/level/block/FenceGateBlock.java @@ -180,8 +180,13 @@ public class FenceGateBlock extends HorizontalDirectionalBlock { @@ -813,13 +806,13 @@ index 26ed17e433cbafbbf788231f27f296f08048adfe..1e28d5175bbb4dc2d4c1c3080825e797 - if ((Boolean) state.getValue(FenceGateBlock.POWERED) != flag1) { - world.setBlock(pos, (BlockState) ((BlockState) state.setValue(FenceGateBlock.POWERED, flag1)).setValue(FenceGateBlock.OPEN, flag1), 2); -+ // Sakura start ++ // Sakura start - physics version api + final boolean legacy = world.localConfig().config(pos).physicsVersion.before(1_11_0); + final boolean powered = state.getValue(FenceGateBlock.POWERED); + if (legacy ? (flag1 || sourceBlock.defaultBlockState().isSignalSource()) : powered != flag1) { + final boolean openGate = legacy && (flag1 == powered || state.getValue(FenceGateBlock.OPEN) != powered) ? state.getValue(OPEN) : flag1; + world.setBlock(pos, (BlockState) ((BlockState) state.setValue(FenceGateBlock.POWERED, flag1)).setValue(FenceGateBlock.OPEN, openGate), 2); -+ // Sakura end ++ // Sakura end - physics version api if ((Boolean) state.getValue(FenceGateBlock.OPEN) != flag1) { world.playSound((Player) null, pos, flag1 ? this.type.fenceGateOpen() : this.type.fenceGateClose(), SoundSource.BLOCKS, 1.0F, world.getRandom().nextFloat() * 0.1F + 0.9F); world.gameEvent((Entity) null, (Holder) (flag1 ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE), pos); @@ -869,7 +862,7 @@ index 4b402a7222f78617ef7d28d329f4daac74954347..54781ea0771327f93a7cf672bb4b2945 case NORTH: return NORTH_AABB; diff --git a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java -index 84623c632d8c2f0fa7ec939c711316d757117d23..baf791608420198493df24c68144fd29ec9fad7f 100644 +index 84623c632d8c2f0fa7ec939c711316d757117d23..afd6549f110ce32a1ad382b17f202ab53e7e6496 100644 --- a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java +++ b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java @@ -190,8 +190,26 @@ public class LiquidBlock extends Block implements BucketPickup { @@ -878,7 +871,7 @@ index 84623c632d8c2f0fa7ec939c711316d757117d23..baf791608420198493df24c68144fd29 if (world.getFluidState(blockposition1).is(FluidTags.WATER)) { - Block block = world.getFluidState(pos).isSource() ? Blocks.OBSIDIAN : Blocks.COBBLESTONE; - -+ // Sakura start ++ // Sakura start - physics version api + final FluidState fluidState = state.getFluidState(); + final Block block; + @@ -897,34 +890,34 @@ index 84623c632d8c2f0fa7ec939c711316d757117d23..baf791608420198493df24c68144fd29 + return true; + } + } -+ // Sakura end ++ // Sakura end - physics version api // CraftBukkit start if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(world, pos, block.defaultBlockState())) { this.fizz(world, pos); diff --git a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java -index 88ddd1747d9786210e8faf412b3b0363df4bab43..a6f0ded367341e6b9f9c7b1c4254dd696ead2f8d 100644 +index 88ddd1747d9786210e8faf412b3b0363df4bab43..b6c3aa283b8c6dcd5af4f770301a5481af82f945 100644 --- a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java +++ b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java @@ -699,6 +699,10 @@ public class RedStoneWireBlock extends Block { protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) { if (!player.getAbilities().mayBuild) { return InteractionResult.PASS; -+ // Sakura start ++ // Sakura start - physics version api + } else if (world.localConfig().config(pos).physicsVersion.before(1_16_0)) { + return InteractionResult.PASS; -+ // Sakura end ++ // Sakura end - physics version api } else { if (RedStoneWireBlock.isCross(state) || RedStoneWireBlock.isDot(state)) { BlockState iblockdata1 = RedStoneWireBlock.isCross(state) ? this.defaultBlockState() : this.crossState; diff --git a/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java b/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java -index 932831bb5632ead5850842fc77192c841571162e..fdf07b8aa7a10da15a9473bcb12e8cb0404654f8 100644 +index 932831bb5632ead5850842fc77192c841571162e..4300c421556b7a1e45812f54017a9b8435d33975 100644 --- a/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java +++ b/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java @@ -21,6 +21,7 @@ public class WaterlilyBlock extends BushBlock { public static final MapCodec CODEC = simpleCodec(WaterlilyBlock::new); protected static final VoxelShape AABB = Block.box(1.0D, 0.0D, 1.0D, 15.0D, 1.5D, 15.0D); -+ protected static final VoxelShape LEGACY_AABB = Block.box(0.0D, 0.0D, 0.0D, 16.0D, 0.25D, 16.0D); // Sakura ++ protected static final VoxelShape LEGACY_AABB = Block.box(0.0D, 0.0D, 0.0D, 16.0D, 0.25D, 16.0D); // Sakura - physics version api @Override public MapCodec codec() { @@ -932,7 +925,7 @@ index 932831bb5632ead5850842fc77192c841571162e..fdf07b8aa7a10da15a9473bcb12e8cb0 } -+ // Sakura start ++ // Sakura start - physics version api + @Override + public final boolean hasDynamicShape() { + return true; @@ -943,12 +936,12 @@ index 932831bb5632ead5850842fc77192c841571162e..fdf07b8aa7a10da15a9473bcb12e8cb0 + if (world instanceof net.minecraft.world.level.Level level && level.localConfig().config(pos).physicsVersion.before(1_9_0)) { + return LEGACY_AABB; + } -+ // Sakura end ++ // Sakura end - physics version api return WaterlilyBlock.AABB; } diff --git a/src/main/java/net/minecraft/world/level/block/piston/MovingPistonBlock.java b/src/main/java/net/minecraft/world/level/block/piston/MovingPistonBlock.java -index f970fca8a8b479f2d2b927bcee2d73f02bfcd1b3..ff31e517a5f2eb51acef070d6455a0f86e40dd9f 100644 +index f970fca8a8b479f2d2b927bcee2d73f02bfcd1b3..eeb7563ad312f048b02690fc9bfa2e00b1f1e845 100644 --- a/src/main/java/net/minecraft/world/level/block/piston/MovingPistonBlock.java +++ b/src/main/java/net/minecraft/world/level/block/piston/MovingPistonBlock.java @@ -110,6 +110,17 @@ public class MovingPistonBlock extends BaseEntityBlock { @@ -965,12 +958,12 @@ index f970fca8a8b479f2d2b927bcee2d73f02bfcd1b3..ff31e517a5f2eb51acef070d6455a0f8 + return pistonMovingBlockEntity.getMovedState().getCollisionShape(world, pos); + } + } -+ // Sakura end ++ // Sakura end - physics version api return pistonMovingBlockEntity != null ? pistonMovingBlockEntity.getCollisionShape(world, pos) : Shapes.empty(); } diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java -index e0c62227b279a5fe0f3868fbf9ce8c78c515a09c..3f840b82827b803a5fc594f6008cddb09926ca2d 100644 +index e0c62227b279a5fe0f3868fbf9ce8c78c515a09c..4550217fdc3e804649b9c7e5e129680cae3ee0a5 100644 --- a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java +++ b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java @@ -171,6 +171,11 @@ public class PistonBaseBlock extends DirectionalBlock { @@ -981,12 +974,12 @@ index e0c62227b279a5fe0f3868fbf9ce8c78c515a09c..3f840b82827b803a5fc594f6008cddb0 + if (world.localConfig().config(pos).physicsVersion.before(1_9_0)) { + world.setBlock(pos, state.setValue(PistonBaseBlock.EXTENDED, false), 18); + } -+ // Sakura end ++ // Sakura end - physics version api world.blockEvent(pos, this, b0, enumdirection.get3DDataValue()); } diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonHeadBlock.java b/src/main/java/net/minecraft/world/level/block/piston/PistonHeadBlock.java -index bf58f33a5dc11b9e36cb9a26a73558c675a429df..755cbe2925fb5230fdff6c14b94dbafb924ba2c2 100644 +index bf58f33a5dc11b9e36cb9a26a73558c675a429df..30bbacceb253256fc083902512d5b6212a346315 100644 --- a/src/main/java/net/minecraft/world/level/block/piston/PistonHeadBlock.java +++ b/src/main/java/net/minecraft/world/level/block/piston/PistonHeadBlock.java @@ -138,6 +138,11 @@ public class PistonHeadBlock extends DirectionalBlock { @@ -997,7 +990,7 @@ index bf58f33a5dc11b9e36cb9a26a73558c675a429df..755cbe2925fb5230fdff6c14b94dbafb + if (world instanceof Level level && level.localConfig().config(pos).physicsVersion.before(1_9_0)) { + return this.isFittingBase(state, blockState); + } -+ // Sakura end ++ // Sakura end - physics version api return this.isFittingBase(state, blockState) || blockState.is(Blocks.MOVING_PISTON) && blockState.getValue(FACING) == state.getValue(FACING); } @@ -1008,7 +1001,7 @@ index bf58f33a5dc11b9e36cb9a26a73558c675a429df..755cbe2925fb5230fdff6c14b94dbafb + // Sakura start - physics version api + } else if (world.localConfig().config(pos).physicsVersion.before(1_9_0)) { + world.setBlock(pos, Blocks.AIR.defaultBlockState(), 19); -+ // Sakura end ++ // Sakura end - physics version api } } @@ -1222,10 +1215,10 @@ index d555ad1dd2f648b84920eceec6cc99e8801334b3..b2ecc615379856f661ba87bdeb28f75a } diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -index 5ad6880845ed699077ad355ef1edcfb1c6c7bee4..e6acf6c4bcdb8d2548aa6a8b8d9af7c56877cfd4 100644 +index 9dda53434f72a14567fc77275726d67183f13317..70ff7e116e83ab3d4aaa39d2644a9ef3e5a89e95 100644 --- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -@@ -525,7 +525,7 @@ public abstract class FlowingFluid extends Fluid { +@@ -514,7 +514,7 @@ public abstract class FlowingFluid extends Fluid { this.spread(world, pos, state); } @@ -1235,7 +1228,7 @@ index 5ad6880845ed699077ad355ef1edcfb1c6c7bee4..e6acf6c4bcdb8d2548aa6a8b8d9af7c5 } diff --git a/src/main/java/net/minecraft/world/level/material/LavaFluid.java b/src/main/java/net/minecraft/world/level/material/LavaFluid.java -index 3bb4a9a1a6249e8ba2de237f801210e7f4fd5825..8dfa05a586bd21ef8aeab713cad4628166982bfa 100644 +index 3bb4a9a1a6249e8ba2de237f801210e7f4fd5825..65e4b4e16335e2e5be6e6a6a52f12f853040338d 100644 --- a/src/main/java/net/minecraft/world/level/material/LavaFluid.java +++ b/src/main/java/net/minecraft/world/level/material/LavaFluid.java @@ -175,7 +175,10 @@ public abstract class LavaFluid extends FlowingFluid { @@ -1243,15 +1236,15 @@ index 3bb4a9a1a6249e8ba2de237f801210e7f4fd5825..8dfa05a586bd21ef8aeab713cad46281 @Override public boolean canBeReplacedWith(FluidState state, BlockGetter world, BlockPos pos, Fluid fluid, Direction direction) { - return state.getHeight(world, pos) >= 0.44444445F && fluid.is(FluidTags.WATER); -+ // Sakura start ++ // Sakura start - physics version api + return state.getHeight(world, pos) >= 0.44444445F && fluid.is(FluidTags.WATER) + && world instanceof Level level && level.localConfig().config(pos).physicsVersion.afterOrEqual(1_13_0); -+ // Sakura end ++ // Sakura end - physics version api } @Override diff --git a/src/main/java/net/minecraft/world/level/material/WaterFluid.java b/src/main/java/net/minecraft/world/level/material/WaterFluid.java -index 109f71401c65f476ccf6813137386fc9fef10254..786aba3810b71a543469dab6b2b2c1ff4ca2edd5 100644 +index 109f71401c65f476ccf6813137386fc9fef10254..8621e60b50cc27ef0b5468d978828c61f286be84 100644 --- a/src/main/java/net/minecraft/world/level/material/WaterFluid.java +++ b/src/main/java/net/minecraft/world/level/material/WaterFluid.java @@ -120,7 +120,10 @@ public abstract class WaterFluid extends FlowingFluid { @@ -1259,10 +1252,10 @@ index 109f71401c65f476ccf6813137386fc9fef10254..786aba3810b71a543469dab6b2b2c1ff @Override public boolean canBeReplacedWith(FluidState state, BlockGetter world, BlockPos pos, Fluid fluid, Direction direction) { - return direction == Direction.DOWN && !fluid.is(FluidTags.WATER); -+ // Sakura start ++ // Sakura start - physics version api + return direction == Direction.DOWN && !fluid.is(FluidTags.WATER) + || fluid.is(FluidTags.LAVA) && world instanceof Level level && level.localConfig().config(pos).physicsVersion.before(1_13_0); -+ // Sakura end ++ // Sakura end - physics version api } @Override diff --git a/patches/server/0044-Allow-water-in-the-nether.patch b/patches/server/0040-Allow-water-in-the-nether.patch similarity index 100% rename from patches/server/0044-Allow-water-in-the-nether.patch rename to patches/server/0040-Allow-water-in-the-nether.patch diff --git a/patches/server/0045-Configure-concrete-solidifying-in-water.patch b/patches/server/0041-Configure-concrete-solidifying-in-water.patch similarity index 93% rename from patches/server/0045-Configure-concrete-solidifying-in-water.patch rename to patches/server/0041-Configure-concrete-solidifying-in-water.patch index 1934cbf..afb9997 100644 --- a/patches/server/0045-Configure-concrete-solidifying-in-water.patch +++ b/patches/server/0041-Configure-concrete-solidifying-in-water.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configure concrete solidifying in water diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index c1de583f8336e18a591ebf4a0f8bf92ac8c5735f..0cf2068d7c817b5ff357f99e4e2baa227120da7f 100644 +index 0fdf97cd8d13b29db25348fc59d98391fce3346d..0fab05cfce5460d7025b626ff6438c72616b78c8 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -281,7 +281,7 @@ public class FallingBlockEntity extends Entity { diff --git a/patches/server/0046-Option-for-fast-nether-dimension-lava.patch b/patches/server/0042-Option-for-fast-nether-dimension-lava.patch similarity index 90% rename from patches/server/0046-Option-for-fast-nether-dimension-lava.patch rename to patches/server/0042-Option-for-fast-nether-dimension-lava.patch index eaffbe6..0c844c5 100644 --- a/patches/server/0046-Option-for-fast-nether-dimension-lava.patch +++ b/patches/server/0042-Option-for-fast-nether-dimension-lava.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Option for fast nether dimension lava diff --git a/src/main/java/net/minecraft/world/level/material/LavaFluid.java b/src/main/java/net/minecraft/world/level/material/LavaFluid.java -index 8dfa05a586bd21ef8aeab713cad4628166982bfa..f8fdb09b7b3f696cd6c7309a90c7fd2138c845ba 100644 +index 65e4b4e16335e2e5be6e6a6a52f12f853040338d..8de74ad65e9b9019d1b4ffc01c44783c1c769cf1 100644 --- a/src/main/java/net/minecraft/world/level/material/LavaFluid.java +++ b/src/main/java/net/minecraft/world/level/material/LavaFluid.java @@ -183,7 +183,7 @@ public abstract class LavaFluid extends FlowingFluid { diff --git a/patches/server/0047-Allow-explosions-to-destroy-lava.patch b/patches/server/0043-Allow-explosions-to-destroy-lava.patch similarity index 90% rename from patches/server/0047-Allow-explosions-to-destroy-lava.patch rename to patches/server/0043-Allow-explosions-to-destroy-lava.patch index a02d19a..aa994f1 100644 --- a/patches/server/0047-Allow-explosions-to-destroy-lava.patch +++ b/patches/server/0043-Allow-explosions-to-destroy-lava.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Allow explosions to destroy lava diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 77fcdc8aa11df094e4ac2185bf16d0b1a8b4aacb..a4997b0de4300d0d4c86f8068e81ec22ecef28f6 100644 +index 665e96abe709587bdff15eab78bf9fd88b8ce9dc..72927b5f91a6cd6c5416fd077454b692ea56cf20 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -290,7 +290,7 @@ public class Explosion { +@@ -169,7 +169,7 @@ public class Explosion { if (material != null && material.resistance() >= 0.0f && (this.level.sakuraConfig().cannons.explosion.allowNonTntBreakingDurableBlocks || this.source instanceof net.minecraft.world.entity.item.PrimedTnt)) { return Optional.of(material.resistance()); // Sakura start - destroy water logged blocks diff --git a/patches/server/0048-Disable-bubble-columns-affecting-cannon-entities.patch b/patches/server/0044-Disable-bubble-columns-affecting-cannon-entities.patch similarity index 100% rename from patches/server/0048-Disable-bubble-columns-affecting-cannon-entities.patch rename to patches/server/0044-Disable-bubble-columns-affecting-cannon-entities.patch diff --git a/patches/server/0045-Treat-solid-blocks-as-full-when-moving-fast.patch b/patches/server/0045-Treat-solid-blocks-as-full-when-moving-fast.patch new file mode 100644 index 0000000..b31847e --- /dev/null +++ b/patches/server/0045-Treat-solid-blocks-as-full-when-moving-fast.patch @@ -0,0 +1,68 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik <40902469+Samsuik@users.noreply.github.com> +Date: Sun, 26 Nov 2023 17:57:50 +0000 +Subject: [PATCH] Treat solid blocks as full when moving fast + + +diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java +index 523cd7cfd5e883cc4758ab9ad13251c3c38584cb..bc70364df17b9e893c1c774fed61999993c90aa3 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java ++++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java +@@ -1597,6 +1597,7 @@ public final class CollisionUtil { + public static final int COLLISION_FLAG_CHECK_BORDER = 1 << 2; + public static final int COLLISION_FLAG_CHECK_ONLY = 1 << 3; + public static final int COLLISION_FLAG_ADD_TICKET = 1 << 4; // Sakura - load chunks on movement ++ public static final int COLLISION_FLAG_FULL_BLOCKS = 1 << 5; // Sakura - treat solid blocks as full when moving fast + + public static boolean getCollisionsForBlocksOrWorldBorder(final net.minecraft.world.level.Level world, final net.minecraft.world.entity.Entity entity, final net.minecraft.world.phys.AABB aabb, + final java.util.List intoVoxel, final java.util.List intoAABB, +@@ -1648,6 +1649,7 @@ public final class CollisionUtil { + + final boolean loadChunks = (collisionFlags & COLLISION_FLAG_LOAD_CHUNKS) != 0; + final boolean addTicket = (collisionFlags & COLLISION_FLAG_ADD_TICKET) != 0; // Sakura ++ final boolean fullBlocks = (collisionFlags & COLLISION_FLAG_FULL_BLOCKS) != 0; // Sakura - treat solid blocks as full when moving fast + final net.minecraft.world.level.chunk.ChunkSource chunkSource = world.getChunkSource(); + + for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) { +@@ -1688,7 +1690,7 @@ public final class CollisionUtil { + continue; + } + +- final boolean hasSpecial = ((ca.spottedleaf.moonrise.patches.collisions.world.CollisionLevelChunkSection)section).moonrise$getSpecialCollidingBlocks() != 0; ++ final boolean hasSpecial = !fullBlocks && ((ca.spottedleaf.moonrise.patches.collisions.world.CollisionLevelChunkSection)section).moonrise$getSpecialCollidingBlocks() != 0; // Sakura - treat solid blocks as full when moving fast + final int sectionAdjust = !hasSpecial ? 1 : 0; + + final net.minecraft.world.level.chunk.PalettedContainer blocks = section.states; +@@ -1724,7 +1726,11 @@ public final class CollisionUtil { + net.minecraft.world.phys.shapes.VoxelShape blockCollision = ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)blockData).moonrise$getConstantCollisionShape(); + + if (edgeCount == 0 || ((edgeCount != 1 || blockData.hasLargeCollisionShape()) && (edgeCount != 2 || blockData.getBlock() == net.minecraft.world.level.block.Blocks.MOVING_PISTON))) { +- if (blockCollision == null) { ++ // Sakura start - treat solid blocks as full when moving fast ++ if (fullBlocks) { ++ blockCollision = net.minecraft.world.phys.shapes.Shapes.block(); ++ } else if (blockCollision == null) { ++ // Sakura end - treat solid blocks as full when moving fast + mutablePos.set(blockX, blockY, blockZ); + blockCollision = blockData.getCollisionShape(world, mutablePos, collisionShape); + } +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 183204d645f294a74049c5cf1501250b0552839a..d398bc33522e199ca912f460d6092464348b1a1e 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -565,6 +565,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + flags |= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_ADD_TICKET; + } + ++ // Sakura start - treat solid blocks as full when moving fast ++ if (this.level().sakuraConfig().cannons.treatAllBlocksAsFullWhenMoving && (this.isPrimedTNT || this.isFallingBlock)) { ++ this.syncDeltaMovement(); ++ double horizontalMovementSqr = this.movementX*this.movementX + this.movementZ*this.movementZ; ++ if (horizontalMovementSqr > Math.pow(this.level().sakuraConfig().cannons.treatAllBlocksAsFullWhenMovingFasterThan, 2.0)) { ++ flags |= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_FULL_BLOCKS; ++ } ++ } ++ // Sakura end - treat solid blocks as full when moving fast + return flags; + } + // Sakura end - load chunks on movement diff --git a/patches/server/0050-Add-redstone-implementation-API.patch b/patches/server/0046-Add-redstone-implementation-API.patch similarity index 98% rename from patches/server/0050-Add-redstone-implementation-API.patch rename to patches/server/0046-Add-redstone-implementation-API.patch index 2aef88a..a24a9a4 100644 --- a/patches/server/0050-Add-redstone-implementation-API.patch +++ b/patches/server/0046-Add-redstone-implementation-API.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add redstone implementation API diff --git a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java -index a6f0ded367341e6b9f9c7b1c4254dd696ead2f8d..e99dcd24d4af8450ceb436b929c9a9ecf75249bc 100644 +index b6c3aa283b8c6dcd5af4f770301a5481af82f945..43a579795c19d4edd06ec6f301d6c107beaca96f 100644 --- a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java +++ b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java @@ -268,7 +268,7 @@ public class RedStoneWireBlock extends Block { diff --git a/patches/server/0047-Reduce-entity-tracker-player-updates.patch b/patches/server/0047-Reduce-entity-tracker-player-updates.patch new file mode 100644 index 0000000..3d81849 --- /dev/null +++ b/patches/server/0047-Reduce-entity-tracker-player-updates.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik <40902469+Samsuik@users.noreply.github.com> +Date: Thu, 30 Nov 2023 15:54:49 +0000 +Subject: [PATCH] Reduce entity tracker player updates + + +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index ac00b69197964e5c82bc597594d28fcf74f87a02..6855e10e33ec09645dd770f4b9337cc7ca4f4932 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -961,7 +961,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + if (tracker == null) { + continue; + } +- ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity)tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition())); ++ // Sakura start - reduce entity tracker player updates ++ if (tracker.shouldUpdatePlayers()) { ++ ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity) tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition())); ++ } ++ // Sakura end - reduce entity tracker player updates + tracker.serverEntity.sendChanges(); + } + +@@ -1213,12 +1217,30 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + } + // Paper end - optimise entity tracker ++ // Sakura start - reduce entity tracker player updates ++ private final int playerUpdateInterval; ++ private Vec3 entityPosition; ++ ++ public final boolean shouldUpdatePlayers() { ++ // We have to always update players otherwise they can turn invisible on teleports (why?) ++ if (this.entity instanceof net.minecraft.world.entity.player.Player || this.entity.tickCount % this.playerUpdateInterval == 0) { ++ return true; ++ } ++ ++ Vec3 lastPosition = this.entityPosition; ++ this.entityPosition = this.entity.position(); ++ ++ return this.entity.position().distanceToSqr(lastPosition) >= (double) this.range / 2.0; ++ } ++ // Sakura start - reduce entity tracker player updates + + public TrackedEntity(final Entity entity, final int i, final int j, final boolean flag) { + this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit + this.entity = entity; + this.range = i; + this.lastSectionPos = SectionPos.of((EntityAccess) entity); ++ this.playerUpdateInterval = Math.min(j, 20); // Sakura - reduce entity tracker player updates ++ this.entityPosition = entity.position(); // Sakura - reduce entity tracker player updates + } + + public boolean equals(Object object) { diff --git a/patches/server/0052-Add-option-for-legacy-lava-block-formation.patch b/patches/server/0048-Add-option-for-legacy-lava-block-formation.patch similarity index 80% rename from patches/server/0052-Add-option-for-legacy-lava-block-formation.patch rename to patches/server/0048-Add-option-for-legacy-lava-block-formation.patch index 7b0defa..fe0808f 100644 --- a/patches/server/0052-Add-option-for-legacy-lava-block-formation.patch +++ b/patches/server/0048-Add-option-for-legacy-lava-block-formation.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add option for legacy lava block formation diff --git a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java -index baf791608420198493df24c68144fd29ec9fad7f..08285f41e2f4904fe6747d91ef8fca697772db15 100644 +index afd6549f110ce32a1ad382b17f202ab53e7e6496..6640dd9041cc4567e53b8c6d6acc24851e932c78 100644 --- a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java +++ b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java @@ -197,7 +197,15 @@ public class LiquidBlock extends Block implements BucketPickup { @@ -13,7 +13,7 @@ index baf791608420198493df24c68144fd29ec9fad7f..08285f41e2f4904fe6747d91ef8fca69 block = Blocks.OBSIDIAN; } else { - final me.samsuik.sakura.physics.PhysicsVersion physics = world.localConfig().config(pos).physicsVersion; -+ // Sakura start - legacy block formation ++ // Sakura start - legacy lava block formation + final me.samsuik.sakura.physics.PhysicsVersion physics; + + if (world.sakuraConfig().environment.blockGeneration.legacyBlockFormation) { @@ -21,33 +21,33 @@ index baf791608420198493df24c68144fd29ec9fad7f..08285f41e2f4904fe6747d91ef8fca69 + } else { + physics = world.localConfig().config(pos).physicsVersion; + } -+ // Sakura end ++ // Sakura end - legacy lava block formation // SANITY: In legacy a patch by paper removes the fluid level condition from vanilla. if (physics.afterOrEqual(1_16_0) || physics.isLegacy() diff --git a/src/main/java/net/minecraft/world/level/material/LavaFluid.java b/src/main/java/net/minecraft/world/level/material/LavaFluid.java -index f8fdb09b7b3f696cd6c7309a90c7fd2138c845ba..c3c24f616345e41772062032406c308ca130147d 100644 +index 8de74ad65e9b9019d1b4ffc01c44783c1c769cf1..1c82fd204919c22761bd29c039dd0b1dbc45d3e4 100644 --- a/src/main/java/net/minecraft/world/level/material/LavaFluid.java +++ b/src/main/java/net/minecraft/world/level/material/LavaFluid.java @@ -177,7 +177,7 @@ public abstract class LavaFluid extends FlowingFluid { public boolean canBeReplacedWith(FluidState state, BlockGetter world, BlockPos pos, Fluid fluid, Direction direction) { - // Sakura start + // Sakura start - physics version api return state.getHeight(world, pos) >= 0.44444445F && fluid.is(FluidTags.WATER) - && world instanceof Level level && level.localConfig().config(pos).physicsVersion.afterOrEqual(1_13_0); -+ && world instanceof Level level && !level.sakuraConfig().environment.blockGeneration.legacyBlockFormation && level.localConfig().config(pos).physicsVersion.afterOrEqual(1_13_0); - // Sakura end ++ && world instanceof Level level && !level.sakuraConfig().environment.blockGeneration.legacyBlockFormation && level.localConfig().config(pos).physicsVersion.before(1_13_0); // Sakura - legacy lava block formation + // Sakura end - physics version api } diff --git a/src/main/java/net/minecraft/world/level/material/WaterFluid.java b/src/main/java/net/minecraft/world/level/material/WaterFluid.java -index 786aba3810b71a543469dab6b2b2c1ff4ca2edd5..f883b9b0a336591dcf685cf34ad09eee832992c2 100644 +index 8621e60b50cc27ef0b5468d978828c61f286be84..7a202194c9dff4225b80d17d7cc462b82bcdaeb0 100644 --- a/src/main/java/net/minecraft/world/level/material/WaterFluid.java +++ b/src/main/java/net/minecraft/world/level/material/WaterFluid.java @@ -122,7 +122,7 @@ public abstract class WaterFluid extends FlowingFluid { public boolean canBeReplacedWith(FluidState state, BlockGetter world, BlockPos pos, Fluid fluid, Direction direction) { - // Sakura start + // Sakura start - physics version api return direction == Direction.DOWN && !fluid.is(FluidTags.WATER) - || fluid.is(FluidTags.LAVA) && world instanceof Level level && level.localConfig().config(pos).physicsVersion.before(1_13_0); -+ || fluid.is(FluidTags.LAVA) && world instanceof Level level && (level.sakuraConfig().environment.blockGeneration.legacyBlockFormation || level.localConfig().config(pos).physicsVersion.before(1_13_0)); - // Sakura end ++ || fluid.is(FluidTags.LAVA) && world instanceof Level level && (level.sakuraConfig().environment.blockGeneration.legacyBlockFormation || level.localConfig().config(pos).physicsVersion.before(1_13_0)); // Sakura - legacy lava block formation + // Sakura end - physics version api } diff --git a/patches/server/0053-Configure-mob-spawner-defaults.patch b/patches/server/0049-Configure-mob-spawner-defaults.patch similarity index 100% rename from patches/server/0053-Configure-mob-spawner-defaults.patch rename to patches/server/0049-Configure-mob-spawner-defaults.patch diff --git a/patches/server/0049-Treat-all-collidable-blocks-as-full-while-moving-fas.patch b/patches/server/0049-Treat-all-collidable-blocks-as-full-while-moving-fas.patch deleted file mode 100644 index 6673396..0000000 --- a/patches/server/0049-Treat-all-collidable-blocks-as-full-while-moving-fas.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik <40902469+Samsuik@users.noreply.github.com> -Date: Sun, 26 Nov 2023 17:57:50 +0000 -Subject: [PATCH] Treat all collidable blocks as full while moving fast - - -diff --git a/src/main/java/io/papermc/paper/util/CollisionUtil.java b/src/main/java/io/papermc/paper/util/CollisionUtil.java -index d8d98cd2290f4af61a45a1150f8230a2c6b54352..e99375ef42c54341b91a4eefe4299e7babf65ad2 100644 ---- a/src/main/java/io/papermc/paper/util/CollisionUtil.java -+++ b/src/main/java/io/papermc/paper/util/CollisionUtil.java -@@ -1592,6 +1592,7 @@ public final class CollisionUtil { - public static final int COLLISION_FLAG_CHECK_BORDER = 1 << 2; - public static final int COLLISION_FLAG_CHECK_ONLY = 1 << 3; - public static final int COLLISION_FLAG_ADD_TICKET = 1 << 4; // Sakura -+ public static final int COLLISION_FLAG_FULL_BLOCKS = 1 << 5; // Sakura - - public static boolean getCollisionsForBlocksOrWorldBorder(final Level world, final Entity entity, final AABB aabb, - final List intoVoxel, final List intoAABB, -@@ -1643,6 +1644,7 @@ public final class CollisionUtil { - - final boolean loadChunks = (collisionFlags & COLLISION_FLAG_LOAD_CHUNKS) != 0; - final boolean addTicket = (collisionFlags & COLLISION_FLAG_ADD_TICKET) != 0; // Sakura -+ final boolean fullBlocks = (collisionFlags & COLLISION_FLAG_FULL_BLOCKS) != 0; // Sakura - final ServerChunkCache chunkSource = (ServerChunkCache)world.getChunkSource(); - - for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) { -@@ -1684,7 +1686,7 @@ public final class CollisionUtil { - continue; - } - -- final boolean hasSpecial = section.getSpecialCollidingBlocks() != 0; -+ final boolean hasSpecial = !fullBlocks && section.getSpecialCollidingBlocks() != 0; // Sakura - final int sectionAdjust = !hasSpecial ? 1 : 0; - - final PalettedContainer blocks = section.states; -@@ -1718,12 +1720,20 @@ public final class CollisionUtil { - } - - if (edgeCount == 0 || ((edgeCount != 1 || blockData.hasLargeCollisionShape()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON))) { -- VoxelShape blockCollision = blockData.getConstantCollisionShape(); -+ // Sakura start - if flag is set treat all block as full -+ VoxelShape blockCollision; - -- if (blockCollision == null) { -- mutablePos.set(blockX, blockY, blockZ); -- blockCollision = blockData.getCollisionShape(world, mutablePos, collisionShape); -+ if (fullBlocks) { -+ blockCollision = Shapes.block(); -+ } else { -+ blockCollision = blockData.getConstantCollisionShape(); -+ -+ if (blockCollision == null) { -+ mutablePos.set(blockX, blockY, blockZ); -+ blockCollision = blockData.getCollisionShape(world, mutablePos, collisionShape); -+ } - } -+ // Sakura end - - AABB singleAABB = blockCollision.getSingleAABBRepresentation(); - if (singleAABB != null) { -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 2b94b7bf6cc132a54596ad71c8e2fa801e2eba77..fe1ce43158b84639abae6f7cee3a7ecdfb3d383a 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -576,6 +576,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - flags |= io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_LOAD_CHUNKS | io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_ADD_TICKET; - } - -+ if (this.level().sakuraConfig().cannons.treatAllBlocksAsFullWhenMoving && (this.isPrimedTNT || this.isFallingBlock)) { -+ this.syncDeltaMovement(); -+ double horizontalMovementSqr = this.movementX*this.movementX + this.movementZ*this.movementZ; -+ if (horizontalMovementSqr > Math.pow(this.level().sakuraConfig().cannons.treatAllBlocksAsFullWhenMovingFasterThan, 2.0)) { -+ flags |= io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_FULL_BLOCKS; -+ } -+ } -+ - return flags; - } - // Sakura end - load chunks on cannon entity movement diff --git a/patches/server/0054-Allow-disabling-random-dispenser-item-selection.patch b/patches/server/0050-Allow-disabling-random-dispenser-item-selection.patch similarity index 100% rename from patches/server/0054-Allow-disabling-random-dispenser-item-selection.patch rename to patches/server/0050-Allow-disabling-random-dispenser-item-selection.patch diff --git a/patches/server/0055-Add-instant-mob-death-animation.patch b/patches/server/0051-Add-instant-mob-death-animation.patch similarity index 85% rename from patches/server/0055-Add-instant-mob-death-animation.patch rename to patches/server/0051-Add-instant-mob-death-animation.patch index 9be5636..eb19a91 100644 --- a/patches/server/0055-Add-instant-mob-death-animation.patch +++ b/patches/server/0051-Add-instant-mob-death-animation.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add instant mob death animation diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 57929f4c2642d1a66f5158a59a5b905e977b2fa2..5a7f7de0bcdd3d6ed946f0d3e3100e21ff1cbea9 100644 +index 0fed8714e1d01c6f584d6de00fba3635740298f6..7acb574bf1ca8de1ecd023e96ef9c0013118091d 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1778,6 +1778,12 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1815,6 +1815,12 @@ public abstract class LivingEntity extends Entity implements Attackable { // Paper start if (this.dead) { // Paper diff --git a/patches/server/0051-Reduce-entity-tracker-player-updates.patch b/patches/server/0051-Reduce-entity-tracker-player-updates.patch deleted file mode 100644 index 05ddb45..0000000 --- a/patches/server/0051-Reduce-entity-tracker-player-updates.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik <40902469+Samsuik@users.noreply.github.com> -Date: Thu, 30 Nov 2023 15:54:49 +0000 -Subject: [PATCH] Reduce entity tracker player updates - - -diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index c46e07638db51bd549e39f0fcbe122d48ead9c31..d779e010ba7c0e99aa3b0ce8462b7e3fa7cced8c 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1027,6 +1027,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - try { - for (TrackedEntity tracker : this.entityMap.values()) { - // update tracker entry -+ if (!tracker.shouldLookForPlayers()) continue; // Sakura - delay entities looking for nearby players - tracker.updatePlayers(tracker.entity.getPlayersInTrackRange()); - } - } finally { -@@ -1199,14 +1200,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - private final int range; - SectionPos lastSectionPos; - public final Set seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl -+ private final int playerSearchInterval; // Sakura - reduce entity tracker player updates -+ private Vec3 entityPosition; // Sakura - reduce entity tracker player updates - - public TrackedEntity(final Entity entity, final int i, final int j, final boolean flag) { - this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit - this.entity = entity; - this.range = i; - this.lastSectionPos = SectionPos.of((EntityAccess) entity); -+ // Sakura start - reduce entities looking for nearby players -+ // Use a maximum of 20 ticks because stationary entities use Integer.MAX_VALUE -+ // which causes them to turn invisible after untracking. -+ this.playerSearchInterval = Math.min(j, 20); -+ this.entityPosition = entity.position(); - } - -+ final boolean shouldLookForPlayers() { -+ // We have to always update players otherwise they can turn invisible on teleports (why?) -+ if (entity instanceof net.minecraft.world.entity.player.Player || entity.tickCount % playerSearchInterval == 0) { -+ return true; -+ } -+ -+ Vec3 lastPosition = entityPosition; -+ entityPosition = entity.position(); -+ -+ return entity.position().distanceToSqr(lastPosition) >= (double) range / 2.0; -+ } -+ // Sakura end - reduce entities looking for nearby players -+ - // Paper start - use distance map to optimise tracker - com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet lastTrackerCandidates; - diff --git a/patches/server/0056-Configure-fluids-breaking-redstone.patch b/patches/server/0052-Configure-fluids-breaking-redstone.patch similarity index 89% rename from patches/server/0056-Configure-fluids-breaking-redstone.patch rename to patches/server/0052-Configure-fluids-breaking-redstone.patch index 5bdb091..149889a 100644 --- a/patches/server/0056-Configure-fluids-breaking-redstone.patch +++ b/patches/server/0052-Configure-fluids-breaking-redstone.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Configure fluids breaking redstone diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -index e6acf6c4bcdb8d2548aa6a8b8d9af7c56877cfd4..a229c3676ac2e59eb0c9dc114f54b1ae8f9e65ac 100644 +index 70ff7e116e83ab3d4aaa39d2644a9ef3e5a89e95..eb7333d2740a666b2302ff3200020dddf3985532 100644 --- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -@@ -477,6 +477,10 @@ public abstract class FlowingFluid extends Fluid { +@@ -466,6 +466,10 @@ public abstract class FlowingFluid extends Fluid { if (block instanceof LiquidBlockContainer ifluidcontainer) { return ifluidcontainer.canPlaceLiquid((Player) null, world, pos, state, fluid); diff --git a/patches/server/0057-Option-to-disable-explosions-hurting-players.patch b/patches/server/0053-Option-to-disable-explosions-hurting-players.patch similarity index 91% rename from patches/server/0057-Option-to-disable-explosions-hurting-players.patch rename to patches/server/0053-Option-to-disable-explosions-hurting-players.patch index 665798c..198b7d6 100644 --- a/patches/server/0057-Option-to-disable-explosions-hurting-players.patch +++ b/patches/server/0053-Option-to-disable-explosions-hurting-players.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Option to disable explosions hurting players diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 852ceb5aa9dc92981f0d9d012cff04177f88e606..8bed7279377884498fc1c4ed7c44b193bb89c1e7 100644 +index 4c5779112c5e7b880627ecc446a295eeeadb396e..d2babdee314202c5f9795ea21866f6f79cda2eca 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -966,7 +966,7 @@ public abstract class Player extends LivingEntity { +@@ -967,7 +967,7 @@ public abstract class Player extends LivingEntity { @Override public boolean isInvulnerableTo(DamageSource damageSource) { diff --git a/patches/server/0058-Iron-golems-take-fall-damage.patch b/patches/server/0054-Iron-golems-take-fall-damage.patch similarity index 100% rename from patches/server/0058-Iron-golems-take-fall-damage.patch rename to patches/server/0054-Iron-golems-take-fall-damage.patch diff --git a/patches/server/0059-Add-explosions-dropping-items-config.patch b/patches/server/0055-Add-explosions-dropping-items-config.patch similarity index 86% rename from patches/server/0059-Add-explosions-dropping-items-config.patch rename to patches/server/0055-Add-explosions-dropping-items-config.patch index 1b20d0c..b92d467 100644 --- a/patches/server/0059-Add-explosions-dropping-items-config.patch +++ b/patches/server/0055-Add-explosions-dropping-items-config.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add explosions dropping items config diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index a4997b0de4300d0d4c86f8068e81ec22ecef28f6..94df0527699462e29fb76f68194386c50f1f7e04 100644 +index 72927b5f91a6cd6c5416fd077454b692ea56cf20..a7d7ccd8277f1e710808710faf06ebe37ee71dc4 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -939,6 +939,11 @@ public class Explosion { +@@ -911,6 +911,11 @@ public class Explosion { this.level.densityCache.clear(-1); } // Sakura end - explosion density cache diff --git a/patches/server/0060-Optimise-check-inside-blocks-and-fluids.patch b/patches/server/0056-Optimise-check-inside-blocks-and-fluids.patch similarity index 93% rename from patches/server/0060-Optimise-check-inside-blocks-and-fluids.patch rename to patches/server/0056-Optimise-check-inside-blocks-and-fluids.patch index 7162202..0b9bfc0 100644 --- a/patches/server/0060-Optimise-check-inside-blocks-and-fluids.patch +++ b/patches/server/0056-Optimise-check-inside-blocks-and-fluids.patch @@ -5,12 +5,12 @@ Subject: [PATCH] Optimise check inside blocks and fluids diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index fe1ce43158b84639abae6f7cee3a7ecdfb3d383a..92cf9e492d3cd904691a325e0af9b205487e5ab7 100644 +index d398bc33522e199ca912f460d6092464348b1a1e..f7c619df4deacd9a9c559c582b388cdd900f0165 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -2069,18 +2069,37 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2018,18 +2018,37 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess BlockPos blockposition1 = BlockPos.containing(axisalignedbb.maxX - offset, axisalignedbb.maxY - offset, axisalignedbb.maxZ - offset); - // Sakura end + // Sakura end - physics version api - if (this.level().hasChunksAt(blockposition, blockposition1)) { + // Sakura start - optimise check inside blocks @@ -50,7 +50,7 @@ index fe1ce43158b84639abae6f7cee3a7ecdfb3d383a..92cf9e492d3cd904691a325e0af9b205 try { iblockdata.entityInside(this.level(), blockposition_mutableblockposition, this); -@@ -4871,7 +4890,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4821,7 +4840,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public boolean updateFluidHeightAndDoFluidPushing(TagKey tag, double speed) { @@ -59,7 +59,7 @@ index fe1ce43158b84639abae6f7cee3a7ecdfb3d383a..92cf9e492d3cd904691a325e0af9b205 return false; } else { AABB axisalignedbb = this.getBoundingBox().deflate(0.001D); -@@ -4888,11 +4907,30 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4838,11 +4857,30 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess int k1 = 0; BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(); diff --git a/patches/server/0062-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch b/patches/server/0057-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch similarity index 92% rename from patches/server/0062-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch rename to patches/server/0057-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch index 4419b2d..f4dd760 100644 --- a/patches/server/0062-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch +++ b/patches/server/0057-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Avoid searching for lava if throttled water flow speed is diff --git a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java -index 08285f41e2f4904fe6747d91ef8fca697772db15..ff7dfdb79e5a24947135f65b101e7f0afd891c6d 100644 +index 6640dd9041cc4567e53b8c6d6acc24851e932c78..36ff185ce68348f42d83451d825fb9575136c8b8 100644 --- a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java +++ b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java @@ -145,7 +145,7 @@ public class LiquidBlock extends Block implements BucketPickup { diff --git a/patches/server/0063-Calculate-biome-noise-once-per-chunk-section.patch b/patches/server/0058-Calculate-biome-noise-once-per-chunk-section.patch similarity index 88% rename from patches/server/0063-Calculate-biome-noise-once-per-chunk-section.patch rename to patches/server/0058-Calculate-biome-noise-once-per-chunk-section.patch index 2c06498..a699caa 100644 --- a/patches/server/0063-Calculate-biome-noise-once-per-chunk-section.patch +++ b/patches/server/0058-Calculate-biome-noise-once-per-chunk-section.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Calculate biome noise once per chunk section diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -index c7b4c4463b26445964ac1fa51e66e35798ea4c41..10ce2dd78d01524cb43774f13cae6f9a4560ace4 100644 +index d4bd71f70f80ea74947e6d478ff636be2ab7d432..40502c4de98cb2b97b9389fc10e319686e684141 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -@@ -228,12 +228,18 @@ public class LevelChunkSection { +@@ -241,12 +241,18 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.collis public void fillBiomesFromNoise(BiomeResolver biomeSupplier, Climate.Sampler sampler, int x, int y, int z) { PalettedContainer> datapaletteblock = this.biomes.recreate(); diff --git a/patches/server/0064-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch b/patches/server/0059-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch similarity index 95% rename from patches/server/0064-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch rename to patches/server/0059-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch index 35d6f3f..2d5c272 100644 --- a/patches/server/0064-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch +++ b/patches/server/0059-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Fix doEntityDrops gamerule preventing falling blocks from diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index 0cf2068d7c817b5ff357f99e4e2baa227120da7f..71dcdecbfc2189d3d1229001ad686cd8b41bf89e 100644 +index 0fab05cfce5460d7025b626ff6438c72616b78c8..4d06f4eb4c36b8426fae4883154e1ae16e4064e3 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -356,10 +356,14 @@ public class FallingBlockEntity extends Entity { diff --git a/patches/server/0065-Add-entity-travel-distance-limits.patch b/patches/server/0060-Add-entity-travel-distance-limits.patch similarity index 86% rename from patches/server/0065-Add-entity-travel-distance-limits.patch rename to patches/server/0060-Add-entity-travel-distance-limits.patch index 98df0d9..f4b9e42 100644 --- a/patches/server/0065-Add-entity-travel-distance-limits.patch +++ b/patches/server/0060-Add-entity-travel-distance-limits.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add entity travel distance limits diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 82ff83fd00b976d41d1d5b30ad34ede9af1a7e5b..f00afd6dca2d8a0d5d8061a2e4e00a344761bff6 100644 +index 5f09b0a94e6b1087c9207c333aa5595281a57985..d69439c51adb330066ddca62bda0e71ff36872ba 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1197,6 +1197,11 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. +@@ -1260,6 +1260,11 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. if (isActive) { // Paper - EAR 2 TimingHistory.activatedEntityTicks++; entity.tick(); @@ -21,10 +21,10 @@ index 82ff83fd00b976d41d1d5b30ad34ede9af1a7e5b..f00afd6dca2d8a0d5d8061a2e4e00a34 } else { entity.inactiveTick(); } // Paper - EAR 2 this.getProfiler().pop(); diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 92cf9e492d3cd904691a325e0af9b205487e5ab7..5decb8c3791a8b72d1d2df5092cfdad0560fd82b 100644 +index f7c619df4deacd9a9c559c582b388cdd900f0165..d90fc6c3057102b2f14687e3c3294f7f47e270b1 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -724,6 +724,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -714,6 +714,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return this.physics; } // Sakura end - physics version api @@ -44,7 +44,7 @@ index 92cf9e492d3cd904691a325e0af9b205487e5ab7..5decb8c3791a8b72d1d2df5092cfdad0 public Entity(EntityType type, Level world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); -@@ -774,6 +787,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -764,6 +777,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.setPos(0.0D, 0.0D, 0.0D); this.eyeHeight = this.dimensions.eyeHeight(); this.mergeLevel = level.sakuraConfig().cannons.mergeLevel; // Sakura diff --git a/patches/server/0066-Configure-potion-speed-and-breaking-inside-entities.patch b/patches/server/0061-Configure-potion-speed-and-breaking-inside-entities.patch similarity index 100% rename from patches/server/0066-Configure-potion-speed-and-breaking-inside-entities.patch rename to patches/server/0061-Configure-potion-speed-and-breaking-inside-entities.patch diff --git a/patches/server/0061-Fix-paper-findSupportingBlock-not-updating-last-chun.patch b/patches/server/0061-Fix-paper-findSupportingBlock-not-updating-last-chun.patch deleted file mode 100644 index db9ebf0..0000000 --- a/patches/server/0061-Fix-paper-findSupportingBlock-not-updating-last-chun.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik -Date: Sun, 24 Dec 2023 16:56:39 +0000 -Subject: [PATCH] Fix paper findSupportingBlock not updating last chunk - coordinates - - -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 8a8ea38bb3e88e27fcd7e9f9f4c604956120b1d3..60c84c04651c390df7efb9a8fa840a9d1651cbf8 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -901,6 +901,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - - if (chunkDiff != 0) { - lastChunk = chunkProvider.getChunkAtIfLoadedImmediately(newChunkX, newChunkZ); -+ // Sakura start -+ lastChunkX = newChunkX; -+ lastChunkZ = newChunkZ; -+ // Sakura end - } - - if (lastChunk == null) { diff --git a/patches/server/0067-Add-outline-colliison-to-enderpearls.patch b/patches/server/0062-Add-outline-colliison-to-enderpearls.patch similarity index 100% rename from patches/server/0067-Add-outline-colliison-to-enderpearls.patch rename to patches/server/0062-Add-outline-colliison-to-enderpearls.patch diff --git a/patches/server/0068-Disable-player-poses-shrinking-collision-box.patch b/patches/server/0063-Disable-player-poses-shrinking-collision-box.patch similarity index 87% rename from patches/server/0068-Disable-player-poses-shrinking-collision-box.patch rename to patches/server/0063-Disable-player-poses-shrinking-collision-box.patch index 7b8c5f6..3c52f3e 100644 --- a/patches/server/0068-Disable-player-poses-shrinking-collision-box.patch +++ b/patches/server/0063-Disable-player-poses-shrinking-collision-box.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Disable player poses shrinking collision box diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 8bed7279377884498fc1c4ed7c44b193bb89c1e7..1967a38277d12e09bf39af271eb6c7ed75fe0218 100644 +index d2babdee314202c5f9795ea21866f6f79cda2eca..5c3bebf932b6f0376711a2bd679062575bff04a3 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -2298,7 +2298,13 @@ public abstract class Player extends LivingEntity { +@@ -2301,7 +2301,13 @@ public abstract class Player extends LivingEntity { @Override public EntityDimensions getDefaultDimensions(Pose pose) { diff --git a/patches/server/0069-Mob-spawner-behaviour.patch b/patches/server/0064-Mob-spawner-behaviour.patch similarity index 100% rename from patches/server/0069-Mob-spawner-behaviour.patch rename to patches/server/0064-Mob-spawner-behaviour.patch diff --git a/patches/server/0070-Use-random-chance-for-crop-growth-instead-of-age.patch b/patches/server/0065-Use-random-chance-for-crop-growth-instead-of-age.patch similarity index 100% rename from patches/server/0070-Use-random-chance-for-crop-growth-instead-of-age.patch rename to patches/server/0065-Use-random-chance-for-crop-growth-instead-of-age.patch diff --git a/patches/server/0071-Protect-block-shapes-against-plugins.patch b/patches/server/0066-Protect-block-shapes-against-plugins.patch similarity index 77% rename from patches/server/0071-Protect-block-shapes-against-plugins.patch rename to patches/server/0066-Protect-block-shapes-against-plugins.patch index 7c898b5..dea998d 100644 --- a/patches/server/0071-Protect-block-shapes-against-plugins.patch +++ b/patches/server/0066-Protect-block-shapes-against-plugins.patch @@ -5,14 +5,14 @@ Subject: [PATCH] Protect block shapes against plugins diff --git a/src/main/java/net/minecraft/world/level/block/CarpetBlock.java b/src/main/java/net/minecraft/world/level/block/CarpetBlock.java -index 0cc3df1f8a7784af812a1519e0a508b67314ce9a..9a267f4cfe10d4145d6a2966b7a2bfb6fe4688c6 100644 +index 0cc3df1f8a7784af812a1519e0a508b67314ce9a..ab606a3ba037223898f1d947083aeeed13afdc4c 100644 --- a/src/main/java/net/minecraft/world/level/block/CarpetBlock.java +++ b/src/main/java/net/minecraft/world/level/block/CarpetBlock.java @@ -14,6 +14,7 @@ import net.minecraft.world.phys.shapes.VoxelShape; public class CarpetBlock extends Block { public static final MapCodec CODEC = simpleCodec(CarpetBlock::new); protected static final VoxelShape SHAPE = Block.box(0.0, 0.0, 0.0, 16.0, 1.0, 16.0); -+ private static final VoxelShape SHAPE_COPY = SHAPE.copy(); // Sakura - protect block shapes ++ private static final VoxelShape SHAPE_COPY = SHAPE.copy(); // Sakura - protect block shapes against plugins @Override public MapCodec codec() { @@ -21,24 +21,24 @@ index 0cc3df1f8a7784af812a1519e0a508b67314ce9a..9a267f4cfe10d4145d6a2966b7a2bfb6 @Override protected VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { - return SHAPE; -+ return SHAPE_COPY; // Sakura - protect block shapes ++ return SHAPE_COPY; // Sakura - protect block shapes against plugins } @Override diff --git a/src/main/java/net/minecraft/world/level/block/LadderBlock.java b/src/main/java/net/minecraft/world/level/block/LadderBlock.java -index 54781ea0771327f93a7cf672bb4b2945700c47e5..e26a86fb8595705bcbb747872c3d8f73a519b42c 100644 +index 54781ea0771327f93a7cf672bb4b2945700c47e5..b03783b02176aad08123acf1fba2cf39daa5a0f2 100644 --- a/src/main/java/net/minecraft/world/level/block/LadderBlock.java +++ b/src/main/java/net/minecraft/world/level/block/LadderBlock.java @@ -28,6 +28,12 @@ public class LadderBlock extends Block implements SimpleWaterloggedBlock { protected static final VoxelShape WEST_AABB = Block.box(13.0, 0.0, 0.0, 16.0, 16.0, 16.0); protected static final VoxelShape SOUTH_AABB = Block.box(0.0, 0.0, 0.0, 16.0, 16.0, 3.0); protected static final VoxelShape NORTH_AABB = Block.box(0.0, 0.0, 13.0, 16.0, 16.0, 16.0); -+ // Sakura start - protect block shapes ++ // Sakura start - protect block shapes against plugins + private static final VoxelShape EAST_AABB_COPY = EAST_AABB.copy(); + private static final VoxelShape WEST_AABB_COPY = WEST_AABB.copy(); + private static final VoxelShape SOUTH_AABB_COPY = SOUTH_AABB.copy(); + private static final VoxelShape NORTH_AABB_COPY = NORTH_AABB.copy(); -+ // Sakura end - protect block shapes ++ // Sakura end - protect block shapes against plugins // Sakura start - physics version api protected static final VoxelShape LEGACY_EAST_AABB = Block.box(0.0, 0.0, 0.0, 2.0, 16.0, 16.0); protected static final VoxelShape LEGACY_WEST_AABB = Block.box(14.0, 0.0, 0.0, 16.0, 16.0, 16.0); @@ -47,7 +47,7 @@ index 54781ea0771327f93a7cf672bb4b2945700c47e5..e26a86fb8595705bcbb747872c3d8f73 switch ((Direction)state.getValue(FACING)) { case NORTH: - return NORTH_AABB; -+ // Sakura start - protect block shapes ++ // Sakura start - protect block shapes against plugins + return NORTH_AABB_COPY; case SOUTH: - return SOUTH_AABB; @@ -59,24 +59,25 @@ index 54781ea0771327f93a7cf672bb4b2945700c47e5..e26a86fb8595705bcbb747872c3d8f73 default: - return EAST_AABB; + return EAST_AABB_COPY; -+ // Sakura end - protect block shapes ++ // Sakura end - protect block shapes against plugins } } diff --git a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java -index 138fd28437fb4923773b8e6ba9bb53c3ce540e0d..ff6776b8c74eedb3414790f7edef8f25fc478d0c 100644 +index 11824d39e72fa003b3a56aa9b8d679fe8e23a1a4..132dd7028384126cef5d1d404adee4fbdec4c31b 100644 --- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java +++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java -@@ -481,6 +481,12 @@ public abstract class VoxelShape { - return new OffsetDoubleList(src, by); +@@ -539,6 +539,13 @@ public abstract class VoxelShape implements ca.spottedleaf.moonrise.patches.coll + return this.isEmpty; // Paper - optimise collisions } - // Paper end - optimise collisions -+ // Sakura start - protect block shapes + ++ // Sakura start - protect block shapes against plugins + public final VoxelShape copy() { + this.cachedToAABBs = null; + return this.move(0.0, 0.0, 0.0); + } -+ // Sakura end - protect block shapes - ++ // Sakura end - protect block shapes against plugins ++ public VoxelShape move(double x, double y, double z) { // Paper start - optimise collisions + if (this.isEmpty) { diff --git a/patches/server/0072-Legacy-player-combat-mechanics.patch b/patches/server/0067-Legacy-player-combat-mechanics.patch similarity index 95% rename from patches/server/0072-Legacy-player-combat-mechanics.patch rename to patches/server/0067-Legacy-player-combat-mechanics.patch index 8e2af00..9f0e405 100644 --- a/patches/server/0072-Legacy-player-combat-mechanics.patch +++ b/patches/server/0067-Legacy-player-combat-mechanics.patch @@ -50,7 +50,7 @@ index 0000000000000000000000000000000000000000..b1c55ef758f61914e6df9b2c8096bce6 + } +} diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 5a7f7de0bcdd3d6ed946f0d3e3100e21ff1cbea9..7d451c2924ab944e453c56a02fb02dd0b81381ef 100644 +index 7acb574bf1ca8de1ecd023e96ef9c0013118091d..72e435537f9b2cb3bd15deba0e5e9c49c8b19d3f 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java @@ -299,6 +299,79 @@ public abstract class LivingEntity extends Entity implements Attackable { @@ -133,10 +133,10 @@ index 5a7f7de0bcdd3d6ed946f0d3e3100e21ff1cbea9..7d451c2924ab944e453c56a02fb02dd0 protected LivingEntity(EntityType type, Level world) { super(type, world); -@@ -2173,7 +2246,16 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -2210,7 +2283,16 @@ public abstract class LivingEntity extends Entity implements Attackable { protected float getDamageAfterArmorAbsorb(DamageSource source, float amount) { if (!source.is(DamageTypeTags.BYPASSES_ARMOR)) { - // this.hurtArmor(damagesource, f); // CraftBukkit - Moved into actuallyHurt(DamageSource, float) + // this.hurtArmor(damagesource, f); // CraftBukkit - actuallyHurt(DamageSource, float, EntityDamageEvent) for handle damage + // Sakura start - legacy combat mechanics + if (!this.level().sakuraConfig().players.combat.legacyCombatMechanics) { amount = CombatRules.getDamageAfterAbsorb(this, amount, source, (float) this.getArmorValue(), (float) this.getAttributeValue(Attributes.ARMOR_TOUGHNESS)); @@ -150,7 +150,7 @@ index 5a7f7de0bcdd3d6ed946f0d3e3100e21ff1cbea9..7d451c2924ab944e453c56a02fb02dd0 } return amount; -@@ -3297,6 +3379,12 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3352,6 +3434,12 @@ public abstract class LivingEntity extends Entity implements Attackable { }); } @@ -164,10 +164,10 @@ index 5a7f7de0bcdd3d6ed946f0d3e3100e21ff1cbea9..7d451c2924ab944e453c56a02fb02dd0 } diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 1967a38277d12e09bf39af271eb6c7ed75fe0218..57f891099e7b66e75e9d8eb0f961c4395ed875ff 100644 +index 5c3bebf932b6f0376711a2bd679062575bff04a3..d4485ecff16969e57241d3841efbe1745c0814aa 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -1277,14 +1277,20 @@ public abstract class Player extends LivingEntity { +@@ -1278,14 +1278,20 @@ public abstract class Player extends LivingEntity { if (playerAttackEntityEvent.callEvent() && willAttack) { // Logic moved to willAttack local variable. { // Paper end - PlayerAttackEntityEvent @@ -189,7 +189,7 @@ index 1967a38277d12e09bf39af271eb6c7ed75fe0218..57f891099e7b66e75e9d8eb0f961c439 // this.resetAttackStrengthTicker(); // CraftBukkit - Moved to EntityLiving to reset the cooldown after the damage is dealt if (target.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE) && target instanceof Projectile) { Projectile iprojectile = (Projectile) target; -@@ -1312,7 +1318,7 @@ public abstract class Player extends LivingEntity { +@@ -1313,7 +1319,7 @@ public abstract class Player extends LivingEntity { } f += itemstack.getItem().getAttackDamageBonus(target, f, damagesource); diff --git a/patches/server/0073-Allow-disabling-sweep-attacks.patch b/patches/server/0068-Allow-disabling-sweep-attacks.patch similarity index 86% rename from patches/server/0073-Allow-disabling-sweep-attacks.patch rename to patches/server/0068-Allow-disabling-sweep-attacks.patch index 0316b8e..3211b8b 100644 --- a/patches/server/0073-Allow-disabling-sweep-attacks.patch +++ b/patches/server/0068-Allow-disabling-sweep-attacks.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Allow disabling sweep attacks diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 57f891099e7b66e75e9d8eb0f961c4395ed875ff..80ffde5ebebaaee1cd8c97cb238126ffee1c7958 100644 +index d4485ecff16969e57241d3841efbe1745c0814aa..a5417a76897395129baaf0eea8ed0a0cd60d9335 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -1385,7 +1385,7 @@ public abstract class Player extends LivingEntity { +@@ -1386,7 +1386,7 @@ public abstract class Player extends LivingEntity { LivingEntity entityliving2; diff --git a/patches/server/0074-Change-shields-to-reduce-damage.patch b/patches/server/0069-Change-shields-to-reduce-damage.patch similarity index 62% rename from patches/server/0074-Change-shields-to-reduce-damage.patch rename to patches/server/0069-Change-shields-to-reduce-damage.patch index 16aa9c5..c13e1d6 100644 --- a/patches/server/0074-Change-shields-to-reduce-damage.patch +++ b/patches/server/0069-Change-shields-to-reduce-damage.patch @@ -5,20 +5,20 @@ Subject: [PATCH] Change shields to reduce damage diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 7d451c2924ab944e453c56a02fb02dd0b81381ef..3f79e5a83bb85af6ff1a256736394622384cf5b1 100644 +index 72e435537f9b2cb3bd15deba0e5e9c49c8b19d3f..2f7e51b1509d3db78ed8dbd073e2f9ded820390b 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -2330,7 +2330,13 @@ public abstract class LivingEntity extends Entity implements Attackable { - Function blocking = new Function() { - @Override - public Double apply(Double f) { +@@ -2377,7 +2377,13 @@ public abstract class LivingEntity extends Entity implements Attackable { + Function blocking = new Function() { + @Override + public Double apply(Double f) { + // Sakura start - shield damage reduction + if (!level().sakuraConfig().players.combat.shieldDamageReduction || damagesource.getDirectEntity() instanceof AbstractArrow) { - return -((LivingEntity.this.isDamageSourceBlocked(damagesource)) ? f : 0.0); + return -((LivingEntity.this.isDamageSourceBlocked(damagesource)) ? f : 0.0); + } else { + return -(LivingEntity.this.isBlocking() ? f * 0.5 : 0.0); + } + // Sakura end - shield damage reduction - } - }; - float blockingModifier = blocking.apply((double) f).floatValue(); + } + }; + float blockingModifier = blocking.apply((double) f).floatValue(); diff --git a/patches/server/0075-Old-enchanted-golden-apples.patch b/patches/server/0070-Old-enchanted-golden-apples.patch similarity index 93% rename from patches/server/0075-Old-enchanted-golden-apples.patch rename to patches/server/0070-Old-enchanted-golden-apples.patch index 3f2dd81..8e1dd9e 100644 --- a/patches/server/0075-Old-enchanted-golden-apples.patch +++ b/patches/server/0070-Old-enchanted-golden-apples.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Old enchanted golden apples diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 3f79e5a83bb85af6ff1a256736394622384cf5b1..5842543211e415aa1754d7002c9abb7ea65a56e6 100644 +index 2f7e51b1509d3db78ed8dbd073e2f9ded820390b..a4b59cb05111aed52758061c314aafc56d75dc2d 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -4498,6 +4498,11 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -4571,6 +4571,11 @@ public abstract class LivingEntity extends Entity implements Attackable { public final ItemStack eat(Level world, ItemStack stack) { FoodProperties foodinfo = (FoodProperties) stack.get(DataComponents.FOOD); diff --git a/patches/server/0076-Configure-fast-health-regen.patch b/patches/server/0071-Configure-fast-health-regen.patch similarity index 100% rename from patches/server/0076-Configure-fast-health-regen.patch rename to patches/server/0071-Configure-fast-health-regen.patch diff --git a/patches/server/0077-Add-option-for-fishing-hooks-pulling-entities.patch b/patches/server/0072-Add-option-for-fishing-hooks-pulling-entities.patch similarity index 100% rename from patches/server/0077-Add-option-for-fishing-hooks-pulling-entities.patch rename to patches/server/0072-Add-option-for-fishing-hooks-pulling-entities.patch diff --git a/patches/server/0078-Old-combat-sounds-and-particle-effects.patch b/patches/server/0073-Old-combat-sounds-and-particle-effects.patch similarity index 90% rename from patches/server/0078-Old-combat-sounds-and-particle-effects.patch rename to patches/server/0073-Old-combat-sounds-and-particle-effects.patch index cc48a1f..fd6c2c4 100644 --- a/patches/server/0078-Old-combat-sounds-and-particle-effects.patch +++ b/patches/server/0073-Old-combat-sounds-and-particle-effects.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Old combat sounds and particle effects diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 80ffde5ebebaaee1cd8c97cb238126ffee1c7958..baa35f5ebe7953f0b3cae0381b0391e4d873b562 100644 +index a5417a76897395129baaf0eea8ed0a0cd60d9335..2c88d4956894e19e773f569679396d5c6058781b 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -1493,7 +1493,7 @@ public abstract class Player extends LivingEntity { +@@ -1496,7 +1496,7 @@ public abstract class Player extends LivingEntity { float f8 = f4 - ((LivingEntity) target).getHealth(); this.awardStat(Stats.DAMAGE_DEALT, Math.round(f8 * 10.0F)); @@ -17,7 +17,7 @@ index 80ffde5ebebaaee1cd8c97cb238126ffee1c7958..baa35f5ebe7953f0b3cae0381b0391e4 int i = (int) ((double) f8 * 0.5D); ((ServerLevel) this.level()).sendParticles(ParticleTypes.DAMAGE_INDICATOR, target.getX(), target.getY(0.5D), target.getZ(), i, 0.1D, 0.0D, 0.1D, 0.2D); -@@ -1912,6 +1912,7 @@ public abstract class Player extends LivingEntity { +@@ -1915,6 +1915,7 @@ public abstract class Player extends LivingEntity { } // Paper start - send while respecting visibility private static void sendSoundEffect(Player fromEntity, double x, double y, double z, SoundEvent soundEffect, SoundSource soundCategory, float volume, float pitch) { diff --git a/patches/server/0079-Protect-scaffolding-from-creepers.patch b/patches/server/0074-Protect-scaffolding-from-creepers.patch similarity index 90% rename from patches/server/0079-Protect-scaffolding-from-creepers.patch rename to patches/server/0074-Protect-scaffolding-from-creepers.patch index 6376dcd..dff27d1 100644 --- a/patches/server/0079-Protect-scaffolding-from-creepers.patch +++ b/patches/server/0074-Protect-scaffolding-from-creepers.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Protect scaffolding from creepers diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 94df0527699462e29fb76f68194386c50f1f7e04..e4e59ee1f59b175205f2bd31d2abe542c5123123 100644 +index a7d7ccd8277f1e710808710faf06ebe37ee71dc4..0ad8a54f6f3cabc8d5dc5891339a49401e1e4605 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -287,6 +287,11 @@ public class Explosion { +@@ -166,6 +166,11 @@ public class Explosion { Block block = blockState.getBlock(); me.samsuik.sakura.explosion.durable.DurableMaterial material = this.level.localConfig().config(pos).durableMaterials.get(block); diff --git a/patches/server/0080-Entity-tracking-range-modifier.patch b/patches/server/0075-Entity-tracking-range-modifier.patch similarity index 87% rename from patches/server/0080-Entity-tracking-range-modifier.patch rename to patches/server/0075-Entity-tracking-range-modifier.patch index daedb3c..b047044 100644 --- a/patches/server/0080-Entity-tracking-range-modifier.patch +++ b/patches/server/0075-Entity-tracking-range-modifier.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Entity tracking range modifier diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index d779e010ba7c0e99aa3b0ce8462b7e3fa7cced8c..c7e994953c68faffce60ba3c91be1800c8d47020 100644 +index 6855e10e33ec09645dd770f4b9337cc7ca4f4932..5e76cc1e5a5597d84605e249b8b79d3e27a44981 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1319,7 +1319,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1298,7 +1298,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider double vec3d_dz = player.getZ() - this.entity.getZ(); // Paper end - remove allocation of Vec3D here int i = ChunkMap.this.getPlayerViewDistance(player); @@ -21,7 +21,7 @@ index d779e010ba7c0e99aa3b0ce8462b7e3fa7cced8c..c7e994953c68faffce60ba3c91be1800 double d2 = d0 * d0; boolean flag = d1 <= d2 && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z); diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 98e052fecf852d9426671615d9c21f8cc94fb349..229522e8d54d93a676c6e7f65d975ce6c11b66ff 100644 +index dd57c78168a63c77c123abaa6e596632afd390a8..95eebc70eb5d58c1a22634a11db86de53af0d333 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -332,6 +332,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple @@ -33,10 +33,10 @@ index 98e052fecf852d9426671615d9c21f8cc94fb349..229522e8d54d93a676c6e7f65d975ce6 public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile, ClientInformation clientOptions) { super(world, world.getSharedSpawnPos(), world.getSharedSpawnAngle(), profile); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index a280b9c9c1451f72758b30b9c514c889c846c88c..49f16d135b7df71e6976ff720bb2e243fb00aae8 100644 +index 6fb40ff3198e72314ade642102e11b7e007782e2..498c8751e7dbc0b7c17ac10df4e4e6afae0dc9ed 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -3017,6 +3017,18 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -3018,6 +3018,18 @@ public class CraftPlayer extends CraftHumanEntity implements Player { return (this.getHandle().requestedViewDistance() == 0) ? Bukkit.getViewDistance() : this.getHandle().requestedViewDistance(); }