aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0755-Optimise-random-block-ticking.patch
diff options
context:
space:
mode:
authorJason <[email protected]>2022-06-09 02:37:28 -0700
committerGitHub <[email protected]>2022-06-09 11:37:28 +0200
commitd7cc306336b28e3287ff9fcdd6b8cc709468320f (patch)
tree959c030c091b9dad82336091f7584a2e265b1980 /patches/server/0755-Optimise-random-block-ticking.patch
parent172d260d677a05c59e9e8f3f6bfe9216063eef72 (diff)
downloadPaper-d7cc306336b28e3287ff9fcdd6b8cc709468320f.tar.gz
Paper-d7cc306336b28e3287ff9fcdd6b8cc709468320f.zip
Update Optimise general POI access (#7903)
Diffstat (limited to 'patches/server/0755-Optimise-random-block-ticking.patch')
-rw-r--r--patches/server/0755-Optimise-random-block-ticking.patch479
1 files changed, 0 insertions, 479 deletions
diff --git a/patches/server/0755-Optimise-random-block-ticking.patch b/patches/server/0755-Optimise-random-block-ticking.patch
deleted file mode 100644
index 6e42fccec5..0000000000
--- a/patches/server/0755-Optimise-random-block-ticking.patch
+++ /dev/null
@@ -1,479 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
-Date: Sun, 20 Jun 2021 16:19:26 -0700
-Subject: [PATCH] Optimise random block ticking
-
-Massive performance improvement for random block ticking.
-The performance increase comes from the fact that the vast
-majority of attempted block ticks (~95% in my testing) fail
-because the randomly selected block is not tickable.
-
-Now only tickable blocks are targeted, however this means that
-the maximum number of block ticks occurs per chunk. However,
-not all chunks are going to be targeted. The percent chance
-of a chunk being targeted is based on how many tickable blocks
-are in the chunk.
-This means that while block ticks are spread out less, the
-total number of blocks ticked per world tick remains the same.
-Therefore, the chance of a random tickable block being ticked
-remains the same.
-
-diff --git a/src/main/java/io/papermc/paper/util/math/ThreadUnsafeRandom.java b/src/main/java/io/papermc/paper/util/math/ThreadUnsafeRandom.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..16ca915c33e31b50d33336408b041e401cb9cbfc
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/util/math/ThreadUnsafeRandom.java
-@@ -0,0 +1,95 @@
-+package io.papermc.paper.util.math;
-+
-+import net.minecraft.util.RandomSource;
-+import net.minecraft.world.level.levelgen.PositionalRandomFactory;
-+
-+import java.util.Random;
-+
-+public final class ThreadUnsafeRandom extends Random implements RandomSource {
-+
-+ // See javadoc and internal comments for java.util.Random where these values come from, how they are used, and the author for them.
-+ private static final long multiplier = 0x5DEECE66DL;
-+ private static final long addend = 0xBL;
-+ private static final long mask = (1L << 48) - 1;
-+
-+ private static long initialScramble(long seed) {
-+ return (seed ^ multiplier) & mask;
-+ }
-+
-+ private long seed;
-+
-+ @Override
-+ public RandomSource fork() {
-+ return new ThreadUnsafeRandom();
-+ }
-+
-+ @Override
-+ public PositionalRandomFactory forkPositional() {
-+ throw new UnsupportedOperationException();
-+ }
-+
-+ @Override
-+ public int nextInt(int origin, int bound) {
-+ return RandomSource.super.nextInt(origin, bound);
-+ }
-+
-+ @Override
-+ public void setSeed(long seed) {
-+ // note: called by Random constructor
-+ this.seed = initialScramble(seed);
-+ }
-+
-+ @Override
-+ protected int next(int bits) {
-+ // avoid the expensive CAS logic used by superclass
-+ return (int) (((this.seed = this.seed * multiplier + addend) & mask) >>> (48 - bits));
-+ }
-+
-+ // Taken from
-+ // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
-+ // https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2016/06/25/fastrange.c
-+ // Original license is public domain
-+ public static int fastRandomBounded(final long randomInteger, final long limit) {
-+ // randomInteger must be [0, pow(2, 32))
-+ // limit must be [0, pow(2, 32))
-+ return (int)((randomInteger * limit) >>> 32);
-+ }
-+
-+ @Override
-+ public int nextInt(int bound) {
-+ // yes this breaks random's spec
-+ // however there's nothing that uses this class that relies on it
-+ return fastRandomBounded(this.next(32) & 0xFFFFFFFFL, bound);
-+ }
-+
-+ // these below are added to fix reobf issues that I don't wanna deal with right now
-+ @Override
-+ public long nextLong() {
-+ return super.nextInt();
-+ }
-+
-+ @Override
-+ public int nextInt() {
-+ return super.nextInt();
-+ }
-+
-+ @Override
-+ public boolean nextBoolean() {
-+ return super.nextBoolean();
-+ }
-+
-+ @Override
-+ public float nextFloat() {
-+ return super.nextFloat();
-+ }
-+
-+ @Override
-+ public double nextDouble() {
-+ return super.nextDouble();
-+ }
-+
-+ @Override
-+ public double nextGaussian() {
-+ return super.nextGaussian();
-+ }
-+}
-diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 6d1489332681d938a1c3db832ab967ba494f3b97..3ef55cb31b8d9a308fab3ac1e463e98a7dce726e 100644
---- a/src/main/java/net/minecraft/server/level/ServerLevel.java
-+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -662,6 +662,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
- entityplayer.stopSleepInBed(false, false);
- });
- }
-+ // Paper start - optimise random block ticking
-+ private final BlockPos.MutableBlockPos chunkTickMutablePosition = new BlockPos.MutableBlockPos();
-+ private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom();
-+ // Paper end
-
- public void tickChunk(LevelChunk chunk, int randomTickSpeed) {
- ChunkPos chunkcoordintpair = chunk.getPos();
-@@ -671,10 +675,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
- ProfilerFiller gameprofilerfiller = this.getProfiler();
-
- gameprofilerfiller.push("thunder");
-- BlockPos blockposition;
-+ final BlockPos.MutableBlockPos blockposition = this.chunkTickMutablePosition; // Paper - use mutable to reduce allocation rate, final to force compile fail on change
-
- if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - disable thunder
-- blockposition = this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15));
-+ blockposition.set(this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15))); // Paper
- if (this.isRainingAt(blockposition)) {
- DifficultyInstance difficultydamagescaler = this.getCurrentDifficultyAt(blockposition);
- boolean flag1 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < (double) difficultydamagescaler.getEffectiveDifficulty() * this.paperConfig().entities.spawning.skeletonHorseThunderSpawnChance.or(0.01D) && !this.getBlockState(blockposition.below()).is(Blocks.LIGHTNING_ROD); // Paper
-@@ -698,64 +702,75 @@ public class ServerLevel extends Level implements WorldGenLevel {
-
- gameprofilerfiller.popPush("iceandsnow");
- if (!this.paperConfig().environment.disableIceAndSnow && this.random.nextInt(16) == 0) { // Paper - Disable ice and snow
-- blockposition = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, this.getBlockRandomPos(j, 0, k, 15));
-- BlockPos blockposition1 = blockposition.below();
-+ // Paper start - optimise chunk ticking
-+ this.getRandomBlockPosition(j, 0, k, 15, blockposition);
-+ int normalY = chunk.getHeight(Heightmap.Types.MOTION_BLOCKING, blockposition.getX() & 15, blockposition.getZ() & 15) + 1;
-+ int downY = normalY - 1;
-+ blockposition.setY(normalY);
-+ // Paper end
- Biome biomebase = (Biome) this.getBiome(blockposition).value();
-
-- if (biomebase.shouldFreeze(this, blockposition1)) {
-- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, Blocks.ICE.defaultBlockState(), null); // CraftBukkit
-+ // Paper start - optimise chunk ticking
-+ blockposition.setY(downY);
-+ if (biomebase.shouldFreeze(this, blockposition)) {
-+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition, Blocks.ICE.defaultBlockState(), null); // CraftBukkit
-+ // Paper end
- }
-
- if (flag) {
-+ blockposition.setY(normalY); // Paper
- if (biomebase.shouldSnow(this, blockposition)) {
- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition, Blocks.SNOW.defaultBlockState(), null); // CraftBukkit
- }
-+ blockposition.setY(downY); // Paper
-
-- BlockState iblockdata = this.getBlockState(blockposition1);
-+ BlockState iblockdata = this.getBlockState(blockposition); // Paper
- Biome.Precipitation biomebase_precipitation = biomebase.getPrecipitation();
-
-- if (biomebase_precipitation == Biome.Precipitation.RAIN && biomebase.coldEnoughToSnow(blockposition1)) {
-+ if (biomebase_precipitation == Biome.Precipitation.RAIN && biomebase.coldEnoughToSnow(blockposition)) { // Paper
- biomebase_precipitation = Biome.Precipitation.SNOW;
- }
-
-- iblockdata.getBlock().handlePrecipitation(iblockdata, this, blockposition1, biomebase_precipitation);
-+ iblockdata.getBlock().handlePrecipitation(iblockdata, this, blockposition, biomebase_precipitation); // Paper
- }
- }
-
-- gameprofilerfiller.popPush("tickBlocks");
-+ // Paper start - optimise random block ticking
-+ gameprofilerfiller.popPush("randomTick");
- timings.chunkTicksBlocks.startTiming(); // Paper
- if (randomTickSpeed > 0) {
-- LevelChunkSection[] achunksection = chunk.getSections();
-- int l = achunksection.length;
--
-- for (int i1 = 0; i1 < l; ++i1) {
-- LevelChunkSection chunksection = achunksection[i1];
--
-- if (chunksection.isRandomlyTicking()) {
-- int j1 = chunksection.bottomBlockY();
--
-- for (int k1 = 0; k1 < randomTickSpeed; ++k1) {
-- BlockPos blockposition2 = this.getBlockRandomPos(j, j1, k, 15);
--
-- gameprofilerfiller.push("randomTick");
-- BlockState iblockdata1 = chunksection.getBlockState(blockposition2.getX() - j, blockposition2.getY() - j1, blockposition2.getZ() - k);
-+ LevelChunkSection[] sections = chunk.getSections();
-+ int minSection = io.papermc.paper.util.WorldUtil.getMinSection(this);
-+ for (int sectionIndex = 0; sectionIndex < sections.length; ++sectionIndex) {
-+ LevelChunkSection section = sections[sectionIndex];
-+ if (section == null || section.tickingList.size() == 0) {
-+ continue;
-+ }
-
-- if (iblockdata1.isRandomlyTicking()) {
-- iblockdata1.randomTick(this, blockposition2, this.random);
-- }
-+ int yPos = (sectionIndex + minSection) << 4;
-+ for (int a = 0; a < randomTickSpeed; ++a) {
-+ int tickingBlocks = section.tickingList.size();
-+ int index = this.randomTickRandom.nextInt(16 * 16 * 16);
-+ if (index >= tickingBlocks) {
-+ continue;
-+ }
-
-- FluidState fluid = iblockdata1.getFluidState();
-+ long raw = section.tickingList.getRaw(index);
-+ int location = com.destroystokyo.paper.util.maplist.IBlockDataList.getLocationFromRaw(raw);
-+ int randomX = location & 15;
-+ int randomY = ((location >>> (4 + 4)) & 255) | yPos;
-+ int randomZ = (location >>> 4) & 15;
-
-- if (fluid.isRandomlyTicking()) {
-- fluid.randomTick(this, blockposition2, this.random);
-- }
-+ BlockPos blockposition2 = blockposition.set(j + randomX, randomY, k + randomZ);
-+ BlockState iblockdata = com.destroystokyo.paper.util.maplist.IBlockDataList.getBlockDataFromRaw(raw);
-
-- gameprofilerfiller.pop();
-- }
-+ iblockdata.randomTick(this, blockposition2, this.randomTickRandom);
-+ // We drop the fluid tick since LAVA is ALREADY TICKED by the above method (See LiquidBlock).
-+ // TODO CHECK ON UPDATE
- }
- }
- }
--
-+ // Paper end - optimise random block ticking
- timings.chunkTicksBlocks.stopTiming(); // Paper
- gameprofilerfiller.pop();
- }
-diff --git a/src/main/java/net/minecraft/util/BitStorage.java b/src/main/java/net/minecraft/util/BitStorage.java
-index 62251727788d48a461ea6f7945771d7d6bdc7282..106610ccc74b70b557b01c61262d56c4f1147acf 100644
---- a/src/main/java/net/minecraft/util/BitStorage.java
-+++ b/src/main/java/net/minecraft/util/BitStorage.java
-@@ -20,4 +20,15 @@ public interface BitStorage {
- void unpack(int[] is);
-
- BitStorage copy();
-+
-+ // Paper start
-+ void forEach(DataBitConsumer consumer);
-+
-+ @FunctionalInterface
-+ interface DataBitConsumer {
-+
-+ void accept(int location, int data);
-+
-+ }
-+ // Paper end
- }
-diff --git a/src/main/java/net/minecraft/util/SimpleBitStorage.java b/src/main/java/net/minecraft/util/SimpleBitStorage.java
-index 9b81ce9d85cba07e9752c29fb5a842c4b00aa873..36e33923bf48e56c743ed043bcbc66bc32f0422f 100644
---- a/src/main/java/net/minecraft/util/SimpleBitStorage.java
-+++ b/src/main/java/net/minecraft/util/SimpleBitStorage.java
-@@ -124,6 +124,28 @@ public class SimpleBitStorage implements BitStorage {
- return this.bits;
- }
-
-+ // Paper start
-+ @Override
-+ public final void forEach(DataBitConsumer consumer) {
-+ int i = 0;
-+ long[] along = this.data;
-+ int j = along.length;
-+
-+ for (int k = 0; k < j; ++k) {
-+ long l = along[k];
-+
-+ for (int i1 = 0; i1 < this.valuesPerLong; ++i1) {
-+ consumer.accept(i, (int) (l & this.mask));
-+ l >>= this.bits;
-+ ++i;
-+ if (i >= this.size) {
-+ return;
-+ }
-+ }
-+ }
-+ }
-+ // Paper end
-+
- @Override
- public void getAll(IntConsumer action) {
- int i = 0;
-diff --git a/src/main/java/net/minecraft/util/ZeroBitStorage.java b/src/main/java/net/minecraft/util/ZeroBitStorage.java
-index 9686ce7536c9924b1b2aced4f013f46759cbc72e..5d8e9bdf5538b19681f21949368d862fab8a89ad 100644
---- a/src/main/java/net/minecraft/util/ZeroBitStorage.java
-+++ b/src/main/java/net/minecraft/util/ZeroBitStorage.java
-@@ -46,6 +46,15 @@ public class ZeroBitStorage implements BitStorage {
- return 0;
- }
-
-+ // Paper start
-+ @Override
-+ public void forEach(DataBitConsumer consumer) {
-+ for(int i = 0; i < this.size; ++i) {
-+ consumer.accept(i, 0);
-+ }
-+ }
-+ // Paper end
-+
- @Override
- public void getAll(IntConsumer action) {
- for(int i = 0; i < this.size; ++i) {
-diff --git a/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
-index 69c98c2cb2fd8f149a39bbddcbfe0c5c5adc3904..5575730aa6f77a91467c394fa8465c335d73db8e 100644
---- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
-@@ -83,7 +83,7 @@ public class Turtle extends Animal {
- }
-
- public void setHomePos(BlockPos pos) {
-- this.entityData.set(Turtle.HOME_POS, pos);
-+ this.entityData.set(Turtle.HOME_POS, pos.immutable()); // Paper - called with mutablepos...
- }
-
- public BlockPos getHomePos() {
-diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index e07b094cf19bc94a4e3ef726f37973eaff7aaf6d..03f7eb8a56fd4e609bc7b1a00d18cad5586a41dd 100644
---- a/src/main/java/net/minecraft/world/level/Level.java
-+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -1302,10 +1302,18 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
- public abstract RecipeManager getRecipeManager();
-
- public BlockPos getBlockRandomPos(int x, int y, int z, int l) {
-+ // Paper start - allow use of mutable pos
-+ BlockPos.MutableBlockPos ret = new BlockPos.MutableBlockPos();
-+ this.getRandomBlockPosition(x, y, z, l, ret);
-+ return ret.immutable();
-+ }
-+ public final BlockPos.MutableBlockPos getRandomBlockPosition(int x, int y, int z, int l, BlockPos.MutableBlockPos out) {
-+ // Paper end
- this.randValue = this.randValue * 3 + 1013904223;
- int i1 = this.randValue >> 2;
-
-- return new BlockPos(x + (i1 & 15), y + (i1 >> 16 & l), z + (i1 >> 8 & 15));
-+ out.set(x + (i1 & 15), y + (i1 >> 16 & l), z + (i1 >> 8 & 15)); // Paper - change to setValues call
-+ return out; // Paper
- }
-
- public boolean noSave() {
-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 2ad73237f4664535c3d5120a54b713f44cddb793..c2e3df8331cec5fe5650501a4dc4ac47f23ef11b 100644
---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
-@@ -27,6 +27,7 @@ public class LevelChunkSection {
- public final PalettedContainer<BlockState> states;
- // CraftBukkit start - read/write
- private PalettedContainer<Holder<Biome>> biomes;
-+ public final com.destroystokyo.paper.util.maplist.IBlockDataList tickingList = new com.destroystokyo.paper.util.maplist.IBlockDataList(); // Paper
-
- public LevelChunkSection(int i, PalettedContainer<BlockState> datapaletteblock, PalettedContainer<Holder<Biome>> palettedcontainerro) {
- // CraftBukkit end
-@@ -85,6 +86,9 @@ public class LevelChunkSection {
- --this.nonEmptyBlockCount;
- if (iblockdata1.isRandomlyTicking()) {
- --this.tickingBlockCount;
-+ // Paper start
-+ this.tickingList.remove(x, y, z);
-+ // Paper end
- }
- }
-
-@@ -96,6 +100,9 @@ public class LevelChunkSection {
- ++this.nonEmptyBlockCount;
- if (state.isRandomlyTicking()) {
- ++this.tickingBlockCount;
-+ // Paper start
-+ this.tickingList.add(x, y, z, state);
-+ // Paper end
- }
- }
-
-@@ -127,40 +134,31 @@ public class LevelChunkSection {
- }
-
- public void recalcBlockCounts() {
-- class a implements PalettedContainer.CountConsumer<BlockState> {
--
-- public int nonEmptyBlockCount;
-- public int tickingBlockCount;
-- public int tickingFluidCount;
--
-- a() {}
--
-- public void accept(BlockState iblockdata, int i) {
-- FluidState fluid = iblockdata.getFluidState();
--
-- if (!iblockdata.isAir()) {
-- this.nonEmptyBlockCount += i;
-- if (iblockdata.isRandomlyTicking()) {
-- this.tickingBlockCount += i;
-- }
-+ // Paper start - unfuck this
-+ this.tickingList.clear();
-+ this.nonEmptyBlockCount = 0;
-+ this.tickingBlockCount = 0;
-+ this.tickingFluidCount = 0;
-+ this.states.forEachLocation((BlockState iblockdata, int i) -> {
-+ FluidState fluid = iblockdata.getFluidState();
-+
-+ if (!iblockdata.isAir()) {
-+ this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + 1);
-+ if (iblockdata.isRandomlyTicking()) {
-+ this.tickingBlockCount = (short)(this.tickingBlockCount + 1);
-+ this.tickingList.add(i, iblockdata);
- }
-+ }
-
-- if (!fluid.isEmpty()) {
-- this.nonEmptyBlockCount += i;
-- if (fluid.isRandomlyTicking()) {
-- this.tickingFluidCount += i;
-- }
-+ if (!fluid.isEmpty()) {
-+ this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + 1);
-+ if (fluid.isRandomlyTicking()) {
-+ this.tickingFluidCount = (short) (this.tickingFluidCount + 1);
- }
--
- }
-- }
-
-- a a0 = new a();
--
-- this.states.count(a0);
-- this.nonEmptyBlockCount = (short) a0.nonEmptyBlockCount;
-- this.tickingBlockCount = (short) a0.tickingBlockCount;
-- this.tickingFluidCount = (short) a0.tickingFluidCount;
-+ });
-+ // Paper end
- }
-
- public PalettedContainer<BlockState> getStates() {
-diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-index 6800452604e0db660e8d5dca9778abd6e2f66478..6fb87dcdc4fab4b430a5c9003548c874e6b26734 100644
---- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-@@ -382,6 +382,14 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- }
- }
-
-+ // Paper start
-+ public void forEachLocation(PalettedContainer.CountConsumer<T> consumer) {
-+ this.data.storage.forEach((int location, int data) -> {
-+ consumer.accept(this.data.palette.valueFor(data), location);
-+ });
-+ }
-+ // Paper end
-+
- @FunctionalInterface
- public interface CountConsumer<T> {
- void accept(T object, int count);