mirror of
https://github.com/PaperMC/Paper.git
synced 2025-09-03 13:53:51 -07:00
Readd more patches
This commit is contained in:
@@ -1,176 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Mon, 6 Apr 2020 04:20:44 -0700
|
||||
Subject: [PATCH] Execute chunk tasks mid-tick
|
||||
|
||||
This will help the server load chunks if tick times are high.
|
||||
|
||||
diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/co/aikar/timings/MinecraftTimings.java
|
||||
+++ b/src/main/java/co/aikar/timings/MinecraftTimings.java
|
||||
@@ -0,0 +0,0 @@ public final class MinecraftTimings {
|
||||
public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate");
|
||||
public static final Timing scoreboardScoreSearch = Timings.ofSafe("Scoreboard score search"); // Paper - add timings for scoreboard search
|
||||
|
||||
+ public static final Timing midTickChunkTasks = Timings.ofSafe("Mid Tick Chunk Tasks");
|
||||
+
|
||||
private static final Map<Class<?>, String> taskNameCache = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
private MinecraftTimings() {}
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
return flag;
|
||||
}
|
||||
|
||||
+ // Paper start - execute chunk tasks mid tick
|
||||
+ static final long CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME = 25L * 1000L; // 25us
|
||||
+ static final long MAX_CHUNK_EXEC_TIME = 1000L; // 1us
|
||||
+
|
||||
+ static final long TASK_EXECUTION_FAILURE_BACKOFF = 5L * 1000L; // 5us
|
||||
+
|
||||
+ private static long lastMidTickExecute;
|
||||
+ private static long lastMidTickExecuteFailure;
|
||||
+
|
||||
+ private boolean tickMidTickTasks() {
|
||||
+ // give all worlds a fair chance at by targetting them all.
|
||||
+ // if we execute too many tasks, that's fine - we have logic to correctly handle overuse of allocated time.
|
||||
+ boolean executed = false;
|
||||
+ for (ServerLevel world : this.getAllLevels()) {
|
||||
+ long currTime = System.nanoTime();
|
||||
+ if (currTime - world.lastMidTickExecuteFailure <= TASK_EXECUTION_FAILURE_BACKOFF) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (!world.getChunkSource().pollTask()) {
|
||||
+ // we need to back off if this fails
|
||||
+ world.lastMidTickExecuteFailure = currTime;
|
||||
+ } else {
|
||||
+ executed = true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return executed;
|
||||
+ }
|
||||
+
|
||||
+ public final void executeMidTickTasks() {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("mid tick chunk task execution");
|
||||
+ long startTime = System.nanoTime();
|
||||
+ if ((startTime - lastMidTickExecute) <= CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME || (startTime - lastMidTickExecuteFailure) <= TASK_EXECUTION_FAILURE_BACKOFF) {
|
||||
+ // it's shown to be bad to constantly hit the queue (chunk loads slow to a crawl), even if no tasks are executed.
|
||||
+ // so, backoff to prevent this
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ co.aikar.timings.MinecraftTimings.midTickChunkTasks.startTiming();
|
||||
+ try {
|
||||
+ for (;;) {
|
||||
+ boolean moreTasks = this.tickMidTickTasks();
|
||||
+ long currTime = System.nanoTime();
|
||||
+ long diff = currTime - startTime;
|
||||
+
|
||||
+ if (!moreTasks || diff >= MAX_CHUNK_EXEC_TIME) {
|
||||
+ if (!moreTasks) {
|
||||
+ lastMidTickExecuteFailure = currTime;
|
||||
+ }
|
||||
+
|
||||
+ // note: negative values reduce the time
|
||||
+ long overuse = diff - MAX_CHUNK_EXEC_TIME;
|
||||
+ if (overuse >= (10L * 1000L * 1000L)) { // 10ms
|
||||
+ // make sure something like a GC or dumb plugin doesn't screw us over...
|
||||
+ overuse = 10L * 1000L * 1000L; // 10ms
|
||||
+ }
|
||||
+
|
||||
+ double overuseCount = (double)overuse/(double)MAX_CHUNK_EXEC_TIME;
|
||||
+ long extraSleep = (long)Math.round(overuseCount*CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME);
|
||||
+
|
||||
+ lastMidTickExecute = currTime + extraSleep;
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+ } finally {
|
||||
+ co.aikar.timings.MinecraftTimings.midTickChunkTasks.stopTiming();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - execute chunk tasks mid tick
|
||||
+
|
||||
private boolean pollTaskInternal() {
|
||||
if (super.pollTask()) {
|
||||
+ this.executeMidTickTasks(); // Paper - execute chunk tasks mid tick
|
||||
return true;
|
||||
} else {
|
||||
boolean ret = false; // Paper - force execution of all worlds, do not just bias the first
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
||||
boolean flag1 = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getLevelData().getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit
|
||||
Iterator iterator1 = list.iterator();
|
||||
|
||||
+ int chunksTicked = 0; // Paper
|
||||
while (iterator1.hasNext()) {
|
||||
ServerChunkCache.ChunkAndHolder chunkproviderserver_a = (ServerChunkCache.ChunkAndHolder) iterator1.next();
|
||||
LevelChunk chunk1 = chunkproviderserver_a.chunk;
|
||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
||||
|
||||
if (this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) {
|
||||
this.level.tickChunk(chunk1, l);
|
||||
+ if ((chunksTicked++ & 1) == 0) net.minecraft.server.MinecraftServer.getServer().executeMidTickTasks(); // Paper
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
private final StructureCheck structureCheck;
|
||||
private final boolean tickTime;
|
||||
private final RandomSequences randomSequences;
|
||||
+ public long lastMidTickExecuteFailure; // Paper - execute chunk tasks mid tick
|
||||
|
||||
// CraftBukkit start
|
||||
public final LevelStorageSource.LevelStorageAccess convertable;
|
||||
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
if (fluid1.is(fluid)) {
|
||||
fluid1.tick(this, pos);
|
||||
}
|
||||
+ MinecraftServer.getServer().executeMidTickTasks(); // Paper - exec chunk tasks during world tick
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
if (iblockdata.is(block)) {
|
||||
iblockdata.tick(this, pos, this.random);
|
||||
}
|
||||
+ MinecraftServer.getServer().executeMidTickTasks(); // Paper - exec chunk tasks during world tick
|
||||
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
// Spigot end
|
||||
} else if (flag && this.shouldTickBlocksAt(tickingblockentity.getPos())) {
|
||||
tickingblockentity.tick();
|
||||
+ // Paper start - execute chunk tasks during tick
|
||||
+ if ((this.tileTickPosition & 7) == 0) {
|
||||
+ MinecraftServer.getServer().executeMidTickTasks();
|
||||
+ }
|
||||
+ // Paper end - execute chunk tasks during tick
|
||||
}
|
||||
}
|
||||
this.blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075
|
||||
@@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
public <T extends Entity> void guardEntityTick(Consumer<T> tickConsumer, T entity) {
|
||||
try {
|
||||
tickConsumer.accept(entity);
|
||||
+ MinecraftServer.getServer().executeMidTickTasks(); // Paper - execute chunk tasks mid tick
|
||||
} catch (Throwable throwable) {
|
||||
if (throwable instanceof ThreadDeath) throw throwable; // Paper
|
||||
// Paper start - Prevent block entity and entity crashes
|
@@ -1,243 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sat, 15 Jun 2019 08:54:33 -0700
|
||||
Subject: [PATCH] Fix World#isChunkGenerated calls
|
||||
|
||||
Optimize World#loadChunk() too
|
||||
This patch also adds a chunk status cache on region files (note that
|
||||
its only purpose is to cache the status on DISK)
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
// Paper end
|
||||
|
||||
private CompletableFuture<Optional<CompoundTag>> readChunk(ChunkPos chunkPos) {
|
||||
- return this.read(chunkPos).thenApplyAsync((optional) -> {
|
||||
- return optional.map((nbttagcompound) -> this.upgradeChunkTag(nbttagcompound, chunkPos)); // CraftBukkit
|
||||
- }, Util.backgroundExecutor());
|
||||
+ // Paper start - Cache chunk status on disk
|
||||
+ try {
|
||||
+ return CompletableFuture.completedFuture(Optional.ofNullable(this.readConvertChunkSync(chunkPos)));
|
||||
+ } catch (Throwable thr) {
|
||||
+ return CompletableFuture.failedFuture(thr);
|
||||
+ }
|
||||
+ // Paper end - Cache chunk status on disk
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
+ // Paper start - Cache chunk status on disk
|
||||
+ @Nullable
|
||||
+ public CompoundTag readConvertChunkSync(ChunkPos pos) throws IOException {
|
||||
+ CompoundTag nbttagcompound = this.readSync(pos);
|
||||
+ if (nbttagcompound == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ nbttagcompound = this.upgradeChunkTag(nbttagcompound, pos); // CraftBukkit
|
||||
+ if (nbttagcompound == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ this.updateChunkStatusOnDisk(pos, nbttagcompound);
|
||||
+
|
||||
+ return nbttagcompound;
|
||||
+ }
|
||||
+
|
||||
+ public ChunkStatus getChunkStatusOnDiskIfCached(ChunkPos chunkPos) {
|
||||
+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFileIfLoaded(chunkPos);
|
||||
+
|
||||
+ return regionFile == null ? null : regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
|
||||
+ }
|
||||
+
|
||||
+ public ChunkStatus getChunkStatusOnDisk(ChunkPos chunkPos) throws IOException {
|
||||
+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, true);
|
||||
+
|
||||
+ if (regionFile == null || !regionFileCache.chunkExists(chunkPos)) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ ChunkStatus status = regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
|
||||
+
|
||||
+ if (status != null) {
|
||||
+ return status;
|
||||
+ }
|
||||
+
|
||||
+ this.readChunk(chunkPos);
|
||||
+
|
||||
+ return regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
|
||||
+ }
|
||||
+
|
||||
+ public void updateChunkStatusOnDisk(ChunkPos chunkPos, @Nullable CompoundTag compound) throws IOException {
|
||||
+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, false);
|
||||
+
|
||||
+ regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkSerializer.getStatus(compound));
|
||||
+ }
|
||||
+
|
||||
+ public ChunkAccess getUnloadingChunk(int chunkX, int chunkZ) {
|
||||
+ ChunkHolder chunkHolder = io.papermc.paper.chunk.system.ChunkSystem.getUnloadingChunkHolder(this.level, chunkX, chunkZ);
|
||||
+ return chunkHolder == null ? null : chunkHolder.getAvailableChunkNow();
|
||||
+ }
|
||||
+ // Paper end - Cache chunk status on disk
|
||||
+
|
||||
public boolean anyPlayerCloseEnoughForSpawning(ChunkPos pos) { // Paper - public
|
||||
// Spigot start
|
||||
return this.anyPlayerCloseEnoughForSpawning(pos, false);
|
||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
|
||||
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable {
|
||||
@VisibleForTesting
|
||||
protected final RegionBitmap usedSectors;
|
||||
public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(); // Paper
|
||||
+ // Paper start - Cache chunk status
|
||||
+ private final net.minecraft.world.level.chunk.status.ChunkStatus[] statuses = new net.minecraft.world.level.chunk.status.ChunkStatus[32 * 32];
|
||||
+
|
||||
+ private boolean closed;
|
||||
+
|
||||
+ // invoked on write/read
|
||||
+ public void setStatus(int x, int z, net.minecraft.world.level.chunk.status.ChunkStatus status) {
|
||||
+ if (this.closed) {
|
||||
+ // We've used an invalid region file.
|
||||
+ throw new IllegalStateException("RegionFile is closed");
|
||||
+ }
|
||||
+ this.statuses[getChunkLocation(x, z)] = status;
|
||||
+ }
|
||||
+
|
||||
+ public net.minecraft.world.level.chunk.status.ChunkStatus getStatusIfCached(int x, int z) {
|
||||
+ if (this.closed) {
|
||||
+ // We've used an invalid region file.
|
||||
+ throw new IllegalStateException("RegionFile is closed");
|
||||
+ }
|
||||
+ final int location = getChunkLocation(x, z);
|
||||
+ return this.statuses[location];
|
||||
+ }
|
||||
+ // Paper end - Cache chunk status
|
||||
|
||||
public RegionFile(RegionStorageInfo storageKey, Path directory, Path path, boolean dsync) throws IOException {
|
||||
this(storageKey, directory, path, RegionFileVersion.getCompressionFormat(), dsync); // Paper - Configurable region compression format
|
||||
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable {
|
||||
return this.getOffset(pos) != 0;
|
||||
}
|
||||
|
||||
+ private static int getChunkLocation(int x, int z) { return (x & 31) + (z & 31) * 32; } // Paper - Cache chunk status; OBFHELPER - sort of, mirror of logic below
|
||||
private static int getOffsetIndex(ChunkPos pos) {
|
||||
return pos.getRegionLocalX() + pos.getRegionLocalZ() * 32;
|
||||
}
|
||||
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable {
|
||||
synchronized (this) {
|
||||
try {
|
||||
// Paper end
|
||||
+ this.closed = true; // Paper - Cache chunk status
|
||||
try {
|
||||
this.padToFullSector();
|
||||
} finally {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable {
|
||||
|
||||
try {
|
||||
NbtIo.write(nbt, (DataOutput) dataoutputstream);
|
||||
+ regionfile.setStatus(pos.x, pos.z, ChunkSerializer.getStatus(nbt)); // Paper - Cache chunk status
|
||||
regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
|
||||
// Paper start - don't write garbage data to disk if writing serialization fails
|
||||
dataoutputstream.close(); // Only write if successful
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
|
||||
@Override
|
||||
public boolean isChunkGenerated(int x, int z) {
|
||||
+ // Paper start - Fix this method
|
||||
+ if (!Bukkit.isPrimaryThread()) {
|
||||
+ return java.util.concurrent.CompletableFuture.supplyAsync(() -> {
|
||||
+ return CraftWorld.this.isChunkGenerated(x, z);
|
||||
+ }, world.getChunkSource().mainThreadProcessor).join();
|
||||
+ }
|
||||
+ ChunkAccess chunk = world.getChunkSource().getChunkAtImmediately(x, z);
|
||||
+ if (chunk == null) {
|
||||
+ chunk = world.getChunkSource().chunkMap.getUnloadingChunk(x, z);
|
||||
+ }
|
||||
+ if (chunk != null) {
|
||||
+ return chunk instanceof ImposterProtoChunk || chunk instanceof net.minecraft.world.level.chunk.LevelChunk;
|
||||
+ }
|
||||
try {
|
||||
- return this.isChunkLoaded(x, z) || this.world.getChunkSource().chunkMap.read(new ChunkPos(x, z)).get().isPresent();
|
||||
- } catch (InterruptedException | ExecutionException ex) {
|
||||
+ return world.getChunkSource().chunkMap.getChunkStatusOnDisk(new ChunkPos(x, z)) == ChunkStatus.FULL;
|
||||
+ } catch (java.io.IOException ex) {
|
||||
+ // Paper end - Fix this method
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
public boolean loadChunk(int x, int z, boolean generate) {
|
||||
org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
|
||||
warnUnsafeChunk("loading a faraway chunk", x, z); // Paper
|
||||
- ChunkAccess chunk = this.world.getChunkSource().getChunk(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper
|
||||
-
|
||||
- // If generate = false, but the chunk already exists, we will get this back.
|
||||
- if (chunk instanceof ImposterProtoChunk) {
|
||||
- // We then cycle through again to get the full chunk immediately, rather than after the ticket addition
|
||||
- chunk = this.world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true);
|
||||
- }
|
||||
-
|
||||
- if (chunk instanceof net.minecraft.world.level.chunk.LevelChunk) {
|
||||
- this.world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 0, Unit.INSTANCE); // Paper
|
||||
+ // Paper start - Optimize this method
|
||||
+ ChunkPos chunkPos = new ChunkPos(x, z);
|
||||
+ ChunkAccess immediate = world.getChunkSource().getChunkAtIfLoadedImmediately(x, z);
|
||||
+ if (immediate != null) {
|
||||
+ // Plugins should use plugin tickets instead of this method to keep a chunk perpetually loaded
|
||||
return true;
|
||||
}
|
||||
|
||||
- return false;
|
||||
+ if (!generate) {
|
||||
+ immediate = world.getChunkSource().chunkMap.getUnloadingChunk(x, z);
|
||||
+ if (immediate != null) {
|
||||
+ if (!(immediate instanceof ImposterProtoChunk) && !(immediate instanceof net.minecraft.world.level.chunk.LevelChunk)) {
|
||||
+ return false; // not full status
|
||||
+ }
|
||||
+ world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 0, Unit.INSTANCE); // Paper
|
||||
+ world.getChunk(x, z); // make sure we're at ticket level 32 or lower
|
||||
+ return true;
|
||||
+ }
|
||||
+ net.minecraft.world.level.chunk.storage.RegionFile file;
|
||||
+ try {
|
||||
+ file = world.getChunkSource().chunkMap.regionFileCache.getRegionFile(chunkPos, false);
|
||||
+ } catch (java.io.IOException ex) {
|
||||
+ throw new RuntimeException(ex);
|
||||
+ }
|
||||
+
|
||||
+ ChunkStatus status = file.getStatusIfCached(x, z);
|
||||
+ if (!file.hasChunk(chunkPos) || (status != null && status != ChunkStatus.FULL)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ ChunkAccess chunk = world.getChunkSource().getChunk(x, z, ChunkStatus.EMPTY, true);
|
||||
+ if (!(chunk instanceof ImposterProtoChunk) && !(chunk instanceof net.minecraft.world.level.chunk.LevelChunk)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // fall through to load
|
||||
+ // we do this so we do not re-read the chunk data on disk
|
||||
+ }
|
||||
+ world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 0, Unit.INSTANCE); // Paper
|
||||
+ world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true);
|
||||
+ return true;
|
||||
+ // Paper end - Optimize this method
|
||||
}
|
||||
|
||||
@Override
|
@@ -1,68 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Mon, 2 Aug 2021 10:10:40 +0200
|
||||
Subject: [PATCH] Improve boat collision performance
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/Util.java
|
||||
+++ b/src/main/java/net/minecraft/Util.java
|
||||
@@ -0,0 +0,0 @@ public class Util {
|
||||
.filter(fileSystemProvider -> fileSystemProvider.getScheme().equalsIgnoreCase("jar"))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("No jar file system provider found"));
|
||||
+ public static final double COLLISION_EPSILON = 1.0E-7; // Paper - Improve boat collision performance
|
||||
private static Consumer<String> thePauser = message -> {
|
||||
};
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||
if (!source.is(DamageTypeTags.IS_PROJECTILE)) {
|
||||
Entity entity = source.getDirectEntity();
|
||||
|
||||
- if (entity instanceof LivingEntity) {
|
||||
+ if (entity instanceof LivingEntity && entity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Improve boat collision performance
|
||||
LivingEntity entityliving = (LivingEntity) entity;
|
||||
|
||||
this.blockUsingShield(entityliving);
|
||||
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||
}
|
||||
|
||||
if (entity1 != null && !source.is(DamageTypeTags.NO_KNOCKBACK)) {
|
||||
- double d0 = entity1.getX() - this.getX();
|
||||
+ final boolean far = entity1.distanceToSqr(this) > (200.0 * 200.0); // Paper - Improve boat collision performance
|
||||
+ double d0 = far ? (Math.random() - Math.random()) : entity1.getX() - this.getX(); // Paper - Improve boat collision performance
|
||||
|
||||
double d1;
|
||||
|
||||
- for (d1 = entity1.getZ() - this.getZ(); d0 * d0 + d1 * d1 < 1.0E-4D; d1 = (Math.random() - Math.random()) * 0.01D) {
|
||||
+ for (d1 = far ? Math.random() - Math.random() : entity1.getZ() - this.getZ(); d0 * d0 + d1 * d1 < 1.0E-4D; d1 = (Math.random() - Math.random()) * 0.01D) { // Paper - Improve boat collision performance
|
||||
d0 = (Math.random() - Math.random()) * 0.01D;
|
||||
}
|
||||
|
||||
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||
this.hurtCurrentlyUsedShield((float) -event.getDamage(DamageModifier.BLOCKING));
|
||||
Entity entity = damagesource.getDirectEntity();
|
||||
|
||||
- if (entity instanceof LivingEntity) {
|
||||
+ if (entity instanceof LivingEntity && entity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Improve boat collision performance
|
||||
this.blockUsingShield((LivingEntity) entity);
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/Boat.java b/src/main/java/net/minecraft/world/entity/vehicle/Boat.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/vehicle/Boat.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/vehicle/Boat.java
|
||||
@@ -0,0 +0,0 @@ public class Boat extends VehicleEntity implements VariantHolder<Boat.Type> {
|
||||
this.invFriction = 0.05F;
|
||||
if (this.oldStatus == Boat.Status.IN_AIR && this.status != Boat.Status.IN_AIR && this.status != Boat.Status.ON_LAND) {
|
||||
this.waterLevel = this.getY(1.0D);
|
||||
- this.setPos(this.getX(), (double) (this.getWaterLevelAbove() - this.getBbHeight()) + 0.101D, this.getZ());
|
||||
+ this.move(MoverType.SELF, new Vec3(0.0, ((double) (this.getWaterLevelAbove() - this.getBbHeight()) + 0.101D) - this.getY(), 0.0)); // Paper - Improve boat collision performance
|
||||
this.setDeltaMovement(this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D));
|
||||
this.lastYd = 0.0D;
|
||||
this.status = Boat.Status.IN_WATER;
|
@@ -89,15 +89,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
}
|
||||
|
||||
if (this.awaitingPositionFromClient != null) {
|
||||
- if (this.tickCount - this.awaitingTeleportTime > 20) {
|
||||
+ if (false && this.tickCount - this.awaitingTeleportTime > 20) { // Paper - this will greatly screw with clients with > 1000ms RTT
|
||||
this.awaitingTeleportTime = this.tickCount;
|
||||
this.teleport(this.awaitingPositionFromClient.x, this.awaitingPositionFromClient.y, this.awaitingPositionFromClient.z, this.player.getYRot(), this.player.getXRot());
|
||||
}
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
}
|
||||
}
|
||||
@@ -135,6 +126,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK,
|
||||
toX, toY, toZ, toYaw, toPitch, false);
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
|
||||
private boolean updateAwaitingTeleport() {
|
||||
if (this.awaitingPositionFromClient != null) {
|
||||
- if (this.tickCount - this.awaitingTeleportTime > 20) {
|
||||
+ if (false && this.tickCount - this.awaitingTeleportTime > 20) { // Paper - this will greatly screw with clients with > 1000ms RTT
|
||||
this.awaitingTeleportTime = this.tickCount;
|
||||
this.teleport(this.awaitingPositionFromClient.x, this.awaitingPositionFromClient.y, this.awaitingPositionFromClient.z, this.player.getYRot(), this.player.getXRot());
|
||||
}
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,456 +0,0 @@
|
||||
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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/util/math/ThreadUnsafeRandom.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -0,0 +0,0 @@ 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();
|
||||
@@ -0,0 +0,0 @@ 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);
|
||||
@@ -0,0 +0,0 @@ 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
|
||||
@@ -0,0 +0,0 @@ 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();
|
||||
@@ -0,0 +0,0 @@ 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);
|
||||
|
||||
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/util/BitStorage.java
|
||||
+++ b/src/main/java/net/minecraft/util/BitStorage.java
|
||||
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/util/SimpleBitStorage.java
|
||||
+++ b/src/main/java/net/minecraft/util/SimpleBitStorage.java
|
||||
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/util/ZeroBitStorage.java
|
||||
+++ b/src/main/java/net/minecraft/util/ZeroBitStorage.java
|
||||
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
|
||||
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||
@@ -0,0 +0,0 @@ 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
|
||||
@@ -0,0 +0,0 @@ public class LevelChunkSection {
|
||||
--this.nonEmptyBlockCount;
|
||||
if (iblockdata1.isRandomlyTicking()) {
|
||||
--this.tickingBlockCount;
|
||||
+ // Paper start
|
||||
+ this.tickingList.remove(x, y, z);
|
||||
+ // Paper end
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +0,0 @@ public class LevelChunkSection {
|
||||
++this.nonEmptyBlockCount;
|
||||
if (state.isRandomlyTicking()) {
|
||||
++this.tickingBlockCount;
|
||||
+ // Paper start
|
||||
+ this.tickingList.add(x, y, z, state);
|
||||
+ // Paper end
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
||||
@@ -0,0 +0,0 @@ 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);
|
@@ -1,109 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Thu, 2 Apr 2020 02:37:57 -0400
|
||||
Subject: [PATCH] Optimize Collision to not load chunks
|
||||
|
||||
The collision code takes an AABB and generates a cuboid of checks rather
|
||||
than a cylinder, so at high velocity this can generate a lot of chunk checks.
|
||||
|
||||
Treat an unloaded chunk as a collision for entities, and also for players if
|
||||
the "prevent moving into unloaded chunks" setting is enabled.
|
||||
|
||||
If that serting is not enabled, collisions will be ignored for players, since
|
||||
movement will load only the chunk the player enters anyways and avoids loading
|
||||
massive amounts of surrounding chunks due to large AABB lookups.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
@@ -0,0 +0,0 @@ public abstract class PlayerList {
|
||||
entityplayer1.setShiftKeyDown(false);
|
||||
entityplayer1.forceSetPositionRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
|
||||
|
||||
+ worldserver1.getChunkSource().addRegionTicket(net.minecraft.server.level.TicketType.POST_TELEPORT, new net.minecraft.world.level.ChunkPos(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper
|
||||
while (avoidSuffocation && !worldserver1.noCollision((Entity) entityplayer1) && entityplayer1.getY() < (double) worldserver1.getMaxBuildHeight()) {
|
||||
// CraftBukkit end
|
||||
entityplayer1.setPos(entityplayer1.getX(), entityplayer1.getY() + 1.0D, entityplayer1.getZ());
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
// Paper end - Share random for entities to make them more random
|
||||
public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason
|
||||
|
||||
+ public boolean collisionLoadChunks = false; // Paper
|
||||
private CraftEntity bukkitEntity;
|
||||
|
||||
public @org.jetbrains.annotations.Nullable net.minecraft.server.level.ChunkMap.TrackedEntity tracker; // Paper
|
||||
diff --git a/src/main/java/net/minecraft/world/level/BlockCollisions.java b/src/main/java/net/minecraft/world/level/BlockCollisions.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/BlockCollisions.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/BlockCollisions.java
|
||||
@@ -0,0 +0,0 @@ public class BlockCollisions<T> extends AbstractIterator<T> {
|
||||
@Override
|
||||
protected T computeNext() {
|
||||
while (this.cursor.advance()) {
|
||||
- int i = this.cursor.nextX();
|
||||
- int j = this.cursor.nextY();
|
||||
- int k = this.cursor.nextZ();
|
||||
+ int i = this.cursor.nextX(); final int x = i; // Paper - OBFHELPER
|
||||
+ int j = this.cursor.nextY(); final int y = j; // Paper - OBFHELPER
|
||||
+ int k = this.cursor.nextZ(); final int z = k; // Paper - OBFHELPER
|
||||
int l = this.cursor.getNextType();
|
||||
if (l != 3) {
|
||||
- BlockGetter blockGetter = this.getChunk(i, k);
|
||||
- if (blockGetter != null) {
|
||||
- this.pos.set(i, j, k);
|
||||
- BlockState blockState = blockGetter.getBlockState(this.pos);
|
||||
- if ((!this.onlySuffocatingBlocks || blockState.isSuffocating(blockGetter, this.pos))
|
||||
- && (l != 1 || blockState.hasLargeCollisionShape())
|
||||
- && (l != 2 || blockState.is(Blocks.MOVING_PISTON))) {
|
||||
+ // Paper start - ensure we don't load chunks
|
||||
+ // BlockGetter blockGetter = this.getChunk(i, k);
|
||||
+ if (true) {
|
||||
+ final @Nullable Entity source = this.context instanceof net.minecraft.world.phys.shapes.EntityCollisionContext entityContext ? entityContext.getEntity() : null;
|
||||
+ boolean far = source != null && io.papermc.paper.util.MCUtil.distanceSq(source.getX(), y, source.getZ(), x, y, z) > 14;
|
||||
+ this.pos.set(x, y, z);
|
||||
+ BlockState blockState;
|
||||
+ if (this.collisionGetter instanceof net.minecraft.server.level.WorldGenRegion) {
|
||||
+ BlockGetter blockGetter = this.getChunk(x, z);
|
||||
+ if (blockGetter == null) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ blockState = blockGetter.getBlockState(this.pos);
|
||||
+ } else if ((!far && source instanceof net.minecraft.server.level.ServerPlayer) || (source != null && source.collisionLoadChunks)) {
|
||||
+ blockState = this.collisionGetter.getBlockState(this.pos);
|
||||
+ } else {
|
||||
+ blockState = this.collisionGetter.getBlockStateIfLoaded(this.pos);
|
||||
+ }
|
||||
+ if (blockState == null) {
|
||||
+ if (!(source instanceof net.minecraft.server.level.ServerPlayer) || source.level().paperConfig().chunks.preventMovingIntoUnloadedChunks) {
|
||||
+ return this.resultProvider.apply(new BlockPos.MutableBlockPos(x, y, z), Shapes.create(far ? source.getBoundingBox() : new AABB(new BlockPos(x, y, z))));
|
||||
+ }
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (/*(!this.onlySuffocatingBlocks || blockState.isSuffocating(blockGetter, this.pos)) &&*/ (l != 1 || blockState.hasLargeCollisionShape()) && (l != 2 || blockState.is(Blocks.MOVING_PISTON))) { // Paper - onlySuffocatingBlocks is only true on the client, so we don't care about it here
|
||||
+ // Paper end
|
||||
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)) {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/CollisionGetter.java b/src/main/java/net/minecraft/world/level/CollisionGetter.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/CollisionGetter.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/CollisionGetter.java
|
||||
@@ -0,0 +0,0 @@ public interface CollisionGetter extends BlockGetter {
|
||||
}
|
||||
|
||||
default boolean noCollision(@Nullable Entity entity, AABB box) {
|
||||
+ try { if (entity != null) entity.collisionLoadChunks = true; // Paper
|
||||
for (VoxelShape voxelShape : this.getBlockCollisions(entity, box)) {
|
||||
if (!voxelShape.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+ } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper
|
||||
|
||||
if (!this.getEntityCollisions(entity, box).isEmpty()) {
|
||||
return false;
|
@@ -1,78 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: lukas81298 <lukas81298@gommehd.net>
|
||||
Date: Fri, 22 Jan 2021 21:50:18 +0100
|
||||
Subject: [PATCH] optimize dirt and snow spreading
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java b/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
|
||||
@@ -0,0 +0,0 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
|
||||
}
|
||||
|
||||
private static boolean canBeGrass(BlockState state, LevelReader world, BlockPos pos) {
|
||||
+ // Paper start - Perf: optimize dirt and snow spreading
|
||||
+ return canBeGrass(world.getChunk(pos), state, world, pos);
|
||||
+ }
|
||||
+ private static boolean canBeGrass(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader world, BlockPos pos) {
|
||||
+ // Paper end - Perf: optimize dirt and snow spreading
|
||||
BlockPos blockposition1 = pos.above();
|
||||
- BlockState iblockdata1 = world.getBlockState(blockposition1);
|
||||
+ BlockState iblockdata1 = chunk.getBlockState(blockposition1); // Paper - Perf: optimize dirt and snow spreading
|
||||
|
||||
if (iblockdata1.is(Blocks.SNOW) && (Integer) iblockdata1.getValue(SnowLayerBlock.LAYERS) == 1) {
|
||||
return true;
|
||||
@@ -0,0 +0,0 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
|
||||
protected abstract MapCodec<? extends SpreadingSnowyDirtBlock> codec();
|
||||
|
||||
private static boolean canPropagate(BlockState state, LevelReader world, BlockPos pos) {
|
||||
+ // Paper start - Perf: optimize dirt and snow spreading
|
||||
+ return canPropagate(world.getChunk(pos), state, world, pos);
|
||||
+ }
|
||||
+
|
||||
+ private static boolean canPropagate(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader world, BlockPos pos) {
|
||||
+ // Paper end - Perf: optimize dirt and snow spreading
|
||||
BlockPos blockposition1 = pos.above();
|
||||
|
||||
- return SpreadingSnowyDirtBlock.canBeGrass(state, world, pos) && !world.getFluidState(blockposition1).is(FluidTags.WATER);
|
||||
+ return SpreadingSnowyDirtBlock.canBeGrass(chunk, state, world, pos) && !chunk.getFluidState(blockposition1).is(FluidTags.WATER); // Paper - Perf: optimize dirt and snow spreading
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
|
||||
if (this instanceof GrassBlock && world.paperConfig().tickRates.grassSpread != 1 && (world.paperConfig().tickRates.grassSpread < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % world.paperConfig().tickRates.grassSpread != 0)) { return; } // Paper - Configurable random tick rates for blocks
|
||||
- if (!SpreadingSnowyDirtBlock.canBeGrass(state, world, pos)) {
|
||||
+ // Paper start - Perf: optimize dirt and snow spreading
|
||||
+ final net.minecraft.world.level.chunk.ChunkAccess cachedBlockChunk = world.getChunkIfLoaded(pos);
|
||||
+ if (cachedBlockChunk == null) { // Is this needed?
|
||||
+ return;
|
||||
+ }
|
||||
+ if (!SpreadingSnowyDirtBlock.canBeGrass(cachedBlockChunk, state, world, pos)) {
|
||||
+ // Paper end - Perf: optimize dirt and snow spreading
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, pos, Blocks.DIRT.defaultBlockState()).isCancelled()) {
|
||||
return;
|
||||
@@ -0,0 +0,0 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
BlockPos blockposition1 = pos.offset(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1);
|
||||
-
|
||||
- if (world.getBlockState(blockposition1).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(iblockdata1, world, blockposition1)) {
|
||||
- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, (BlockState) iblockdata1.setValue(SpreadingSnowyDirtBlock.SNOWY, world.getBlockState(blockposition1.above()).is(Blocks.SNOW))); // CraftBukkit
|
||||
+ // Paper start - Perf: optimize dirt and snow spreading
|
||||
+ if (pos.getX() == blockposition1.getX() && pos.getY() == blockposition1.getY() && pos.getZ() == blockposition1.getZ()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ net.minecraft.world.level.chunk.ChunkAccess access;
|
||||
+ if (cachedBlockChunk.locX == blockposition1.getX() >> 4 && cachedBlockChunk.locZ == blockposition1.getZ() >> 4) {
|
||||
+ access = cachedBlockChunk;
|
||||
+ } else {
|
||||
+ access = world.getChunkAt(blockposition1);
|
||||
+ }
|
||||
+ if (access.getBlockState(blockposition1).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(access, iblockdata1, world, blockposition1)) {
|
||||
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, (BlockState) iblockdata1.setValue(SpreadingSnowyDirtBlock.SNOWY, access.getBlockState(blockposition1.above()).is(Blocks.SNOW))); // CraftBukkit
|
||||
+ // Paper end - Perf: optimize dirt and snow spreading
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user