diff --git a/patches/server/0039-Optimize-mob-spawning-Async-mob-spawn-state-calc.patch b/patches/server/0039-Optimize-mob-spawning-Async-mob-spawn-state-calc.patch index 8a3825d..2d2401d 100644 --- a/patches/server/0039-Optimize-mob-spawning-Async-mob-spawn-state-calc.patch +++ b/patches/server/0039-Optimize-mob-spawning-Async-mob-spawn-state-calc.patch @@ -225,7 +225,7 @@ index 7ca275826609bcf96f103a8c50beaa47c3b4068b..aae4b59a14fc514a30b133921669c855 final Entity[] ret = Arrays.copyOf(this.toProcessTrackingUnloading.getRawData(), this.toProcessTrackingUnloading.size(), Entity[].class); diff --git a/src/main/java/me/earthme/luminol/LuminolConfig.java b/src/main/java/me/earthme/luminol/LuminolConfig.java -index ca54cc28282ac6441098215d6e1a3531c3f68b83..c79070eb6bb71d473fab758ae9d826276ae7eea6 100644 +index ca54cc28282ac6441098215d6e1a3531c3f68b83..af0087be0615d40d036390afc4944ed516180c7d 100644 --- a/src/main/java/me/earthme/luminol/LuminolConfig.java +++ b/src/main/java/me/earthme/luminol/LuminolConfig.java @@ -2,6 +2,7 @@ package me.earthme.luminol; @@ -248,7 +248,7 @@ index ca54cc28282ac6441098215d6e1a3531c3f68b83..c79070eb6bb71d473fab758ae9d82627 asyncPathProcessingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1); if (!asyncPathProcessing) asyncPathProcessingMaxThreads = 0; -+ enableAsyncMobSpawning = get("optimizations.enable_async_mob_spawning",enableAsyncMobSpawning); ++ enableAsyncMobSpawning = get("optimizations.enable_async_mob_spawning",enableAsyncMobSpawning,"Not recommended to use this because it may cause some thread safe issue"); + RegionizedWorldData.initMobSpawningExecutor(); } diff --git a/patches/server/0040-Purpur-use-alternative-keep-alive.patch b/patches/server/0040-Purpur-use-alternative-keep-alive.patch index c81f844..87f26a6 100644 --- a/patches/server/0040-Purpur-use-alternative-keep-alive.patch +++ b/patches/server/0040-Purpur-use-alternative-keep-alive.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Purpur use alternative keep alive diff --git a/src/main/java/me/earthme/luminol/LuminolConfig.java b/src/main/java/me/earthme/luminol/LuminolConfig.java -index c79070eb6bb71d473fab758ae9d826276ae7eea6..ea9fe545a92ab555479fc518519be5d9f60efe11 100644 +index af0087be0615d40d036390afc4944ed516180c7d..6c60dd4bc09be71f950391adc1d735fe1dc6404a 100644 --- a/src/main/java/me/earthme/luminol/LuminolConfig.java +++ b/src/main/java/me/earthme/luminol/LuminolConfig.java @@ -63,6 +63,7 @@ public class LuminolConfig { @@ -18,7 +18,7 @@ index c79070eb6bb71d473fab758ae9d826276ae7eea6..ea9fe545a92ab555479fc518519be5d9 PARENT_FOLDER.mkdir(); @@ -190,6 +191,7 @@ public class LuminolConfig { asyncPathProcessingMaxThreads = 0; - enableAsyncMobSpawning = get("optimizations.enable_async_mob_spawning",enableAsyncMobSpawning); + enableAsyncMobSpawning = get("optimizations.enable_async_mob_spawning",enableAsyncMobSpawning,"Not recommended to use this because it may cause some thread safe issue"); RegionizedWorldData.initMobSpawningExecutor(); + useAlternateKeepAlive = get("optimizations.enable_alternative_keep_alive_handling",useAlternateKeepAlive,"Enabling this sends a keepalive packet once per second to a player, and only kicks for timeout if none of them were responded to in 30 seconds. Responding to any of them in any order will keep the player connected. AKA, it won't kick your players because one packet gets dropped somewhere along the lines(From purpur)"); } diff --git a/patches/server/0042-Leaves-PCA-sync-protocol.patch b/patches/server/0042-Leaves-PCA-sync-protocol.patch index ee984aa..ffba291 100644 --- a/patches/server/0042-Leaves-PCA-sync-protocol.patch +++ b/patches/server/0042-Leaves-PCA-sync-protocol.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Leaves PCA sync protocol diff --git a/src/main/java/me/earthme/luminol/LuminolConfig.java b/src/main/java/me/earthme/luminol/LuminolConfig.java -index ea9fe545a92ab555479fc518519be5d9f60efe11..2589f04a33b99f812cfc9b471398d5fd4afb7012 100644 +index 6c60dd4bc09be71f950391adc1d735fe1dc6404a..dc8c9f18289a5e01a4a5ccc99d6693ea98c4162a 100644 --- a/src/main/java/me/earthme/luminol/LuminolConfig.java +++ b/src/main/java/me/earthme/luminol/LuminolConfig.java @@ -65,6 +65,10 @@ public class LuminolConfig { @@ -20,7 +20,7 @@ index ea9fe545a92ab555479fc518519be5d9f60efe11..2589f04a33b99f812cfc9b471398d5fd PARENT_FOLDER.mkdir(); @@ -192,6 +196,9 @@ public class LuminolConfig { - enableAsyncMobSpawning = get("optimizations.enable_async_mob_spawning",enableAsyncMobSpawning); + enableAsyncMobSpawning = get("optimizations.enable_async_mob_spawning",enableAsyncMobSpawning,"Not recommended to use this because it may cause some thread safe issue"); RegionizedWorldData.initMobSpawningExecutor(); useAlternateKeepAlive = get("optimizations.enable_alternative_keep_alive_handling",useAlternateKeepAlive,"Enabling this sends a keepalive packet once per second to a player, and only kicks for timeout if none of them were responded to in 30 seconds. Responding to any of them in any order will keep the player connected. AKA, it won't kick your players because one packet gets dropped somewhere along the lines(From purpur)"); + diff --git a/patches/server/0050-Sparkly-Paper-Optimize-canSee-checks.patch b/patches/server/0050-Sparkly-Paper-Optimize-canSee-checks.patch new file mode 100644 index 0000000..1c65e3e --- /dev/null +++ b/patches/server/0050-Sparkly-Paper-Optimize-canSee-checks.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: MrHua269 +Date: Sun, 28 Jan 2024 09:11:58 +0000 +Subject: [PATCH] Sparkly Paper Optimize canSee checks + + +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index d01443026d0d85e017464cdd46d08f37386d46ef..7272b1d5c314465c22df7ffa83a0675e4b1d34d3 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -1422,7 +1422,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // Folia end - region threading + + // CraftBukkit start - respect vanish API +- if (flag && (!io.papermc.paper.util.TickThread.isTickThreadFor(player) || !player.getBukkitEntity().canSee(this.entity.getBukkitEntity()))) { // Paper - only consider hits // Folia - region threading ++ if (flag && (!io.papermc.paper.util.TickThread.isTickThreadFor(player) || !player.getBukkitEntity().canSeeChunkMapUpdatePlayer(this.entity.getBukkitEntity()))) { // Paper - only consider hits // Folia - region threading // SparklyPaper - optimize canSee checks + flag = false; + } + // CraftBukkit end +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index c41537b78922f85e8c7099be0b38f341b8c531a6..9481456671ee7cb880b4f1a83a41d3d4f7e7bc7f 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -181,7 +181,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + private boolean hasPlayedBefore = false; + private final ConversationTracker conversationTracker = new ConversationTracker(); + private final Set channels = new HashSet(); +- private final Map>> invertedVisibilityEntities = new HashMap<>(); ++ private final Map>> invertedVisibilityEntities = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); // SparklyPaper - optimize canSee checks + private final Set unlistedEntities = new HashSet<>(); // Paper - Add Listing API for Player + private static final WeakHashMap> pluginWeakReferences = new WeakHashMap<>(); + private int hash = 0; +@@ -2143,9 +2143,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + + @Override + public boolean canSee(org.bukkit.entity.Entity entity) { +- return this.equals(entity) || entity.isVisibleByDefault() ^ this.invertedVisibilityEntities.containsKey(entity.getUniqueId()); // SPIGOT-7312: Can always see self ++ return this.equals(entity) || entity.isVisibleByDefault() ^ (!invertedVisibilityEntities.isEmpty() && this.invertedVisibilityEntities.containsKey(entity.getUniqueId())); // SPIGOT-7312: Can always see self // SparklyPaper - optimize canSee checks + } + ++ // SparklyPaper - optimize canSee checks ++ // The check in ChunkMap#updatePlayer already rejects if it is the same entity, so we don't need to check it twice, especially because CraftPlayer's equals check is a bit expensive ++ public boolean canSeeChunkMapUpdatePlayer(org.bukkit.entity.Entity entity) { ++ return entity.isVisibleByDefault() ^ (!invertedVisibilityEntities.isEmpty() && this.invertedVisibilityEntities.containsKey(entity.getUniqueId())); // SPIGOT-7312: Can always see self // SparklyPaper - optimize canSee checks ++ } ++ // SparklyPaper end ++ + public boolean canSee(UUID uuid) { + org.bukkit.entity.Entity entity = this.getServer().getPlayer(uuid); + if (entity == null) { diff --git a/patches/server/0051-Gale-Skip-entity-move-if-movement-is-zero.patch b/patches/server/0051-Gale-Skip-entity-move-if-movement-is-zero.patch new file mode 100644 index 0000000..12c9b2e --- /dev/null +++ b/patches/server/0051-Gale-Skip-entity-move-if-movement-is-zero.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: MrHua269 +Date: Sun, 28 Jan 2024 09:29:26 +0000 +Subject: [PATCH] Gale Skip entity move if movement is zero + + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 235f96fd0f2c74356f986017f514d2805f88ef46..0b90847929c3c89ba5b8ac578f181c67d5209957 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -320,6 +320,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S + public float yRotO; + public float xRotO; + private AABB bb; ++ private boolean boundingBoxChanged = false; // Gale - VMP - skip entity move if movement is zero + public boolean onGround; + public boolean horizontalCollision; + public boolean verticalCollision; +@@ -1100,6 +1101,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S + //Luminol end + + public void move(MoverType movementType, Vec3 movement) { ++ // Gale start - VMP - skip entity move if movement is zero ++ if (!this.boundingBoxChanged && movement.equals(Vec3.ZERO)) { ++ return; ++ } ++ // Gale end - VMP - skip entity move if movement is zero + // Paper start - detailed watchdog information + io.papermc.paper.util.TickThread.ensureTickThread("Cannot move an entity off-main"); + //Luminol start - Fix high position moving +@@ -4938,6 +4944,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S + } + + public final void setBoundingBox(AABB boundingBox) { ++ // Gale start - VMP - skip entity move if movement is zero ++ if (!this.bb.equals(boundingBox)) { ++ this.boundingBoxChanged = true; ++ } ++ // Gale end - VMP - skip entity move if movement is zero + // CraftBukkit start - block invalid bounding boxes + double minX = boundingBox.minX, + minY = boundingBox.minY, diff --git a/patches/server/0052-Gale-Check-frozen-ticks-before-landing-block.patch b/patches/server/0052-Gale-Check-frozen-ticks-before-landing-block.patch new file mode 100644 index 0000000..afd7dd6 --- /dev/null +++ b/patches/server/0052-Gale-Check-frozen-ticks-before-landing-block.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: MrHua269 +Date: Sun, 28 Jan 2024 09:30:22 +0000 +Subject: [PATCH] Gale Check frozen ticks before landing block + + +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index 00990c41986ec717a318f756e1a73a0a94a82b39..be4b3fe916b72a5aec61f70e8776e8ae91d19a6e 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -590,11 +590,10 @@ public abstract class LivingEntity extends Entity implements Attackable { + } + + protected void tryAddFrost() { +- if (!this.getBlockStateOnLegacy().isAir()) { + int i = this.getTicksFrozen(); + + if (i > 0) { +- AttributeInstance attributemodifiable = this.getAttribute(Attributes.MOVEMENT_SPEED); ++ AttributeInstance attributemodifiable = this.getBlockStateOnLegacy().isAir() ? null : this.getAttribute(Attributes.MOVEMENT_SPEED); // Gale - Lithium - check frozen ticks before landing block + + if (attributemodifiable == null) { + return; +@@ -604,7 +603,6 @@ public abstract class LivingEntity extends Entity implements Attackable { + + attributemodifiable.addTransientModifier(new AttributeModifier(LivingEntity.SPEED_MODIFIER_POWDER_SNOW_UUID, "Powder snow slow", (double) f, AttributeModifier.Operation.ADDITION)); + } +- } + + } + diff --git a/patches/server/0053-Gale-Don-t-trigger-lootable-refresh-for-non-player-i.patch b/patches/server/0053-Gale-Don-t-trigger-lootable-refresh-for-non-player-i.patch new file mode 100644 index 0000000..f6a0c95 --- /dev/null +++ b/patches/server/0053-Gale-Don-t-trigger-lootable-refresh-for-non-player-i.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: MrHua269 +Date: Sun, 28 Jan 2024 09:31:27 +0000 +Subject: [PATCH] Gale Don't trigger lootable refresh for non-player + interaction + + +diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java +index b41635dd0569ff7df909df492d3e850aef7214be..7ff40b74827a52eb5daaff72e1563b044d41c6c1 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java +@@ -68,6 +68,7 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc + + @Override + public void unpackLootTable(@org.jetbrains.annotations.Nullable final Player player) { ++ if (player == null) return; // Gale - EMC - don't trigger lootable refresh for non-player interaction + // Copied from super with changes, always check the original method + net.minecraft.world.level.Level level = this.getLevel(); + BlockPos blockPos = this.getBlockPos(); diff --git a/patches/server/0054-Gale-Use-platform-math-functions.patch b/patches/server/0054-Gale-Use-platform-math-functions.patch new file mode 100644 index 0000000..1f938cf --- /dev/null +++ b/patches/server/0054-Gale-Use-platform-math-functions.patch @@ -0,0 +1,187 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: MrHua269 +Date: Sun, 28 Jan 2024 09:31:27 +0000 +Subject: [PATCH] Gale Use platform math functions + +License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) +Gale - https://galemc.org + +This patch is based on the following patch: +"Use Math.floor instead of fastfloor" +By: Xymb +As part of: Kaiiju (https://github.com/KaiijuMC/Kaiiju) +Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) + +* Comparison of floor methods used in Paper * + +Measure shown is floored number of milliseconds +(nanoseconds integer-divided by 1_000_000 +taken to get the floor of 1000 randomly chosen doubles +(all in the range of [-Integer.MAX_VALUE + 10, Integer.MAX_VALUE - 10]) +100_000 times (making it 100_000_000 floor operations total) +and adding it to a total. + +We are testing the following methods: +* net.minecraft.util.Mth.floor +* java.lang.Math.floor +* java.lang.StrictMath.floor +* org.apache.commons.math3.util.FastMath.floor +* org.apache.commons.math3.util.FastMath.floor, but with a hot start (see comment in code) +* io.papermc.paper.util.MCUtil.fastFloor + +The tests performed clearly show that Math.floor is the fastest. +This is most likely due to java.lang.Math's usage of the @IntrinsicCandidate +annotation, which allows the JVM to use a more optimized implementation at runtime. +However, in the case that there is no intrinsic replacement for Math.floor, +it defers to StrictMath.floor, which relies on a number of native methods, and is +still much faster than the existing Minecraft utility functions. +Therefore, using Math.floor instead of these functions is better regardless. +In Apache Commons Math 4, FastMath.floor has also been removed in favor of Math.floor. + +The versions used: +* Windows 10 Home 21H2 19044.3086 +* OpenJDK Runtime Environment Temurin-19.0.2+7 (build 19.0.2+7) +* Paper a3c760e6af1e8c7244ef75c6da6e6df278a79e14 on Minecraft 1.20.1 +* Apache Commons Math 3.6.1 + +Results: +Total is of type int Total is of type double +---------------------------------------------------------------------------------- +Mth.floor 2113 (double) Mth.floor 2658 +(int) Math.floor 130 Math.floor 194 +(int) StrictMath.floor 835 StrictMath.floor 381 +(int) FastMath.floor 412 FastMath.floor 376 +(int) FastMath.floor with hot start 359 FastMath.floor with hot start 321 +MCUtil.fastFloor 2284 (double) MCUtil.fastFloor 2469 + +Code is below: +```java +package somepackage; + +import io.papermc.paper.util.MCUtil; +import net.minecraft.util.Mth; + +import java.util.Random; + +public class Main { + + public static void main(String[] args) { + + // IF FastMath.floor with a hot start: + // FastMath.floor(37485.5); + + var random = new Random(4889338); + int size = 1000; + var values = new double[size]; + double bound = Integer.MAX_VALUE - 10; + for (int i = 0; i < size; i++) { + values[i] = random.nextDouble(bound * 2) - bound; + } + int repeats = 100_000; + + // int total = 0; + // OR + // double total = 0; + + long start = System.nanoTime(); + for (int repeat = 0; repeat < repeats; repeat++) { + for (int index = 0; index < size; index++) { + total += insert_function_being_tested_here(values[index]); + } + } + long diff = System.nanoTime() - start; + System.out.println(total); + System.out.println(diff / 1_000_000L); + + } + +} +``` + +diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java +index 8ec8084195e0400de76460b445012d605fe05724..e8fed9c43073eb443af289c524935cfaccf8e12c 100644 +--- a/src/main/java/io/papermc/paper/util/MCUtil.java ++++ b/src/main/java/io/papermc/paper/util/MCUtil.java +@@ -165,13 +165,11 @@ public final class MCUtil { + } + + public static int fastFloor(double x) { +- int truncated = (int)x; +- return x < (double)truncated ? truncated - 1 : truncated; ++ return (int) Math.floor(x); // Gale - use platform math functions + } + + public static int fastFloor(float x) { +- int truncated = (int)x; +- return x < (double)truncated ? truncated - 1 : truncated; ++ return (int) Math.floor(x); // Gale - use platform math functions + } + + public static float normalizeYaw(float f) { +@@ -232,11 +230,11 @@ public final class MCUtil { + } + + public static int getChunkCoordinate(final double coordinate) { +- return MCUtil.fastFloor(coordinate) >> 4; ++ return ((int) Math.floor(coordinate)) >> 4; // Gale - use platform math functions + } + + public static int getBlockCoordinate(final double coordinate) { +- return MCUtil.fastFloor(coordinate); ++ return (int) Math.floor(coordinate); // Gale - use platform math functions + } + + public static long getBlockKey(final int x, final int y, final int z) { +diff --git a/src/main/java/net/minecraft/util/Mth.java b/src/main/java/net/minecraft/util/Mth.java +index 03854a2d26a3cfc1817acfdc28cbf151ba59e05b..d9e0224dc108a99c194d4947d18f2d855a341534 100644 +--- a/src/main/java/net/minecraft/util/Mth.java ++++ b/src/main/java/net/minecraft/util/Mth.java +@@ -56,13 +56,11 @@ public class Mth { + } + + public static int floor(float value) { +- int i = (int)value; +- return value < (float)i ? i - 1 : i; ++ return (int) Math.floor(value); // Gale - use platform math functions + } + + public static int floor(double value) { +- int i = (int)value; +- return value < (double)i ? i - 1 : i; ++ return (int) Math.floor(value); // Gale - use platform math functions + } + + public static long lfloor(double value) { +@@ -79,13 +77,11 @@ public class Mth { + } + + public static int ceil(float value) { +- int i = (int)value; +- return value > (float)i ? i + 1 : i; ++ return (int) Math.ceil(value); // Gale - use platform math functions + } + + public static int ceil(double value) { +- int i = (int)value; +- return value > (double)i ? i + 1 : i; ++ return (int) Math.ceil(value); // Gale - use platform math functions + } + + public static int clamp(int value, int min, int max) { +@@ -121,15 +117,7 @@ public class Mth { + } + + public static double absMax(double a, double b) { +- if (a < 0.0D) { +- a = -a; +- } +- +- if (b < 0.0D) { +- b = -b; +- } +- +- return Math.max(a, b); ++ return Math.max(Math.abs(a), Math.abs(b)); // Gale - use platform math functions + } + + public static int floorDiv(int dividend, int divisor) { diff --git a/patches/server/0055-Gale-Faster-floating-point-positive-modulo.patch b/patches/server/0055-Gale-Faster-floating-point-positive-modulo.patch new file mode 100644 index 0000000..2d9980b --- /dev/null +++ b/patches/server/0055-Gale-Faster-floating-point-positive-modulo.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: MrHua269 +Date: Sun, 28 Jan 2024 09:31:27 +0000 +Subject: [PATCH] Gale Faster floating-point positive modulo + +License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) +Gale - https://galemc.org + +diff --git a/src/main/java/net/minecraft/util/Mth.java b/src/main/java/net/minecraft/util/Mth.java +index d9e0224dc108a99c194d4947d18f2d855a341534..79419f6e1ba900c1686fa9b43866fc43649123a1 100644 +--- a/src/main/java/net/minecraft/util/Mth.java ++++ b/src/main/java/net/minecraft/util/Mth.java +@@ -148,14 +148,26 @@ public class Mth { + return Math.floorMod(dividend, divisor); + } + +- public static float positiveModulo(float dividend, float divisor) { ++ public static float positiveModuloForAnyDivisor(float dividend, float divisor) { // Gale - faster floating-point positive modulo + return (dividend % divisor + divisor) % divisor; + } + +- public static double positiveModulo(double dividend, double divisor) { ++ public static double positiveModuloForAnyDivisor(double dividend, double divisor) { // Gale - faster floating-point positive modulo + return (dividend % divisor + divisor) % divisor; + } + ++ // Gale start - faster floating-point positive modulo ++ public static float positiveModuloForPositiveIntegerDivisor(float dividend, float divisor) { ++ var modulo = dividend % divisor; ++ return modulo < 0 ? modulo + divisor : modulo; ++ } ++ ++ public static double positiveModuloForPositiveIntegerDivisor(double dividend, double divisor) { ++ var modulo = dividend % divisor; ++ return modulo < 0 ? modulo + divisor : modulo; ++ } ++ // Gale end - faster floating-point positive modulo ++ + public static boolean isMultipleOf(int a, int b) { + return a % b == 0; + } +diff --git a/src/main/java/net/minecraft/world/level/levelgen/blending/Blender.java b/src/main/java/net/minecraft/world/level/levelgen/blending/Blender.java +index 8d40205f56a7b204a65505f9e1b4e20000221755..fe4a27e9e8265fc60e1db6dd070f91b212904e30 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/blending/Blender.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/blending/Blender.java +@@ -139,7 +139,7 @@ public class Blender { + private static double heightToOffset(double height) { + double d = 1.0D; + double e = height + 0.5D; +- double f = Mth.positiveModulo(e, 8.0D); ++ double f = Mth.positiveModuloForPositiveIntegerDivisor(e, 8.0D); // Gale - faster floating-point positive modulo + return 1.0D * (32.0D * (e - 128.0D) - 3.0D * (e - 120.0D) * f + 3.0D * f * f) / (128.0D * (32.0D - 3.0D * f)); + } +