diff --git a/build.gradle.kts b/build.gradle.kts index 7ecacc3..bc9cf00 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ repositories { } dependencies { - remapper("net.fabricmc:tiny-remapper:0.10.2:fat") + remapper("net.fabricmc:tiny-remapper:0.10.3:fat") decompiler("org.vineflower:vineflower:1.10.1") paperclip("io.papermc:paperclip:3.0.3") } diff --git a/gradle.properties b/gradle.properties index d2c275b..02c6fa7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ group=me.samsuik.sakura -version=1.20.6-R0.1-SNAPSHOT +version=1.21-R0.1-SNAPSHOT -mcVersion=1.20.6 -paperRef=79e2cb620ef03539390d97940824b38b707918f5 +mcVersion=1.21 +paperRef=dd49fba8c534d48c3693a751075ecb5836a9d458 org.gradle.jvmargs=-Xmx2G diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23..a441313 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/patches/api/0001-Customise-Version-Command.patch b/patches/api/0001-Customise-Version-Command.patch index c865e6a..f74ef1f 100644 --- a/patches/api/0001-Customise-Version-Command.patch +++ b/patches/api/0001-Customise-Version-Command.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Customise Version Command diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index 47039514503d99e84cb99f4941707a7726286516..3e426dc442fd7c82cfd89c4c46290f0c857eddf9 100644 +index b9b751ea0d11381e846d5f35f39f285c075c171a..d629598719298aa15666b559490a0f6db92b2483 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java -@@ -124,6 +124,20 @@ public final class Bukkit { +@@ -125,6 +125,20 @@ public final class Bukkit { // Paper end } diff --git a/patches/api/0003-Visibility-API.patch b/patches/api/0003-Visibility-API.patch index 41005ce..ace32c1 100644 --- a/patches/api/0003-Visibility-API.patch +++ b/patches/api/0003-Visibility-API.patch @@ -209,10 +209,10 @@ index 0000000000000000000000000000000000000000..3df11f07ce533b8b911ec423be850374 + +} diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 6c327a07bf8a6aa11a2d7dad12b2830acc539484..078c379e17050b747163b08d7f1fcc2290a6f513 100644 +index 7c56182acaf827f4b1a986a61cea8e9960604c98..4c2e82a6421722d9a990ff4a216cedc159c7e579 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java -@@ -58,6 +58,15 @@ import org.jetbrains.annotations.Nullable; +@@ -59,6 +59,15 @@ import org.jetbrains.annotations.Nullable; */ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified, net.kyori.adventure.bossbar.BossBarViewer, com.destroystokyo.paper.network.NetworkClient { // Paper diff --git a/patches/server/0003-Sakura-Configuration-Files.patch b/patches/server/0003-Sakura-Configuration-Files.patch index 16221cd..dfcfa90 100644 --- a/patches/server/0003-Sakura-Configuration-Files.patch +++ b/patches/server/0003-Sakura-Configuration-Files.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Sakura Configuration Files diff --git a/src/main/java/io/papermc/paper/configuration/Configurations.java b/src/main/java/io/papermc/paper/configuration/Configurations.java -index 96142deb42700f888ea08689ab62c27ef2b881fd..92bbebf2d09225ac67685bec1a724f1d3ae25fe9 100644 +index 87e5f614ba988547a827486740db217e28585773..0375c9eb296ba7e8ed64e8d00dbc13a581dd4f90 100644 --- a/src/main/java/io/papermc/paper/configuration/Configurations.java +++ b/src/main/java/io/papermc/paper/configuration/Configurations.java @@ -96,7 +96,7 @@ public abstract class Configurations { @@ -1040,10 +1040,10 @@ 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 3751c2a077bd13bac330b93c6efc2a640a17f4f2..420e375f7dac5c001278c0cb15a98aa39d609aec 100644 +index 68f60e77e0bfd42b6419491c1d59b6432974216b..1af29c2d6c591b11fd509821d54e6ea781c0929d 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -310,6 +310,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop getTypeKey(); - -- 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, java.util.concurrent.Executor executor) { // Paper - create paper world config; Async-Anti-Xray: Pass executor -+ 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; Async-Anti-Xray: Pass executor +- 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, 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 this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config + this.sakuraConfig = sakuraWorldConfigCreator.get(); // Sakura - sakura configuration files @@ -1121,10 +1121,10 @@ index 14281a4e72f49dc4eb2ca3da8479c1f81a3a175d..f2cd3046fdd280b03586e645216ccef1 this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 5db08432b6afd3639688830e717f40ceaf599248..4ea6a5af351d5bf40154571a2e8d6af26a1bcb58 100644 +index caf6ff33b42472d30f28629470e12889f50490cc..1e2dcd25fc9e605481c809fcb3121b4197640957 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1056,6 +1056,7 @@ public final class CraftServer implements Server { +@@ -1059,6 +1059,7 @@ public final class CraftServer implements Server { org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot this.console.paperConfigurations.reloadConfigs(this.console); @@ -1132,7 +1132,7 @@ index 5db08432b6afd3639688830e717f40ceaf599248..4ea6a5af351d5bf40154571a2e8d6af2 for (ServerLevel world : this.console.getAllLevels()) { // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters, config.spawnAnimals); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) -@@ -1087,6 +1088,7 @@ public final class CraftServer implements Server { +@@ -1090,6 +1091,7 @@ public final class CraftServer implements Server { this.reloadData(); org.spigotmc.SpigotConfig.registerCommands(); // Spigot io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper @@ -1141,7 +1141,7 @@ index 5db08432b6afd3639688830e717f40ceaf599248..4ea6a5af351d5bf40154571a2e8d6af2 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 b86ba59158964f73abd6622341a9acb98a33fa44..163be3f86f3fabce0e6f10b83ac6db07596dac5d 100644 +index 6e1f92b45504cef0c4dcbbebf3df339ef4cad0a1..c361c408b0382ac7bffa960ba56c37157544c25f 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/0004-Paper-Unapplied-Use-distance-map-to-optimise-entity-.patch b/patches/server/0004-Paper-Unapplied-Use-distance-map-to-optimise-entity-.patch new file mode 100644 index 0000000..27b01b3 --- /dev/null +++ b/patches/server/0004-Paper-Unapplied-Use-distance-map-to-optimise-entity-.patch @@ -0,0 +1,340 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +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 new file mode 100644 index 0000000..4af6995 --- /dev/null +++ b/patches/server/0005-Paper-Unapplied-Collision-optimisations.patch @@ -0,0 +1,4723 @@ +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/0006-Paper-Unapplied-Actually-optimise-explosions.patch b/patches/server/0006-Paper-Unapplied-Actually-optimise-explosions.patch new file mode 100644 index 0000000..574eae9 --- /dev/null +++ b/patches/server/0006-Paper-Unapplied-Actually-optimise-explosions.patch @@ -0,0 +1,521 @@ +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/0004-Local-Config-and-Value-Storage-API.patch b/patches/server/0007-Local-Config-and-Value-Storage-API.patch similarity index 95% rename from patches/server/0004-Local-Config-and-Value-Storage-API.patch rename to patches/server/0007-Local-Config-and-Value-Storage-API.patch index 168936e..14deb42 100644 --- a/patches/server/0004-Local-Config-and-Value-Storage-API.patch +++ b/patches/server/0007-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 48648f73925c79282df19158ef52678d10608f44..9677324fc698789e9cd880705985d6112c6bd9bb 100644 +index 1af29c2d6c591b11fd509821d54e6ea781c0929d..3f4997b642c9de1982a500d3a6ff07a9ca59da10 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1798,6 +1798,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop entity instanceof Player); + } + // Paper end - rewrite chunk system + // Sakura start - visibility api and command + public boolean isPrimedTNT; + public boolean isFallingBlock; @@ -496,10 +496,10 @@ index 7ef9f67d27cc240191dd5d07e8dcf5fbdebe1049..c33e325d7eb4584986de338f966a79e8 public Entity(EntityType type, Level world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); 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 d504d10fbe45dfe3f2f3d08d2473df6cd18f6dcf..28b8efdd224e4dbb32f3eac1c9e2c512e2695dc8 100644 +index b83be9bbb9f348da83c0fd1ecc7f65c8a58b45b9..1bfdebe6c1057ee19fb96d8f0f571c9d20d14cc6 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -@@ -74,6 +74,7 @@ public class FallingBlockEntity extends Entity { +@@ -78,6 +78,7 @@ public class FallingBlockEntity extends Entity { this.blockState = Blocks.SAND.defaultBlockState(); this.dropItem = true; this.fallDamageMax = 40; @@ -508,10 +508,10 @@ index d504d10fbe45dfe3f2f3d08d2473df6cd18f6dcf..28b8efdd224e4dbb32f3eac1c9e2c512 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 f1f352ec0e51f5db59254841a06c176c5a876fc9..dace8dc0c051ce9355d878154466ee3a548a3832 100644 +index 42bd2d9a1528b6210e4dfb56233062fd97c9743b..28a15f612239614c6c56974da5855ce3bfd7c89e 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -37,6 +37,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -57,6 +57,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { public PrimedTnt(EntityType type, Level world) { super(type, world); this.blocksBuilding = true; @@ -520,23 +520,22 @@ index f1f352ec0e51f5db59254841a06c176c5a876fc9..dace8dc0c051ce9355d878154466ee3a 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 1059530bb6f817352c8304f6a1544aeb5ac14b30..5a43957309019bec27b9624e5268d6a8c50c165d 100644 +index 439f1e69cc330aa590311eb4e308e539815ad0d5..05a4b69fbaf36269bea3a050022a54d28211c285 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -217,6 +217,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - - public abstract ResourceKey getTypeKey(); - +@@ -269,6 +269,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + return this.getChunkSource().getChunk(chunkX, chunkZ, leastStatus, false); + } + // Paper end - rewrite chunk system + 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; Async-Anti-Xray: Pass executor + + 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 - this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 7e6116963d835d4606ef3d93b69d3e44b61288e1..c0109fd2db0724418d55e68a27c363c8c24ff8c1 100644 +index 7eea190ce8a62960ecc42ff56a4ef71b754184fb..a280b9c9c1451f72758b30b9c514c889c846c88c 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -543,6 +543,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -545,6 +545,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player { this.getHandle().displayName = name == null ? this.getName() : name; } diff --git a/patches/server/0006-Optimise-rayTracing.patch b/patches/server/0009-Optimise-rayTracing.patch similarity index 96% rename from patches/server/0006-Optimise-rayTracing.patch rename to patches/server/0009-Optimise-rayTracing.patch index 0ad3cfb..8ba37b7 100644 --- a/patches/server/0006-Optimise-rayTracing.patch +++ b/patches/server/0009-Optimise-rayTracing.patch @@ -5,10 +5,10 @@ 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 dc88014c4d9f172cc54e5d77b488128f9ffbc73d..9ea24dc18f70e8e3c8998f6c2b5eb3ac885df54f 100644 +index 7aa9ddb1d61ffb7da3f867e5a5bd04e3432b5621..3d5131e51fb924778c392267699fe2c11bf2f40f 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -296,7 +296,7 @@ public class Explosion { +@@ -297,7 +297,7 @@ public class Explosion { } } @@ -18,7 +18,7 @@ index dc88014c4d9f172cc54e5d77b488128f9ffbc73d..9ea24dc18f70e8e3c8998f6c2b5eb3ac } } 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 e6b17f32f2b6930739a98c6139442383c1847add..24c1caa060e7c41eac81c19f873ac9273c6b84c2 100644 +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 { diff --git a/patches/server/0007-Reduce-deltaMovement-Allocations.patch b/patches/server/0010-Reduce-deltaMovement-Allocations.patch similarity index 75% rename from patches/server/0007-Reduce-deltaMovement-Allocations.patch rename to patches/server/0010-Reduce-deltaMovement-Allocations.patch index c0b7961..7193cdb 100644 --- a/patches/server/0007-Reduce-deltaMovement-Allocations.patch +++ b/patches/server/0010-Reduce-deltaMovement-Allocations.patch @@ -5,10 +5,10 @@ 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 c33e325d7eb4584986de338f966a79e868ef300e..29e609be885944dbb4868e34e7eb83b8a136619c 100644 +index 198ad71c934dd48f8a55927a3f15b6d8fa398903..3fcc7eb45a1729a31697788870c7375e9c1f7303 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1294,7 +1294,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1253,7 +1253,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.tryCheckInsideBlocks(); float f = this.getBlockSpeedFactor(); @@ -17,7 +17,7 @@ index c33e325d7eb4584986de338f966a79e868ef300e..29e609be885944dbb4868e34e7eb83b8 // Paper start - remove expensive streams from here boolean noneMatch = true; AABB fireSearchBox = this.getBoundingBox().deflate(1.0E-6D); -@@ -1831,7 +1831,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1871,7 +1871,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess double d0 = this.getGravity(); if (d0 != 0.0D) { @@ -26,7 +26,7 @@ index c33e325d7eb4584986de338f966a79e868ef300e..29e609be885944dbb4868e34e7eb83b8 } } -@@ -2123,6 +2123,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2171,6 +2171,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,26 +34,26 @@ index c33e325d7eb4584986de338f966a79e868ef300e..29e609be885944dbb4868e34e7eb83b8 this.deltaMovement = Vec3.ZERO; } else { this.preserveMotion = false; -@@ -3525,29 +3526,33 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -3600,29 +3601,33 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public void onAboveBubbleCol(boolean drag) { - Vec3 vec3d = this.getDeltaMovement(); -+ // Sakura start - reduce movement allocations ++ // Sakura start - remove bubble column allocations + // Vec3 vec3d = this.getDeltaMovement(); + this.syncDeltaMovement(); double d0; if (drag) { - d0 = Math.max(-0.9D, vec3d.y - 0.03D); -+ d0 = Math.max(-0.9D, movementY - 0.03D); ++ d0 = Math.max(-0.9D, this.movementY - 0.03D); } else { - d0 = Math.min(1.8D, vec3d.y + 0.1D); -+ d0 = Math.min(1.8D, movementY + 0.1D); ++ d0 = Math.min(1.8D, this.movementY + 0.1D); } - this.setDeltaMovement(vec3d.x, d0, vec3d.z); -+ this.setDeltaMovement(movementX, d0, movementZ); ++ this.setDeltaMovement(this.movementX, d0, this.movementZ); } public void onInsideBubbleColumn(boolean drag) { @@ -64,19 +64,19 @@ index c33e325d7eb4584986de338f966a79e868ef300e..29e609be885944dbb4868e34e7eb83b8 if (drag) { - d0 = Math.max(-0.3D, vec3d.y - 0.03D); -+ d0 = Math.max(-0.3D, movementY - 0.03D); ++ d0 = Math.max(-0.3D, this.movementY - 0.03D); } else { - d0 = Math.min(0.7D, vec3d.y + 0.06D); -+ d0 = Math.min(0.7D, movementY + 0.06D); ++ d0 = Math.min(0.7D, this.movementY + 0.06D); } - this.setDeltaMovement(vec3d.x, d0, vec3d.z); -+ this.setDeltaMovement(movementX, d0, movementZ); -+ // Sakura end ++ this.setDeltaMovement(this.movementX, d0, this.movementZ); ++ // Sakura end - remove bubble column allocations this.resetFallDistance(); } -@@ -4546,16 +4551,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4560,16 +4565,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess vec3d = vec3d.normalize(); } @@ -85,7 +85,7 @@ index c33e325d7eb4584986de338f966a79e868ef300e..29e609be885944dbb4868e34e7eb83b8 + // Vec3 vec3d2 = this.getDeltaMovement(); + this.syncDeltaMovement(); - vec3d = vec3d.scale(speed * 1.0D); + vec3d = vec3d.scale(speed); double d3 = 0.003D; - if (Math.abs(vec3d2.x) < 0.003D && Math.abs(vec3d2.z) < 0.003D && vec3d.length() < 0.0045000000000000005D) { @@ -99,7 +99,7 @@ index c33e325d7eb4584986de338f966a79e868ef300e..29e609be885944dbb4868e34e7eb83b8 } this.fluidHeight.put(tag, d1); -@@ -4626,11 +4634,53 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4640,11 +4648,53 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return this.chunkPosition; } @@ -109,68 +109,68 @@ index c33e325d7eb4584986de338f966a79e868ef300e..29e609be885944dbb4868e34e7eb83b8 + private double movementZ; + private boolean movementDirty; + -+ public void addDeltaMovement(double x, double y, double z) { -+ syncDeltaMovement(); -+ movementX += x; -+ movementY += y; -+ movementZ += z; ++ public final void addDeltaMovement(double x, double y, double z) { ++ this.syncDeltaMovement(); ++ this.movementX += x; ++ this.movementY += y; ++ this.movementZ += z; + } + -+ public void scaleDeltaMovement(double n) { -+ syncDeltaMovement(); -+ movementX *= n; -+ movementY *= n; -+ movementZ *= n; ++ public final void scaleDeltaMovement(double n) { ++ this.syncDeltaMovement(); ++ this.movementX *= n; ++ this.movementY *= n; ++ this.movementZ *= n; + } + -+ public void multiplyDeltaMovement(double x, double y, double z) { -+ syncDeltaMovement(); -+ movementX *= x; -+ movementY *= y; -+ movementZ *= z; ++ public final void multiplyDeltaMovement(double x, double y, double z) { ++ this.syncDeltaMovement(); ++ this.movementX *= x; ++ this.movementY *= y; ++ this.movementZ *= z; + } + + private void updateDeltaMovement() { -+ if (movementDirty) { -+ deltaMovement = new Vec3(movementX, movementY, movementZ); -+ movementDirty = false; ++ if (this.movementDirty) { ++ this.deltaMovement = new Vec3(this.movementX, this.movementY, this.movementZ); ++ this.movementDirty = false; + } + } + + private void syncDeltaMovement() { -+ if (!movementDirty) { -+ setDeltaMovement(deltaMovement.x, deltaMovement.y, deltaMovement.z); ++ if (!this.movementDirty) { ++ this.setDeltaMovement(this.deltaMovement.x, this.deltaMovement.y, this.deltaMovement.z); + } + } + public Vec3 getDeltaMovement() { -+ updateDeltaMovement(); ++ this.updateDeltaMovement(); return this.deltaMovement; } public void setDeltaMovement(Vec3 velocity) { -+ movementDirty = false; - synchronized (this.posLock) { // Paper ++ this.movementDirty = false; this.deltaMovement = velocity; - } // Paper -@@ -4641,7 +4691,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + +@@ -4653,7 +4703,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public void setDeltaMovement(double x, double y, double z) { - this.setDeltaMovement(new Vec3(x, y, z)); -+ movementX = x; -+ movementY = y; -+ movementZ = z; -+ movementDirty = true; -+ // Sakura end ++ this.movementX = x; ++ this.movementY = y; ++ this.movementZ = z; ++ this.movementDirty = true; ++ // Sakura end - reduce deltamovement allocations } public final int getBlockX() { 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 28b8efdd224e4dbb32f3eac1c9e2c512e2695dc8..7da6420aed597ee1bf544059c79a063512adcd40 100644 +index 1bfdebe6c1057ee19fb96d8f0f571c9d20d14cc6..0b84c21749d942851d5126b795e4b609070ed31f 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -@@ -183,7 +183,7 @@ public class FallingBlockEntity extends Entity { +@@ -188,7 +188,7 @@ public class FallingBlockEntity extends Entity { } else { BlockState iblockdata = this.level().getBlockState(blockposition); @@ -179,7 +179,7 @@ index 28b8efdd224e4dbb32f3eac1c9e2c512e2695dc8..7da6420aed597ee1bf544059c79a0635 if (!iblockdata.is(Blocks.MOVING_PISTON)) { if (!this.cancelDrop) { boolean flag2 = iblockdata.canBeReplaced((BlockPlaceContext) (new DirectionalPlaceContext(this.level(), blockposition, Direction.DOWN, ItemStack.EMPTY, Direction.UP))); -@@ -250,7 +250,7 @@ public class FallingBlockEntity extends Entity { +@@ -255,7 +255,7 @@ public class FallingBlockEntity extends Entity { } } @@ -189,10 +189,10 @@ index 28b8efdd224e4dbb32f3eac1c9e2c512e2695dc8..7da6420aed597ee1bf544059c79a0635 } 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 dace8dc0c051ce9355d878154466ee3a548a3832..fda88328472130df8317a84fc726a73c35850c3a 100644 +index 28a15f612239614c6c56974da5855ce3bfd7c89e..d14128dd5d63e93a3d4aec76b0ee8d0e7d786687 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -85,9 +85,9 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -106,9 +106,9 @@ public class PrimedTnt extends Entity implements TraceableEntity { return; } // Paper end - Configurable TNT height nerf @@ -205,10 +205,10 @@ index dace8dc0c051ce9355d878154466ee3a548a3832..fda88328472130df8317a84fc726a73c 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 37896ccb5eea0ec4e301ca97092c8ccef3aa1da5..85c0855eaf20013b60ed643052b783f7f6e30584 100644 +index 3d5131e51fb924778c392267699fe2c11bf2f40f..3a35dcdfd639240363891f412beeef47b8b41e06 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -618,17 +618,23 @@ public class Explosion { +@@ -619,17 +619,23 @@ public class Explosion { d8 *= d13; d9 *= d13; d10 *= d13; @@ -235,10 +235,10 @@ index 37896ccb5eea0ec4e301ca97092c8ccef3aa1da5..85c0855eaf20013b60ed643052b783f7 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 cf8b8c8efd1c9c81eb5f02d75bd75875eb66771f..30e23f67a1a816dfb4f32cfe45bc8dd40938317a 100644 +index 2c356d2c40fe52672f108b2dbb89b7a27616e4d1..472f64bb1fc2ebf7e89e4e90d780732de3c85aef 100644 --- a/src/main/java/net/minecraft/world/level/block/Block.java +++ b/src/main/java/net/minecraft/world/level/block/Block.java -@@ -461,7 +461,7 @@ public class Block extends BlockBehaviour implements ItemLike { +@@ -460,7 +460,7 @@ public class Block extends BlockBehaviour implements ItemLike { } public void updateEntityAfterFallOn(BlockGetter world, Entity entity) { diff --git a/patches/server/0008-Optional-Force-Position-Updates.patch b/patches/server/0011-Optional-Force-Position-Updates.patch similarity index 86% rename from patches/server/0008-Optional-Force-Position-Updates.patch rename to patches/server/0011-Optional-Force-Position-Updates.patch index 1efca73..73bf0ac 100644 --- a/patches/server/0008-Optional-Force-Position-Updates.patch +++ b/patches/server/0011-Optional-Force-Position-Updates.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Optional Force Position Updates 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 7890f84d7a69e2e6820ef0daa35f898534f7372f..5aa02f28b6be60688e340099b1637fb0ebfe7aa9 100644 +index d14128dd5d63e93a3d4aec76b0ee8d0e7d786687..2eb69c7d7843a7b58479049633b838212d003c10 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -108,6 +108,14 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -129,6 +129,14 @@ public class PrimedTnt extends Entity implements TraceableEntity { } } diff --git a/patches/server/0009-Load-Chunks-on-Movement.patch b/patches/server/0012-Load-Chunks-on-Movement.patch similarity index 76% rename from patches/server/0009-Load-Chunks-on-Movement.patch rename to patches/server/0012-Load-Chunks-on-Movement.patch index d3991ba..53b7294 100644 --- a/patches/server/0009-Load-Chunks-on-Movement.patch +++ b/patches/server/0012-Load-Chunks-on-Movement.patch @@ -5,7 +5,7 @@ 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 ee8e9c0e3690e78f3cc621ddfca89ea4256d4803..299237a0c828e48425cc35a14d366020c78daefb 100644 +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 { @@ -39,22 +39,22 @@ index ee8e9c0e3690e78f3cc621ddfca89ea4256d4803..299237a0c828e48425cc35a14d366020 if (chunk == null) { if ((collisionFlags & COLLISION_FLAG_COLLIDE_WITH_UNLOADED_CHUNKS) != 0) { diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java -index 658e63ebde81dc14c8ab5850fb246dc0aab25dea..f1ff1a67fee37ee7b241ceaa164fa4ee64d3767b 100644 +index f56e5c0f53f9b52a9247b9be9265b949494fc924..d0fc85480cc88aa2d04a03b0d3637fee41f003a4 100644 --- a/src/main/java/net/minecraft/server/level/TicketType.java +++ b/src/main/java/net/minecraft/server/level/TicketType.java -@@ -37,6 +37,7 @@ public class TicketType { - public static final TicketType NON_FULL_SYNC_LOAD = create("non_full_sync_load", Long::compareTo); - public static final TicketType DELAY_UNLOAD = create("delay_unload", Comparator.comparingLong(ChunkPos::toLong), 1); - // Paper end - rewrite chunk system +@@ -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 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 a2104fae2b288f4cbd9e810d73bf986be0b7b969..88d01e59869f8e6c528fb3ae1485cd86a23cb383 100644 +index 3fcc7eb45a1729a31697788870c7375e9c1f7303..117f36743e1fa8a468528d44f0e16e6f7283880e 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -572,6 +572,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -566,6 +566,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess public boolean isPrimedTNT; public boolean isFallingBlock; // Sakura end - visibility api and command @@ -74,21 +74,21 @@ index a2104fae2b288f4cbd9e810d73bf986be0b7b969..88d01e59869f8e6c528fb3ae1485cd86 public Entity(EntityType type, Level world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); -@@ -1555,7 +1568,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1515,7 +1528,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 - null, null + 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 + null, null ); -@@ -4957,12 +4970,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4969,12 +4982,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() || !this.hasAnyPlayerPassengers()); // Paper - rewrite chunk system - it should check if the entity has ANY player passengers -+ return this.removalReason != null && !this.removalReason.shouldSave() ? false : (this.loadChunks || this.isPassenger() ? false : !this.isVehicle() || !this.hasAnyPlayerPassengers()); // Sakura - used to determine whether a chunk should unload // Paper - rewrite chunk system - it should check if the entity has ANY player passengers +- 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 } @Override @@ -99,10 +99,10 @@ index a2104fae2b288f4cbd9e810d73bf986be0b7b969..88d01e59869f8e6c528fb3ae1485cd86 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 7da6420aed597ee1bf544059c79a063512adcd40..b6a4753252b748c06a62b7faa6b05c1fb904d62e 100644 +index 0b84c21749d942851d5126b795e4b609070ed31f..3feffddfb537451d88e316f39ee28a6fa22921aa 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -@@ -75,6 +75,7 @@ public class FallingBlockEntity extends Entity { +@@ -79,6 +79,7 @@ public class FallingBlockEntity extends Entity { this.dropItem = true; this.fallDamageMax = 40; this.isFallingBlock = true; // Sakura @@ -111,10 +111,10 @@ index 7da6420aed597ee1bf544059c79a063512adcd40..b6a4753252b748c06a62b7faa6b05c1f 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 bbf6b4f705b3e9b289a7fdf82a78ef02f777297d..09f09a8fbf0eee62295001ce5b7ed5329445d2b0 100644 +index 2eb69c7d7843a7b58479049633b838212d003c10..bcee8b6254618dc7f444992ec8cdfe53b12458a7 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -38,6 +38,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -58,6 +58,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { super(type, world); this.blocksBuilding = true; this.isPrimedTNT = true; // Sakura @@ -123,13 +123,13 @@ index bbf6b4f705b3e9b289a7fdf82a78ef02f777297d..09f09a8fbf0eee62295001ce5b7ed532 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 14ee7b5b9b804bebd4e2a846b238547a28a36035..684ca1da2ca1d51f56461c60a900a5fc67e21ff3 100644 +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 -@@ -220,6 +220,17 @@ public class LevelChunk extends ChunkAccess { - } +@@ -127,6 +127,17 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + return this.postProcessingDone; } - // Paper end + // Paper end - rewrite chunk system + // Sakura start + private long lastMovementLoadTicket = 0; + diff --git a/patches/server/0010-TPS-Graph-Command.patch b/patches/server/0013-TPS-Graph-Command.patch similarity index 92% rename from patches/server/0010-TPS-Graph-Command.patch rename to patches/server/0013-TPS-Graph-Command.patch index 67f501f..13aaf6f 100644 --- a/patches/server/0010-TPS-Graph-Command.patch +++ b/patches/server/0013-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 606ae62f597761296412e14750758fa185fc3c48..b72c796d30d4892e30d726c3b2a6b097b941979d 100644 +index 524ca2d1719ac52ae287f6c4c56ca9d024f084eb..041e42c25e536a960a2e59114d2761c54520002e 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1130,6 +1130,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { - return false; - } : this::haveTime); -+ tickTracking.tickSample((System.nanoTime() - currentTime) / 1_000_000L); // Sakura +@@ -1258,6 +1260,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop public - //public final PersistentEntitySectionManager entityManager; // Paper - rewrite chunk system + // Paper - rewrite chunk system private final GameEventDispatcher gameEventDispatcher; public boolean noSave; diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java -index 83a39f900551e39d5af6f17a339a386ddee4feef..172c8d9cc94ed6ddfd3f785ca97a6e6ebb58d2d9 100644 +index d8b4196adf955f8d414688dc451caac2d9c609d9..47556521b28e0bd1b1c3ef3781684feb3bdf8911 100644 --- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java @@ -9,7 +9,7 @@ import javax.annotation.Nullable; import net.minecraft.world.entity.Entity; public class EntityTickList { -- private final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? -+ public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Sakura - tps graph // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? +- private final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet entities = new ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<>(); // Paper - rewrite chunk system ++ public final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet entities = new ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<>(); // Sakura - expose entity list // Paper - rewrite chunk system private void ensureActiveIsNotIterated() { - // Paper - replace with better logic, do not delay removals + // Paper - rewrite chunk system diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java -index 6c2a3813e7d63d57f07a8fa2edbb9d231221d818..a7333afe790be4ca876cb15c5f32a085dd13c3bb 100644 +index db9c812cf7267adf0bfd8be7368140e91245d640..d852fbe24669f878899381979a559650d6580f91 100644 --- a/src/main/java/org/spigotmc/SpigotConfig.java +++ b/src/main/java/org/spigotmc/SpigotConfig.java @@ -283,7 +283,7 @@ public class SpigotConfig diff --git a/patches/server/0014-Copy-EntityList-methods-to-BasicEntityList.patch b/patches/server/0014-Copy-EntityList-methods-to-BasicEntityList.patch deleted file mode 100644 index aa01830..0000000 --- a/patches/server/0014-Copy-EntityList-methods-to-BasicEntityList.patch +++ /dev/null @@ -1,117 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik -Date: Thu, 14 Oct 2021 19:16:49 +0100 -Subject: [PATCH] Copy EntityList methods to BasicEntityList - - -diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java -index c78cbec447032de9fe69748591bef6be300160ed..01701cdd0d984e35ff3453e6253ba9bcaa0db7b9 100644 ---- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java -+++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java -@@ -26,6 +26,8 @@ import java.util.List; - import java.util.function.Predicate; - import org.bukkit.event.entity.EntityRemoveEvent; - -+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; // Sakura -+ - public final class ChunkEntitySlices { - - protected final int minSection; -@@ -304,6 +306,13 @@ public final class ChunkEntitySlices { - - protected static final class BasicEntityList { - -+ // Sakura start - copy entitylist methods across -+ protected final Int2IntOpenHashMap entityToIndex = new Int2IntOpenHashMap(2, 0.8f); -+ { -+ this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE); -+ } -+ // Sakura end -+ - protected static final Entity[] EMPTY = new Entity[0]; - protected static final int DEFAULT_CAPACITY = 4; - -@@ -326,55 +335,52 @@ public final class ChunkEntitySlices { - return this.size; - } - -- private void resize() { -- if (this.storage == EMPTY) { -- this.storage = (E[])new Entity[DEFAULT_CAPACITY]; -- } else { -- this.storage = Arrays.copyOf(this.storage, this.storage.length * 2); -- } -- } -- -+ // Sakura start - copy entitylist methods across - public void add(final E entity) { -- final int idx = this.size++; -- if (idx >= this.storage.length) { -- this.resize(); -- this.storage[idx] = entity; -- } else { -- this.storage[idx] = entity; -+ final int count = this.size; -+ final int currIndex = this.entityToIndex.putIfAbsent(entity.getId(), count); -+ -+ if (currIndex != Integer.MIN_VALUE) { -+ return; // already in this list - } -- } - -- public int indexOf(final E entity) { -- final E[] storage = this.storage; -+ E[] list = this.storage; - -- for (int i = 0, len = Math.min(this.storage.length, this.size); i < len; ++i) { -- if (storage[i] == entity) { -- return i; -- } -+ if (list.length == count) { -+ // resize required -+ list = this.storage = Arrays.copyOf(list, (int)Math.max(4L, count * 2L)); // overflow results in negative - } - -- return -1; -+ list[count] = entity; -+ this.size = count + 1; -+ } -+ -+ public int indexOf(final E entity) { -+ return this.entityToIndex.getOrDefault(entity.getId(), -1); - } - - public boolean remove(final E entity) { -- final int idx = this.indexOf(entity); -- if (idx == -1) { -+ final int index = this.entityToIndex.remove(entity.getId()); -+ if (index == Integer.MIN_VALUE) { - return false; - } - -- final int size = --this.size; -- final E[] storage = this.storage; -- if (idx != size) { -- System.arraycopy(storage, idx + 1, storage, idx, size - idx); -+ // move the entity at the end to this index -+ final int endIndex = --this.size; -+ final E end = this.storage[endIndex]; -+ if (index != endIndex) { -+ // not empty after this call -+ this.entityToIndex.put(end.getId(), index); // update index - } -- -- storage[size] = null; -+ this.storage[index] = end; -+ this.storage[endIndex] = null; - - return true; - } - - public boolean has(final E entity) { -- return this.indexOf(entity) != -1; -+ return this.entityToIndex.containsKey(entity.getId()); -+ // Sakura end - } - } - diff --git a/patches/server/0011-Optimise-New-Liquid-Level.patch b/patches/server/0014-Optimise-New-Liquid-Level.patch similarity index 97% rename from patches/server/0011-Optimise-New-Liquid-Level.patch rename to patches/server/0014-Optimise-New-Liquid-Level.patch index dcf8ea3..2e339f4 100644 --- a/patches/server/0011-Optimise-New-Liquid-Level.patch +++ b/patches/server/0014-Optimise-New-Liquid-Level.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Optimise New Liquid Level 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 6d8ff6c06af5545634f255ed17dc1e489ece2548..67db57686dd29713128d5b233454b6147b8e460b 100644 +index c2943d892b067b3f1fb3b93301a092e912d71f08..5ad6880845ed699077ad355ef1edcfb1c6c7bee4 100644 --- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java @@ -137,7 +137,7 @@ public abstract class FlowingFluid extends Fluid { diff --git a/patches/server/0012-Slice-Packet-obfuscation-and-reduction.patch b/patches/server/0015-Slice-Packet-obfuscation-and-reduction.patch similarity index 87% rename from patches/server/0012-Slice-Packet-obfuscation-and-reduction.patch rename to patches/server/0015-Slice-Packet-obfuscation-and-reduction.patch index 0005726..0c9ef42 100644 --- a/patches/server/0012-Slice-Packet-obfuscation-and-reduction.patch +++ b/patches/server/0015-Slice-Packet-obfuscation-and-reduction.patch @@ -155,19 +155,19 @@ index 0f99733660f91280e4c6262cf75b3c9cae86f65a..ba9f8fe6fafc54bbdfb104de28af4b39 return SynchedEntityData.DataValue.create(this.accessor, this.value); } diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java -index a2279262c93408c11f5d2290b48fd794975e8cfe..09975c9be245207281b616f77c768aa048b0c140 100644 +index 1d849ce4e2c85f149af25318b8ffb6dcef6c6788..055e8691467e18ee3a70c857f0972f34a5a58e58 100644 --- a/src/main/java/net/minecraft/server/level/ServerEntity.java +++ b/src/main/java/net/minecraft/server/level/ServerEntity.java -@@ -148,7 +148,7 @@ public class ServerEntity { +@@ -140,7 +140,7 @@ public class ServerEntity { } } -- if (this.forceStateResync || this.tickCount % this.updateInterval == 0 || this.entity.hasImpulse || this.entity.getEntityData().isDirty()) { // Paper - fix desync when a player is added to the tracker -+ if (this.forceStateResync || this.tickCount % this.updateInterval == 0 || this.entity.hasImpulse || this.entity.getEntityData().isForeignDirty()) { // Slice// Paper - fix desync when a player is added to the tracker +- if (this.tickCount % this.updateInterval == 0 || this.entity.hasImpulse || this.entity.getEntityData().isDirty()) { ++ if (this.tickCount % this.updateInterval == 0 || this.entity.hasImpulse || this.entity.getEntityData().isForeignDirty()) { // Slice int i; int j; -@@ -390,7 +390,15 @@ public class ServerEntity { +@@ -398,7 +398,15 @@ public class ServerEntity { if (list != null) { this.trackedDataValues = datawatcher.getNonDefaultValues(); @@ -185,10 +185,10 @@ index a2279262c93408c11f5d2290b48fd794975e8cfe..09975c9be245207281b616f77c768aa0 if (this.entity instanceof LivingEntity) { diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 88d01e59869f8e6c528fb3ae1485cd86a23cb383..6f330f99a80f291bce638b0769e5ed00c283ac78 100644 +index 117f36743e1fa8a468528d44f0e16e6f7283880e..71e8a4f30afac822cc0da9620b904a02c201e452 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3470,7 +3470,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -3545,7 +3545,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.entityData.markDirty(Entity.DATA_AIR_SUPPLY_ID); return; } @@ -198,10 +198,10 @@ index 88d01e59869f8e6c528fb3ae1485cd86a23cb383..6f330f99a80f291bce638b0769e5ed00 } 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 b6a4753252b748c06a62b7faa6b05c1fb904d62e..86e12495ce807ba06210535350caa7db5e15d139 100644 +index 3feffddfb537451d88e316f39ee28a6fa22921aa..79c2ea81444c0ac9598f7fe334055326958a4f30 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -@@ -111,7 +111,7 @@ public class FallingBlockEntity extends Entity { +@@ -115,7 +115,7 @@ public class FallingBlockEntity extends Entity { } public void setStartPos(BlockPos pos) { @@ -211,10 +211,10 @@ index b6a4753252b748c06a62b7faa6b05c1fb904d62e..86e12495ce807ba06210535350caa7db public BlockPos getStartPos() { 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 09f09a8fbf0eee62295001ce5b7ed5329445d2b0..16340c2f8a948136c9fe0b2314c9ec7cbee74937 100644 +index bcee8b6254618dc7f444992ec8cdfe53b12458a7..ac9781762a987b4d0121f69b9e873d169dfc24a0 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -182,7 +182,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -203,7 +203,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { } public void setFuse(int fuse) { @@ -224,10 +224,10 @@ index 09f09a8fbf0eee62295001ce5b7ed5329445d2b0..16340c2f8a948136c9fe0b2314c9ec7c public int getFuse() { 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 686374e89e41b0917791264f3281f7384835bca8..74130ccef989757b81507f084cc30a0e593ecaf3 100644 +index 54ba25632c2a9e1c93a5b3a0b92e5280864c49d6..b352a8d299a13026d8e2c8e3405eb7f40b289d38 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -645,7 +645,7 @@ public abstract class Player extends LivingEntity { +@@ -649,7 +649,7 @@ public abstract class Player extends LivingEntity { public void increaseScore(int score) { int j = this.getScore(); @@ -235,4 +235,4 @@ index 686374e89e41b0917791264f3281f7384835bca8..74130ccef989757b81507f084cc30a0e + this.entityData.set(Player.DATA_SCORE_ID, j + score, 0); // Slice } - public void startAutoSpinAttack(int riptideTicks) { + public void startAutoSpinAttack(int riptideTicks, float riptideAttackDamage, ItemStack stack) { diff --git a/patches/server/0013-Use-Optimised-TrackedEntityMap.patch b/patches/server/0016-Use-Optimised-TrackedEntityMap.patch similarity index 80% rename from patches/server/0013-Use-Optimised-TrackedEntityMap.patch rename to patches/server/0016-Use-Optimised-TrackedEntityMap.patch index ff5fadc..9ab59b1 100644 --- a/patches/server/0013-Use-Optimised-TrackedEntityMap.patch +++ b/patches/server/0016-Use-Optimised-TrackedEntityMap.patch @@ -5,15 +5,15 @@ Subject: [PATCH] Use Optimised TrackedEntityMap diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 0bfd2005dc82b0262badfe6867835e80a6e6a1dc..34fdb52904a26bc7c4c95eee91882a3924ab037c 100644 +index a9e41f1f87afa9778ec4d4bc3f01ef5e6cbefc91..c46e07638db51bd549e39f0fcbe122d48ead9c31 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -254,7 +254,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - // Paper - rewrite chunk system +@@ -242,7 +242,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.toDrop = new LongOpenHashSet(); this.tickingGenerated = new AtomicInteger(); this.playerMap = new PlayerMap(); - this.entityMap = new Int2ObjectOpenHashMap(); + this.entityMap = new me.samsuik.sakura.utils.collections.TrackedEntityChunkMap(); // Sakura - optimised tracked entity map this.chunkTypeCache = new Long2ByteOpenHashMap(); this.chunkSaveCooldowns = new Long2LongOpenHashMap(); - this.unloadQueue = Queues.newConcurrentLinkedQueue(); + // Paper - rewrite chunk system diff --git a/patches/server/0017-Copy-EntityList-methods-to-BasicEntityList.patch b/patches/server/0017-Copy-EntityList-methods-to-BasicEntityList.patch new file mode 100644 index 0000000..0e76358 --- /dev/null +++ b/patches/server/0017-Copy-EntityList-methods-to-BasicEntityList.patch @@ -0,0 +1,81 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Thu, 14 Oct 2021 19:16:49 +0100 +Subject: [PATCH] Copy EntityList methods to BasicEntityList + + +diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +index 87d2b3ec165e2e9e4bdbedd7adddaa2130ed507b..01e29fc7c91aa7ffeb54584c8ca0989900235251 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java ++++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +@@ -413,6 +413,13 @@ public final class ChunkEntitySlices { + + private E[] storage; + private int size; ++ // Sakura start - use methods from EntityList ++ private it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap entityToIndex = null; ++ private void setupIndexMap() { ++ this.entityToIndex = new it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap(2, 0.8f); ++ this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE); ++ } ++ // Sakura end - use methods from EntityList + + public BasicEntityList() { + this(0); +@@ -433,6 +440,7 @@ public final class ChunkEntitySlices { + private void resize() { + if (this.storage == EMPTY) { + this.storage = (E[])new Entity[DEFAULT_CAPACITY]; ++ this.setupIndexMap(); // Sakura - use methods from EntityList + } else { + this.storage = Arrays.copyOf(this.storage, this.storage.length * 2); + } +@@ -446,6 +454,7 @@ public final class ChunkEntitySlices { + } else { + this.storage[idx] = entity; + } ++ this.entityToIndex.put(entity.getId(), idx); // Sakura - use methods from EntityList + } + + public int indexOf(final E entity) { +@@ -461,24 +470,32 @@ public final class ChunkEntitySlices { + } + + public boolean remove(final E entity) { +- final int idx = this.indexOf(entity); +- if (idx == -1) { ++ // Sakura start - use methods from EntityList ++ if (this.entityToIndex == null) { + return false; + } + +- final int size = --this.size; +- final E[] storage = this.storage; +- if (idx != size) { +- System.arraycopy(storage, idx + 1, storage, idx, size - idx); ++ final int index = this.entityToIndex.remove(entity.getId()); ++ if (index == Integer.MIN_VALUE) { ++ return false; + } + +- storage[size] = null; ++ // move the entity at the end to this index ++ final int endIndex = --this.size; ++ final E end = this.storage[endIndex]; ++ if (index != endIndex) { ++ // not empty after this call ++ this.entityToIndex.put(end.getId(), index); // update index ++ } ++ this.storage[index] = end; ++ this.storage[endIndex] = null; ++ // Sakura end - use methods from EntityList + + return true; + } + + public boolean has(final E entity) { +- return this.indexOf(entity) != -1; ++ return this.entityToIndex != null && this.entityToIndex.containsKey(entity.getId()); // Sakura - use methods from EntityList + } + } + diff --git a/patches/server/0015-Add-utility-methods-to-EntitySlices.patch b/patches/server/0018-Add-utility-methods-to-EntitySlices.patch similarity index 58% rename from patches/server/0015-Add-utility-methods-to-EntitySlices.patch rename to patches/server/0018-Add-utility-methods-to-EntitySlices.patch index 4b1383a..390b534 100644 --- a/patches/server/0015-Add-utility-methods-to-EntitySlices.patch +++ b/patches/server/0018-Add-utility-methods-to-EntitySlices.patch @@ -4,31 +4,30 @@ Date: Thu, 3 Aug 2023 13:48:27 +0100 Subject: [PATCH] Add utility methods to EntitySlices -diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java -index 01701cdd0d984e35ff3453e6253ba9bcaa0db7b9..8fcaa00e461c7f4413bf655ddd8165a2b908f900 100644 ---- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java -+++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java -@@ -252,6 +252,12 @@ public final class ChunkEntitySlices { - +diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +index 01e29fc7c91aa7ffeb54584c8ca0989900235251..c140e3a73712d2123bc9ed09e9bab25e48c4420e 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java ++++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +@@ -318,6 +318,12 @@ public final class ChunkEntitySlices { return true; } -+ + + // Sakura start + public Entity[] getSectionEntities(int sectionY) { + return this.allEntities.getSectionEntities(sectionY); + } + // Sakura end - ++ public void getHardCollidingEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { this.hardCollidingEntities.getEntities(except, box, into, predicate); -@@ -430,6 +436,18 @@ public final class ChunkEntitySlices { - this.nonEmptyBitset[sectionIndex >>> 6] ^= (1L << (sectionIndex & (Long.SIZE - 1))); + } +@@ -542,6 +548,18 @@ public final class ChunkEntitySlices { } } -+ + + // Sakura start + public Entity[] getSectionEntities(int sectionY) { -+ var list = entitiesBySection[sectionY - this.manager.minSection]; ++ BasicEntityList list = this.entitiesBySection[sectionY - this.slices.minSection]; + + if (list != null) { + return list.storage; @@ -37,6 +36,7 @@ index 01701cdd0d984e35ff3453e6253ba9bcaa0db7b9..8fcaa00e461c7f4413bf655ddd8165a2 + return new Entity[0]; + } + // Sakura end - ++ public void getEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { if (this.count == 0) { + return; diff --git a/patches/server/0016-Optimise-paper-explosions.patch b/patches/server/0019-Optimise-paper-explosions.patch similarity index 93% rename from patches/server/0016-Optimise-paper-explosions.patch rename to patches/server/0019-Optimise-paper-explosions.patch index ae4e918..a9af908 100644 --- a/patches/server/0016-Optimise-paper-explosions.patch +++ b/patches/server/0019-Optimise-paper-explosions.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Optimise paper explosions diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 85c0855eaf20013b60ed643052b783f7f6e30584..f6a66790dc489be03ecc4b3fbcfdd2585b46d10b 100644 +index 3a35dcdfd639240363891f412beeef47b8b41e06..b6364b02142a71741bdeb5df3da58dcbd41d1ba5 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -112,6 +112,41 @@ public class Explosion { +@@ -113,6 +113,41 @@ public class Explosion { this.yield = this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F; // CraftBukkit } @@ -50,7 +50,7 @@ index 85c0855eaf20013b60ed643052b783f7f6e30584..f6a66790dc489be03ecc4b3fbcfdd258 // Paper start - optimise collisions private static final double[] CACHED_RAYS; static { -@@ -137,7 +172,7 @@ public class Explosion { +@@ -138,7 +173,7 @@ public class Explosion { } } @@ -59,7 +59,7 @@ index 85c0855eaf20013b60ed643052b783f7f6e30584..f6a66790dc489be03ecc4b3fbcfdd258 } private static final int CHUNK_CACHE_SHIFT = 2; -@@ -431,7 +466,7 @@ public class Explosion { +@@ -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)); @@ -68,7 +68,7 @@ index 85c0855eaf20013b60ed643052b783f7f6e30584..f6a66790dc489be03ecc4b3fbcfdd258 boolean flag = true; int i; -@@ -457,6 +492,17 @@ public class Explosion { +@@ -458,6 +493,17 @@ public class Explosion { initialCache = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true); } @@ -86,7 +86,7 @@ index 85c0855eaf20013b60ed643052b783f7f6e30584..f6a66790dc489be03ecc4b3fbcfdd258 // 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 -@@ -542,23 +588,63 @@ public class Explosion { +@@ -543,23 +589,62 @@ public class Explosion { } this.toBlow.addAll(set); @@ -128,12 +128,11 @@ index 85c0855eaf20013b60ed643052b783f7f6e30584..f6a66790dc489be03ecc4b3fbcfdd258 + 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; - -+ io.papermc.paper.chunk.system.entity.EntityLookup entityLookup = ((net.minecraft.server.level.ServerLevel) this.level).getEntityLookup(); + ++ 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) { -+ io.papermc.paper.world.ChunkEntitySlices chunk = entityLookup.getChunk(chunkX, chunkZ); ++ ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices chunk = entityLookup.getChunk(chunkX, chunkZ); + if (chunk == null) continue; // empty slice + + for (int chunkY = minChunkY; chunkY <= maxChunkY; ++chunkY) { @@ -151,14 +150,14 @@ index 85c0855eaf20013b60ed643052b783f7f6e30584..f6a66790dc489be03ecc4b3fbcfdd258 + 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) { + 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; -@@ -582,24 +668,27 @@ public class Explosion { +@@ -583,24 +668,27 @@ public class Explosion { // - Damaging EntityEnderDragon does nothing // - EntityEnderDragon hitbock always covers the other parts and is therefore always present if (entity instanceof EnderDragonPart) { @@ -191,7 +190,7 @@ index 85c0855eaf20013b60ed643052b783f7f6e30584..f6a66790dc489be03ecc4b3fbcfdd258 } // CraftBukkit end } -@@ -647,8 +736,12 @@ public class Explosion { +@@ -648,8 +736,12 @@ public class Explosion { } } } diff --git a/patches/server/0017-Store-Entity-Data-State.patch b/patches/server/0020-Store-Entity-Data-State.patch similarity index 89% rename from patches/server/0017-Store-Entity-Data-State.patch rename to patches/server/0020-Store-Entity-Data-State.patch index 5de0941..259944d 100644 --- a/patches/server/0017-Store-Entity-Data-State.patch +++ b/patches/server/0020-Store-Entity-Data-State.patch @@ -52,10 +52,10 @@ 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 6f330f99a80f291bce638b0769e5ed00c283ac78..ce16878d3c9c52e8edd4b073916bb3f7fd595649 100644 +index 71e8a4f30afac822cc0da9620b904a02c201e452..6b90cfe63dce4a56033c8ecc9e68895fe3d77390 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -585,6 +585,25 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -579,6 +579,25 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return flags; } // Sakura end - load chunks on cannon entity movement @@ -82,14 +82,14 @@ index 6f330f99a80f291bce638b0769e5ed00c283ac78..ce16878d3c9c52e8edd4b073916bb3f7 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 5a43957309019bec27b9624e5268d6a8c50c165d..829a21a6da2de36f38cae9e1a3920fcc732eac0e 100644 +index 05a4b69fbaf36269bea3a050022a54d28211c285..9afc93364fd1176f0dedced6042abc2df938228e 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -1313,6 +1313,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1358,6 +1358,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl public void guardEntityTick(Consumer tickConsumer, T entity) { try { + entity.storeEntityState(); // Sakura - store entity state tickConsumer.accept(entity); - MinecraftServer.getServer().executeMidTickTasks(); // Paper - execute chunk tasks mid tick } catch (Throwable throwable) { + if (throwable instanceof ThreadDeath) throw throwable; // Paper diff --git a/patches/server/0018-Merge-Cannon-Entities.patch b/patches/server/0021-Merge-Cannon-Entities.patch similarity index 89% rename from patches/server/0018-Merge-Cannon-Entities.patch rename to patches/server/0021-Merge-Cannon-Entities.patch index ba81ca4..c1166fb 100644 --- a/patches/server/0018-Merge-Cannon-Entities.patch +++ b/patches/server/0021-Merge-Cannon-Entities.patch @@ -169,10 +169,10 @@ index 0000000000000000000000000000000000000000..e63935c17e213bf60571d120ad9ce311 + +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index b72c796d30d4892e30d726c3b2a6b097b941979d..054783956aa6179f9f0813393b7a9afa41a6d54a 100644 +index 041e42c25e536a960a2e59114d2761c54520002e..5b366c69a28e3806d5507ad029e27231f8ef7c5c 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1803,6 +1803,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 -@@ -918,6 +919,15 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -732,6 +733,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. entity.stopRiding(); } @@ -209,10 +209,10 @@ index 4a3690c71eda93d2542e8c039442a51882f10475..ada18c755cee73620e6c0020674dd0a5 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 ce16878d3c9c52e8edd4b073916bb3f7fd595649..88964adf81cd03b26556b50b764caaddd49b59be 100644 +index 6b90cfe63dce4a56033c8ecc9e68895fe3d77390..190e0022cb45545d8a76f0f7819b6f2be0b64307 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -604,6 +604,116 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -598,6 +598,116 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return to.entityState() != null && to.entityState().isCurrentState(this); } // Sakura end - store entity data/state @@ -329,7 +329,7 @@ index ce16878d3c9c52e8edd4b073916bb3f7fd595649..88964adf81cd03b26556b50b764caadd public Entity(EntityType type, Level world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); -@@ -653,6 +763,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -647,6 +757,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 ce16878d3c9c52e8edd4b073916bb3f7fd595649..88964adf81cd03b26556b50b764caadd } public boolean isColliding(BlockPos pos, BlockState state) { -@@ -2591,6 +2702,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2643,6 +2754,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess nbttagcompound.putBoolean("Paper.FreezeLock", true); } // Paper end @@ -349,7 +349,7 @@ index ce16878d3c9c52e8edd4b073916bb3f7fd595649..88964adf81cd03b26556b50b764caadd return nbttagcompound; } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Saving entity NBT"); -@@ -2738,6 +2854,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2790,6 +2906,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess freezeLocked = nbt.getBoolean("Paper.FreezeLock"); } // Paper end @@ -361,7 +361,7 @@ index ce16878d3c9c52e8edd4b073916bb3f7fd595649..88964adf81cd03b26556b50b764caadd } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Loading entity NBT"); -@@ -4949,6 +5070,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4961,6 +5082,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess // Paper end - rewrite chunk system CraftEventFactory.callEntityRemoveEvent(this, cause); // CraftBukkit end @@ -374,10 +374,10 @@ index ce16878d3c9c52e8edd4b073916bb3f7fd595649..88964adf81cd03b26556b50b764caadd 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 86e12495ce807ba06210535350caa7db5e15d139..a148befad883e5d524dcf643690f80a0bfd079e5 100644 +index 79c2ea81444c0ac9598f7fe334055326958a4f30..fa95135e3af7fdd610cad2230cc39b547cda0a8a 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -@@ -133,6 +133,59 @@ public class FallingBlockEntity extends Entity { +@@ -137,6 +137,59 @@ public class FallingBlockEntity extends Entity { return !this.isRemoved(); } @@ -437,7 +437,7 @@ index 86e12495ce807ba06210535350caa7db5e15d139..a148befad883e5d524dcf643690f80a0 @Override protected double getDefaultGravity() { return 0.04D; -@@ -202,6 +255,7 @@ public class FallingBlockEntity extends Entity { +@@ -207,6 +260,7 @@ public class FallingBlockEntity extends Entity { return; } // CraftBukkit end @@ -446,10 +446,10 @@ index 86e12495ce807ba06210535350caa7db5e15d139..a148befad883e5d524dcf643690f80a0 ((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 16340c2f8a948136c9fe0b2314c9ec7cbee74937..dc9d72bc033735dd83d7b0f3fe79d0a51b9661ab 100644 +index ac9781762a987b4d0121f69b9e873d169dfc24a0..71f01c2cb7182764ef4c527b8e84e162ebfd761c 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -70,6 +70,44 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -90,6 +90,44 @@ public class PrimedTnt extends Entity implements TraceableEntity { return !this.isRemoved(); } @@ -494,7 +494,7 @@ index 16340c2f8a948136c9fe0b2314c9ec7cbee74937..dc9d72bc033735dd83d7b0f3fe79d0a5 @Override protected double getDefaultGravity() { return 0.04D; -@@ -97,6 +135,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -118,6 +156,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { if (i <= 0) { // CraftBukkit start - Need to reverse the order of the explosion and the entity death so we have a location for the event // this.discard(); @@ -503,17 +503,17 @@ index 16340c2f8a948136c9fe0b2314c9ec7cbee74937..dc9d72bc033735dd83d7b0f3fe79d0a5 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 829a21a6da2de36f38cae9e1a3920fcc732eac0e..557472039d6451d5a8471ff56609ce49620d30df 100644 +index 9afc93364fd1176f0dedced6042abc2df938228e..66af21d21f42e796ae49a2ac8a4f7e2ef9fad99c 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -218,6 +218,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - public abstract ResourceKey getTypeKey(); +@@ -220,6 +220,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - public final it.unimi.dsi.fastutil.longs.Long2IntMap minimalTNT = new it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap(); // Sakura - visibility api + // Paper start - rewrite chunk system + 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 - 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; Async-Anti-Xray: Pass executor - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot + @Override + public final ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup moonrise$getEntityLookup() { diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java index 1359d25a32b4a5d5e8e68ce737bd19f7b5afaf69..0afa2cfb04b5097788927076669e85fe24041df9 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java diff --git a/patches/server/0019-Replace-explosion-density-cache.patch b/patches/server/0022-Replace-explosion-density-cache.patch similarity index 88% rename from patches/server/0019-Replace-explosion-density-cache.patch rename to patches/server/0022-Replace-explosion-density-cache.patch index 6314313..a6aebd4 100644 --- a/patches/server/0019-Replace-explosion-density-cache.patch +++ b/patches/server/0022-Replace-explosion-density-cache.patch @@ -129,10 +129,10 @@ index 0000000000000000000000000000000000000000..d7e24638f07f243502004970ab4ce646 + } +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 701dfa3017bdcc73d55f28ca0623f9791a4c712d..9257505431d00aec2225b052492b8eb88ed4d63d 100644 +index 5b366c69a28e3806d5507ad029e27231f8ef7c5c..2df0f8a92174be654acb27785ff1f4880dcf3362 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1804,6 +1804,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, Supplier sakuraWorldConfigCreator, java.util.concurrent.Executor executor) { // Sakura - sakura configuration files // Paper - create paper world config; Async-Anti-Xray: Pass executor - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot + @Override + public final ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup moonrise$getEntityLookup() { diff --git a/src/main/java/net/minecraft/world/level/block/BasePressurePlateBlock.java b/src/main/java/net/minecraft/world/level/block/BasePressurePlateBlock.java index 8b33e35c843e5c0b8988a2ef2a38a2673035292f..5ec557652558bb44e9be7d32fc214091d29aac78 100644 --- a/src/main/java/net/minecraft/world/level/block/BasePressurePlateBlock.java @@ -253,10 +253,10 @@ index 76aca266d3f3222502ff4c196228f08fcd88c5f8..6a63e60d80ee53a4611dfcdd9ab974f8 TripWireHookBlock.notifyNeighbors(block, world, blockposition1, enumdirection1); TripWireHookBlock.emitState(world, blockposition1, flag4, flag5, flag2, flag3); diff --git a/src/main/java/net/minecraft/world/phys/AABB.java b/src/main/java/net/minecraft/world/phys/AABB.java -index 92394960fc76886f393cba02ac33c57739a4b383..f6c420fc86c08dcc49bfd56be6c47d2b32f813b6 100644 +index e4e99fb9f1670dbc1c032dd834c4bfb3c9d4a8ca..acbaa9f85caaadafb165d1fd835e4dde9e73e200 100644 --- a/src/main/java/net/minecraft/world/phys/AABB.java +++ b/src/main/java/net/minecraft/world/phys/AABB.java -@@ -502,4 +502,28 @@ public class AABB { +@@ -519,4 +519,28 @@ public class AABB { public static AABB ofSize(Vec3 center, double dx, double dy, double dz) { return new AABB(center.x - dx / 2.0, center.y - dy / 2.0, center.z - dz / 2.0, center.x + dx / 2.0, center.y + dy / 2.0, center.z + dz / 2.0); } diff --git a/patches/server/0023-Limited-Get-Entities.patch b/patches/server/0023-Limited-Get-Entities.patch deleted file mode 100644 index d13fb46..0000000 --- a/patches/server/0023-Limited-Get-Entities.patch +++ /dev/null @@ -1,374 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik -Date: Thu, 23 Sep 2021 15:19:31 +0100 -Subject: [PATCH] Limited Get Entities - - -diff --git a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java -index 15ee41452992714108efe53b708b5a4e1da7c1ff..b62a1ae53a891802db17046271b9981ab3af1df0 100644 ---- a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java -+++ b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java -@@ -522,6 +522,128 @@ public final class EntityLookup implements LevelEntityGetter { - return slices; - } - -+ // Sakura start - limited get entities -+ public void getLimitedEntities(final Entity except, final AABB box, final List into, -+ final Predicate predicate, final int limit, final int search) { -+ 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; -+ final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4; -+ -+ final int minRegionX = minChunkX >> REGION_SHIFT; -+ final int minRegionZ = minChunkZ >> REGION_SHIFT; -+ final int maxRegionX = maxChunkX >> REGION_SHIFT; -+ final int maxRegionZ = maxChunkZ >> REGION_SHIFT; -+ -+ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { -+ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; -+ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; -+ -+ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { -+ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); -+ -+ if (region == null) { -+ continue; -+ } -+ -+ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; -+ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; -+ -+ for (int currZ = minZ; currZ <= maxZ; ++currZ) { -+ for (int currX = minX; currX <= maxX; ++currX) { -+ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); -+ if (chunk == null || !chunk.status.isOrAfter(FullChunkStatus.FULL)) { -+ continue; -+ } -+ -+ chunk.getLimitedEntities(except, box, into, predicate, limit, search); -+ } -+ } -+ } -+ } -+ } -+ -+ public void getLimitedEntities(final EntityType type, final AABB box, final List into, -+ final Predicate predicate, final int limit, final int search) { -+ 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; -+ final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4; -+ -+ final int minRegionX = minChunkX >> REGION_SHIFT; -+ final int minRegionZ = minChunkZ >> REGION_SHIFT; -+ final int maxRegionX = maxChunkX >> REGION_SHIFT; -+ final int maxRegionZ = maxChunkZ >> REGION_SHIFT; -+ -+ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { -+ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; -+ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; -+ -+ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { -+ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); -+ -+ if (region == null) { -+ continue; -+ } -+ -+ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; -+ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; -+ -+ for (int currZ = minZ; currZ <= maxZ; ++currZ) { -+ for (int currX = minX; currX <= maxX; ++currX) { -+ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); -+ if (chunk == null || !chunk.status.isOrAfter(FullChunkStatus.FULL)) { -+ continue; -+ } -+ -+ chunk.getLimitedEntities(type, box, (List)into, (Predicate)predicate, limit, search); -+ } -+ } -+ } -+ } -+ } -+ -+ public void getLimitedEntities(final Class clazz, final Entity except, final AABB box, final List into, -+ final Predicate predicate, final int limit, final int search) { -+ 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; -+ final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4; -+ -+ final int minRegionX = minChunkX >> REGION_SHIFT; -+ final int minRegionZ = minChunkZ >> REGION_SHIFT; -+ final int maxRegionX = maxChunkX >> REGION_SHIFT; -+ final int maxRegionZ = maxChunkZ >> REGION_SHIFT; -+ -+ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { -+ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; -+ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; -+ -+ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { -+ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); -+ -+ if (region == null) { -+ continue; -+ } -+ -+ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; -+ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; -+ -+ for (int currZ = minZ; currZ <= maxZ; ++currZ) { -+ for (int currX = minX; currX <= maxX; ++currX) { -+ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); -+ if (chunk == null || !chunk.status.isOrAfter(FullChunkStatus.FULL)) { -+ continue; -+ } -+ -+ chunk.getLimitedEntities(clazz, except, box, into, predicate, limit, search); -+ } -+ } -+ } -+ } -+ } -+ // Sakura end -+ - public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List into, final Predicate predicate) { - final int minChunkX = (Mth.floor(box.minX) - 2) >> 4; - final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4; -diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java -index 8fcaa00e461c7f4413bf655ddd8165a2b908f900..404b99def4562942e036089085a667979b1cac81 100644 ---- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java -+++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java -@@ -259,6 +259,30 @@ public final class ChunkEntitySlices { - } - // Sakura end - -+ // Sakura start - limited get entities -+ public void getLimitedEntities(final Entity except, final AABB box, final List into, final Predicate predicate, final int limit, final int search) { -+ this.allEntities.getLimitedEntities(except, box, into, predicate, limit, search); -+ } -+ -+ public void getLimitedEntities(final EntityType type, final AABB box, final List into, -+ final Predicate predicate, final int limit, final int search) { -+ this.allEntities.getLimitedEntities(type, box, (List)into, (Predicate)predicate, limit, search); -+ } -+ -+ public void getLimitedEntities(final Class clazz, final Entity except, final AABB box, final List into, -+ final Predicate predicate, final int limit, final int search) { -+ EntityCollectionBySection collection = this.entitiesByClass.get(clazz); -+ if (collection != null) { -+ collection.getLimitedEntities(except, clazz, box, (List)into, (Predicate)predicate, limit, search); -+ } else { -+ synchronized (this) { -+ this.entitiesByClass.putIfAbsent(clazz, collection = this.initClass(clazz)); -+ } -+ collection.getLimitedEntities(except, clazz, box, (List)into, (Predicate)predicate, limit, search); -+ } -+ } -+ // Sakura end -+ - public void getHardCollidingEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { - this.hardCollidingEntities.getEntities(except, box, into, predicate); - } -@@ -449,6 +473,155 @@ public final class ChunkEntitySlices { - } - // Sakura end - -+ // Sakura start - limited get entities -+ public void getLimitedEntities(final Entity except, final Class clazz, final AABB box, final List into, -+ final Predicate predicate, final int limit, int search) { -+ if (this.count == 0) { -+ return; -+ } -+ -+ final int minSection = this.manager.minSection; -+ final int maxSection = this.manager.maxSection; -+ -+ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); -+ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); -+ -+ // TODO use the bitset -+ -+ final BasicEntityList[] entitiesBySection = this.entitiesBySection; -+ -+ for (int section = min; section <= max; ++section) { -+ final BasicEntityList list = entitiesBySection[section - minSection]; -+ -+ if (list == null) { -+ continue; -+ } -+ -+ final Entity[] storage = list.storage; -+ -+ int len = Math.min(storage.length, list.size()); -+ int index = manager.world.random.nextInt(len); // starting position -+ -+ for (int i = 0; i < len; ++i) { -+ index = (index + 1) % len; // Update index -+ final Entity entity = storage[index]; -+ -+ if (into.size() >= limit || search-- <= 0) { -+ return; -+ } -+ -+ if (entity == null || entity == except || !clazz.isInstance(entity) || !entity.getBoundingBox().intersects(box)) { -+ continue; -+ } -+ -+ if (predicate != null && !predicate.test(entity)) { -+ continue; -+ } -+ -+ into.add(entity); -+ } -+ } -+ } -+ -+ public void getLimitedEntities(final EntityType type, final AABB box, final List into, -+ final Predicate predicate, final int limit, int search) { -+ if (this.count == 0) { -+ return; -+ } -+ -+ final int minSection = this.manager.minSection; -+ final int maxSection = this.manager.maxSection; -+ -+ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); -+ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); -+ -+ // TODO use the bitset -+ -+ final BasicEntityList[] entitiesBySection = this.entitiesBySection; -+ -+ for (int section = min; section <= max; ++section) { -+ final BasicEntityList list = entitiesBySection[section - minSection]; -+ -+ if (list == null) { -+ continue; -+ } -+ -+ final Entity[] storage = list.storage; -+ -+ int len = Math.min(storage.length, list.size()); -+ int index = manager.world.random.nextInt(len); // starting position -+ -+ for (int i = 0; i < len; ++i) { -+ index = (index + 1) % len; // Update index -+ final Entity entity = storage[index]; -+ -+ if (into.size() >= limit || search-- <= 0) { -+ return; -+ } -+ -+ if (entity == null || (type != null && entity.getType() != type) || !entity.getBoundingBox().intersects(box)) { -+ continue; -+ } -+ -+ if (predicate != null && !predicate.test((T)entity)) { -+ continue; -+ } -+ -+ into.add((T)entity); -+ } -+ } -+ } -+ -+ public void getLimitedEntities(final Entity except, final AABB box, final List into, -+ final Predicate predicate, final int limit, int search) { -+ if (this.count == 0) { -+ return; -+ } -+ -+ final int minSection = this.manager.minSection; -+ final int maxSection = this.manager.maxSection; -+ -+ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); -+ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); -+ -+ // TODO use the bitset -+ -+ final BasicEntityList[] entitiesBySection = this.entitiesBySection; -+ -+ for (int section = min; section <= max; ++section) { -+ final BasicEntityList list = entitiesBySection[section - minSection]; -+ -+ if (list == null) { -+ continue; -+ } -+ -+ final Entity[] storage = list.storage; -+ -+ int len = Math.min(storage.length, list.size()); -+ int index = manager.world.random.nextInt(len); // starting position -+ -+ for (int i = 0; i < len; ++i) { -+ index = (index + 1) % len; // Update index -+ final Entity entity = storage[index]; -+ -+ if (into.size() >= limit || search-- <= 0) { -+ return; -+ } -+ -+ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { -+ continue; -+ } -+ -+ if (predicate != null && !predicate.test(entity)) { -+ continue; -+ } -+ -+ into.add(entity); -+ } -+ } -+ } -+ // Sakura end -+ - public void getEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { - if (this.count == 0) { - return; -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 507d676c58daf2297eb729543a7f86c896a984d6..d041ad1e52c78c553da4d92745d2b07fc3eab522 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -221,6 +221,39 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - 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 - -+ // Sakura start - add entity retrival methods with search limits -+ public final void getLimitedEntities(Entity except, AABB box, Predicate predicate, List into, int limit, int search) { -+ ((ServerLevel)this).getEntityLookup().getLimitedEntities(except, box, into, predicate, limit, search); -+ } -+ -+ public final List getLimitedEntities(net.minecraft.world.entity.EntityType filter, AABB box, Predicate predicate, int limit, int search) { -+ List ret = new java.util.ArrayList<>(); -+ ((ServerLevel)this).getEntityLookup().getLimitedEntities(filter, box, ret, predicate, limit, search); -+ return ret; -+ } -+ -+ public final void getLimitedEntitiesByClass(Class clazz, Entity except, final AABB box, List into, -+ Predicate predicate, int limit, int search) { -+ ((ServerLevel)this).getEntityLookup().getLimitedEntities((Class)clazz, except, box, (List)into, (Predicate)predicate, limit, search); -+ } -+ -+ public final List getLimitedEntitiesOfClass(Class entityClass, AABB box, Predicate predicate, int limit, int search) { -+ List ret = new java.util.ArrayList<>(); -+ ((ServerLevel)this).getEntityLookup().getLimitedEntities(entityClass, null, box, ret, predicate, limit, search); -+ return ret; -+ } -+ -+ public final List getLimitedEntities(@Nullable Entity except, AABB box, Predicate predicate, int limit, int search) { -+ List list = Lists.newArrayList(); -+ getLimitedEntities(except, box, predicate, list, limit, search); -+ return list; -+ } -+ -+ public final List getLimitedEntities(@Nullable Entity except, AABB box, int limit, int search) { -+ return this.getLimitedEntities(except, box, net.minecraft.world.entity.EntitySelector.NO_SPECTATORS, limit, search); -+ } -+ // Sakura end - add entity retrival methods with search limits -+ - 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; Async-Anti-Xray: Pass executor - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot - this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config diff --git a/patches/server/0020-Optimise-explosions-in-protected-regions.patch b/patches/server/0023-Optimise-explosions-in-protected-regions.patch similarity index 92% rename from patches/server/0020-Optimise-explosions-in-protected-regions.patch rename to patches/server/0023-Optimise-explosions-in-protected-regions.patch index a7a9794..86cd9db 100644 --- a/patches/server/0020-Optimise-explosions-in-protected-regions.patch +++ b/patches/server/0023-Optimise-explosions-in-protected-regions.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Optimise explosions in protected regions diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index ead8ddd7fd9941d4349b9b94ad313bf9d46bdc0b..d7e8dd148098de080c5bc0f76b36207501e1089f 100644 +index c5668c12a20628e1f3b216590488b432ad9a0adf..576b7260016b770a62ed9a291dd20bf7063897cd 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -416,6 +416,21 @@ public class Explosion { +@@ -417,6 +417,21 @@ public class Explosion { return (float)missedRays / (float)totalRays; } // Paper end - optimise collisions @@ -30,7 +30,7 @@ index ead8ddd7fd9941d4349b9b94ad313bf9d46bdc0b..d7e8dd148098de080c5bc0f76b362075 private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity entity) { return (ExplosionDamageCalculator) (entity == null ? Explosion.EXPLOSION_DAMAGE_CALCULATOR : new EntityBasedExplosionDamageCalculator(entity)); -@@ -507,7 +522,7 @@ public class Explosion { +@@ -508,7 +523,7 @@ public class Explosion { initialCache = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true); } // Sakura start - optimise paper explosions diff --git a/patches/server/0021-Specialised-Explosions.patch b/patches/server/0024-Specialised-Explosions.patch similarity index 92% rename from patches/server/0021-Specialised-Explosions.patch rename to patches/server/0024-Specialised-Explosions.patch index d64aa47..e841b4b 100644 --- a/patches/server/0021-Specialised-Explosions.patch +++ b/patches/server/0024-Specialised-Explosions.patch @@ -4,11 +4,11 @@ Date: Fri, 3 May 2024 15:04:31 +0100 Subject: [PATCH] Specialised Explosions -diff --git a/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java -index 0fd814f1d65c111266a2b20f86561839a4cef755..932f7a0d030d2d4932e6e6d4a5805e9b683cce67 100644 ---- a/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java -+++ b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java -@@ -125,6 +125,12 @@ public final class IteratorSafeOrderedReferenceSet { +diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java b/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java +index c21e00812f1aaa1279834a0562d360d6b89e146c..442119e7c4670582556b067dfc03e39add7c2c9d 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java ++++ b/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java +@@ -107,6 +107,12 @@ public final class IteratorSafeOrderedReferenceSet { } } @@ -23,7 +23,7 @@ index 0fd814f1d65c111266a2b20f86561839a4cef755..932f7a0d030d2d4932e6e6d4a5805e9b 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..d15eb10fa203236060b90c5fc1364564dec8753f +index 0000000000000000000000000000000000000000..55bf4424d8e995ea7384f88f43465f90ae3c6704 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java @@ -0,0 +1,200 @@ @@ -155,10 +155,10 @@ index 0000000000000000000000000000000000000000..d15eb10fa203236060b90c5fc1364564 + int maxChunkY = Mth.clamp(Mth.floor(bb.maxY) >> 4, minSection, maxSection); + int maxChunkZ = Mth.floor(bb.maxZ) >> 4; + -+ io.papermc.paper.chunk.system.entity.EntityLookup entityLookup = this.level.getEntityLookup(); ++ 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) { -+ io.papermc.paper.world.ChunkEntitySlices chunk = entityLookup.getChunk(chunkX, chunkZ); ++ ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices chunk = entityLookup.getChunk(chunkX, chunkZ); + + if (chunk == null) { + continue; @@ -229,13 +229,13 @@ index 0000000000000000000000000000000000000000..d15eb10fa203236060b90c5fc1364564 +} 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..1690428df289ac55a5a6eaf406a7accc3e6143af +index 0000000000000000000000000000000000000000..3888c76dc16b76ae214aeb7265e8b0e9d600b863 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java @@ -0,0 +1,191 @@ +package me.samsuik.sakura.explosion.special; + -+import io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet; ++import ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import me.samsuik.sakura.entity.EntityState; +import net.minecraft.core.BlockPos; @@ -425,10 +425,10 @@ index 0000000000000000000000000000000000000000..1690428df289ac55a5a6eaf406a7accc + } +} diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index ada18c755cee73620e6c0020674dd0a5a1d8f79d..00259cc930a0b1db38c3abbb5fcf7430d5f50079 100644 +index 0f49093fce5e4eab4e8912d407edca04f0e124a4..82ff83fd00b976d41d1d5b30ad34ede9af1a7e5b 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1964,6 +1964,12 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1702,6 +1702,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. explosion.clearToBlow(); } @@ -441,7 +441,7 @@ index ada18c755cee73620e6c0020674dd0a5a1d8f79d..00259cc930a0b1db38c3abbb5fcf7430 Iterator iterator = this.players.iterator(); while (iterator.hasNext()) { -@@ -1974,7 +1980,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1712,7 +1718,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. } } @@ -451,10 +451,10 @@ index ada18c755cee73620e6c0020674dd0a5a1d8f79d..00259cc930a0b1db38c3abbb5fcf7430 @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 dc9d72bc033735dd83d7b0f3fe79d0a51b9661ab..6cfa54da058939003576025cb0512e0bcc3e715b 100644 +index 71f01c2cb7182764ef4c527b8e84e162ebfd761c..a86ca8e7c66605260d8932b489d5d4fe8c74ab64 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -84,28 +84,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -104,28 +104,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { && (tnt.entityState().fallDistance() == fallDistance || tnt.entityState().fallDistance() > 2.5f && fallDistance > 2.5f); } @@ -485,10 +485,10 @@ index dc9d72bc033735dd83d7b0f3fe79d0a51b9661ab..6cfa54da058939003576025cb0512e0b @Override diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 103eca429d899f4d004ac0cb1996339e66543080..d10d2581c6dfdaf7518bead4f2340c1b63b4bc44 100644 +index 576b7260016b770a62ed9a291dd20bf7063897cd..a376d53dba483b1da96bb468877a958f187a8680 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -56,9 +56,11 @@ public class Explosion { +@@ -57,9 +57,11 @@ public class Explosion { private final Explosion.BlockInteraction blockInteraction; private final RandomSource random; private final Level level; @@ -503,7 +503,7 @@ index 103eca429d899f4d004ac0cb1996339e66543080..d10d2581c6dfdaf7518bead4f2340c1b @Nullable public final Entity source; private final float radius; -@@ -186,7 +188,7 @@ public class Explosion { +@@ -187,7 +189,7 @@ 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); @@ -512,7 +512,7 @@ index 103eca429d899f4d004ac0cb1996339e66543080..d10d2581c6dfdaf7518bead4f2340c1b public static final class ExplosionBlockCache { -@@ -213,7 +215,29 @@ public class Explosion { +@@ -214,7 +216,29 @@ public class Explosion { private long[] chunkPosCache = null; private net.minecraft.world.level.chunk.LevelChunk[] chunkCache = null; @@ -543,7 +543,7 @@ index 103eca429d899f4d004ac0cb1996339e66543080..d10d2581c6dfdaf7518bead4f2340c1b final long key, final boolean calculateResistance) { ExplosionBlockCache ret = this.blockCache.get(key); if (ret != null) { -@@ -311,7 +335,7 @@ public class Explosion { +@@ -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) { @@ -552,7 +552,7 @@ index 103eca429d899f4d004ac0cb1996339e66543080..d10d2581c6dfdaf7518bead4f2340c1b } final BlockState blockState = cachedBlock.blockState; -@@ -502,14 +526,7 @@ public class Explosion { +@@ -503,14 +527,7 @@ public class Explosion { int j; // Paper start - optimise explosions @@ -568,7 +568,7 @@ index 103eca429d899f4d004ac0cb1996339e66543080..d10d2581c6dfdaf7518bead4f2340c1b // use initial cache value that is most likely to be used: the source position final ExplosionBlockCache initialCache; { -@@ -994,7 +1011,7 @@ public class Explosion { +@@ -998,7 +1015,7 @@ public class Explosion { private BlockInteraction() {} } // Paper start - Optimize explosions @@ -578,10 +578,10 @@ index 103eca429d899f4d004ac0cb1996339e66543080..d10d2581c6dfdaf7518bead4f2340c1b 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 d41086a80ea03855fde694947fa61f2d48eaa114..507d676c58daf2297eb729543a7f86c896a984d6 100644 +index 1d822411f6cef0137eeb6383f404500642d3a731..459d8b792c40caaebe38cfcf87533b325e76ceea 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -1407,7 +1407,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1451,7 +1451,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl } Explosion.BlockInteraction explosion_effect1 = explosion_effect; diff --git a/patches/server/0022-Optimise-Fast-Movement.patch b/patches/server/0025-Optimise-Fast-Movement.patch similarity index 92% rename from patches/server/0022-Optimise-Fast-Movement.patch rename to patches/server/0025-Optimise-Fast-Movement.patch index 810dcda..8c8db7e 100644 --- a/patches/server/0022-Optimise-Fast-Movement.patch +++ b/patches/server/0025-Optimise-Fast-Movement.patch @@ -5,12 +5,12 @@ Subject: [PATCH] Optimise Fast Movement diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 88964adf81cd03b26556b50b764caaddd49b59be..e689173f44a06ad320740dab2eceda5ff0e68fe2 100644 +index 190e0022cb45545d8a76f0f7819b6f2be0b64307..348714eb8d74cf917daa5586f0b4962351a96102 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1276,6 +1276,95 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1245,6 +1245,95 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + return this.onGround; } - // Paper end - detailed watchdog information + // Sakura start - stripped back movement method for basic entities + public void moveBasic(MoverType movementType, Vec3 movement) { @@ -40,7 +40,7 @@ index 88964adf81cd03b26556b50b764caaddd49b59be..e689173f44a06ad320740dab2eceda5f + 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. -+ if (this.fallDistance != 0.0F && d0 >= 1.0D && !isFallingBlock) { ++ 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)); + + if (movingobjectpositionblock.getType() != HitResult.Type.MISS) { @@ -65,7 +65,7 @@ index 88964adf81cd03b26556b50b764caaddd49b59be..e689173f44a06ad320740dab2eceda5f + this.minorHorizontalCollision = false; + } + -+ this.setOnGroundWithKnownMovement(this.verticalCollisionBelow, vec3d1); ++ this.setOnGroundWithMovement(this.verticalCollisionBelow, vec3d1); + BlockPos blockposition = this.getOnPosLegacy(); + BlockState iblockdata = this.level().getBlockState(blockposition); + @@ -103,8 +103,8 @@ index 88964adf81cd03b26556b50b764caaddd49b59be..e689173f44a06ad320740dab2eceda5f + public void move(MoverType movementType, Vec3 movement) { final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity - // Paper start - detailed watchdog information -@@ -1654,6 +1743,95 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + if (this.noPhysics) { +@@ -1606,6 +1695,95 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return offsetFactor; } @@ -199,12 +199,12 @@ index 88964adf81cd03b26556b50b764caaddd49b59be..e689173f44a06ad320740dab2eceda5f + private Vec3 collide(Vec3 movement) { // Paper start - optimise collisions - final boolean xZero = movement.x == 0.0; + // Sakura - This is temporary workaround until this patch is updated by Paper. 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 a148befad883e5d524dcf643690f80a0bfd079e5..eec86ec7088079f31a5348089f47f29e7a1adaa8 100644 +index fa95135e3af7fdd610cad2230cc39b547cda0a8a..533b7db0adc9aa9677106f807a992bc09ba60b28 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -@@ -200,7 +200,7 @@ public class FallingBlockEntity extends Entity { +@@ -204,7 +204,7 @@ public class FallingBlockEntity extends Entity { ++this.time; this.applyGravity(); @@ -214,12 +214,12 @@ index a148befad883e5d524dcf643690f80a0bfd079e5..eec86ec7088079f31a5348089f47f29e 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 6cfa54da058939003576025cb0512e0bcc3e715b..d3073a058e4d200b0146d7b72ef3cd56a6a1d8d8 100644 +index a86ca8e7c66605260d8932b489d5d4fe8c74ab64..96878f577174b0ccef7c063153a0787ec3e97177 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -96,7 +96,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { - public void tick() { +@@ -117,7 +117,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 diff --git a/patches/server/0024-isPushedByFluid-API.patch b/patches/server/0026-isPushedByFluid-API.patch similarity index 81% rename from patches/server/0024-isPushedByFluid-API.patch rename to patches/server/0026-isPushedByFluid-API.patch index 290201e..f887b8b 100644 --- a/patches/server/0024-isPushedByFluid-API.patch +++ b/patches/server/0026-isPushedByFluid-API.patch @@ -5,10 +5,10 @@ Subject: [PATCH] isPushedByFluid API diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index e689173f44a06ad320740dab2eceda5ff0e68fe2..cef9b3f34e13fb1b6b80179e12dfb1e03d66e39a 100644 +index 348714eb8d74cf917daa5586f0b4962351a96102..6066121c7bd432228b0503c8200d2dbe891bce29 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -714,6 +714,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -708,6 +708,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } } // Sakura end - cannon entity merging @@ -16,7 +16,7 @@ index e689173f44a06ad320740dab2eceda5ff0e68fe2..cef9b3f34e13fb1b6b80179e12dfb1e0 public Entity(EntityType type, Level world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); -@@ -4305,7 +4306,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4329,7 +4330,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public boolean isPushedByFluid() { @@ -26,10 +26,10 @@ index e689173f44a06ad320740dab2eceda5ff0e68fe2..cef9b3f34e13fb1b6b80179e12dfb1e0 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 a2d336ceb52b63db5c03432ee7bc94dc6a742b82..b51f8f2975918db3a510cb64a93bdb935b8e7510 100644 +index 2cde808bfa797256409879505ba205a71f381981..f06b71b62bef13c9de519fd2758d107241904a1c 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -@@ -201,6 +201,18 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { +@@ -203,6 +203,18 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { return this.entity.isInWater(); } diff --git a/patches/server/0025-Cannon-Mechanics.patch b/patches/server/0027-Cannon-Mechanics.patch similarity index 86% rename from patches/server/0025-Cannon-Mechanics.patch rename to patches/server/0027-Cannon-Mechanics.patch index e3d45c9..15a9e9e 100644 --- a/patches/server/0025-Cannon-Mechanics.patch +++ b/patches/server/0027-Cannon-Mechanics.patch @@ -5,18 +5,18 @@ 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 eec86ec7088079f31a5348089f47f29e7a1adaa8..755c620be47f3f93852ac5e916b14d3c94167f27 100644 +index 533b7db0adc9aa9677106f807a992bc09ba60b28..ab68447f818c0dca777c25c04ac959e5f2b80132 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -@@ -68,6 +68,7 @@ public class FallingBlockEntity extends Entity { - public CompoundTag blockData; +@@ -72,6 +72,7 @@ public class FallingBlockEntity extends Entity { + public boolean forceTickAfterTeleportToDuplicate; protected static final EntityDataAccessor DATA_START_POS = SynchedEntityData.defineId(FallingBlockEntity.class, EntityDataSerializers.BLOCK_POS); public boolean autoExpire = true; // Paper - Expand FallingBlock API + public boolean heightParity; // Sakura - configure cannon mechanics public FallingBlockEntity(EntityType type, Level world) { super(type, world); -@@ -76,6 +77,7 @@ public class FallingBlockEntity extends Entity { +@@ -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 @@ -24,7 +24,7 @@ index eec86ec7088079f31a5348089f47f29e7a1adaa8..755c620be47f3f93852ac5e916b14d3c } public FallingBlockEntity(Level world, double x, double y, double z, BlockState block) { -@@ -185,6 +187,12 @@ public class FallingBlockEntity extends Entity { +@@ -189,6 +191,12 @@ public class FallingBlockEntity extends Entity { return itemEntity; } // Sakura end @@ -38,10 +38,10 @@ index eec86ec7088079f31a5348089f47f29e7a1adaa8..755c620be47f3f93852ac5e916b14d3c @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 d3073a058e4d200b0146d7b72ef3cd56a6a1d8d8..8760cfe061e33ac257c5fcf9c0833ba5b470d95b 100644 +index 96878f577174b0ccef7c063153a0787ec3e97177..ffb453616a5b5664c5b5a8a6410f3096d6c7f4e0 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -52,6 +52,12 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -72,6 +72,12 @@ public class PrimedTnt extends Entity implements TraceableEntity { this.yo = y; this.zo = z; this.owner = igniter; @@ -54,7 +54,7 @@ index d3073a058e4d200b0146d7b72ef3cd56a6a1d8d8..8760cfe061e33ac257c5fcf9c0833ba5 } @Override -@@ -218,7 +224,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -255,7 +261,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { // Paper start - Option to prevent TNT from moving in water @Override public boolean isPushedByFluid() { diff --git a/patches/server/0026-Cache-MovingBlockEntity-collision-shape.patch b/patches/server/0028-Cache-MovingBlockEntity-collision-shape.patch similarity index 100% rename from patches/server/0026-Cache-MovingBlockEntity-collision-shape.patch rename to patches/server/0028-Cache-MovingBlockEntity-collision-shape.patch diff --git a/patches/server/0027-Optimise-TNT-fluid-state-and-pushing.patch b/patches/server/0029-Optimise-TNT-fluid-state-and-pushing.patch similarity index 83% rename from patches/server/0027-Optimise-TNT-fluid-state-and-pushing.patch rename to patches/server/0029-Optimise-TNT-fluid-state-and-pushing.patch index 2c0e9d2..9d80af9 100644 --- a/patches/server/0027-Optimise-TNT-fluid-state-and-pushing.patch +++ b/patches/server/0029-Optimise-TNT-fluid-state-and-pushing.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Optimise TNT fluid state and pushing diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index cef9b3f34e13fb1b6b80179e12dfb1e03d66e39a..e8b850b0608e8d876f0f3415880e4f90ca3b9d85 100644 +index 6066121c7bd432228b0503c8200d2dbe891bce29..0946d71f8ef41f42db038c2efd527a319f55e069 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -2256,7 +2256,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2296,7 +2296,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return this.isInWater() || flag; } @@ -18,10 +18,10 @@ index cef9b3f34e13fb1b6b80179e12dfb1e03d66e39a..e8b850b0608e8d876f0f3415880e4f90 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 8760cfe061e33ac257c5fcf9c0833ba5b470d95b..9d0d73ef60a739d37e2678f9e80398f9f33f6207 100644 +index ffb453616a5b5664c5b5a8a6410f3096d6c7f4e0..1bebee482d0a5ca1a56474660888be01d257b231 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -92,6 +92,19 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -112,6 +112,19 @@ public class PrimedTnt extends Entity implements TraceableEntity { } // Sakura - specialised explosions // Sakura end diff --git a/patches/server/0030-Add-maxSearch-to-getEntities.patch b/patches/server/0030-Add-maxSearch-to-getEntities.patch new file mode 100644 index 0000000..0b52bef --- /dev/null +++ b/patches/server/0030-Add-maxSearch-to-getEntities.patch @@ -0,0 +1,132 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Thu, 27 Jun 2024 17:02:32 +0100 +Subject: [PATCH] Add maxSearch to getEntities + + +diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +index c140e3a73712d2123bc9ed09e9bab25e48c4420e..ee58a65582800be827c8f386ec2d50a2eadad404 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java ++++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +@@ -336,10 +336,16 @@ public final class ChunkEntitySlices { + this.allEntities.getEntities(except, box, into, predicate); + } + ++ // Sakura start - add maxSearch to getEntities ++ public boolean getEntities(final Entity except, final AABB box, final List into, final Predicate predicate, ++ final int maxCount) { ++ return this.getEntities(except, box, into, predicate, maxCount, Integer.MAX_VALUE); ++ } + + public boolean getEntities(final Entity except, final AABB box, final List into, final Predicate predicate, +- final int maxCount) { +- return this.allEntities.getEntitiesWithEnderDragonPartsLimited(except, box, into, predicate, maxCount); ++ final int maxCount, final int maxSearch) { ++ return this.allEntities.getEntitiesWithEnderDragonPartsLimited(except, box, into, predicate, maxCount, maxSearch); ++ // Sakura end - add maxSearch to getEntities + } + + public boolean getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List into, final Predicate predicate, +@@ -693,8 +699,16 @@ public final class ChunkEntitySlices { + } + } + ++ // Sakura start - add maxSearch to getEntities + public boolean getEntitiesWithEnderDragonPartsLimited(final Entity except, final AABB box, final List into, + final Predicate predicate, final int maxCount) { ++ return this.getEntitiesWithEnderDragonPartsLimited(except, box, into, predicate, maxCount, Integer.MAX_VALUE); ++ } ++ ++ public boolean getEntitiesWithEnderDragonPartsLimited(final Entity except, final AABB box, final List into, ++ final Predicate predicate, final int maxCount, ++ final int maxSearch) { ++ // Sakura end - add maxSearch to getEntities + if (this.count == 0) { + return false; + } +@@ -716,8 +730,14 @@ public final class ChunkEntitySlices { + + final Entity[] storage = list.storage; + +- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { +- final Entity entity = storage[i]; ++ // Sakura start - add maxSearch to getEntities ++ final int len = Math.min(storage.length, list.size()); ++ final int offset = this.slices.world.random.nextInt(len); ++ for (int i = 0; i < len; ++i) { ++ final int pos = (i + offset) % len; ++ final Entity entity = storage[pos]; ++ if (i > maxSearch) break; ++ // Sakura end - add maxSearch to getEntities + + 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 +--- 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 { + } + } + ++ // Sakura start - add maxSearch to getEntities + public void getEntities(final Entity except, final AABB box, final List into, final Predicate predicate, + final int maxCount) { ++ this.getEntities(except, box, into, predicate, maxCount, Integer.MAX_VALUE); ++ } ++ ++ public void getEntities(final Entity except, final AABB box, final List into, final Predicate predicate, ++ final int maxCount, final int maxSearch) { ++ // Sakura end - add maxSearch to getEntities + 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 { + continue; + } + +- if (chunk.getEntities(except, box, into, predicate, maxCount)) { ++ if (chunk.getEntities(except, box, into, predicate, maxCount, maxSearch)) { // Sakura - add maxSearch to getEntities + return; + } + } +@@ -1041,4 +1048,4 @@ public abstract class EntityLookup implements LevelEntityGetter { + @Override + public void onRemove(final Entity.RemovalReason reason) {} + } +-} +\ 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 +--- 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 + this.getEntities(filter, box, predicate, result, Integer.MAX_VALUE); + } + +- // Paper start - rewrite chunk system ++ // Sakura start - add maxSearch to getEntities + public void getEntities(final EntityTypeTest entityTypeTest, + final AABB boundingBox, final Predicate predicate, + final List into, final int maxCount) { ++ this.getEntities(entityTypeTest, boundingBox, predicate, into, maxCount, Integer.MAX_VALUE); ++ } ++ ++ // Paper start - rewrite chunk system ++ public void getEntities(final EntityTypeTest entityTypeTest, ++ final AABB boundingBox, final Predicate predicate, ++ final List into, final int maxCount, final int maxSearch) { ++ // Sakura end - add maxSearch to getEntities + 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 + + if (entityTypeTest == null) { + if (maxCount != Integer.MAX_VALUE) { +- ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities((Entity)null, boundingBox, (List)into, (Predicate)predicate, maxCount); ++ ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities((Entity)null, boundingBox, (List)into, (Predicate)predicate, maxCount, maxSearch); // Sakura - add maxSearch to getEntities + return; + } else { + ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities((Entity)null, boundingBox, (List)into, (Predicate)predicate); diff --git a/patches/server/0030-Configure-Entity-Knockback.patch b/patches/server/0030-Configure-Entity-Knockback.patch deleted file mode 100644 index 4751b44..0000000 --- a/patches/server/0030-Configure-Entity-Knockback.patch +++ /dev/null @@ -1,134 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik <40902469+Samsuik@users.noreply.github.com> -Date: Wed, 15 Nov 2023 20:52:56 +0000 -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 a7bbd4d1ddb7e80724cdd05a6d6b37161e711264..72e32af2d329a4f27f91932fec0d3a92fae414fb 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1562,7 +1562,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - d0 = (Math.random() - Math.random()) * 0.01D; - } - -- 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); - } -@@ -1616,7 +1616,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - } - - protected void blockedByShield(LivingEntity target) { -- target.knockback(0.5D, target.getX() - this.getX(), target.getZ() - this.getZ(), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.SHIELD_BLOCK); // CraftBukkit // Paper - fix attacker & knockback events -+ target.knockback((float) this.level().sakuraConfig().players.knockback.shieldHitKnockback, target.getX() - this.getX(), target.getZ() - this.getZ(), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.SHIELD_BLOCK); // CraftBukkit // Paper - fix attacker & knockback events // Sakura - configure entity knockback - } - - private boolean checkTotemDeathProtection(DamageSource source) { -@@ -1934,14 +1934,26 @@ 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 -- d0 *= 1.0D - this.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE); -+ d0 *= 1.0D - this.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE) * this.level().sakuraConfig().players.knockback.knockbackResistanceModifier; // Sakura - configure entity knockback - if (true || d0 > 0.0D) { // CraftBukkit - Call event even when force is 0 - //this.hasImpulse = true; // CraftBukkit - Move down - Vec3 vec3d = this.getDeltaMovement(); - Vec3 vec3d1 = (new Vec3(d1, 0.0D, d2)).normalize().scale(d0); -+ // Sakura start - configure entity knockback -+ double velocityX = vec3d.x / 2.0D - vec3d1.x; -+ double velocityY = vec3d.y / 2.0D + this.level().sakuraConfig().players.knockback.knockbackVertical.or(d0); -+ double velocityZ = vec3d.z / 2.0D - vec3d1.z; -+ -+ // this.onGround() ? Math.min(0.4D, vec3d.y / 2.0D + d0) : vec3d.y -+ if (!this.level().sakuraConfig().players.knockback.verticalKnockbackRequireGround || this.onGround()) { -+ velocityY = Math.min(this.level().sakuraConfig().players.knockback.knockbackVerticalLimit, velocityY); -+ } else { -+ velocityY = vec3d.y; -+ } -+ // Sakura end - configure entity knockback - - // Paper start - knockback events -- Vec3 finalVelocity = new Vec3(vec3d.x / 2.0D - vec3d1.x, this.onGround() ? Math.min(0.4D, vec3d.y / 2.0D + d0) : vec3d.y, vec3d.z / 2.0D - vec3d1.z); -+ Vec3 finalVelocity = new Vec3(velocityX, velocityY, velocityZ); // Sakura - configure entity knockback - Vec3 diff = finalVelocity.subtract(vec3d); - 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 c34de2a7303746ae236b7e81d87e7af42242baa7..3e0958f7b71fd7693d8f10d9b52144c32a039a0b 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -195,6 +195,7 @@ public abstract class Player extends LivingEntity { - public boolean ignoreFallDamageFromCurrentImpulse; - 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 -+ private long lastSprintKnockback = -1; // Sakura - configure entity knockback - - // CraftBukkit start - public boolean fauxSleeping; -@@ -1290,7 +1291,7 @@ public abstract class Player extends LivingEntity { - int i = 0; - - i += EnchantmentHelper.getKnockbackBonus(this); -- if (this.isSprinting() && flag) { -+ if (this.isSprinting() && (!level().sakuraConfig().players.knockback.sprinting.requireFullAttack || flag)) { // Sakura - configure entity knockback - sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_KNOCKBACK, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility - ++i; - flag1 = true; -@@ -1340,10 +1341,26 @@ public abstract class Player extends LivingEntity { - - if (flag5) { - if (i > 0) { -- if (target instanceof LivingEntity) { -- ((LivingEntity) target).knockback((double) ((float) i * 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); // CraftBukkit // Paper - knockback events -+ // Sakura start - configure extra sprinting knockback -+ float extraKnockback = (float) i * 0.5F; -+ long millis = System.currentTimeMillis(); -+ long sinceLastKnockback = millis - this.lastSprintKnockback; -+ if (flag1) { // attackHasExtraKnockback -+ double knockbackToApply = 0.0; -+ if (sinceLastKnockback >= this.level().sakuraConfig().players.knockback.sprinting.knockbackDelay.value().orElse(0)) { -+ knockbackToApply = this.level().sakuraConfig().players.knockback.sprinting.extraKnockback; -+ this.lastSprintKnockback = millis; -+ } -+ // -0.5 is to negate the vanilla sprinting knockback -+ extraKnockback += -0.5F + (float) knockbackToApply; -+ } -+ if (extraKnockback == 0.0) { -+ // required -+ } else if (target instanceof LivingEntity) { -+ ((LivingEntity) target).knockback((double) (extraKnockback), (double) Mth.sin(this.getYRot() * 0.017453292F), (double) (-Mth.cos(this.getYRot() * 0.017453292F)), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // CraftBukkit // Paper - knockback events - } else { -- target.push((double) (-Mth.sin(this.getYRot() * 0.017453292F) * (float) i * 0.5F), 0.1D, (double) (Mth.cos(this.getYRot() * 0.017453292F) * (float) i * 0.5F), this); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent -+ target.push((double) (-Mth.sin(this.getYRot() * 0.017453292F) * extraKnockback), 0.1D, (double) (Mth.cos(this.getYRot() * 0.017453292F) * extraKnockback), this); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent -+ // Sakura end - configure extra sprinting knockback - } - - this.setDeltaMovement(this.getDeltaMovement().multiply(0.6D, 1.0D, 0.6D)); -@@ -1365,7 +1382,7 @@ public abstract class Player extends LivingEntity { - if (entityliving != this && entityliving != target && !this.isAlliedTo((Entity) entityliving) && (!(entityliving instanceof ArmorStand) || !((ArmorStand) entityliving).isMarker()) && this.distanceToSqr((Entity) entityliving) < 9.0D) { - // CraftBukkit start - Only apply knockback if the damage hits - if (entityliving.hurt(this.damageSources().playerAttack(this).sweep().critical(flag2), f4)) { // Paper - add critical damage API -- entityliving.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 -+ entityliving.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); // CraftBukkit // Paper - knockback events // Sakura - configure entity knockback - } - // CraftBukkit end - } -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 7dd5e0b935d98d552c916f8412569ff4aa0e9b04..d9deb1b47b2430936d74e782cfc4a6ac32a16c91 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java -@@ -298,6 +298,12 @@ public class FishingHook extends Projectile { - this.setHookedEntity(entityHitResult.getEntity()); - } - -+ // Sakura start - configure entity knockback -+ if (this.level().sakuraConfig().players.knockback.fishingHooksApplyKnockback) { -+ Entity entity = entityHitResult.getEntity(); -+ entity.hurt(this.damageSources().thrown(this, this.getOwner()), 0.0f); -+ } -+ // Sakura end - configure entity knockback - } - - @Override diff --git a/patches/server/0028-Optimise-LivingEntity-pushEntities.patch b/patches/server/0031-Optimise-LivingEntity-pushEntities.patch similarity index 72% rename from patches/server/0028-Optimise-LivingEntity-pushEntities.patch rename to patches/server/0031-Optimise-LivingEntity-pushEntities.patch index 8399b2b..73ce316 100644 --- a/patches/server/0028-Optimise-LivingEntity-pushEntities.patch +++ b/patches/server/0031-Optimise-LivingEntity-pushEntities.patch @@ -5,19 +5,20 @@ 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 21e61bb75ac7ce468bc757633ce678b21bcb9deb..a7bbd4d1ddb7e80724cdd05a6d6b37161e711264 100644 +index e980c8c356b30d25e2fc5a73b91ad2c6edd4fe05..cdfcd8e3e7e357151b8a1d5ed8b051d6cf550fc1 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -3554,7 +3554,11 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3574,7 +3574,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 - List list = this.level().getEntities((Entity) this, this.getBoundingBox(), EntitySelector.pushable(this, this.level().paperConfig().collisions.fixClimbingBypassingCrammingRule)); // Paper - Climbing should not bypass cramming gamerule -+ // Sakura start - use maxEntityCollision limit for entity retrival ++ // Sakura start - use maxEntityCollision limit for entity retrieval + int limit = Math.max(i, this.level().paperConfig().collisions.maxEntityCollisions); + int search = limit * limit; -+ List list = this.level().getLimitedEntities((Entity) this, this.getBoundingBox(), EntitySelector.pushable(this, this.level().paperConfig().collisions.fixClimbingBypassingCrammingRule), limit, search); // Paper - Climbing should not bypass cramming gamerule -+ // Sakura end - use maxEntityCollision limit for entity retrival ++ List list = new ArrayList<>(); ++ this.level().getEntities(null, this.getBoundingBox(), EntitySelector.pushable(this, this.level().paperConfig().collisions.fixClimbingBypassingCrammingRule), list, limit, search); // Paper - Climbing should not bypass cramming gamerule ++ // Sakura end - use maxEntityCollision limit for entity retrieval if (!list.isEmpty()) { // Paper - don't run getEntities if we're not going to use its result; moved up diff --git a/patches/server/0029-Despawn-falling-blocks-inside-moving-pistons.patch b/patches/server/0032-Despawn-falling-blocks-inside-moving-pistons.patch similarity index 89% rename from patches/server/0029-Despawn-falling-blocks-inside-moving-pistons.patch rename to patches/server/0032-Despawn-falling-blocks-inside-moving-pistons.patch index 793b5dd..7cc0b78 100644 --- a/patches/server/0029-Despawn-falling-blocks-inside-moving-pistons.patch +++ b/patches/server/0032-Despawn-falling-blocks-inside-moving-pistons.patch @@ -5,10 +5,10 @@ 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 fe21305f66cc60e2036d2023f5f49e4a23e014dc..b1193e2d8b43e66cc5a5a03bcc0e77577d98b863 100644 +index ab68447f818c0dca777c25c04ac959e5f2b80132..3e861f3546fee1dc4129abf3b72cf57a593db91c 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -@@ -234,7 +234,7 @@ public class FallingBlockEntity extends Entity { +@@ -239,7 +239,7 @@ public class FallingBlockEntity extends Entity { } } diff --git a/patches/server/0033-Configure-Entity-Knockback.patch b/patches/server/0033-Configure-Entity-Knockback.patch new file mode 100644 index 0000000..7588394 --- /dev/null +++ b/patches/server/0033-Configure-Entity-Knockback.patch @@ -0,0 +1,130 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik <40902469+Samsuik@users.noreply.github.com> +Date: Wed, 15 Nov 2023 20:52:56 +0000 +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 +--- 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(); + } + +- 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 { + } + + protected void blockedByShield(LivingEntity target) { +- target.knockback(0.5D, target.getX() - this.getX(), target.getZ() - this.getZ(), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.SHIELD_BLOCK); // CraftBukkit // Paper - fix attacker & knockback events ++ target.knockback((float) this.level().sakuraConfig().players.knockback.shieldHitKnockback, target.getX() - this.getX(), target.getZ() - this.getZ(), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.SHIELD_BLOCK); // CraftBukkit // Paper - fix attacker & knockback events // Sakura - configure entity knockback + } + + private boolean checkTotemDeathProtection(DamageSource source) { +@@ -1918,7 +1918,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 +- d0 *= 1.0D - this.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE); ++ d0 *= 1.0D - this.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE) * this.level().sakuraConfig().players.knockback.knockbackResistanceModifier; // Sakura - configure entity knockback + 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 { + } + + Vec3 vec3d1 = (new Vec3(d1, 0.0D, d2)).normalize().scale(d0); ++ // Sakura start - configure entity knockback ++ double velocityX = vec3d.x / 2.0D - vec3d1.x; ++ double velocityY = vec3d.y / 2.0D + this.level().sakuraConfig().players.knockback.knockbackVertical.or(d0); ++ double velocityZ = vec3d.z / 2.0D - vec3d1.z; ++ ++ // this.onGround() ? Math.min(0.4D, vec3d.y / 2.0D + d0) : vec3d.y ++ if (!this.level().sakuraConfig().players.knockback.verticalKnockbackRequireGround || this.onGround()) { ++ velocityY = Math.min(this.level().sakuraConfig().players.knockback.knockbackVerticalLimit, velocityY); ++ } else { ++ velocityY = vec3d.y; ++ } ++ // Sakura end - configure entity knockback + + // Paper start - knockback events +- Vec3 finalVelocity = new Vec3(vec3d.x / 2.0D - vec3d1.x, this.onGround() ? Math.min(0.4D, vec3d.y / 2.0D + d0) : vec3d.y, vec3d.z / 2.0D - vec3d1.z); ++ Vec3 finalVelocity = new Vec3(velocityX, velocityY, velocityZ); // Sakura - configure entity knockback + Vec3 diff = finalVelocity.subtract(vec3d); + 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 +--- 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 { + 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 ++ private long lastSprintKnockback = -1; // Sakura - configure entity knockback + + // CraftBukkit start + public boolean fauxSleeping; +@@ -1303,7 +1304,7 @@ public abstract class Player extends LivingEntity { + boolean flag = f2 > 0.9F; + boolean flag1; + +- if (this.isSprinting() && flag) { ++ if (this.isSprinting() && (!this.level().sakuraConfig().players.knockback.sprinting.requireFullAttack || flag)) { // Sakura - configure entity knockback + 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 { + 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 ++ long millis = System.currentTimeMillis(); ++ long sinceLastKnockback = millis - this.lastSprintKnockback; ++ if (flag1) { // attackHasExtraKnockback ++ double knockbackToApply = 0.0; ++ if (sinceLastKnockback >= this.level().sakuraConfig().players.knockback.sprinting.knockbackDelay.value().orElse(0)) { ++ knockbackToApply = this.level().sakuraConfig().players.knockback.sprinting.extraKnockback; ++ this.lastSprintKnockback = millis; ++ } ++ f5 = (f5 - 1.0f) + ((float) knockbackToApply * 2.0f); ++ } ++ if (f5 == 0.0f) { ++ // required ++ } else if (target instanceof LivingEntity) { ++ // Sakura end - configure 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 + } + // CraftBukkit end + 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 ++++ b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java +@@ -299,6 +299,12 @@ public class FishingHook extends Projectile { + this.setHookedEntity(entityHitResult.getEntity()); + } + ++ // Sakura start - configure entity knockback ++ if (this.level().sakuraConfig().players.knockback.fishingHooksApplyKnockback) { ++ Entity entity = entityHitResult.getEntity(); ++ entity.hurt(this.damageSources().thrown(this, this.getOwner()), 0.0f); ++ } ++ // Sakura end - configure entity knockback + } + + @Override diff --git a/patches/server/0031-Explosion-Durable-Blocks.patch b/patches/server/0034-Explosion-Durable-Blocks.patch similarity index 91% rename from patches/server/0031-Explosion-Durable-Blocks.patch rename to patches/server/0034-Explosion-Durable-Blocks.patch index fd01044..0f95e41 100644 --- a/patches/server/0031-Explosion-Durable-Blocks.patch +++ b/patches/server/0034-Explosion-Durable-Blocks.patch @@ -74,10 +74,10 @@ index 0000000000000000000000000000000000000000..c58e52f7cc012babf4235e405e5fb501 + +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 9257505431d00aec2225b052492b8eb88ed4d63d..a342a604ad58e3ecc8907e7ec6d6fbf58c31d246 100644 +index 2df0f8a92174be654acb27785ff1f4880dcf3362..6ca79a23d7be043166a421aab881c55ef1238a4c 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1805,6 +1805,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop predicate, List into, int limit, int search) { + @Override + public final ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup moonrise$getEntityLookup() { diff --git a/patches/server/0032-Destroy-Waterlogged-Blocks.patch b/patches/server/0035-Destroy-Waterlogged-Blocks.patch similarity index 89% rename from patches/server/0032-Destroy-Waterlogged-Blocks.patch rename to patches/server/0035-Destroy-Waterlogged-Blocks.patch index dabebc6..57a333d 100644 --- a/patches/server/0032-Destroy-Waterlogged-Blocks.patch +++ b/patches/server/0035-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 243ac586aad29e3a145eec3c9a85b24901450121..c2edd4021fe2bdf96f58f6d402b767f911801cf6 100644 +index f87b7a27c1d735ccb836f13dcb17156cbaac0231..210ab5feebd3f1554d8dc7191ab092937b218f5d 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -284,7 +284,11 @@ public class Explosion { +@@ -285,7 +285,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/0033-Cache-Vanillia-and-Eigen-Redstone.patch b/patches/server/0036-Cache-Vanillia-and-Eigen-Redstone.patch similarity index 96% rename from patches/server/0033-Cache-Vanillia-and-Eigen-Redstone.patch rename to patches/server/0036-Cache-Vanillia-and-Eigen-Redstone.patch index 842623c..45d2056 100644 --- a/patches/server/0033-Cache-Vanillia-and-Eigen-Redstone.patch +++ b/patches/server/0036-Cache-Vanillia-and-Eigen-Redstone.patch @@ -343,10 +343,10 @@ index 0000000000000000000000000000000000000000..4f5af6e241b0194ed982144fd9320315 + +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 786a08850dcb31dc1d08b16391e40914bb4c6235..1a6466d655f981b19bbcaa3ff1724a8d99044289 100644 +index 6ca79a23d7be043166a421aab881c55ef1238a4c..1c875d1ba3ead33fc3f9f369fc89bcbeb82c6844 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1806,6 +1806,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop predicate, List into, int limit, int search) { -@@ -994,6 +995,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + @Override + public final ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup moonrise$getEntityLookup() { +@@ -1012,6 +1013,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl } else { BlockState iblockdata2 = this.getBlockState(pos); diff --git a/patches/server/0034-Falling-Block-Stacking-Restrictions.patch b/patches/server/0037-Falling-Block-Stacking-Restrictions.patch similarity index 89% rename from patches/server/0034-Falling-Block-Stacking-Restrictions.patch rename to patches/server/0037-Falling-Block-Stacking-Restrictions.patch index 02c5623..a447185 100644 --- a/patches/server/0034-Falling-Block-Stacking-Restrictions.patch +++ b/patches/server/0037-Falling-Block-Stacking-Restrictions.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Falling Block Stacking Restrictions 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 b1193e2d8b43e66cc5a5a03bcc0e77577d98b863..d22c1357006d2785ff2a0e9465a214cf5a262cb1 100644 +index 3e861f3546fee1dc4129abf3b72cf57a593db91c..abf41ee186fd7d8e0963112b886d833496c2fb1a 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -@@ -252,7 +252,7 @@ public class FallingBlockEntity extends Entity { +@@ -257,7 +257,7 @@ public class FallingBlockEntity extends Entity { boolean flag3 = FallingBlock.isFree(this.level().getBlockState(blockposition.below())) && (!flag || !flag1); boolean flag4 = this.blockState.canSurvive(this.level(), blockposition) && !flag3; diff --git a/patches/server/0037-Reduce-living-entity-sensing.patch b/patches/server/0037-Reduce-living-entity-sensing.patch deleted file mode 100644 index 3ca881a..0000000 --- a/patches/server/0037-Reduce-living-entity-sensing.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik <40902469+Samsuik@users.noreply.github.com> -Date: Sat, 18 Nov 2023 17:24:19 +0000 -Subject: [PATCH] Reduce living entity sensing - - -diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -index 5a059e1ec232d82e8e891ae78fea962bec2f878e..43a5d969595e8ecb3da3cf2ac949f042a7015578 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -@@ -15,8 +15,21 @@ public class NearestLivingEntitySensor extends Sensor - @Override - protected void doTick(ServerLevel world, T entity) { - AABB aABB = entity.getBoundingBox().inflate((double)this.radiusXZ(), (double)this.radiusY(), (double)this.radiusXZ()); -- List list = world.getEntitiesOfClass(LivingEntity.class, aABB, e -> e != entity && e.isAlive()); -- list.sort(Comparator.comparingDouble(entity::distanceToSqr)); -+ // Sakura start - reduce nearest living entity sensing -+ var distanceMap = new it.unimi.dsi.fastutil.objects.Reference2DoubleOpenHashMap<>(); -+ distanceMap.defaultReturnValue(Double.MAX_VALUE); -+ List list = world.getLimitedEntitiesOfClass(LivingEntity.class, aABB, (e) -> { -+ if (e == entity || !e.isAlive()) -+ return false; -+ double stored = distanceMap.getDouble(e.getType()); -+ double distance = e.distanceToSqr(entity); -+ if (stored < distance) -+ return false; -+ distanceMap.put(e.getType(), distance); -+ return true; -+ }, 12, Integer.MAX_VALUE); -+ java.util.Collections.reverse(list); -+ // Sakura end - reduce nearest living entity sensing - Brain brain = entity.getBrain(); - brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, list); - brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(entity, list)); diff --git a/patches/server/0035-Added-list-of-ItemEntity-s-that-ignore-explosions.patch b/patches/server/0038-Added-list-of-ItemEntity-s-that-ignore-explosions.patch similarity index 86% rename from patches/server/0035-Added-list-of-ItemEntity-s-that-ignore-explosions.patch rename to patches/server/0038-Added-list-of-ItemEntity-s-that-ignore-explosions.patch index f074799..d5b1862 100644 --- a/patches/server/0035-Added-list-of-ItemEntity-s-that-ignore-explosions.patch +++ b/patches/server/0038-Added-list-of-ItemEntity-s-that-ignore-explosions.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Added list of ItemEntity's that ignore explosions diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -index 8fd3845c4965843be9c37498760d93f1ebdff541..4e3775f47b6124fd0e3ebc2074c5dc35a05e1249 100644 +index ea0d9335446b20073b9aafb9de453097355db79c..d3b3194751df29ac5bd12339dc64695d308780d3 100644 --- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -@@ -590,6 +590,17 @@ public class ItemEntity extends Entity implements TraceableEntity { +@@ -591,6 +591,17 @@ public class ItemEntity extends Entity implements TraceableEntity { } diff --git a/patches/server/0036-Add-option-to-disable-entity-ai.patch b/patches/server/0039-Add-option-to-disable-entity-ai.patch similarity index 81% rename from patches/server/0036-Add-option-to-disable-entity-ai.patch rename to patches/server/0039-Add-option-to-disable-entity-ai.patch index 4e0ab2b..7aa4024 100644 --- a/patches/server/0036-Add-option-to-disable-entity-ai.patch +++ b/patches/server/0039-Add-option-to-disable-entity-ai.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add option to disable entity ai diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index e89f9c3e887601d8461eb967ae0bf582b672f631..cf3889188d1e31d121f86b1f01459f3029e8802f 100644 +index 7b93c6a04cca2ac31d137f06ef83bb08559b10bf..8b432763d382f3134ecbab67850d8acdc0c9ff4f 100644 --- a/src/main/java/net/minecraft/world/entity/Mob.java +++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -955,7 +955,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -910,7 +910,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab protected final void serverAiStep() { ++this.noActionTime; // Paper start - Allow nerfed mobs to jump and float diff --git a/patches/server/0038-Consistent-Explosion-Radius.patch b/patches/server/0040-Consistent-Explosion-Radius.patch similarity index 88% rename from patches/server/0038-Consistent-Explosion-Radius.patch rename to patches/server/0040-Consistent-Explosion-Radius.patch index 433ed5f..a7737f3 100644 --- a/patches/server/0038-Consistent-Explosion-Radius.patch +++ b/patches/server/0040-Consistent-Explosion-Radius.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Consistent Explosion Radius diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 9d9fe71897839d5ee7f3ec7467616021a8791a29..f7e01cb367b5b75f1f267d727b539066845d6e14 100644 +index 210ab5feebd3f1554d8dc7191ab092937b218f5d..b35a51c8e51103260f9d250e318af8a00c21533f 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -75,6 +75,7 @@ public class Explosion { +@@ -76,6 +76,7 @@ public class Explosion { public boolean wasCanceled = false; public float yield; // CraftBukkit end @@ -16,7 +16,7 @@ index 9d9fe71897839d5ee7f3ec7467616021a8791a29..f7e01cb367b5b75f1f267d727b539066 public static DamageSource getDefaultDamageSource(Level world, @Nullable Entity source) { return world.damageSources().explosion(source, Explosion.getIndirectSourceEntityInternal(source)); -@@ -112,6 +113,7 @@ public class Explosion { +@@ -113,6 +114,7 @@ public class Explosion { this.largeExplosionParticles = emitterParticle; this.explosionSound = soundEvent; this.yield = this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F; // CraftBukkit @@ -24,7 +24,7 @@ index 9d9fe71897839d5ee7f3ec7467616021a8791a29..f7e01cb367b5b75f1f267d727b539066 } // Sakura start - optimise paper explosions -@@ -585,7 +587,7 @@ public class Explosion { +@@ -586,7 +588,7 @@ public class Explosion { double d2 = CACHED_RAYS[ray + 2]; ray += 3; // Paper end - optimise explosions diff --git a/patches/server/0039-Remove-spigot-max-tnt-per-tick.patch b/patches/server/0041-Remove-spigot-max-tnt-per-tick.patch similarity index 81% rename from patches/server/0039-Remove-spigot-max-tnt-per-tick.patch rename to patches/server/0041-Remove-spigot-max-tnt-per-tick.patch index c86fbe2..2316c93 100644 --- a/patches/server/0039-Remove-spigot-max-tnt-per-tick.patch +++ b/patches/server/0041-Remove-spigot-max-tnt-per-tick.patch @@ -5,15 +5,15 @@ Subject: [PATCH] Remove spigot max tnt per tick 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 bfe1b2debf94c54f923daf81ad51a7e9ca92d3d1..30443bc74abbe0a557b5d9d9bae0eb48ef1dd7d3 100644 +index 1bebee482d0a5ca1a56474660888be01d257b231..a5b7f3bf42e43a9b3673e8725c5f20f7a3e7dbe6 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -113,7 +113,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -133,7 +133,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { @Override public void tick() { - if (this.level().spigotConfig.maxTntTicksPerTick > 0 && ++this.level().spigotConfig.currentPrimedTnt > this.level().spigotConfig.maxTntTicksPerTick) { return; } // Spigot + // Sakura - remove max tnt per tick + this.handlePortal(); this.applyGravity(); this.moveBasic(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise simple entity movement - // Paper start - Configurable TNT height nerf diff --git a/patches/server/0040-Option-to-configure-entity-water-sensitivity.patch b/patches/server/0042-Option-to-configure-entity-water-sensitivity.patch similarity index 85% rename from patches/server/0040-Option-to-configure-entity-water-sensitivity.patch rename to patches/server/0042-Option-to-configure-entity-water-sensitivity.patch index ae8fc8d..a00b262 100644 --- a/patches/server/0040-Option-to-configure-entity-water-sensitivity.patch +++ b/patches/server/0042-Option-to-configure-entity-water-sensitivity.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Option to configure entity water sensitivity diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 72e32af2d329a4f27f91932fec0d3a92fae414fb..2fb84d928884050c63cfa598d45dda39a7c2cec4 100644 +index 1ac872c7cc0330d11d995849513b12a7adbe3126..57929f4c2642d1a66f5158a59a5b905e977b2fa2 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -3503,7 +3503,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3523,7 +3523,7 @@ public abstract class LivingEntity extends Entity implements Attackable { } } // Paper end - Add EntityMoveEvent diff --git a/patches/server/0041-Configure-cannon-physics-by-version.patch b/patches/server/0043-Configure-cannon-physics-by-version.patch similarity index 93% rename from patches/server/0041-Configure-cannon-physics-by-version.patch rename to patches/server/0043-Configure-cannon-physics-by-version.patch index f3922cf..269cbaf 100644 --- a/patches/server/0041-Configure-cannon-physics-by-version.patch +++ b/patches/server/0043-Configure-cannon-physics-by-version.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configure cannon physics by version diff --git a/src/main/java/io/papermc/paper/util/CollisionUtil.java b/src/main/java/io/papermc/paper/util/CollisionUtil.java -index 299237a0c828e48425cc35a14d366020c78daefb..8014ebfb391825c31d9d1b39f5304a7e76b1ee44 100644 +index f941c4bbf94f80dc3db3153297713a41c6c8ca48..d8d98cd2290f4af61a45a1150f8230a2c6b54352 100644 --- a/src/main/java/io/papermc/paper/util/CollisionUtil.java +++ b/src/main/java/io/papermc/paper/util/CollisionUtil.java @@ -1457,7 +1457,15 @@ public final class CollisionUtil { @@ -68,7 +68,7 @@ index 299237a0c828e48425cc35a14d366020c78daefb..8014ebfb391825c31d9d1b39f5304a7e 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 d15eb10fa203236060b90c5fc1364564dec8753f..d67f9d241f6070a2c391df52146d48434e7e096a 100644 +index 55bf4424d8e995ea7384f88f43465f90ae3c6704..eaef186a6f8857e442e33087f323ea878f5799b1 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 { @@ -89,7 +89,7 @@ index d15eb10fa203236060b90c5fc1364564dec8753f..d67f9d241f6070a2c391df52146d4843 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 1690428df289ac55a5a6eaf406a7accc3e6143af..3fd2633601baed389ca1f503022ecc14109241d9 100644 +index 3888c76dc16b76ae214aeb7265e8b0e9d600b863..31700b381d495c1d1273ab45e914fdb788e712c8 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 { @@ -107,10 +107,10 @@ index 1690428df289ac55a5a6eaf406a7accc3e6143af..3fd2633601baed389ca1f503022ecc14 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 50ae421b40681366523932a2cd445592ec067897..df083dd2f9af19c768b23727852194e68a53fd7d 100644 +index 0946d71f8ef41f42db038c2efd527a319f55e069..2b94b7bf6cc132a54596ad71c8e2fa801e2eba77 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -381,7 +381,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -385,7 +385,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess private final double[] pistonDeltas; private long pistonDeltasGameTime; private EntityDimensions dimensions; @@ -119,7 +119,7 @@ index 50ae421b40681366523932a2cd445592ec067897..df083dd2f9af19c768b23727852194e6 public boolean isInPowderSnow; public boolean wasInPowderSnow; public boolean wasOnFire; -@@ -715,6 +715,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -709,6 +709,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Sakura end - cannon entity merging public boolean pushedByFluid = true; // Sakura - entity pushed by fluid api @@ -133,7 +133,7 @@ index 50ae421b40681366523932a2cd445592ec067897..df083dd2f9af19c768b23727852194e6 public Entity(EntityType type, Level world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); -@@ -1222,7 +1229,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1216,7 +1223,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } protected void checkSupportingBlock(boolean onGround, @Nullable Vec3 movement) { @@ -142,7 +142,7 @@ index 50ae421b40681366523932a2cd445592ec067897..df083dd2f9af19c768b23727852194e6 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); -@@ -1284,7 +1291,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1253,7 +1260,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,7 +151,7 @@ index 50ae421b40681366523932a2cd445592ec067897..df083dd2f9af19c768b23727852194e6 movement = this.limitPistonMovement(movement); if (movement.equals(Vec3.ZERO)) { return; -@@ -1302,10 +1309,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1271,10 +1278,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess Vec3 vec3d1 = this.collideScan(movement); double d0 = vec3d1.lengthSqr(); @@ -159,12 +159,12 @@ index 50ae421b40681366523932a2cd445592ec067897..df083dd2f9af19c768b23727852194e6 + 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. -- if (this.fallDistance != 0.0F && d0 >= 1.0D && !isFallingBlock) { -+ if (this.fallDistance != 0.0F && d0 >= 1.0D && !isFallingBlock && this.physics.afterOrEqual(1_18_2)) { // Sakura - physics version api +- 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) { -@@ -1341,6 +1348,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1310,6 +1317,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess if (this.horizontalCollision) { Vec3 vec3d2 = this.getDeltaMovement(); @@ -177,7 +177,7 @@ index 50ae421b40681366523932a2cd445592ec067897..df083dd2f9af19c768b23727852194e6 this.setDeltaMovement(flag ? 0.0D : vec3d2.x, vec3d2.y, flag1 ? 0.0D : vec3d2.z); } -@@ -1382,7 +1395,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1341,7 +1354,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 +186,7 @@ index 50ae421b40681366523932a2cd445592ec067897..df083dd2f9af19c768b23727852194e6 this.activatedTick = Math.max(this.activatedTick, MinecraftServer.currentTick + 20); // Paper this.activatedImmunityTick = Math.max(this.activatedImmunityTick, MinecraftServer.currentTick + 20); // Paper movement = this.limitPistonMovement(movement); -@@ -1409,8 +1422,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1368,8 +1381,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess Vec3 vec3d1 = this.collide(movement); double d0 = vec3d1.lengthSqr(); @@ -197,7 +197,7 @@ index 50ae421b40681366523932a2cd445592ec067897..df083dd2f9af19c768b23727852194e6 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) { -@@ -1446,6 +1459,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1405,6 +1418,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess if (this.horizontalCollision) { Vec3 vec3d2 = this.getDeltaMovement(); @@ -210,7 +210,7 @@ index 50ae421b40681366523932a2cd445592ec067897..df083dd2f9af19c768b23727852194e6 this.setDeltaMovement(flag ? 0.0D : vec3d2.x, vec3d2.y, flag1 ? 0.0D : vec3d2.z); } -@@ -1760,7 +1779,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1712,7 +1731,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); @@ -219,7 +219,7 @@ index 50ae421b40681366523932a2cd445592ec067897..df083dd2f9af19c768b23727852194e6 } } -@@ -1769,7 +1788,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1721,7 +1740,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess double y = movement.y; double z = movement.z; @@ -231,7 +231,7 @@ index 50ae421b40681366523932a2cd445592ec067897..df083dd2f9af19c768b23727852194e6 if (y != 0.0) { y = scanY(currBoundingBox, y, voxelList, bbList); -@@ -1885,7 +1907,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1845,7 +1867,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return movement; } @@ -239,8 +239,8 @@ index 50ae421b40681366523932a2cd445592ec067897..df083dd2f9af19c768b23727852194e6 + final Vec3 limitedMoveVector = io.papermc.paper.util.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)) -@@ -2001,8 +2023,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + && (onGround || (limitedMoveVector.y != movement.y && movement.y < 0.0)) +@@ -2033,8 +2055,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess protected void checkInsideBlocks() { AABB axisalignedbb = this.getBoundingBox(); @@ -255,10 +255,10 @@ index 50ae421b40681366523932a2cd445592ec067897..df083dd2f9af19c768b23727852194e6 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 779a6686d491a8c4c95d5b8087fc55ee2f184f38..c01ed76846ad9ab893b0a7cdeadc077ccd829f1a 100644 +index abf41ee186fd7d8e0963112b886d833496c2fb1a..c1de583f8336e18a591ebf4a0f8bf92ac8c5735f 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -@@ -90,6 +90,8 @@ public class FallingBlockEntity extends Entity { +@@ -94,6 +94,8 @@ public class FallingBlockEntity extends Entity { this.yo = y; this.zo = z; this.setStartPos(this.blockPosition()); @@ -267,7 +267,7 @@ index 779a6686d491a8c4c95d5b8087fc55ee2f184f38..c01ed76846ad9ab893b0a7cdeadc077c } public static FallingBlockEntity fall(Level world, BlockPos pos, BlockState state) { -@@ -102,7 +104,11 @@ public class FallingBlockEntity extends Entity { +@@ -106,7 +108,11 @@ public class FallingBlockEntity extends Entity { FallingBlockEntity entityfallingblock = new FallingBlockEntity(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, iblockdata.hasProperty(BlockStateProperties.WATERLOGGED) ? (BlockState) iblockdata.setValue(BlockStateProperties.WATERLOGGED, false) : iblockdata); if (!CraftEventFactory.callEntityChangeBlockEvent(entityfallingblock, blockposition, iblockdata.getFluidState().createLegacyBlock())) return entityfallingblock; // CraftBukkit @@ -280,7 +280,7 @@ index 779a6686d491a8c4c95d5b8087fc55ee2f184f38..c01ed76846ad9ab893b0a7cdeadc077c world.addFreshEntity(entityfallingblock, spawnReason); // CraftBukkit return entityfallingblock; } -@@ -190,13 +196,50 @@ public class FallingBlockEntity extends Entity { +@@ -194,13 +200,50 @@ public class FallingBlockEntity extends Entity { // Sakura start @Override public final double getEyeY() { @@ -333,7 +333,7 @@ index 779a6686d491a8c4c95d5b8087fc55ee2f184f38..c01ed76846ad9ab893b0a7cdeadc077c } @Override -@@ -206,6 +249,11 @@ public class FallingBlockEntity extends Entity { +@@ -210,6 +253,11 @@ public class FallingBlockEntity extends Entity { } else { Block block = this.blockState.getBlock(); @@ -345,15 +345,15 @@ index 779a6686d491a8c4c95d5b8087fc55ee2f184f38..c01ed76846ad9ab893b0a7cdeadc077c ++this.time; this.applyGravity(); this.moveBasic(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise simple entity movement -@@ -219,8 +267,15 @@ public class FallingBlockEntity extends Entity { - return; +@@ -224,8 +272,15 @@ public class FallingBlockEntity extends Entity { } // Paper end - Configurable falling blocks height nerf + this.handlePortal(); + // Sakura start - physics version api + if (this.physics.before(1_12_0)) { + this.scaleDeltaMovement(0.98F); + } - if (!this.level().isClientSide) { + if (!this.level().isClientSide && (this.isAlive() || this.forceTickAfterTeleportToDuplicate)) { - BlockPos blockposition = this.blockPosition(); + // Patching the floating point issue on modern versions can break some cannons that rely on it. + // However, it makes sense for legacy versions pre-1.17 before the world height change. @@ -362,7 +362,7 @@ index 779a6686d491a8c4c95d5b8087fc55ee2f184f38..c01ed76846ad9ab893b0a7cdeadc077c boolean flag = this.blockState.getBlock() instanceof ConcretePowderBlock; boolean flag1 = flag && this.level().getFluidState(blockposition).is(FluidTags.WATER); double d0 = this.getDeltaMovement().lengthSqr(); -@@ -245,8 +300,11 @@ public class FallingBlockEntity extends Entity { +@@ -250,8 +305,11 @@ public class FallingBlockEntity extends Entity { } else { BlockState iblockdata = this.level().getBlockState(blockposition); @@ -376,7 +376,7 @@ index 779a6686d491a8c4c95d5b8087fc55ee2f184f38..c01ed76846ad9ab893b0a7cdeadc077c if (!this.cancelDrop) { boolean flag2 = iblockdata.canBeReplaced((BlockPlaceContext) (new DirectionalPlaceContext(this.level(), blockposition, Direction.DOWN, ItemStack.EMPTY, Direction.UP))); boolean flag3 = FallingBlock.isFree(this.level().getBlockState(blockposition.below())) && (!flag || !flag1); -@@ -313,7 +371,12 @@ public class FallingBlockEntity extends Entity { +@@ -318,7 +376,12 @@ public class FallingBlockEntity extends Entity { } } @@ -391,10 +391,10 @@ index 779a6686d491a8c4c95d5b8087fc55ee2f184f38..c01ed76846ad9ab893b0a7cdeadc077c } 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 df2a37e57012333a618937e61cb5a99c3ef4a0f9..62d40333c42f673479db5e6312343161aacf7078 100644 +index a5b7f3bf42e43a9b3673e8725c5f20f7a3e7dbe6..cdba6fd4c79919b9f1dcb41b8df1dca8563a6174 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,13 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -78,6 +78,13 @@ public class PrimedTnt extends Entity implements TraceableEntity { case Y -> multiplyDeltaMovement(0, 1, 0); } // Sakura end @@ -408,7 +408,7 @@ index df2a37e57012333a618937e61cb5a99c3ef4a0f9..62d40333c42f673479db5e6312343161 } @Override -@@ -105,10 +112,26 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -125,10 +132,26 @@ public class PrimedTnt extends Entity implements TraceableEntity { } } // Sakura end @@ -436,7 +436,7 @@ index df2a37e57012333a618937e61cb5a99c3ef4a0f9..62d40333c42f673479db5e6312343161 } @Override -@@ -122,15 +145,19 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -143,15 +166,19 @@ public class PrimedTnt extends Entity implements TraceableEntity { return; } // Paper end - Configurable TNT height nerf @@ -459,19 +459,19 @@ index df2a37e57012333a618937e61cb5a99c3ef4a0f9..62d40333c42f673479db5e6312343161 // 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 -@@ -183,7 +210,10 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -204,7 +231,10 @@ public class PrimedTnt extends Entity implements TraceableEntity { ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent((org.bukkit.entity.Explosive)this.getBukkitEntity()); if (!event.isCancelled()) { -- this.level().explode(this, this.getX(), this.getY(0.0625D), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.TNT); +- this.level().explode(this, Explosion.getDefaultDamageSource(this.level(), this), this.usedPortal ? PrimedTnt.USED_PORTAL_DAMAGE_CALCULATOR : null, this.getX(), this.getY(0.0625D), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.TNT); + // 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, this.getX(), pos, this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.TNT); ++ 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 } // CraftBukkit end } -@@ -237,7 +267,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -274,7 +304,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { // Paper start - Option to prevent TNT from moving in water @Override public boolean isPushedByFluid() { @@ -481,10 +481,10 @@ index df2a37e57012333a618937e61cb5a99c3ef4a0f9..62d40333c42f673479db5e6312343161 // 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 b75f3527bad174cb309ffb34f6bf73540007cf53..73ccabd524ad24962ba790bb93e8494dff866166 100644 +index b35a51c8e51103260f9d250e318af8a00c21533f..77fcdc8aa11df094e4ac2185bf16d0b1a8b4aacb 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -76,6 +76,7 @@ public class Explosion { +@@ -77,6 +77,7 @@ public class Explosion { public float yield; // CraftBukkit end private final boolean consistentRadius; // Sakura - consistent explosion radius @@ -492,7 +492,7 @@ index b75f3527bad174cb309ffb34f6bf73540007cf53..73ccabd524ad24962ba790bb93e8494d public static DamageSource getDefaultDamageSource(Level world, @Nullable Entity source) { return world.damageSources().explosion(source, Explosion.getIndirectSourceEntityInternal(source)); -@@ -114,6 +115,7 @@ public class Explosion { +@@ -115,6 +116,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 @@ -500,7 +500,7 @@ index b75f3527bad174cb309ffb34f6bf73540007cf53..73ccabd524ad24962ba790bb93e8494d } // Sakura start - optimise paper explosions -@@ -506,8 +508,12 @@ public class Explosion { +@@ -507,8 +509,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,7 +514,7 @@ index b75f3527bad174cb309ffb34f6bf73540007cf53..73ccabd524ad24962ba790bb93e8494d } if (hitResult == HitResult.Type.MISS) { // Sakura end - replace density cache -@@ -612,6 +618,14 @@ public class Explosion { +@@ -613,6 +619,14 @@ public class Explosion { } if (cachedBlock.outOfWorld) { @@ -546,7 +546,7 @@ index b75f3527bad174cb309ffb34f6bf73540007cf53..73ccabd524ad24962ba790bb93e8494d if (d11 != 0.0D) { d8 /= d11; -@@ -1046,7 +1066,7 @@ public class Explosion { +@@ -1050,7 +1070,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,7 +555,7 @@ index b75f3527bad174cb309ffb34f6bf73540007cf53..73ccabd524ad24962ba790bb93e8494d this.level.densityCache.putDensity(vec3d, entity, blockDensity); // Sakura end - replace density cache } -@@ -1054,6 +1074,17 @@ public class Explosion { +@@ -1058,6 +1078,17 @@ public class Explosion { return blockDensity; } @@ -574,13 +574,13 @@ index b75f3527bad174cb309ffb34f6bf73540007cf53..73ccabd524ad24962ba790bb93e8494d 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 ef37c34036bc79505786ee38ab5472a45effa4a3..6f7aa220d13a1135dde24012544bf3b978af7e50 100644 +index 7a079b432092f70bac657520a0c71d0a0b4ccdb0..8a8ea38bb3e88e27fcd7e9f9f4c604956120b1d3 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -255,6 +255,205 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - return this.getLimitedEntities(except, box, net.minecraft.world.entity.EntitySelector.NO_SPECTATORS, limit, search); +@@ -195,6 +195,205 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + public CraftWorld getWorld() { + return this.world; } - // Sakura end - add entity retrival methods with search limits + // Sakura start - physics version api + public net.minecraft.world.phys.BlockHitResult.Type rayTrace(net.minecraft.world.phys.Vec3 vec3d, net.minecraft.world.phys.Vec3 vec3d1) { + // May deviate from vanilla here; I remember noticing a bug and there's no fix commit. @@ -781,8 +781,8 @@ index ef37c34036bc79505786ee38ab5472a45effa4a3..6f7aa220d13a1135dde24012544bf3b9 + } + // Sakura end - physics version 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; Async-Anti-Xray: Pass executor - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot + 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 --- a/src/main/java/net/minecraft/world/level/block/FallingBlock.java @@ -804,7 +804,7 @@ index 1ea1232a5ba3e48eef3a139d6487c9a190155ebd..71364fe94cfeefa07fac3ee6359f7abd 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 79df73d352e7c72efb4b0b3ae567b60d19e2150e..7c434d7c55bec4ba1d6013109ed8fd769e8b12f0 100644 +index 26ed17e433cbafbbf788231f27f296f08048adfe..1e28d5175bbb4dc2d4c1c3080825e797b574fc06 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 { diff --git a/patches/server/0042-Allow-water-in-the-nether.patch b/patches/server/0044-Allow-water-in-the-nether.patch similarity index 87% rename from patches/server/0042-Allow-water-in-the-nether.patch rename to patches/server/0044-Allow-water-in-the-nether.patch index cd0e5dc..5df4256 100644 --- a/patches/server/0042-Allow-water-in-the-nether.patch +++ b/patches/server/0044-Allow-water-in-the-nether.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Allow water in the nether diff --git a/src/main/java/net/minecraft/world/item/BucketItem.java b/src/main/java/net/minecraft/world/item/BucketItem.java -index 49557d6f22c5725c663a231deab019d4f6fe95fa..22efc53edf421e6db27a0dcee0961399eaaaeb3e 100644 +index 321188173918d0d60858a258400dfd682ccdb21c..229d71f093f672f323cc2feadf2cd174a62805f8 100644 --- a/src/main/java/net/minecraft/world/item/BucketItem.java +++ b/src/main/java/net/minecraft/world/item/BucketItem.java -@@ -194,7 +194,7 @@ public class BucketItem extends Item implements DispensibleContainerItem { +@@ -196,7 +196,7 @@ public class BucketItem extends Item implements DispensibleContainerItem { // CraftBukkit end if (!flag2) { return movingobjectpositionblock != null && this.emptyContents(entityhuman, world, movingobjectpositionblock.getBlockPos().relative(movingobjectpositionblock.getDirection()), (BlockHitResult) null, enumdirection, clicked, itemstack, enumhand); // CraftBukkit @@ -18,13 +18,13 @@ index 49557d6f22c5725c663a231deab019d4f6fe95fa..22efc53edf421e6db27a0dcee0961399 int j = blockposition.getY(); int k = blockposition.getZ(); diff --git a/src/main/java/net/minecraft/world/level/block/IceBlock.java b/src/main/java/net/minecraft/world/level/block/IceBlock.java -index 013302623d3ca3ff88f242d740af935dcf4844a6..f043ce5cfbffeeb9789e2f9592397410845c2ba0 100644 +index ac775afb265430ac202cfa3900a036d11a308b1e..d5b82bc2932fdb10304c6120dc692769c8288989 100644 --- a/src/main/java/net/minecraft/world/level/block/IceBlock.java +++ b/src/main/java/net/minecraft/world/level/block/IceBlock.java @@ -41,7 +41,7 @@ public class IceBlock extends HalfTransparentBlock { public void afterDestroy(Level world, BlockPos pos, ItemStack tool) { // Paper end - Improve Block#breakNaturally API - if (EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) == 0) { + if (!EnchantmentHelper.hasTag(tool, EnchantmentTags.PREVENTS_ICE_MELTING)) { - if (world.dimensionType().ultraWarm()) { + if (!world.sakuraConfig().environment.allowWaterInTheNether && world.dimensionType().ultraWarm()) { // Sakura world.removeBlock(pos, false); diff --git a/patches/server/0043-Configure-concrete-solidifying-in-water.patch b/patches/server/0045-Configure-concrete-solidifying-in-water.patch similarity index 88% rename from patches/server/0043-Configure-concrete-solidifying-in-water.patch rename to patches/server/0045-Configure-concrete-solidifying-in-water.patch index 179e5f5..1934cbf 100644 --- a/patches/server/0043-Configure-concrete-solidifying-in-water.patch +++ b/patches/server/0045-Configure-concrete-solidifying-in-water.patch @@ -5,10 +5,10 @@ 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 b0a6d829a9790744a030cd4b230b71a752e546d0..28ba55a756af15683a48c6badb90f0624bf68911 100644 +index c1de583f8336e18a591ebf4a0f8bf92ac8c5735f..0cf2068d7c817b5ff357f99e4e2baa227120da7f 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -@@ -276,7 +276,7 @@ public class FallingBlockEntity extends Entity { +@@ -281,7 +281,7 @@ public class FallingBlockEntity extends Entity { // However, it makes sense for legacy versions pre-1.17 before the world height change. BlockPos blockposition = this.physics.before(1_17_0) ? this.patchedBlockPosition() : this.blockPosition(); // Sakura end - physics version api diff --git a/patches/server/0044-Option-for-fast-nether-dimension-lava.patch b/patches/server/0046-Option-for-fast-nether-dimension-lava.patch similarity index 100% rename from patches/server/0044-Option-for-fast-nether-dimension-lava.patch rename to patches/server/0046-Option-for-fast-nether-dimension-lava.patch diff --git a/patches/server/0045-Allow-explosions-to-destroy-lava.patch b/patches/server/0047-Allow-explosions-to-destroy-lava.patch similarity index 90% rename from patches/server/0045-Allow-explosions-to-destroy-lava.patch rename to patches/server/0047-Allow-explosions-to-destroy-lava.patch index ef8b19f..a02d19a 100644 --- a/patches/server/0045-Allow-explosions-to-destroy-lava.patch +++ b/patches/server/0047-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 c5db31bd92a441eccd0f4c5ea1b8c7f17a00de65..fd53dff917b65b94eceb5796fc80b1a490454cc2 100644 +index 77fcdc8aa11df094e4ac2185bf16d0b1a8b4aacb..a4997b0de4300d0d4c86f8068e81ec22ecef28f6 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -289,7 +289,7 @@ public class Explosion { +@@ -290,7 +290,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/0046-Disable-bubble-columns-affecting-cannon-entities.patch b/patches/server/0048-Disable-bubble-columns-affecting-cannon-entities.patch similarity index 100% rename from patches/server/0046-Disable-bubble-columns-affecting-cannon-entities.patch rename to patches/server/0048-Disable-bubble-columns-affecting-cannon-entities.patch diff --git a/patches/server/0047-Treat-all-collidable-blocks-as-full-while-moving-fas.patch b/patches/server/0049-Treat-all-collidable-blocks-as-full-while-moving-fas.patch similarity index 94% rename from patches/server/0047-Treat-all-collidable-blocks-as-full-while-moving-fas.patch rename to patches/server/0049-Treat-all-collidable-blocks-as-full-while-moving-fas.patch index 00d98fd..6673396 100644 --- a/patches/server/0047-Treat-all-collidable-blocks-as-full-while-moving-fas.patch +++ b/patches/server/0049-Treat-all-collidable-blocks-as-full-while-moving-fas.patch @@ -5,7 +5,7 @@ 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 8014ebfb391825c31d9d1b39f5304a7e76b1ee44..d58d1fb1148db13c5d24422a1553e0b83677ab3d 100644 +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 { @@ -59,10 +59,10 @@ index 8014ebfb391825c31d9d1b39f5304a7e76b1ee44..d58d1fb1148db13c5d24422a1553e0b8 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 8c508df03a80d7f8fbb1bc50067f84090457511f..b4977a8b97f5417883cf3ffab5f89d98a6a6079b 100644 +index 2b94b7bf6cc132a54596ad71c8e2fa801e2eba77..fe1ce43158b84639abae6f7cee3a7ecdfb3d383a 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -582,6 +582,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -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; } diff --git a/patches/server/0048-Add-redstone-implementation-API.patch b/patches/server/0050-Add-redstone-implementation-API.patch similarity index 98% rename from patches/server/0048-Add-redstone-implementation-API.patch rename to patches/server/0050-Add-redstone-implementation-API.patch index cc94709..2aef88a 100644 --- a/patches/server/0048-Add-redstone-implementation-API.patch +++ b/patches/server/0050-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 719bac24f454f19460f7abd253fcc82a15648f8a..337d2c020acf0565401d21bce3ba701c0da0ad99 100644 +index a6f0ded367341e6b9f9c7b1c4254dd696ead2f8d..e99dcd24d4af8450ceb436b929c9a9ecf75249bc 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/0049-Reduce-entity-tracker-player-updates.patch b/patches/server/0051-Reduce-entity-tracker-player-updates.patch similarity index 92% rename from patches/server/0049-Reduce-entity-tracker-player-updates.patch rename to patches/server/0051-Reduce-entity-tracker-player-updates.patch index 603fa12..05ddb45 100644 --- a/patches/server/0049-Reduce-entity-tracker-player-updates.patch +++ b/patches/server/0051-Reduce-entity-tracker-player-updates.patch @@ -5,10 +5,10 @@ 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 34fdb52904a26bc7c4c95eee91882a3924ab037c..e372993dffc7012c6dfadacb99b2a7fc43b2aebd 100644 +index c46e07638db51bd549e39f0fcbe122d48ead9c31..d779e010ba7c0e99aa3b0ce8462b7e3fa7cced8c 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1161,6 +1161,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1027,6 +1027,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider try { for (TrackedEntity tracker : this.entityMap.values()) { // update tracker entry @@ -16,7 +16,7 @@ index 34fdb52904a26bc7c4c95eee91882a3924ab037c..e372993dffc7012c6dfadacb99b2a7fc tracker.updatePlayers(tracker.entity.getPlayersInTrackRange()); } } finally { -@@ -1325,14 +1326,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -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 diff --git a/patches/server/0050-Add-option-for-legacy-lava-block-formation.patch b/patches/server/0052-Add-option-for-legacy-lava-block-formation.patch similarity index 100% rename from patches/server/0050-Add-option-for-legacy-lava-block-formation.patch rename to patches/server/0052-Add-option-for-legacy-lava-block-formation.patch diff --git a/patches/server/0051-Configure-mob-spawner-defaults.patch b/patches/server/0053-Configure-mob-spawner-defaults.patch similarity index 100% rename from patches/server/0051-Configure-mob-spawner-defaults.patch rename to patches/server/0053-Configure-mob-spawner-defaults.patch diff --git a/patches/server/0052-Allow-disabling-random-dispenser-item-selection.patch b/patches/server/0054-Allow-disabling-random-dispenser-item-selection.patch similarity index 93% rename from patches/server/0052-Allow-disabling-random-dispenser-item-selection.patch rename to patches/server/0054-Allow-disabling-random-dispenser-item-selection.patch index a5a1e5c..411f776 100644 --- a/patches/server/0052-Allow-disabling-random-dispenser-item-selection.patch +++ b/patches/server/0054-Allow-disabling-random-dispenser-item-selection.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Allow disabling random dispenser item selection diff --git a/src/main/java/net/minecraft/world/level/block/entity/DispenserBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/DispenserBlockEntity.java -index ab63a233f7db352e325fa4498dd35b125fa8fcea..11cb8b32b3891878f4217d27b7051a4f787f5ebf 100644 +index 431fb6a658c6aac43b6f9dbd1f578b83f261a4e3..92bcba5fc21e39a54aa44d0b17c716acce6cc5bd 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/DispenserBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/DispenserBlockEntity.java @@ -74,8 +74,15 @@ public class DispenserBlockEntity extends RandomizableContainerBlockEntity { diff --git a/patches/server/0053-Add-instant-mob-death-animation.patch b/patches/server/0055-Add-instant-mob-death-animation.patch similarity index 84% rename from patches/server/0053-Add-instant-mob-death-animation.patch rename to patches/server/0055-Add-instant-mob-death-animation.patch index 6aa765f..9be5636 100644 --- a/patches/server/0053-Add-instant-mob-death-animation.patch +++ b/patches/server/0055-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 2fb84d928884050c63cfa598d45dda39a7c2cec4..c0a0a8808003d7e56c4de4f4ad1c494b15cc41a2 100644 +index 57929f4c2642d1a66f5158a59a5b905e977b2fa2..5a7f7de0bcdd3d6ed946f0d3e3100e21ff1cbea9 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1796,6 +1796,12 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1778,6 +1778,12 @@ public abstract class LivingEntity extends Entity implements Attackable { // Paper start if (this.dead) { // Paper @@ -19,5 +19,5 @@ index 2fb84d928884050c63cfa598d45dda39a7c2cec4..c0a0a8808003d7e56c4de4f4ad1c494b + } + // Sakura end this.level().broadcastEntityEvent(this, (byte) 3); + this.setPose(Pose.DYING); - } diff --git a/patches/server/0054-Configure-fluids-breaking-redstone.patch b/patches/server/0056-Configure-fluids-breaking-redstone.patch similarity index 100% rename from patches/server/0054-Configure-fluids-breaking-redstone.patch rename to patches/server/0056-Configure-fluids-breaking-redstone.patch diff --git a/patches/server/0055-Option-to-disable-explosions-hurting-players.patch b/patches/server/0057-Option-to-disable-explosions-hurting-players.patch similarity index 91% rename from patches/server/0055-Option-to-disable-explosions-hurting-players.patch rename to patches/server/0057-Option-to-disable-explosions-hurting-players.patch index 5909eb1..665798c 100644 --- a/patches/server/0055-Option-to-disable-explosions-hurting-players.patch +++ b/patches/server/0057-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 e039212df79a22a46a87b518ced8b01b067e0153..3954be72bfd1ac3242dc5e3f07b6f78de9677b16 100644 +index 852ceb5aa9dc92981f0d9d012cff04177f88e606..8bed7279377884498fc1c4ed7c44b193bb89c1e7 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -951,7 +951,7 @@ public abstract class Player extends LivingEntity { +@@ -966,7 +966,7 @@ public abstract class Player extends LivingEntity { @Override public boolean isInvulnerableTo(DamageSource damageSource) { diff --git a/patches/server/0056-Iron-golems-take-fall-damage.patch b/patches/server/0058-Iron-golems-take-fall-damage.patch similarity index 88% rename from patches/server/0056-Iron-golems-take-fall-damage.patch rename to patches/server/0058-Iron-golems-take-fall-damage.patch index ac52e55..7dd5acf 100644 --- a/patches/server/0056-Iron-golems-take-fall-damage.patch +++ b/patches/server/0058-Iron-golems-take-fall-damage.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Iron golems take fall damage diff --git a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -index 932fae98c551052cadba4c6fc6e575fc30a25d58..0f0e9a4c8264fcf3786e7c4707f1a24a368baccb 100644 +index 1807da10d07d1f6e4ddbc0fa1b8da34a688d67c3..0eca342bb4ca5740f69ffc9a57a37308d77375ac 100644 --- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java +++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -@@ -235,6 +235,20 @@ public class IronGolem extends AbstractGolem implements NeutralMob { +@@ -243,6 +243,20 @@ public class IronGolem extends AbstractGolem implements NeutralMob { } diff --git a/patches/server/0057-Add-explosions-dropping-items-config.patch b/patches/server/0059-Add-explosions-dropping-items-config.patch similarity index 90% rename from patches/server/0057-Add-explosions-dropping-items-config.patch rename to patches/server/0059-Add-explosions-dropping-items-config.patch index 5b95637..1b20d0c 100644 --- a/patches/server/0057-Add-explosions-dropping-items-config.patch +++ b/patches/server/0059-Add-explosions-dropping-items-config.patch @@ -5,7 +5,7 @@ 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 4137fe261ae4934e80db2a8f1c0d6fb880839d8f..af71ce8f190e69ce1de758e6a551dbef817491be 100644 +index a4997b0de4300d0d4c86f8068e81ec22ecef28f6..94df0527699462e29fb76f68194386c50f1f7e04 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 { diff --git a/patches/server/0058-Optimise-check-inside-blocks-and-fluids.patch b/patches/server/0060-Optimise-check-inside-blocks-and-fluids.patch similarity index 94% rename from patches/server/0058-Optimise-check-inside-blocks-and-fluids.patch rename to patches/server/0060-Optimise-check-inside-blocks-and-fluids.patch index 8a5726a..7162202 100644 --- a/patches/server/0058-Optimise-check-inside-blocks-and-fluids.patch +++ b/patches/server/0060-Optimise-check-inside-blocks-and-fluids.patch @@ -5,10 +5,10 @@ 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 b4977a8b97f5417883cf3ffab5f89d98a6a6079b..894604b23d7f3749cbaa767705f63b21beea4fde 100644 +index fe1ce43158b84639abae6f7cee3a7ecdfb3d383a..92cf9e492d3cd904691a325e0af9b205487e5ab7 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -2037,18 +2037,37 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2069,18 +2069,37 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess BlockPos blockposition1 = BlockPos.containing(axisalignedbb.maxX - offset, axisalignedbb.maxY - offset, axisalignedbb.maxZ - offset); // Sakura end @@ -50,7 +50,7 @@ index b4977a8b97f5417883cf3ffab5f89d98a6a6079b..894604b23d7f3749cbaa767705f63b21 try { iblockdata.entityInside(this.level(), blockposition_mutableblockposition, this); -@@ -4857,7 +4876,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4871,7 +4890,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public boolean updateFluidHeightAndDoFluidPushing(TagKey tag, double speed) { @@ -59,7 +59,7 @@ index b4977a8b97f5417883cf3ffab5f89d98a6a6079b..894604b23d7f3749cbaa767705f63b21 return false; } else { AABB axisalignedbb = this.getBoundingBox().deflate(0.001D); -@@ -4874,11 +4893,30 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4888,11 +4907,30 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess int k1 = 0; BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(); diff --git a/patches/server/0059-Fix-paper-findSupportingBlock-not-updating-last-chun.patch b/patches/server/0061-Fix-paper-findSupportingBlock-not-updating-last-chun.patch similarity index 80% rename from patches/server/0059-Fix-paper-findSupportingBlock-not-updating-last-chun.patch rename to patches/server/0061-Fix-paper-findSupportingBlock-not-updating-last-chun.patch index 48a960b..db9ebf0 100644 --- a/patches/server/0059-Fix-paper-findSupportingBlock-not-updating-last-chun.patch +++ b/patches/server/0061-Fix-paper-findSupportingBlock-not-updating-last-chun.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Fix paper findSupportingBlock not updating last chunk diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 6f7aa220d13a1135dde24012544bf3b978af7e50..5eda4a84b51bb0fd9ca3ba4ca1666792b6e23a05 100644 +index 8a8ea38bb3e88e27fcd7e9f9f4c604956120b1d3..60c84c04651c390df7efb9a8fa840a9d1651cbf8 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -881,6 +881,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -901,6 +901,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl if (chunkDiff != 0) { lastChunk = chunkProvider.getChunkAtIfLoadedImmediately(newChunkX, newChunkZ); diff --git a/patches/server/0060-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch b/patches/server/0062-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch similarity index 100% rename from patches/server/0060-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch rename to patches/server/0062-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch diff --git a/patches/server/0061-Calculate-biome-noise-once-per-chunk-section.patch b/patches/server/0063-Calculate-biome-noise-once-per-chunk-section.patch similarity index 91% rename from patches/server/0061-Calculate-biome-noise-once-per-chunk-section.patch rename to patches/server/0063-Calculate-biome-noise-once-per-chunk-section.patch index 9e1c5fb..2c06498 100644 --- a/patches/server/0061-Calculate-biome-noise-once-per-chunk-section.patch +++ b/patches/server/0063-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 796bbef3544e06b8e7aac7e8ac5f740a2613f4bd..27ba0a6d3439b9b4dfae3b9c49975879c4295443 100644 +index c7b4c4463b26445964ac1fa51e66e35798ea4c41..10ce2dd78d01524cb43774f13cae6f9a4560ace4 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -@@ -227,12 +227,18 @@ public class LevelChunkSection { +@@ -228,12 +228,18 @@ public class LevelChunkSection { public void fillBiomesFromNoise(BiomeResolver biomeSupplier, Climate.Sampler sampler, int x, int y, int z) { PalettedContainer> datapaletteblock = this.biomes.recreate(); diff --git a/patches/server/0062-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch b/patches/server/0064-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch similarity index 91% rename from patches/server/0062-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch rename to patches/server/0064-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch index 95720df..35d6f3f 100644 --- a/patches/server/0062-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch +++ b/patches/server/0064-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch @@ -6,10 +6,10 @@ 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 28ba55a756af15683a48c6badb90f0624bf68911..adada6c0237e99a226cb24763902dc7bdbccf493 100644 +index 0cf2068d7c817b5ff357f99e4e2baa227120da7f..71dcdecbfc2189d3d1229001ad686cd8b41bf89e 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -@@ -351,10 +351,14 @@ public class FallingBlockEntity extends Entity { +@@ -356,10 +356,14 @@ public class FallingBlockEntity extends Entity { tileentity.setChanged(); } } diff --git a/patches/server/0063-Add-entity-travel-distance-limits.patch b/patches/server/0065-Add-entity-travel-distance-limits.patch similarity index 76% rename from patches/server/0063-Add-entity-travel-distance-limits.patch rename to patches/server/0065-Add-entity-travel-distance-limits.patch index 4402a30..98df0d9 100644 --- a/patches/server/0063-Add-entity-travel-distance-limits.patch +++ b/patches/server/0065-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 00259cc930a0b1db38c3abbb5fcf7430d5f50079..8eafe75e69f5b448feea8354edacdb9f02981118 100644 +index 82ff83fd00b976d41d1d5b30ad34ede9af1a7e5b..f00afd6dca2d8a0d5d8061a2e4e00a344761bff6 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1423,6 +1423,11 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1197,6 +1197,11 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. if (isActive) { // Paper - EAR 2 TimingHistory.activatedEntityTicks++; entity.tick(); @@ -21,10 +21,10 @@ index 00259cc930a0b1db38c3abbb5fcf7430d5f50079..8eafe75e69f5b448feea8354edacdb9f } 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 894604b23d7f3749cbaa767705f63b21beea4fde..24f86d6adbaf295a42a7740ac17b59de95a22431 100644 +index 92cf9e492d3cd904691a325e0af9b205487e5ab7..5decb8c3791a8b72d1d2df5092cfdad0560fd82b 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -730,6 +730,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -724,6 +724,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return this.physics; } // Sakura end - physics version api @@ -32,19 +32,19 @@ index 894604b23d7f3749cbaa767705f63b21beea4fde..24f86d6adbaf295a42a7740ac17b59de + private final double travelDistanceLimit; + + public final boolean isPastTravelDistanceLimit() { -+ if (origin == null) { ++ if (this.origin == null) { + return false; + } + -+ double x = Math.pow(origin.getX() - position.x(), 2); -+ double z = Math.pow(origin.getZ() - position.z(), 2); -+ return Math.max(x, z) >= travelDistanceLimit; ++ double x = Math.pow(this.origin.getX() - this.position.x(), 2); ++ double z = Math.pow(this.origin.getZ() - this.position.z(), 2); ++ return Math.max(x, z) >= this.travelDistanceLimit; + } + // Sakura end - entity travel distance limits public Entity(EntityType type, Level world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); -@@ -780,6 +793,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -774,6 +787,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/0064-Configure-potion-speed-and-breaking-inside-entities.patch b/patches/server/0066-Configure-potion-speed-and-breaking-inside-entities.patch similarity index 89% rename from patches/server/0064-Configure-potion-speed-and-breaking-inside-entities.patch rename to patches/server/0066-Configure-potion-speed-and-breaking-inside-entities.patch index fde0f8b..33b292f 100644 --- a/patches/server/0064-Configure-potion-speed-and-breaking-inside-entities.patch +++ b/patches/server/0066-Configure-potion-speed-and-breaking-inside-entities.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Configure potion speed and breaking inside entities diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -index 74c596264d4da551437bd2a23e1c70022cfc73fc..fc0102cbf4b759001f5b76e4c60b403be9b20185 100644 +index 5f7d152f41eb85f17bcded4bc8099b998e5a338b..81e2161c90001ea941611f1691160df089c1cf97 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -@@ -150,7 +150,7 @@ public abstract class Projectile extends Entity implements TraceableEntity { +@@ -156,7 +156,7 @@ public abstract class Projectile extends Entity implements TraceableEntity { super.tick(); } @@ -18,7 +18,7 @@ index 74c596264d4da551437bd2a23e1c70022cfc73fc..fc0102cbf4b759001f5b76e4c60b403b if (entity != null) { diff --git a/src/main/java/net/minecraft/world/entity/projectile/ProjectileUtil.java b/src/main/java/net/minecraft/world/entity/projectile/ProjectileUtil.java -index 877365a953a0debd14ca8d33073754452ea2f026..5b2728eb9243b0136d77164539f397de406215ca 100644 +index e43b3b37a3afc903f057d49d34339f8022274d3e..4ee08ec33bca0313e105053266cfe5345e3e324e 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/ProjectileUtil.java +++ b/src/main/java/net/minecraft/world/entity/projectile/ProjectileUtil.java @@ -51,7 +51,14 @@ public final class ProjectileUtil { @@ -38,10 +38,10 @@ index 877365a953a0debd14ca8d33073754452ea2f026..5b2728eb9243b0136d77164539f397de hitResult = hitResult2; } diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java -index c64e0d623124ad9116cb6c5c7c3320fad7cc1aa3..95c780bbe78370b3426af41c83905ab98b5d3d6b 100644 +index 86c4b593a97431efd062b8c9d86bf92269c00536..6bab3cad31d230cff33e1fcdba715a698cd5c787 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java +++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java -@@ -62,6 +62,25 @@ public class ThrownPotion extends ThrowableItemProjectile implements ItemSupplie +@@ -64,6 +64,25 @@ public class ThrownPotion extends ThrowableItemProjectile implements ItemSupplie super(EntityType.POTION, x, y, z, world); } diff --git a/patches/server/0065-Add-outline-colliison-to-enderpearls.patch b/patches/server/0067-Add-outline-colliison-to-enderpearls.patch similarity index 79% rename from patches/server/0065-Add-outline-colliison-to-enderpearls.patch rename to patches/server/0067-Add-outline-colliison-to-enderpearls.patch index 30a36b9..769cb67 100644 --- a/patches/server/0065-Add-outline-colliison-to-enderpearls.patch +++ b/patches/server/0067-Add-outline-colliison-to-enderpearls.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add outline colliison to enderpearls diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrowableProjectile.java b/src/main/java/net/minecraft/world/entity/projectile/ThrowableProjectile.java -index ab777952bda1651796ed41e8a7fc6621f27db9aa..72676ea1458e91e46a62bb6fd88d7a425664d56a 100644 +index bf4c1883a1257af89428d6580a177f3af3759ee7..426c128805e5d40d4402e2b385d1a39f71b708a1 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/ThrowableProjectile.java +++ b/src/main/java/net/minecraft/world/entity/projectile/ThrowableProjectile.java -@@ -44,7 +44,15 @@ public abstract class ThrowableProjectile extends Projectile { +@@ -43,7 +43,15 @@ public abstract class ThrowableProjectile extends Projectile { @Override public void tick() { super.tick(); @@ -22,6 +22,6 @@ index ab777952bda1651796ed41e8a7fc6621f27db9aa..72676ea1458e91e46a62bb6fd88d7a42 + } + HitResult movingobjectposition = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity, type); + // Sakura end - enderpearls use outline for collision - boolean flag = false; - if (movingobjectposition.getType() == HitResult.Type.BLOCK) { + if (movingobjectposition.getType() != HitResult.Type.MISS) { + this.preHitTargetOrDeflectSelf(movingobjectposition); // CraftBukkit - projectile hit event diff --git a/patches/server/0066-Disable-player-poses-shrinking-collision-box.patch b/patches/server/0068-Disable-player-poses-shrinking-collision-box.patch similarity index 87% rename from patches/server/0066-Disable-player-poses-shrinking-collision-box.patch rename to patches/server/0068-Disable-player-poses-shrinking-collision-box.patch index db059dc..7b8c5f6 100644 --- a/patches/server/0066-Disable-player-poses-shrinking-collision-box.patch +++ b/patches/server/0068-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 b32a2388d800f5930274af4d439a477fe03838f9..70e3c8b1082d16cdbb9a4c179c53dafcc59ac1ed 100644 +index 8bed7279377884498fc1c4ed7c44b193bb89c1e7..1967a38277d12e09bf39af271eb6c7ed75fe0218 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -2289,7 +2289,13 @@ public abstract class Player extends LivingEntity { +@@ -2298,7 +2298,13 @@ public abstract class Player extends LivingEntity { @Override public EntityDimensions getDefaultDimensions(Pose pose) { diff --git a/patches/server/0067-Mob-spawner-behaviour.patch b/patches/server/0069-Mob-spawner-behaviour.patch similarity index 100% rename from patches/server/0067-Mob-spawner-behaviour.patch rename to patches/server/0069-Mob-spawner-behaviour.patch diff --git a/patches/server/0068-Use-random-chance-for-crop-growth-instead-of-age.patch b/patches/server/0070-Use-random-chance-for-crop-growth-instead-of-age.patch similarity index 100% rename from patches/server/0068-Use-random-chance-for-crop-growth-instead-of-age.patch rename to patches/server/0070-Use-random-chance-for-crop-growth-instead-of-age.patch diff --git a/patches/server/0071-Allow-disabling-sweep-attacks.patch b/patches/server/0071-Allow-disabling-sweep-attacks.patch deleted file mode 100644 index 45fa6ac..0000000 --- a/patches/server/0071-Allow-disabling-sweep-attacks.patch +++ /dev/null @@ -1,19 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik -Date: Fri, 23 Feb 2024 01:49:20 +0000 -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 951fa13dfe333e1e4aa467897d45bbe56d67f6b0..72ce42011eea9b155c161bb03b02ba0a8f142039 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -1377,7 +1377,7 @@ public abstract class Player extends LivingEntity { - // Paper end - Configurable sprint interruption on attack - } - -- if (flag3) { -+ if (flag3 && this.level().sakuraConfig().players.combat.allowSweepAttacks) { // Sakura - allow disabling sweep attacks - float f4 = 1.0F + EnchantmentHelper.getSweepingDamageRatio(this) * f; - List list = this.level().getEntitiesOfClass(LivingEntity.class, target.getBoundingBox().inflate(1.0D, 0.25D, 1.0D)); - Iterator iterator = list.iterator(); diff --git a/patches/server/0069-Protect-block-shapes-against-plugins.patch b/patches/server/0071-Protect-block-shapes-against-plugins.patch similarity index 97% rename from patches/server/0069-Protect-block-shapes-against-plugins.patch rename to patches/server/0071-Protect-block-shapes-against-plugins.patch index 452d574..7c898b5 100644 --- a/patches/server/0069-Protect-block-shapes-against-plugins.patch +++ b/patches/server/0071-Protect-block-shapes-against-plugins.patch @@ -64,7 +64,7 @@ index 54781ea0771327f93a7cf672bb4b2945700c47e5..e26a86fb8595705bcbb747872c3d8f73 } 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 24c1caa060e7c41eac81c19f873ac9273c6b84c2..6de2496807c1701510d3637d6817f6e9438a2362 100644 +index 138fd28437fb4923773b8e6ba9bb53c3ce540e0d..ff6776b8c74eedb3414790f7edef8f25fc478d0c 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 { diff --git a/patches/server/0070-Legacy-player-combat-mechanics.patch b/patches/server/0072-Legacy-player-combat-mechanics.patch similarity index 67% rename from patches/server/0070-Legacy-player-combat-mechanics.patch rename to patches/server/0072-Legacy-player-combat-mechanics.patch index 4192082..8e2af00 100644 --- a/patches/server/0070-Legacy-player-combat-mechanics.patch +++ b/patches/server/0072-Legacy-player-combat-mechanics.patch @@ -50,16 +50,16 @@ 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 c0a0a8808003d7e56c4de4f4ad1c494b15cc41a2..d0f5ace260a913bc5169ea7ea0481c3e3b4b5616 100644 +index 5a7f7de0bcdd3d6ed946f0d3e3100e21ff1cbea9..7d451c2924ab944e453c56a02fb02dd0b81381ef 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -287,6 +287,70 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -299,6 +299,79 @@ public abstract class LivingEntity extends Entity implements Attackable { ++this.noActionTime; // Above all the floats } // Spigot end + // Sakura start - legacy combat mechanics -+ private static final UUID LEGACY_COMBAT_MECHANICS_UUID = UUID.fromString("84ef96f8-9f48-4ae4-a26c-e04551324816"); -+ private static final AttributeModifier LEGACY_ATTACK_SPEED_MODIFIER = new AttributeModifier(LEGACY_COMBAT_MECHANICS_UUID, "Legacy attack speed", 100.0, AttributeModifier.Operation.ADD_VALUE); ++ private static final ResourceLocation LEGACY_COMBAT_MODIFIER_ID = ResourceLocation.fromNamespaceAndPath("sakura", "legacy_combat"); ++ private static final AttributeModifier LEGACY_ATTACK_SPEED_MODIFIER = new AttributeModifier(LEGACY_COMBAT_MODIFIER_ID, 100.0, AttributeModifier.Operation.ADD_VALUE); + + private void updateAttackSpeedModifier() { + AttributeInstance attackSpeed = this.getAttribute(Attributes.ATTACK_SPEED); @@ -109,28 +109,37 @@ index c0a0a8808003d7e56c4de4f4ad1c494b15cc41a2..d0f5ace260a913bc5169ea7ea0481c3e + double attackDifference = (double) legacyAttack - baseAttack; + + if (baseAttack != 0.0f && legacyAttack != Float.MIN_VALUE) { -+ return new AttributeModifier(LEGACY_COMBAT_MECHANICS_UUID, "Legacy attack damage", attackDifference, AttributeModifier.Operation.ADD_VALUE); ++ return new AttributeModifier(LEGACY_COMBAT_MODIFIER_ID, attackDifference, AttributeModifier.Operation.ADD_VALUE); + } + } + return null; + } + -+ protected final float calculateLegacySharpnessDamage() { -+ ItemStack itemstack = this.getMainHandItem(); -+ int level = EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.SHARPNESS, itemstack); -+ return level * 1.25F - net.minecraft.world.item.enchantment.Enchantments.SHARPNESS.getDamageBonus(level, null); ++ protected final float calculateLegacySharpnessDamage(ItemStack itemstack, DamageSource damageSource) { ++ net.minecraft.core.RegistryAccess registryAccess = net.minecraft.server.MinecraftServer.getServer().registryAccess(); ++ net.minecraft.core.HolderLookup.RegistryLookup enchantments = registryAccess.lookupOrThrow(net.minecraft.core.registries.Registries.ENCHANTMENT); ++ Holder sharpness = enchantments.getOrThrow(net.minecraft.world.item.enchantment.Enchantments.SHARPNESS); ++ ++ int enchantmentLevel = itemstack.getEnchantments().getLevel(sharpness); ++ ServerLevel serverLevel = (ServerLevel) this.level(); ++ ++ org.apache.commons.lang3.mutable.MutableFloat damage = new org.apache.commons.lang3.mutable.MutableFloat(); ++ sharpness.value().modifyDamage(serverLevel, enchantmentLevel, itemstack, this, damageSource, damage); ++ ++ // legacy - modern ++ return enchantmentLevel * 1.25F - damage.getValue(); + } + // Sakura end - legacy combat mechanics protected LivingEntity(EntityType type, Level world) { super(type, world); -@@ -2181,7 +2245,16 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -2173,7 +2246,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) + // Sakura start - legacy combat mechanics + if (!this.level().sakuraConfig().players.combat.legacyCombatMechanics) { - amount = CombatRules.getDamageAfterAbsorb(amount, source, (float) this.getArmorValue(), (float) this.getAttributeValue(Attributes.ARMOR_TOUGHNESS)); + amount = CombatRules.getDamageAfterAbsorb(this, amount, source, (float) this.getArmorValue(), (float) this.getAttributeValue(Attributes.ARMOR_TOUGHNESS)); + } else { + // See: applyArmorModifier(DamageSource, float) + int i = 25 - this.getArmorValue(); @@ -141,13 +150,13 @@ index c0a0a8808003d7e56c4de4f4ad1c494b15cc41a2..d0f5ace260a913bc5169ea7ea0481c3e } return amount; -@@ -3277,6 +3350,12 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3297,6 +3379,12 @@ public abstract class LivingEntity extends Entity implements Attackable { }); } + + // Sakura start - legacy combat mechanics -+ if (this instanceof ServerPlayer && enumitemslot == EquipmentSlot.MAINHAND) { ++ if (this instanceof ServerPlayer && enumitemslot1 == EquipmentSlot.MAINHAND) { + this.updateAttackSpeedModifier(); + } + // Sakura end - legacy combat mechanics @@ -155,16 +164,18 @@ index c0a0a8808003d7e56c4de4f4ad1c494b15cc41a2..d0f5ace260a913bc5169ea7ea0481c3e } 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 4e8a41dc9e27244b19e1d45b58ad8363801c5b72..fcc2523e330f1d275f320006374406969cd817fa 100644 +index 1967a38277d12e09bf39af271eb6c7ed75fe0218..57f891099e7b66e75e9d8eb0f961c4395ed875ff 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -1267,12 +1267,18 @@ public abstract class Player extends LivingEntity { +@@ -1277,14 +1277,20 @@ public abstract class Player extends LivingEntity { if (playerAttackEntityEvent.callEvent() && willAttack) { // Logic moved to willAttack local variable. { // Paper end - PlayerAttackEntityEvent -- float f = (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE); -+ float f = this.getAttackDamageFromAttributes(); // Sakura - legacy combat mechanics - float f1 = EnchantmentHelper.getDamageBonus(this.getMainHandItem(), target.getType()); +- float f = this.isAutoSpinAttack() ? this.autoSpinAttackDmg : (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE); ++ float f = this.isAutoSpinAttack() ? this.autoSpinAttackDmg : this.getAttackDamageFromAttributes(); // Sakura - legacy combat mechanics + ItemStack itemstack = this.getWeaponItem(); + DamageSource damagesource = this.damageSources().playerAttack(this); + float f1 = this.getEnchantedDamage(target, f, damagesource) - f; float f2 = this.getAttackStrengthScale(0.5F); + // Sakura start - legacy combat mechanics @@ -172,18 +183,18 @@ index 4e8a41dc9e27244b19e1d45b58ad8363801c5b72..fcc2523e330f1d275f32000637440696 f *= 0.2F + f2 * f2 * 0.8F; f1 *= f2; + } else if (f1 != 0.0) { -+ f1 += this.calculateLegacySharpnessDamage(); ++ f1 += this.calculateLegacySharpnessDamage(itemstack, damagesource); + } + // Sakura end - legacy combat mechanics // 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; -@@ -1298,7 +1304,7 @@ public abstract class Player extends LivingEntity { - } +@@ -1312,7 +1318,7 @@ public abstract class Player extends LivingEntity { + } - f += this.getItemInHand(InteractionHand.MAIN_HAND).getItem().getAttackDamageBonus(this, f); -- boolean flag2 = flag && this.fallDistance > 0.0F && !this.onGround() && !this.onClimbable() && !this.isInWater() && !this.hasEffect(MobEffects.BLINDNESS) && !this.isPassenger() && target instanceof LivingEntity && !this.isSprinting(); // Paper - Add critical damage API; diff on change -+ boolean flag2 = flag && this.fallDistance > 0.0F && !this.onGround() && !this.onClimbable() && !this.isInWater() && !this.hasEffect(MobEffects.BLINDNESS) && !this.isPassenger() && target instanceof LivingEntity && (this.level().sakuraConfig().players.combat.legacyCombatMechanics || !this.isSprinting()); // Sakura - legacy combat mechanics // Paper - Add critical damage API; diff on change + f += itemstack.getItem().getAttackDamageBonus(target, f, damagesource); +- boolean flag2 = flag && this.fallDistance > 0.0F && !this.onGround() && !this.onClimbable() && !this.isInWater() && !this.hasEffect(MobEffects.BLINDNESS) && !this.isPassenger() && target instanceof LivingEntity && !this.isSprinting(); ++ boolean flag2 = flag && this.fallDistance > 0.0F && !this.onGround() && !this.onClimbable() && !this.isInWater() && !this.hasEffect(MobEffects.BLINDNESS) && !this.isPassenger() && target instanceof LivingEntity && (this.level().sakuraConfig().players.combat.legacyCombatMechanics || !this.isSprinting()); // Sakura - legacy combat mechanics - flag2 = flag2 && !this.level().paperConfig().entities.behavior.disablePlayerCrits; // Paper - Toggleable player crits - if (flag2) { + flag2 = flag2 && !this.level().paperConfig().entities.behavior.disablePlayerCrits; // Paper - Toggleable player crits + if (flag2) { diff --git a/patches/server/0073-Allow-disabling-sweep-attacks.patch b/patches/server/0073-Allow-disabling-sweep-attacks.patch new file mode 100644 index 0000000..0316b8e --- /dev/null +++ b/patches/server/0073-Allow-disabling-sweep-attacks.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Fri, 23 Feb 2024 01:49:20 +0000 +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 +--- 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 { + + LivingEntity entityliving2; + +- if (flag3) { ++ if (flag3 && this.level().sakuraConfig().players.combat.allowSweepAttacks) { // Sakura - allow disabling sweep attacks + float f6 = 1.0F + (float) this.getAttributeValue(Attributes.SWEEPING_DAMAGE_RATIO) * f; + List list = this.level().getEntitiesOfClass(LivingEntity.class, target.getBoundingBox().inflate(1.0D, 0.25D, 1.0D)); + Iterator iterator = list.iterator(); diff --git a/patches/server/0072-Change-shields-to-reduce-damage.patch b/patches/server/0074-Change-shields-to-reduce-damage.patch similarity index 88% rename from patches/server/0072-Change-shields-to-reduce-damage.patch rename to patches/server/0074-Change-shields-to-reduce-damage.patch index daf524f..16aa9c5 100644 --- a/patches/server/0072-Change-shields-to-reduce-damage.patch +++ b/patches/server/0074-Change-shields-to-reduce-damage.patch @@ -5,10 +5,10 @@ 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 d0f5ace260a913bc5169ea7ea0481c3e3b4b5616..a6f93d020acff05bcda22293a96e4326edc54c22 100644 +index 7d451c2924ab944e453c56a02fb02dd0b81381ef..3f79e5a83bb85af6ff1a256736394622384cf5b1 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -2321,7 +2321,13 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -2330,7 +2330,13 @@ public abstract class LivingEntity extends Entity implements Attackable { Function blocking = new Function() { @Override public Double apply(Double f) { diff --git a/patches/server/0073-Old-enchanted-golden-apples.patch b/patches/server/0075-Old-enchanted-golden-apples.patch similarity index 75% rename from patches/server/0073-Old-enchanted-golden-apples.patch rename to patches/server/0075-Old-enchanted-golden-apples.patch index 6b54f0c..3f2dd81 100644 --- a/patches/server/0073-Old-enchanted-golden-apples.patch +++ b/patches/server/0075-Old-enchanted-golden-apples.patch @@ -5,12 +5,12 @@ 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 a6f93d020acff05bcda22293a96e4326edc54c22..5b5b82955e28162bde34d6d8781a6936c61320c8 100644 +index 3f79e5a83bb85af6ff1a256736394622384cf5b1..5842543211e415aa1754d7002c9abb7ea65a56e6 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -4479,6 +4479,11 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -4498,6 +4498,11 @@ public abstract class LivingEntity extends Entity implements Attackable { - public ItemStack eat(Level world, ItemStack stack) { + public final ItemStack eat(Level world, ItemStack stack) { FoodProperties foodinfo = (FoodProperties) stack.get(DataComponents.FOOD); + // Sakura start - old enchanted golden apple + if (this.level().sakuraConfig().players.combat.oldEnchantedGoldenApple && foodinfo == net.minecraft.world.food.Foods.ENCHANTED_GOLDEN_APPLE) { @@ -18,13 +18,13 @@ index a6f93d020acff05bcda22293a96e4326edc54c22..5b5b82955e28162bde34d6d8781a6936 + } + // Sakura end - old enchanted golden apple - if (foodinfo != null) { - world.playSound((net.minecraft.world.entity.player.Player) null, this.getX(), this.getY(), this.getZ(), this.getEatingSound(stack), SoundSource.NEUTRAL, 1.0F, 1.0F + (world.random.nextFloat() - world.random.nextFloat()) * 0.4F); + return foodinfo != null ? this.eat(world, stack, foodinfo) : stack; + } diff --git a/src/main/java/net/minecraft/world/food/Foods.java b/src/main/java/net/minecraft/world/food/Foods.java -index 60fd0e89552aeaa78b98dda32caa6766097d3108..7ced9d09ad09b53979214dae57a6347620e55569 100644 +index e13d9ed48431b019b1f68aae315af74cec34fcee..c2e113016b233377f6e8b032336707a4c1b001f5 100644 --- a/src/main/java/net/minecraft/world/food/Foods.java +++ b/src/main/java/net/minecraft/world/food/Foods.java -@@ -36,6 +36,15 @@ public class Foods { +@@ -37,6 +37,15 @@ public class Foods { .effect(new MobEffectInstance(MobEffects.ABSORPTION, 2400, 3), 1.0F) .alwaysEdible() .build(); diff --git a/patches/server/0074-Configure-fast-health-regen.patch b/patches/server/0076-Configure-fast-health-regen.patch similarity index 91% rename from patches/server/0074-Configure-fast-health-regen.patch rename to patches/server/0076-Configure-fast-health-regen.patch index 2f6fd52..44c4880 100644 --- a/patches/server/0074-Configure-fast-health-regen.patch +++ b/patches/server/0076-Configure-fast-health-regen.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configure fast health regen diff --git a/src/main/java/net/minecraft/world/food/FoodData.java b/src/main/java/net/minecraft/world/food/FoodData.java -index b89860d451d92ddda64b7e4144542b7fc5fd86f0..bb3e1a81356bda63e64f519cce96659801022925 100644 +index bd3f78e6453cfe18aa3da38176b04d734d83bb4b..22160241bd421d02f8ecec37899597c0c0afb53e 100644 --- a/src/main/java/net/minecraft/world/food/FoodData.java +++ b/src/main/java/net/minecraft/world/food/FoodData.java @@ -83,7 +83,7 @@ public class FoodData { diff --git a/patches/server/0076-Old-combat-sounds-and-particle-effects.patch b/patches/server/0076-Old-combat-sounds-and-particle-effects.patch deleted file mode 100644 index e2d4e27..0000000 --- a/patches/server/0076-Old-combat-sounds-and-particle-effects.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik -Date: Tue, 11 Jun 2024 13:37:51 +0100 -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 4546df07cdf1d73fcb9c03794efc47c629842e31..a76491cf9bf598ad7e0cbb40cecbeda68c1d822f 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -1473,7 +1473,7 @@ public abstract class Player extends LivingEntity { - // CraftBukkit end - } - -- if (this.level() instanceof ServerLevel && f5 > 2.0F) { -+ if (this.level() instanceof ServerLevel && f5 > 2.0F && !this.level().sakuraConfig().players.combat.oldSoundsAndParticleEffects) { // Sakura - old combat sounds and particles - int k = (int) ((double) f5 * 0.5D); - - ((ServerLevel) this.level()).sendParticles(ParticleTypes.DAMAGE_INDICATOR, target.getX(), target.getY(0.5D), target.getZ(), k, 0.1D, 0.0D, 0.1D, 0.2D); -@@ -1903,6 +1903,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) { -+ if (fromEntity.level().sakuraConfig().players.combat.oldSoundsAndParticleEffects) return; // Sakura - old combat sounds and particles - fromEntity.level().playSound(fromEntity, x, y, z, soundEffect, soundCategory, volume, pitch); // This will not send the effect to the entity himself - if (fromEntity instanceof ServerPlayer) { - ((ServerPlayer) fromEntity).connection.send(new net.minecraft.network.protocol.game.ClientboundSoundPacket(net.minecraft.core.registries.BuiltInRegistries.SOUND_EVENT.wrapAsHolder(soundEffect), soundCategory, x, y, z, volume, pitch, fromEntity.random.nextLong())); diff --git a/patches/server/0075-Add-option-for-fishing-hooks-pulling-entities.patch b/patches/server/0077-Add-option-for-fishing-hooks-pulling-entities.patch similarity index 86% rename from patches/server/0075-Add-option-for-fishing-hooks-pulling-entities.patch rename to patches/server/0077-Add-option-for-fishing-hooks-pulling-entities.patch index 50d58d5..7a115d7 100644 --- a/patches/server/0075-Add-option-for-fishing-hooks-pulling-entities.patch +++ b/patches/server/0077-Add-option-for-fishing-hooks-pulling-entities.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add option for fishing hooks pulling entities 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 d9deb1b47b2430936d74e782cfc4a6ac32a16c91..218b6a72a76b8acfd55952265b34fefafbce3cc3 100644 +index 43a3ac5858ffb2007e6f63ec0f3bc5a9971dce14..bf8f903bb03b58c53c8307323f3f94779300b7e0 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java +++ b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java -@@ -615,7 +615,7 @@ public class FishingHook extends Projectile { +@@ -616,7 +616,7 @@ public class FishingHook extends Projectile { public void pullEntity(Entity entity) { Entity entity1 = this.getOwner(); diff --git a/patches/server/0078-Old-combat-sounds-and-particle-effects.patch b/patches/server/0078-Old-combat-sounds-and-particle-effects.patch new file mode 100644 index 0000000..cc48a1f --- /dev/null +++ b/patches/server/0078-Old-combat-sounds-and-particle-effects.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Tue, 11 Jun 2024 13:37:51 +0100 +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 +--- 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 { + float f8 = f4 - ((LivingEntity) target).getHealth(); + + this.awardStat(Stats.DAMAGE_DEALT, Math.round(f8 * 10.0F)); +- if (this.level() instanceof ServerLevel && f8 > 2.0F) { ++ if (this.level() instanceof ServerLevel && f8 > 2.0F && !this.level().sakuraConfig().players.combat.oldSoundsAndParticleEffects) { // Sakura - old combat sounds and particles + 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 { + } + // 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) { ++ if (fromEntity.level().sakuraConfig().players.combat.oldSoundsAndParticleEffects) return; // Sakura - old combat sounds and particles + fromEntity.level().playSound(fromEntity, x, y, z, soundEffect, soundCategory, volume, pitch); // This will not send the effect to the entity itself + if (fromEntity instanceof ServerPlayer serverPlayer) { + serverPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundSoundPacket(net.minecraft.core.registries.BuiltInRegistries.SOUND_EVENT.wrapAsHolder(soundEffect), soundCategory, x, y, z, volume, pitch, fromEntity.random.nextLong())); diff --git a/patches/server/0078-Protect-scaffolding-from-creepers.patch b/patches/server/0079-Protect-scaffolding-from-creepers.patch similarity index 90% rename from patches/server/0078-Protect-scaffolding-from-creepers.patch rename to patches/server/0079-Protect-scaffolding-from-creepers.patch index c6130ac..6376dcd 100644 --- a/patches/server/0078-Protect-scaffolding-from-creepers.patch +++ b/patches/server/0079-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 22007aa4c85befa80938adf3126bda12b8ff58c9..bf3a05d9787eb64f384b67261ab9e04d7adf343f 100644 +index 94df0527699462e29fb76f68194386c50f1f7e04..e4e59ee1f59b175205f2bd31d2abe542c5123123 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -285,6 +285,11 @@ public class Explosion { +@@ -287,6 +287,11 @@ public class Explosion { Block block = blockState.getBlock(); me.samsuik.sakura.explosion.durable.DurableMaterial material = this.level.localConfig().config(pos).durableMaterials.get(block);