diff --git a/patches/server/0020-Use-a-faster-random-implementation.patch b/patches/server/0020-Use-a-faster-random-implementation.patch new file mode 100644 index 0000000..5bc098b --- /dev/null +++ b/patches/server/0020-Use-a-faster-random-implementation.patch @@ -0,0 +1,549 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: nostalgic853 +Date: Mon, 24 Oct 2022 10:52:07 +0800 +Subject: [PATCH] Use a faster random implementation + + +diff --git a/src/main/java/cc/keyimc/keyi/utils/FastRandom.java b/src/main/java/cc/keyimc/keyi/utils/FastRandom.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a9b3a467e7dbfdc78c11f04e6bb5a9642c20470c +--- /dev/null ++++ b/src/main/java/cc/keyimc/keyi/utils/FastRandom.java +@@ -0,0 +1,395 @@ ++/** ++ * This is a faster implementation of java.util.Random. ++ * Code from https://gist.github.com/Xyene/4637619 ++ * Licensed under GNU Lesser General Public License v3.0 ++ *

++ * A random number generator based on the simple and fast xor-shift pseudo ++ * random number generator (RNG) specified in: ++ * Marsaglia, George. (2003). Xorshift RNGs. ++ * http://www.jstatsoft.org/v08/i14/xorshift.pdf ++ * Translated from: ++ * http://www.codeproject.com/Articles/9187/A-fast-equivalent-for-System-Random. ++ */ ++ ++package cc.keyimc.keyi.utils; ++ ++@SuppressWarnings("SuspiciousNameCombination") ++public class FastRandom extends java.util.Random { ++ final double REAL_UNIT_INT = 1.0 / (0x7FFFFFFFL); ++ final double REAL_UNIT_UINT = 1.0 / (0xFFFFFFFFL); ++ final long Y = 842502087L, Z = 3579807591L, W = 273326509L; ++ long x, y, z, w; ++ ++ public FastRandom() { ++ seed((int) System.currentTimeMillis()); ++ } ++ ++ @Override ++ public void setSeed(long seed) { ++ seed((int) seed); ++ } ++ ++ public void seed(int seed) { ++ // The only stipulation stated for the xorshift RNG is that at least one of ++ // the seeds x,y,z,w is non-zero. We fulfill that requirement by only allowing ++ // resetting of the x seed ++ x = seed; ++ y = Y; ++ z = Z; ++ w = W; ++ } ++ ++ long boolBuffer; ++ int boolBufferBits = 0; ++ ++ @Override ++ public boolean nextBoolean() { ++ if (boolBufferBits == 0) { ++ boolBuffer = nextUInt(); ++ boolBufferBits = 32; ++ } ++ boolBuffer >>= 1; ++ boolean bit = (boolBuffer & 1) == 0; ++ --boolBufferBits; ++ return bit; ++ } ++ ++ @Override ++ public void nextBytes(byte[] buffer) { ++ // Fill up the bulk of the buffer in chunks of 4 bytes at a time. ++ long x = this.x, y = this.y, z = this.z, w = this.w; ++ int i = 0; ++ long t; ++ for (int bound = buffer.length - 3; i < bound; ) { ++ // Generate 4 bytes. ++ // Increased performance is achieved by generating 4 random bytes per loop. ++ // Also note that no mask needs to be applied to zero out the higher order bytes before ++ // casting because the cast ignores thos bytes. Thanks to Stefan Trosché»·z for pointing this out. ++ t = (x ^ (x << 11)); ++ x = y; ++ y = z; ++ z = w; ++ w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); ++ ++ buffer[i++] = (byte) w; ++ buffer[i++] = (byte) (w >> 8); ++ buffer[i++] = (byte) (w >> 16); ++ buffer[i++] = (byte) (w >> 24); ++ } ++ ++ // Fill up any remaining bytes in the buffer. ++ if (i < buffer.length) { ++ // Generate 4 bytes. ++ t = (x ^ (x << 11)); ++ x = y; ++ y = z; ++ z = w; ++ w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); ++ ++ buffer[i++] = (byte) w; ++ if (i < buffer.length) { ++ buffer[i++] = (byte) (w >> 8); ++ if (i < buffer.length) { ++ buffer[i++] = (byte) (w >> 16); ++ if (i < buffer.length) { ++ buffer[i] = (byte) (w >> 24); ++ } ++ } ++ } ++ } ++ this.x = x; ++ this.y = y; ++ this.z = z; ++ this.w = w; ++ } ++ ++ @Override ++ public double nextDouble() { ++ long t = (x ^ (x << 11)); ++ x = y; ++ y = z; ++ z = w; ++ ++ // Here we can gain a 2x speed improvement by generating a value that can be cast to ++ // an int instead of the more easily available uint. If we then explicitly cast to an ++ // int the compiler will then cast the int to a double to perform the multiplication, ++ // this final cast is a lot faster than casting from a uint to a double. The extra cast ++ // to an int is very fast (the allocated bits remain the same) and so the overall effect ++ // of the extra cast is a significant performance improvement. ++ // ++ // Also note that the loss of one bit of precision is equivalent to what occurs within ++ // System.Random. ++ return (REAL_UNIT_INT * (int) (0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))); ++ } ++ ++ public double random() { ++ return nextDouble(); ++ } ++ ++ @Override ++ public float nextFloat() { ++ return (float) nextDouble(); ++ } ++ ++ @Override ++ public int nextInt() { ++ long t = (x ^ (x << 11)); ++ x = y; ++ y = z; ++ z = w; ++ return (int) (0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))); ++ } ++ ++ @Override ++ public int nextInt(int upperBound) { ++ if (upperBound < 0) ++ throw new IllegalArgumentException("upperBound must be >=0"); ++ ++ long t = (x ^ (x << 11)); ++ x = y; ++ y = z; ++ z = w; ++ ++ return (int) ((REAL_UNIT_INT * (int) (0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * upperBound); ++ } ++ ++ public int nextInt(int lowerBound, int upperBound) { ++ if (lowerBound > upperBound) ++ throw new IllegalArgumentException("upperBound must be >=lowerBound"); ++ ++ long t = (x ^ (x << 11)); ++ x = y; ++ y = z; ++ z = w; ++ ++ // The explicit int cast before the first multiplication gives better performance. ++ // See comments in NextDouble. ++ int range = upperBound - lowerBound; ++ if (range < 0) { ++ // If range is <0 then an overflow has occured and must resort to using long integer arithmetic instead (slower). ++ // We also must use all 32 bits of precision, instead of the normal 31, which again is slower. ++ return lowerBound + (int) ((REAL_UNIT_UINT * (double) (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))) * (double) ((long) upperBound - (long) lowerBound)); ++ } ++ // 31 bits of precision will suffice if range<=int.MaxValue. This allows us to cast to an int and gain ++ // a little more performance. ++ return lowerBound + (int) ((REAL_UNIT_INT * (double) (int) (0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * (double) range); ++ } ++ ++ public long nextUInt() { ++ long t = (x ^ (x << 11)); ++ x = y; ++ y = z; ++ z = w; ++ return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))) & (0xFFFFFFFFL); ++ } ++ ++ @Override ++ public long nextLong() { ++ return nextUInt() << 32 + nextUInt(); ++ } ++ ++ double gaussNext; ++ boolean hasGaussNext; ++ final double TWOPI = Math.PI * 2; ++ ++ /** ++ * Get a random number in the range [min, max) or [min, max] depending on rounding. ++ * ++ * @param min Low bound ++ * @param max High bound ++ * @return A uniformly distributed double ++ */ ++ public double uniform(double min, double max) { ++ return min + (max - min) * nextDouble(); ++ } ++ ++ /** ++ * Triangular distribution. ++ *

++ * Continuous distribution bounded by given lower and upper limits, ++ * and having a given mode value in-between. ++ * http://en.wikipedia.org/wiki/Triangular_distribution ++ * ++ * @param low Low bound ++ * @param high High bound ++ * @param mode Mode ++ * @return A number from the triangular distribution specified ++ */ ++ public double triangular(int low, int high, int mode) { ++ double u = nextDouble(); ++ double c = (mode - low) / (high - low); ++ if (u > c) { ++ u = 1.0 - u; ++ c = 1.0 - c; ++ int k = low; ++ low = high; ++ high = k; ++ } ++ return low + (high - low) * Math.sqrt(u * c); ++ } ++ ++ /** ++ * Gaussian distribution, mean is 0 and standard deviation is 1. ++ *

++ * mu is the mean, and sigma is the standard deviation. ++ * ++ * @return A double in Gaussian distribution ++ */ ++ public double gauss() { ++ return nextGaussian(); ++ } ++ ++ /** ++ * Gaussian distribution, with user-specified mean and standard deviation. ++ *

++ * mu is the mean, and sigma is the standard deviation. ++ * ++ * @return A double in Gaussian distribution ++ */ ++ public double gauss(double mu, double sigma) { ++ return mu + sigma * nextGaussian(); ++ } ++ ++ public double gaussUnsigned(double mu, double sigma) { ++ double out = gauss(mu, sigma); ++ return out > 1 ? out : 1; ++ } ++ ++ /** ++ * Log normal distribution. ++ *

++ * If you take the natural logarithm of this distribution, you'll get a ++ * normal distribution with mean mu and standard deviation sigma. ++ * mu can have any value, and sigma must be greater than zero. ++ * ++ * @param mu Mean ++ * @param sigma Standard deviation ++ * @return A number from the log normal distribution specified ++ */ ++ public double logNormal(double mu, double sigma) { ++ return Math.exp(gauss(mu, sigma)); ++ } ++ ++ /** ++ * Exponential distribution. ++ *

++ * lambda is 1.0 divided by the desired mean. It should be ++ * nonzero. Returned values range from 0 to positive infinity ++ * if lambda is positive, and from negative infinity to 0 ++ * if lambda is negative. ++ * ++ * @param lambda A non-zero value ++ */ ++ public double exponential(double lambda) { ++ return -Math.log(1.0 - random()) / lambda; ++ } ++ ++ /** ++ * Circular data distribution. ++ *

++ * If kappa is equal to zero, this distribution reduces ++ * to a uniform random angle over the range 0 to 2*pi. ++ * ++ * @param mu the mean angle, expressed in radians between 0 and 2*pi. ++ * @param kappa the concentration parameter, which must be greater than or ++ * equal to zero. ++ * @return A number from the circular data distribution specified ++ */ ++ public double circularData(double mu, double kappa) { ++ if (kappa <= 1e-6) ++ return TWOPI * nextDouble(); ++ ++ double a = 1.0 + Math.sqrt(1.0 + 4.0 * kappa * kappa); ++ double b = (a - Math.sqrt(2.0 * a)) / (2.0 * kappa); ++ double r = (1.0 + b * b) / (2.0 * b); ++ double u1, u2, u3, f, c, z, theta = 0; ++ ++ while (true) { ++ u1 = nextDouble(); ++ ++ z = Math.cos(Math.PI * u1); ++ f = (1.0 + r * z) / (r + z); ++ c = kappa * (r - f); ++ ++ u2 = nextDouble(); ++ ++ if (u2 < c * (2.0 - c) || u2 <= c * Math.exp(1.0 - c)) ++ break; ++ ++ u3 = nextDouble(); ++ if (u3 > 0.5) ++ theta = (mu % TWOPI) + Math.acos(f); ++ else ++ theta = (mu % TWOPI) - Math.acos(f); ++ } ++ return theta; ++ } ++ ++ ++ final double LOG4 = Math.log(4); ++ final double SG_MAGICCONST = 1.0 + Math.log(4.5); ++ ++ /** ++ * Gamma distribution. Not the gamma function! ++ * Conditions on the parameters are alpha > 0 and beta > 0. ++ *

++ * The probability distribution function is: ++ * pdf(x) = (x ** (alpha - 1) * math.exp(-x / beta)) / (math.gamma(alpha) * beta ** alpha) ++ * ++ * @param alpha Alpha ++ * @param beta Beta ++ * @return A number from the gamma distribution specified ++ */ ++ public double gamma(double alpha, double beta) { ++ if (alpha <= 0.0 || beta <= 0.0) ++ throw new IllegalArgumentException("alpha and beta must be > 0.0"); ++ ++ if (alpha > 1.0) { ++ double ainv = Math.sqrt(2.0 * alpha - 1.0); ++ double bbb = alpha - LOG4; ++ double ccc = alpha + ainv; ++ double u1, u2, v, x, z, r; ++ ++ while (true) { ++ u1 = random(); ++ if (!(1e-7 < u1 && u1 < .9999999)) ++ continue; ++ u2 = 1.0 - random(); ++ v = Math.log(u1 / (1.0 - u1)) / ainv; ++ x = alpha * Math.exp(v); ++ z = u1 * u1 * u2; ++ r = bbb + ccc * v - x; ++ if (r + SG_MAGICCONST - 4.5 * z >= 0.0 || r >= Math.log(z)) ++ return x * beta; ++ } ++ } else if (alpha == 1.0) { ++ // exponential(1) ++ double u; ++ u = random(); ++ while (u <= 1e-7) ++ u = random(); ++ return -Math.log(u) * beta; ++ } else { ++ // alpha is between 0 and 1 (exclusive) ++ // Uses ALGORITHM GS of Statistical Computing -Kennedy & Gentle ++ ++ double u, b, p, x, u1; ++ while (true) { ++ u = random(); ++ b = (Math.E + alpha) / Math.E; ++ p = b * u; ++ if (p <= 1.0) ++ x = Math.pow(p, (1.0 / alpha)); ++ else ++ x = -Math.log((b - p) / alpha); ++ u1 = random(); ++ if (p > 1.0) { ++ if (u1 <= Math.pow(x, (alpha - 1.0))) ++ break; ++ } else if (u1 <= Math.exp(-x)) ++ break; ++ } ++ return x * beta; ++ } ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java +index e5ea9f27a1936ed9e329e74317c91c5df89b9fbd..89a41d396162a1c2eb2df5192b0d888b08fec6ec 100644 +--- a/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java ++++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java +@@ -10,10 +10,11 @@ import java.util.HashMap; + import java.util.Map; + import java.util.Random; + import java.util.UUID; ++import cc.keyimc.keyi.utils.FastRandom; // KeYi + + public class PaperLootableInventoryData { + +- private static final Random RANDOM = new Random(); ++ private static final Random RANDOM = new FastRandom(); // KeYi - use a faster random + + private long lastFill = -1; + private long nextRefill = -1; +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/IterateOutwardsCache.java b/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/IterateOutwardsCache.java +index a5d3aa309d3fdaab9e0fea2dfb91a080a3ac1193..384733b2b0bc7c5108ebdc725c0d13548bd349a3 100644 +--- a/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/IterateOutwardsCache.java ++++ b/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/IterateOutwardsCache.java +@@ -6,6 +6,7 @@ import java.util.Iterator; + import java.util.Random; + import java.util.concurrent.ConcurrentHashMap; + import net.minecraft.core.BlockPos; ++import cc.keyimc.keyi.utils.FastRandom; // KeYi + + /** + * @author 2No2Name, original implemenation by SuperCoder7979 and Gegy1000 +@@ -22,7 +23,7 @@ public class IterateOutwardsCache { + public IterateOutwardsCache(int capacity) { + this.capacity = capacity; + this.table = new ConcurrentHashMap<>(31); +- this.random = new Random(); ++ this.random = new FastRandom(); // KeYi - use a faster random + } + + private void fillPositionsWithIterateOutwards(LongList entry, int xRange, int yRange, int zRange) { +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 bdf67c916fe435f3bd04a61cce6db93c606515ce..f01dd420e2ff4f87b00682681a4f7dbf05955d24 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -53,6 +53,10 @@ import net.minecraft.world.level.material.Fluids; + import net.minecraft.world.ticks.LevelChunkTicks; + import net.minecraft.world.ticks.TickContainerAccess; + import org.slf4j.Logger; ++// KeYi start ++import cc.keyimc.keyi.utils.FastRandom; ++import java.util.Random; // KeYi ++// KeYi end + + public class LevelChunk extends ChunkAccess { + +@@ -957,7 +961,7 @@ public class LevelChunk extends ChunkAccess { + if (this.needsDecoration) { + //try (co.aikar.timings.Timing ignored = this.level.timings.chunkLoadPopulate.startTiming()) { // Paper // Purpur + this.needsDecoration = false; +- java.util.Random random = new java.util.Random(); ++ Random random = new FastRandom(); + random.setSeed(this.level.getSeed()); + long xRand = random.nextLong() / 2L * 2L + 1L; + long zRand = random.nextLong() / 2L * 2L + 1L; +diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java +index fc3442b4c7e1f22080fe6bf36d4fade162d6709e..7de2b353db5b76089c10cba826898bcd228f4ad9 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java ++++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java +@@ -26,6 +26,7 @@ import net.minecraft.nbt.CompoundTag; + import net.minecraft.nbt.NbtIo; + import net.minecraft.world.level.ChunkPos; + import org.slf4j.Logger; ++import cc.keyimc.keyi.utils.FastRandom; // KeYi + + public class RegionFile implements AutoCloseable { + +@@ -109,7 +110,7 @@ public class RegionFile implements AutoCloseable { + } + + private void backupRegionFile() { +- Path backup = this.regionFile.getParent().resolve(this.regionFile.getFileName() + "." + new java.util.Random().nextLong() + ".backup"); ++ Path backup = this.regionFile.getParent().resolve(this.regionFile.getFileName() + "." + new FastRandom().nextLong() + ".backup"); // KeYi - use a faster random + this.backupRegionFile(backup); + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index dabf1649f5bf14ec17dad6d43dcd7e2b7df255d3..ed754df76d3f86e2230c88ebb45125fe6af1e507 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -135,6 +135,7 @@ import org.bukkit.util.Consumer; + import org.bukkit.util.RayTraceResult; + import org.bukkit.util.StructureSearchResult; + import org.bukkit.util.Vector; ++import cc.keyimc.keyi.utils.FastRandom; // KeYi + + public class CraftWorld extends CraftRegionAccessor implements World { + public static final int CUSTOM_DIMENSION_OFFSET = 10; +@@ -227,7 +228,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { + } + // Paper end + +- private static final Random rand = new Random(); ++ private static final Random rand = new FastRandom(); // KeYi - use a faster random + + public CraftWorld(ServerLevel world, ChunkGenerator gen, BiomeProvider biomeProvider, Environment env) { + this.world = world; +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java +index d1c7ab67cba881d96b7a5e9220130d86d0514304..309d1ce15aace9da4a84819e8511af2cc12b1ea8 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java +@@ -12,10 +12,11 @@ import org.bukkit.entity.EntityType; + import org.bukkit.entity.Firework; + import org.bukkit.entity.LivingEntity; + import org.bukkit.inventory.meta.FireworkMeta; ++import cc.keyimc.keyi.utils.FastRandom; // KeYi + + public class CraftFirework extends CraftProjectile implements Firework { + +- private final Random random = new Random(); ++ private final Random random = new FastRandom(); // KeYi - use a faster random + //private CraftItemStack item; // Paper - Remove usage, not accurate representation of current item. + + public CraftFirework(CraftServer server, FireworkRocketEntity entity) { +diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java +index 382879562a808290cc4dd59dcd5022c6c22fb169..4c8e07dc44283b374ebe0a59339110d1f3507d1e 100644 +--- a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java ++++ b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java +@@ -40,13 +40,14 @@ import org.bukkit.craftbukkit.util.RandomSourceWrapper; + import org.bukkit.generator.ChunkGenerator; + import org.bukkit.generator.ChunkGenerator.BiomeGrid; + import org.bukkit.generator.ChunkGenerator.ChunkData; ++import cc.keyimc.keyi.utils.FastRandom; // KeYi + + public class CustomChunkGenerator extends InternalChunkGenerator { + + public final net.minecraft.world.level.chunk.ChunkGenerator delegate; + private final ChunkGenerator generator; + private final ServerLevel world; +- private final Random random = new Random(); ++ private final Random random = new FastRandom(); // KeYi - use a faster random + private boolean newApi; + private boolean implementBaseHeight = true; +