Mirror von
https://github.com/PaperMC/Paper.git
synchronisiert 2024-12-15 02:50:09 +01:00
ac554ad46d
Updated Upstream (Bukkit/CraftBukkit) Upstream has released updates that appear to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: fa99e752 PR-1007: Add ItemMeta#getAsComponentString() 94a91782 Fix copy-pasted BlockType.Typed documentation 9b34ac8c Largely restore deprecated PotionData API 51a6449b PR-1008: Deprecate ITEMS_TOOLS, removed in 1.20.5 702d15fe Fix Javadoc reference 42f6cdf4 PR-919: Add internal ItemType and BlockType, delegate Material methods to them 237bb37b SPIGOT-1166, SPIGOT-7647: Expose Damager BlockState in EntityDamageByBlockEvent 035ea146 SPIGOT-6993: Allow #setVelocity to change the speed of a fireball and add a note to #setDirection about it 8c7880fb PR-1004: Improve field rename handling and centralize conversion between bukkit and string more 87c90e93 SPIGOT-7650: Add DamageSource for EntityDeathEvent and PlayerDeathEvent CraftBukkit Changes: 4af0f22e8 SPIGOT-7664: Item meta should prevail over block states c2ccc46ec SPIGOT-7666: Fix access to llama and horse special slot 124ac66d7 SPIGOT-7665: Fix ThrownPotion#getEffects() implementation only bringing custom effects 66f1f439a Restore null page behaviour of signed books even though not strictly allowed by API 6118e5398 Fix regression listening to minecraft:brand custom payloads c1a26b366 Fix unnecessary and potential not thread-safe chat visibility check 12360a7ec Remove unused imports 147b098b4 PR-1397: Add ItemMeta#getAsComponentString() 428aefe0e Largely restore deprecated PotionData API afe5b5ee9 PR-1275: Add internal ItemType and BlockType, delegate Material methods to them 8afeafa7d SPIGOT-1166, SPIGOT-7647: Expose Damager BlockState in EntityDamageByBlockEvent 4e7d749d4 SPIGOT-6993: Allow #setVelocity to change the speed of a fireball and add a note to #setDirection about it 441880757 Support both entity_data and bucket_entity_data on axolotl/fish buckets 0e22fdd1e Fix custom direct BlockState being not correctly set in DamageSource f2182ed47 SPIGOT-7659: TropicalFishBucketMeta should use BUCKET_ENTITY_DATA 2a6207fe1 PR-1393: Improve field rename handling and centralize conversion between bukkit and string more c024a5039 SPIGOT-7650: Add DamageSource for EntityDeathEvent and PlayerDeathEvent 741b84480 PR-1390: Improve internal handling of damage sources 0364df4e1 SPIGOT-7657: Error when loading angry entities
457 Zeilen
21 KiB
Diff
457 Zeilen
21 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
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..7d93652c1abbb6aee6eb7c26cf35d4d032ef7b69
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/util/math/ThreadUnsafeRandom.java
|
|
@@ -0,0 +1,65 @@
|
|
+package io.papermc.paper.util.math;
|
|
+
|
|
+import net.minecraft.util.RandomSource;
|
|
+import net.minecraft.world.level.levelgen.LegacyRandomSource;
|
|
+import net.minecraft.world.level.levelgen.PositionalRandomFactory;
|
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
|
+
|
|
+@DefaultQualifier(NonNull.class)
|
|
+public final class ThreadUnsafeRandom extends LegacyRandomSource {
|
|
+
|
|
+ // 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;
|
|
+
|
|
+ public ThreadUnsafeRandom(long seed) {
|
|
+ super(seed);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public RandomSource fork() {
|
|
+ return new ThreadUnsafeRandom(this.nextLong());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PositionalRandomFactory forkPositional() {
|
|
+ throw new UnsupportedOperationException();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setSeed(long seed) {
|
|
+ // note: called by Random constructor
|
|
+ this.seed = initialScramble(seed);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public 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);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
index 3a49f8933bc6ca1862994b7e0b3006f5236dd94a..bdad61438e7fd89f5f0cac6632dd395fcf024361 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -861,6 +861,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(this.random.nextLong());
|
|
+ // Paper end
|
|
|
|
public void tickChunk(LevelChunk chunk, int randomTickSpeed) {
|
|
ChunkPos chunkcoordintpair = chunk.getPos();
|
|
@@ -870,8 +874,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
ProfilerFiller gameprofilerfiller = this.getProfiler();
|
|
|
|
gameprofilerfiller.push("thunder");
|
|
+ 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 - Option to disable thunder
|
|
- BlockPos 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);
|
|
@@ -903,7 +909,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
if (!this.paperConfig().environment.disableIceAndSnow) { // Paper - Option to disable ice and snow
|
|
for (int l = 0; l < randomTickSpeed; ++l) {
|
|
if (this.random.nextInt(48) == 0) {
|
|
- this.tickPrecipitation(this.getBlockRandomPos(j, 0, k, 15));
|
|
+ // Paper start
|
|
+ this.getRandomBlockPosition(j, 0, k, 15, blockposition);
|
|
+ this.tickPrecipitation(blockposition, chunk);
|
|
+ // Paper end
|
|
}
|
|
}
|
|
} // Paper - Option to disable ice and snow
|
|
@@ -911,36 +920,37 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
gameprofilerfiller.popPush("tickBlocks");
|
|
timings.chunkTicksBlocks.startTiming(); // Paper
|
|
if (randomTickSpeed > 0) {
|
|
- LevelChunkSection[] achunksection = chunk.getSections();
|
|
-
|
|
- for (int i1 = 0; i1 < achunksection.length; ++i1) {
|
|
- LevelChunkSection chunksection = achunksection[i1];
|
|
-
|
|
- if (chunksection.isRandomlyTicking()) {
|
|
- int j1 = chunk.getSectionYFromSectionIndex(i1);
|
|
- int k1 = SectionPos.sectionToBlockCoord(j1);
|
|
-
|
|
- for (int l1 = 0; l1 < randomTickSpeed; ++l1) {
|
|
- BlockPos blockposition1 = this.getBlockRandomPos(j, k1, k, 15);
|
|
-
|
|
- gameprofilerfiller.push("randomTick");
|
|
- BlockState iblockdata = chunksection.getBlockState(blockposition1.getX() - j, blockposition1.getY() - k1, blockposition1.getZ() - k);
|
|
-
|
|
- if (iblockdata.isRandomlyTicking()) {
|
|
- iblockdata.randomTick(this, blockposition1, this.random);
|
|
- }
|
|
+ // Paper start - optimize random block ticking
|
|
+ LevelChunkSection[] sections = chunk.getSections();
|
|
+ final 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;
|
|
+
|
|
+ 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 = iblockdata.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, blockposition1, 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 (ping the Canadian)
|
|
}
|
|
}
|
|
+ // Paper end - optimise random block ticking
|
|
|
|
timings.chunkTicksBlocks.stopTiming(); // Paper
|
|
gameprofilerfiller.pop();
|
|
@@ -948,17 +958,25 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
|
|
@VisibleForTesting
|
|
public void tickPrecipitation(BlockPos pos) {
|
|
- BlockPos blockposition1 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos);
|
|
- BlockPos blockposition2 = blockposition1.below();
|
|
+ // Paper start - optimise chunk ticking
|
|
+ tickPrecipitation(pos.mutable(), this.getChunkAt(pos));
|
|
+ }
|
|
+ public void tickPrecipitation(BlockPos.MutableBlockPos blockposition1, final LevelChunk chunk) {
|
|
+ int normalY = chunk.getHeight(Heightmap.Types.MOTION_BLOCKING, blockposition1.getX() & 15, blockposition1.getZ() & 15) + 1;
|
|
+ int downY = normalY - 1;
|
|
+ blockposition1.setY(normalY);
|
|
+ // Paper end - optimise chunk ticking
|
|
Biome biomebase = (Biome) this.getBiome(blockposition1).value();
|
|
|
|
- if (biomebase.shouldFreeze(this, blockposition2)) {
|
|
- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition2, Blocks.ICE.defaultBlockState(), null); // CraftBukkit
|
|
+ blockposition1.setY(downY);
|
|
+ if (biomebase.shouldFreeze(this, blockposition1)) {
|
|
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, Blocks.ICE.defaultBlockState(), null); // CraftBukkit
|
|
}
|
|
|
|
if (this.isRaining()) {
|
|
int i = this.getGameRules().getInt(GameRules.RULE_SNOW_ACCUMULATION_HEIGHT);
|
|
|
|
+ blockposition1.setY(normalY); // Paper - optimise chunk ticking
|
|
if (i > 0 && biomebase.shouldSnow(this, blockposition1)) {
|
|
BlockState iblockdata = this.getBlockState(blockposition1);
|
|
|
|
@@ -976,12 +994,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
}
|
|
}
|
|
|
|
- Biome.Precipitation biomebase_precipitation = biomebase.getPrecipitationAt(blockposition2);
|
|
+ blockposition1.setY(downY); // Paper - optimise chunk ticking
|
|
+ Biome.Precipitation biomebase_precipitation = biomebase.getPrecipitationAt(blockposition1); // Paper - optimise chunk ticking
|
|
|
|
if (biomebase_precipitation != Biome.Precipitation.NONE) {
|
|
- BlockState iblockdata2 = this.getBlockState(blockposition2);
|
|
+ BlockState iblockdata2 = this.getBlockState(blockposition1); // Paper - optimise chunk ticking
|
|
|
|
- iblockdata2.getBlock().handlePrecipitation(iblockdata2, this, blockposition2, biomebase_precipitation);
|
|
+ iblockdata2.getBlock().handlePrecipitation(iblockdata2, this, blockposition1, biomebase_precipitation); // Paper - optimise chunk ticking
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/util/BitStorage.java b/src/main/java/net/minecraft/util/BitStorage.java
|
|
index 68648c5a5e3ff079f832092af0f2f801c42d1ede..8bafd5fd7499ba4a04bf706cfd1e156073716e21 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[] out);
|
|
|
|
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 9f438d9c6eb05e43d24e4af68188a3d4c46a938c..8d7d763bf51cac556057645e6169c9447993189b 100644
|
|
--- a/src/main/java/net/minecraft/util/SimpleBitStorage.java
|
|
+++ b/src/main/java/net/minecraft/util/SimpleBitStorage.java
|
|
@@ -315,6 +315,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 50040c497a819cd1229042ab3cb057d34a32cacc..01f5b946fabbe34f31110e75973dab9f39897346 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 ffffa53fcaa6ec8681b283fa20bb5a3320ad9c11..30b87b5cb18c25cdd04eab64cfbe5acd6c1b6d84 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
|
|
@@ -88,7 +88,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 dab55ab08665f2b5ae0c899a4ab07c18460552ae..4001bec504b8fcda1eec03f49abd4e4d82cf6336 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Level.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
|
@@ -1387,10 +1387,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 f2e11bff414b521295bde721e01ae2166a6a3fd6..8cd6c1d838e0332125fde3fc36475034aa4effa0 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,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(PalettedContainer<BlockState> datapaletteblock, PalettedContainer<Holder<Biome>> palettedcontainerro) {
|
|
// CraftBukkit end
|
|
@@ -77,6 +78,9 @@ public class LevelChunkSection {
|
|
--this.nonEmptyBlockCount;
|
|
if (iblockdata1.isRandomlyTicking()) {
|
|
--this.tickingBlockCount;
|
|
+ // Paper start
|
|
+ this.tickingList.remove(x, y, z);
|
|
+ // Paper end
|
|
}
|
|
}
|
|
|
|
@@ -88,6 +92,9 @@ public class LevelChunkSection {
|
|
++this.nonEmptyBlockCount;
|
|
if (state.isRandomlyTicking()) {
|
|
++this.tickingBlockCount;
|
|
+ // Paper start
|
|
+ this.tickingList.add(x, y, z, state);
|
|
+ // Paper end
|
|
}
|
|
}
|
|
|
|
@@ -115,40 +122,34 @@ public class LevelChunkSection {
|
|
}
|
|
|
|
public void recalcBlockCounts() {
|
|
- class a implements PalettedContainer.CountConsumer<BlockState> {
|
|
-
|
|
- public int nonEmptyBlockCount;
|
|
- public int tickingBlockCount;
|
|
- public int tickingFluidCount;
|
|
-
|
|
- a(final LevelChunkSection chunksection) {}
|
|
-
|
|
- public void accept(BlockState iblockdata, int i) {
|
|
+ // Paper start - unfuck this
|
|
+ this.tickingList.clear();
|
|
+ this.nonEmptyBlockCount = 0;
|
|
+ this.tickingBlockCount = 0;
|
|
+ this.tickingFluidCount = 0;
|
|
+ // Don't run this on clearly empty sections
|
|
+ if (this.maybeHas((BlockState state) -> !state.isAir() || !state.getFluidState().isEmpty())) {
|
|
+ this.states.forEachLocation((BlockState iblockdata, int i) -> {
|
|
FluidState fluid = iblockdata.getFluidState();
|
|
|
|
if (!iblockdata.isAir()) {
|
|
- this.nonEmptyBlockCount += i;
|
|
+ this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + 1);
|
|
if (iblockdata.isRandomlyTicking()) {
|
|
- this.tickingBlockCount += i;
|
|
+ this.tickingBlockCount = (short)(this.tickingBlockCount + 1);
|
|
+ this.tickingList.add(i, iblockdata);
|
|
}
|
|
}
|
|
|
|
if (!fluid.isEmpty()) {
|
|
- this.nonEmptyBlockCount += i;
|
|
+ this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + 1);
|
|
if (fluid.isRandomlyTicking()) {
|
|
- this.tickingFluidCount += i;
|
|
+ this.tickingFluidCount = (short) (this.tickingFluidCount + 1);
|
|
}
|
|
}
|
|
|
|
- }
|
|
+ });
|
|
}
|
|
-
|
|
- a a0 = new a(this);
|
|
-
|
|
- this.states.count(a0);
|
|
- this.nonEmptyBlockCount = (short) a0.nonEmptyBlockCount;
|
|
- this.tickingBlockCount = (short) a0.tickingBlockCount;
|
|
- this.tickingFluidCount = (short) a0.tickingFluidCount;
|
|
+ // Paper end - unfuck this
|
|
}
|
|
|
|
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 926c81a25180d508d662eb3fa35f771636164694..81368bf186365878db2e1ed305bb7bf36c26f61f 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
|
@@ -381,6 +381,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);
|