Initial patch apply

This commit is contained in:
Spottedleaf 2023-06-08 20:15:55 -07:00
parent bf7ba50dcd
commit 1b0a5071d0
35 changed files with 1660 additions and 11274 deletions

View File

@ -3,7 +3,7 @@ import io.papermc.paperweight.tasks.RebuildGitPatches
plugins {
java
`maven-publish`
id("com.github.johnrengelman.shadow") version "8.1.0" apply false
id("com.github.johnrengelman.shadow") version "8.1.1" apply false
id("io.papermc.paperweight.patcher") version "1.5.5"
}

View File

@ -1,8 +1,8 @@
group=dev.folia
version=1.19.4-R0.1-SNAPSHOT
mcVersion=1.19.4
paperRef=bc4a6647c99ae98c52c1c81597834be8fec6aa0d
version=1.20-R0.1-SNAPSHOT
mcVersion=1.20
paperRef=04509f0234dfd4c05be002de1985ab4915c2092c
org.gradle.caching=true
org.gradle.parallel=true

0
gradlew.bat vendored Normal file → Executable file
View File

0
install.bat Normal file → Executable file
View File

0
jar.bat Normal file → Executable file
View File

0
patch.bat Normal file → Executable file
View File

View File

@ -5,7 +5,7 @@ Subject: [PATCH] Build changes
diff --git a/build.gradle.kts b/build.gradle.kts
index 4f2fa65ade89c5703451dad4f80eeef162b277d1..ec4ce20c9f3dfb488cbd0874b79cb612a12def6a 100644
index a3a76b9b7efa773117d2ee1ce53ef784b09b277d..85f5bedaf294dc48cab9cd5380ed4edeb4166a40 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -7,8 +7,12 @@ plugins {
@ -88,10 +88,10 @@ index 9d687da5bdf398bb3f6c84cdf1249a7213d09f2e..e2f704c115fd6e00960bb56bb0779f11
).openBufferedStream()) {
JsonObject json = new Gson().fromJson(reader, JsonObject.class);
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 9f15d9dbdfa74a0640b1a2b4ff695609d4758a4c..3219482b96cab8262e393a790c88d903d7de5166 100644
index fb82bb52f219e7683fe1d3c0fb3acbe2251de8d4..cdb9925e8c4771831a7fe8bbcb705278c51aa0d2 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1682,7 +1682,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1681,7 +1681,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@DontObfuscate
public String getServerModName() {
@ -101,7 +101,7 @@ index 9f15d9dbdfa74a0640b1a2b4ff695609d4758a4c..3219482b96cab8262e393a790c88d903
public SystemReport fillSystemReport(SystemReport details) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 9f2536d9a73bdb15b5b3004d4da79ca32cee205b..91c301275fa7b6a2fc16e3bdbf61b6a46eed6ef4 100644
index aefbb6a3f49a74a4c8fd004aa6a4ab1ab907e878..ce4238d0cb82cc16fc2e5f534ab4a7eef50b5437 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -262,7 +262,7 @@ import javax.annotation.Nullable; // Paper

View File

@ -5,7 +5,7 @@ Subject: [PATCH] MC-Dev fixes
diff --git a/src/main/java/net/minecraft/world/entity/ai/Brain.java b/src/main/java/net/minecraft/world/entity/ai/Brain.java
index 10e7b6a846b443362f1e626b8ee2f63a3803fddb..328c3ecd0d35d2cad15173ec80962cee9177eaf8 100644
index ef6e968ed2708272eab407a983928382a2f2049c..a2de99709ce14303a309806c683da6b3548659c1 100644
--- a/src/main/java/net/minecraft/world/entity/ai/Brain.java
+++ b/src/main/java/net/minecraft/world/entity/ai/Brain.java
@@ -72,15 +72,15 @@ public class Brain<E extends LivingEntity> {

File diff suppressed because it is too large Load Diff

View File

@ -1,395 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Thu, 16 Feb 2023 16:50:05 -0800
Subject: [PATCH] Make ChunkStatus.EMPTY not rely on the main thread for
completion
In order to do this, we need to push the POI consistency checks
to a later status. Since FULL is the only other status that
uses the main thread, it can go there.
The consistency checks are only really for when a desync occurs,
and so that delaying the check only matters when the chunk data
has desync'd. As long as the desync is sorted before the
chunk is full loaded (i.e before setBlock can occur on
a chunk), it should not matter.
This change is primarily due to behavioural changes
in the chunk task queue brought by region threading -
which is to split the queue into separate regions. As such,
it is required that in order for the sync load to complete
that the region owning the chunk drain and execute the task
while ticking. However, that is not always possible in
region threading. Thus, removing the main thread reliance allows
the chunk to progress without requiring a tick thread.
Specifically, this allows far sync loads (outside of a specific
regions bounds) to occur without issue - namely with structure
searching.
diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkFullTask.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkFullTask.java
index fb42d776f15f735fb59e972e00e2b512c23a8387..300700477ee34bc22b31315825c0e40f61070cd5 100644
--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkFullTask.java
+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkFullTask.java
@@ -2,6 +2,8 @@ package io.papermc.paper.chunk.system.scheduling;
import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
+import com.mojang.logging.LogUtils;
+import io.papermc.paper.chunk.system.poi.PoiChunk;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
@@ -9,10 +11,13 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ImposterProtoChunk;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
+import org.slf4j.Logger;
import java.lang.invoke.VarHandle;
public final class ChunkFullTask extends ChunkProgressionTask implements Runnable {
+ private static final Logger LOGGER = LogUtils.getClassLogger();
+
protected final NewChunkHolder chunkHolder;
protected final ChunkAccess fromChunk;
protected final PrioritisedExecutor.PrioritisedTask convertToFullTask;
@@ -35,6 +40,15 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl
// See Vanilla protoChunkToFullChunk for what this function should be doing
final LevelChunk chunk;
try {
+ // moved from the load from nbt stage into here
+ final PoiChunk poiChunk = this.chunkHolder.getPoiChunk();
+ if (poiChunk == null) {
+ LOGGER.error("Expected poi chunk to be loaded with chunk for task " + this.toString());
+ } else {
+ poiChunk.load();
+ this.world.getPoiManager().checkConsistency(this.fromChunk);
+ }
+
if (this.fromChunk instanceof ImposterProtoChunk wrappedFull) {
chunk = wrappedFull.getWrapped();
} else {
diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkLoadTask.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkLoadTask.java
index be6f3f6a57668a9bd50d0ea5f2dd2335355b69d6..1f7c146ff0b2a835c818f49da6c1f1411f26aa39 100644
--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkLoadTask.java
+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkLoadTask.java
@@ -25,6 +25,7 @@ import org.slf4j.Logger;
import java.lang.invoke.VarHandle;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
public final class ChunkLoadTask extends ChunkProgressionTask {
@@ -34,9 +35,11 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
private final NewChunkHolder chunkHolder;
private final ChunkDataLoadTask loadTask;
- private boolean cancelled;
+ private volatile boolean cancelled;
private NewChunkHolder.GenericDataLoadTaskCallback entityLoadTask;
private NewChunkHolder.GenericDataLoadTaskCallback poiLoadTask;
+ private GenericDataLoadTask.TaskResult<ChunkAccess, Throwable> loadResult;
+ private final AtomicInteger taskCountToComplete = new AtomicInteger(3); // one for poi, one for entity, and one for chunk data
protected ChunkLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, final int chunkZ,
final NewChunkHolder chunkHolder, final PrioritisedExecutor.Priority priority) {
@@ -44,10 +47,18 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
this.chunkHolder = chunkHolder;
this.loadTask = new ChunkDataLoadTask(scheduler, world, chunkX, chunkZ, priority);
this.loadTask.addCallback((final GenericDataLoadTask.TaskResult<ChunkAccess, Throwable> result) -> {
- ChunkLoadTask.this.complete(result == null ? null : result.left(), result == null ? null : result.right());
+ ChunkLoadTask.this.loadResult = result; // must be before getAndDecrement
+ ChunkLoadTask.this.tryCompleteLoad();
});
}
+ private void tryCompleteLoad() {
+ if (this.taskCountToComplete.decrementAndGet() == 0) {
+ final GenericDataLoadTask.TaskResult<ChunkAccess, Throwable> result = this.cancelled ? null : this.loadResult; // only after the getAndDecrement
+ ChunkLoadTask.this.complete(result == null ? null : result.left(), result == null ? null : result.right());
+ }
+ }
+
@Override
public ChunkStatus getTargetStatus() {
return ChunkStatus.EMPTY;
@@ -65,11 +76,8 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
final NewChunkHolder.GenericDataLoadTaskCallback entityLoadTask;
final NewChunkHolder.GenericDataLoadTaskCallback poiLoadTask;
- final AtomicInteger count = new AtomicInteger();
final Consumer<GenericDataLoadTask.TaskResult<?, ?>> scheduleLoadTask = (final GenericDataLoadTask.TaskResult<?, ?> result) -> {
- if (count.decrementAndGet() == 0) {
- ChunkLoadTask.this.loadTask.schedule(false);
- }
+ ChunkLoadTask.this.tryCompleteLoad();
};
// NOTE: it is IMPOSSIBLE for getOrLoadEntityData/getOrLoadPoiData to complete synchronously, because
@@ -85,16 +93,16 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
}
if (!this.chunkHolder.isEntityChunkNBTLoaded()) {
entityLoadTask = this.chunkHolder.getOrLoadEntityData((Consumer)scheduleLoadTask);
- count.setPlain(count.getPlain() + 1);
} else {
entityLoadTask = null;
+ this.taskCountToComplete.getAndDecrement(); // we know the chunk load is not done here, as it is not scheduled
}
if (!this.chunkHolder.isPoiChunkLoaded()) {
poiLoadTask = this.chunkHolder.getOrLoadPoiData((Consumer)scheduleLoadTask);
- count.setPlain(count.getPlain() + 1);
} else {
poiLoadTask = null;
+ this.taskCountToComplete.getAndDecrement(); // we know the chunk load is not done here, as it is not scheduled
}
this.entityLoadTask = entityLoadTask;
@@ -107,14 +115,11 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
entityLoadTask.schedule();
}
- if (poiLoadTask != null) {
+ if (poiLoadTask != null) {
poiLoadTask.schedule();
}
- if (entityLoadTask == null && poiLoadTask == null) {
- // no need to wait on those, we can schedule now
- this.loadTask.schedule(false);
- }
+ this.loadTask.schedule(false);
}
@Override
@@ -129,15 +134,20 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
/*
Note: The entityLoadTask/poiLoadTask do not complete when cancelled,
- but this is fine because if they are successfully cancelled then
- we will successfully cancel the load task, which will complete when cancelled
+ so we need to manually try to complete in those cases
+ It is also important to note that we set the cancelled field first, just in case
+ the chunk load task attempts to complete with a non-null value
*/
if (this.entityLoadTask != null) {
- this.entityLoadTask.cancel();
+ if (this.entityLoadTask.cancel()) {
+ this.tryCompleteLoad();
+ }
}
if (this.poiLoadTask != null) {
- this.poiLoadTask.cancel();
+ if (this.poiLoadTask.cancel()) {
+ this.tryCompleteLoad();
+ }
}
this.loadTask.cancel();
}
@@ -249,7 +259,7 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
}
}
- public final class ChunkDataLoadTask extends CallbackDataLoadTask<ChunkSerializer.InProgressChunkHolder, ChunkAccess> {
+ public static final class ChunkDataLoadTask extends CallbackDataLoadTask<ChunkAccess, ChunkAccess> {
protected ChunkDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
final int chunkZ, final PrioritisedExecutor.Priority priority) {
super(scheduler, world, chunkX, chunkZ, RegionFileIOThread.RegionFileType.CHUNK_DATA, priority);
@@ -262,7 +272,7 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
@Override
protected boolean hasOnMain() {
- return true;
+ return false;
}
@Override
@@ -272,35 +282,30 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
@Override
protected PrioritisedExecutor.PrioritisedTask createOnMain(final Runnable run, final PrioritisedExecutor.Priority priority) {
- return this.scheduler.createChunkTask(this.chunkX, this.chunkZ, run, priority);
+ throw new UnsupportedOperationException();
}
@Override
- protected TaskResult<ChunkAccess, Throwable> completeOnMainOffMain(final ChunkSerializer.InProgressChunkHolder data, final Throwable throwable) {
- if (data != null) {
- return null;
- }
-
- final PoiChunk poiChunk = ChunkLoadTask.this.chunkHolder.getPoiChunk();
- if (poiChunk == null) {
- LOGGER.error("Expected poi chunk to be loaded with chunk for task " + this.toString());
- } else if (!poiChunk.isLoaded()) {
- // need to call poiChunk.load() on main
- return null;
- }
+ protected TaskResult<ChunkAccess, Throwable> completeOnMainOffMain(final ChunkAccess data, final Throwable throwable) {
+ throw new UnsupportedOperationException();
+ }
- return new TaskResult<>(this.getEmptyChunk(), null);
+ private ProtoChunk getEmptyChunk() {
+ return new ProtoChunk(
+ new ChunkPos(this.chunkX, this.chunkZ), UpgradeData.EMPTY, this.world,
+ this.world.registryAccess().registryOrThrow(Registries.BIOME), (BlendingData)null
+ );
}
@Override
- protected TaskResult<ChunkSerializer.InProgressChunkHolder, Throwable> runOffMain(final CompoundTag data, final Throwable throwable) {
+ protected TaskResult<ChunkAccess, Throwable> runOffMain(final CompoundTag data, final Throwable throwable) {
if (throwable != null) {
LOGGER.error("Failed to load chunk data for task: " + this.toString() + ", chunk data will be lost", throwable);
- return new TaskResult<>(null, null);
+ return new TaskResult<>(this.getEmptyChunk(), null);
}
if (data == null) {
- return new TaskResult<>(null, null);
+ return new TaskResult<>(this.getEmptyChunk(), null);
}
// need to convert data, and then deserialize it
@@ -319,53 +324,18 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
this.world, chunkMap.getPoiManager(), chunkPos, converted, true
);
- return new TaskResult<>(chunkHolder, null);
+ return new TaskResult<>(chunkHolder.protoChunk, null);
} catch (final ThreadDeath death) {
throw death;
} catch (final Throwable thr2) {
LOGGER.error("Failed to parse chunk data for task: " + this.toString() + ", chunk data will be lost", thr2);
- return new TaskResult<>(null, thr2);
+ return new TaskResult<>(this.getEmptyChunk(), null);
}
}
- private ProtoChunk getEmptyChunk() {
- return new ProtoChunk(
- new ChunkPos(this.chunkX, this.chunkZ), UpgradeData.EMPTY, this.world,
- this.world.registryAccess().registryOrThrow(Registries.BIOME), (BlendingData)null
- );
- }
-
@Override
- protected TaskResult<ChunkAccess, Throwable> runOnMain(final ChunkSerializer.InProgressChunkHolder data, final Throwable throwable) {
- final PoiChunk poiChunk = ChunkLoadTask.this.chunkHolder.getPoiChunk();
- if (poiChunk == null) {
- LOGGER.error("Expected poi chunk to be loaded with chunk for task " + this.toString());
- } else {
- poiChunk.load();
- }
-
- if (data == null || data.protoChunk == null) {
- // throwable could be non-null, but the off-main task will print its exceptions - so we don't need to care,
- // it's handled already
-
- return new TaskResult<>(this.getEmptyChunk(), null);
- }
-
- // have tasks to run (at this point, it's just the POI consistency checking)
- try {
- if (data.tasks != null) {
- for (int i = 0, len = data.tasks.size(); i < len; ++i) {
- data.tasks.poll().run();
- }
- }
-
- return new TaskResult<>(data.protoChunk, null);
- } catch (final ThreadDeath death) {
- throw death;
- } catch (final Throwable thr2) {
- LOGGER.error("Failed to parse main tasks for task " + this.toString() + ", chunk data will be lost", thr2);
- return new TaskResult<>(this.getEmptyChunk(), null);
- }
+ protected TaskResult<ChunkAccess, Throwable> runOnMain(final ChunkAccess data, final Throwable throwable) {
+ throw new UnsupportedOperationException();
}
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
index 8950b220b9a3512cd4667beb7bdec0e82e07edc6..9be85eb0abec02bc0e0eded71c34ab1c565c63e7 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
@@ -328,6 +328,12 @@ public class PoiManager extends SectionStorage<PoiSection> {
}
}
}
+
+ public void checkConsistency(net.minecraft.world.level.chunk.ChunkAccess chunk) {
+ for (LevelChunkSection section : chunk.getSections()) {
+ this.checkConsistencyWithBlocks(chunk.getPos(), section);
+ }
+ }
// Paper end - rewrite chunk system
public void checkConsistencyWithBlocks(ChunkPos chunkPos, LevelChunkSection chunkSection) {
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
index 0ec80b83a99bfdb1f985045d98a81905a8a5a3ac..9d6f4749ed72fe319754ccea28f20fa97286527d 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
@@ -122,13 +122,11 @@ public class ChunkSerializer {
public static final class InProgressChunkHolder {
public final ProtoChunk protoChunk;
- public final java.util.ArrayDeque<Runnable> tasks;
public CompoundTag poiData;
- public InProgressChunkHolder(final ProtoChunk protoChunk, final java.util.ArrayDeque<Runnable> tasks) {
+ public InProgressChunkHolder(final ProtoChunk protoChunk) {
this.protoChunk = protoChunk;
- this.tasks = tasks;
}
}
// Paper end
@@ -136,7 +134,6 @@ public class ChunkSerializer {
public static ProtoChunk read(ServerLevel world, PoiManager poiStorage, ChunkPos chunkPos, CompoundTag nbt) {
// Paper start - add variant for async calls
InProgressChunkHolder holder = loadChunk(world, poiStorage, chunkPos, nbt, true);
- holder.tasks.forEach(Runnable::run);
return holder.protoChunk;
}
@@ -145,7 +142,6 @@ public class ChunkSerializer {
private static final boolean JUST_CORRUPT_IT = Boolean.getBoolean("Paper.ignoreWorldDataVersion");
// Paper end
public static InProgressChunkHolder loadChunk(ServerLevel world, PoiManager poiStorage, ChunkPos chunkPos, CompoundTag nbt, boolean distinguish) {
- java.util.ArrayDeque<Runnable> tasksToExecuteOnMain = new java.util.ArrayDeque<>();
// Paper end
// Paper start - Do NOT attempt to load chunks saved with newer versions
if (nbt.contains("DataVersion", 99)) {
@@ -223,9 +219,7 @@ public class ChunkSerializer {
LevelChunkSection chunksection = new LevelChunkSection(b0, datapaletteblock, (PalettedContainer) object); // CraftBukkit - read/write
achunksection[k] = chunksection;
- tasksToExecuteOnMain.add(() -> { // Paper - delay this task since we're executing off-main
- poiStorage.checkConsistencyWithBlocks(chunkPos, chunksection);
- }); // Paper - delay this task since we're executing off-main
+ // Paper - rewrite chunk system - moved to final load stage
}
boolean flag3 = nbttagcompound1.contains("BlockLight", 7);
@@ -403,7 +397,7 @@ public class ChunkSerializer {
}
if (chunkstatus_type == ChunkStatus.ChunkType.LEVELCHUNK) {
- return new InProgressChunkHolder(new ImposterProtoChunk((LevelChunk) object1, false), tasksToExecuteOnMain); // Paper - Async chunk loading
+ return new InProgressChunkHolder(new ImposterProtoChunk((LevelChunk) object1, false)); // Paper - Async chunk loading
} else {
ProtoChunk protochunk1 = (ProtoChunk) object1;
@@ -446,7 +440,7 @@ public class ChunkSerializer {
protochunk1.setCarvingMask(worldgenstage_features, new CarvingMask(nbttagcompound4.getLongArray(s1), ((ChunkAccess) object1).getMinBuildHeight()));
}
- return new InProgressChunkHolder(protochunk1, tasksToExecuteOnMain); // Paper - Async chunk loading
+ return new InProgressChunkHolder(protochunk1); // Paper - Async chunk loading
}
}

View File

@ -19,7 +19,7 @@ index 2e96377d628b3a07fb565020074d665f594f32e8..75b1877f8c3e4da3183437f327ef3376
} // Folia - region threading - remove delayed accept
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 7d99233f4b36d699aae81be554e78313909c9a7d..7416d8eed50ef7156db158c05c89d6062193bf2e 100644
index 576e60f2ce00733254895f83f4bef76152dc3fe9..f35192cd5f08fdac6eaee549d6b73cccd22fefe4 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -180,6 +180,17 @@ public abstract class PlayerList {

View File

@ -51,10 +51,10 @@ index d9687722e02dfd4088c7030abbf5008eb0a092c8..62484ebf4550b05182f693a3180bbac5
TickThread.ensureTickThread(thisEntity, "May not tick entity scheduler asynchronously");
final List<ScheduledTask> toRun;
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index e8e6743ec078d9dd95d3583ff24cb6791abddf19..885805170e84ce6f26b2789e39c5b5b453c026f0 100644
index 154ae1492969c4fe9f56ddc190e27337d0396da8..1b7596a8a66c416d8b43495a70b23d6fdeebb245 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -2769,6 +2769,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@@ -2889,6 +2889,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
// CraftBukkit start
com.google.common.base.Preconditions.checkState(!entity.passengers.contains(this), "Circular entity riding! %s %s", this, entity);
@ -62,7 +62,7 @@ index e8e6743ec078d9dd95d3583ff24cb6791abddf19..885805170e84ce6f26b2789e39c5b5b4
CraftEntity craft = (CraftEntity) entity.getBukkitEntity().getVehicle();
Entity orig = craft == null ? null : craft.getHandle();
if (this.getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity) {
@@ -2796,6 +2797,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@@ -2916,6 +2917,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
if (event.isCancelled()) {
return false;
}
@ -70,7 +70,7 @@ index e8e6743ec078d9dd95d3583ff24cb6791abddf19..885805170e84ce6f26b2789e39c5b5b4
// Spigot end
if (this.passengers.isEmpty()) {
this.passengers = ImmutableList.of(entity);
@@ -2824,6 +2826,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@@ -2944,6 +2946,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
throw new IllegalStateException("Use x.stopRiding(y), not y.removePassenger(x)");
} else {
// CraftBukkit start
@ -78,7 +78,7 @@ index e8e6743ec078d9dd95d3583ff24cb6791abddf19..885805170e84ce6f26b2789e39c5b5b4
CraftEntity craft = (CraftEntity) entity.getBukkitEntity().getVehicle();
Entity orig = craft == null ? null : craft.getHandle();
if (this.getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity) {
@@ -2851,6 +2854,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@@ -2971,6 +2974,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
if (event.isCancelled()) {
return false;
}
@ -87,7 +87,7 @@ index e8e6743ec078d9dd95d3583ff24cb6791abddf19..885805170e84ce6f26b2789e39c5b5b4
if (this.passengers.size() == 1 && this.passengers.get(0) == entity) {
this.passengers = ImmutableList.of();
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index c43248f5a8e4fe514cfcc1dcf3003baedd950904..0fa6d701f4a16b3a74b55bdd660accb000f06565 100644
index aa877017799c573a114832fd1809bc80e322ed2c..d68e0ae2e6332a6a99630e2635b22d2cd509377f 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -376,7 +376,7 @@ public final class CraftServer implements Server {
@ -365,7 +365,7 @@ index a82acbbe62bf6aa497e627587e2f3b9be2fbf487..dec03f00091ca781f0eaa4063649883f
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java
index 8f25bb253c2b22e1964afeae705901e926432ef0..ebadeb928d405564d3b029246ac4f2c66958ad9c 100644
index e405488ba5e0159ff84a72fac1d2da6e9c45238e..631250c15711d0912d2ae34efed02d78d20a0098 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java
@@ -9,8 +9,16 @@ public class CraftBat extends CraftAmbient implements Bat {
@ -722,7 +722,7 @@ index 2f7bf9963c3adfc9d2475a86e53a7dcf9f386bfe..e5ffc16d4d5595a7065119c97293f5b4
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java
index c1db88ceb65eb81c542171fc5465224ef613ce3b..108e0bbe345075a92e133c58df0d1009fc06009d 100644
index 18623159932df2dd5e43133b4396b43731693780..66f3a67ea0a81d4cfff8365ed055fff6967360b7 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java
@@ -10,8 +10,16 @@ public class CraftDolphin extends CraftWaterMob implements Dolphin {
@ -932,7 +932,7 @@ index 75c7645fb5732c43d1da15181cf5c7ee4c3ecd6c..6d3325436a77153438bc40aa86819562
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index fa762f490fea59e2ec21cfb1c050c4adc30d9998..e4b85cab0d5e0a6d6214e7d57ab2c741439618b4 100644
index 6a54b582fdab953c452a32e9839e2a916c659c2e..b23e399f65a75e10b6b3aabe8d3ea3660104125b 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -827,7 +827,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
@ -1023,7 +1023,7 @@ index 84899284703baeb04bfc79251941265d52ac07e8..5b8333e342c639f33acf62e5f8eb72d0
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java
index 05778023c3b2809c52c148efdfc8677dcc087a7b..92998b85df126725f376941c13dee3dfd407e96a 100644
index e99314b905a7ed54ceeb156ed92ff1a5793df99a..61bed78df554dc6a9689ea19e159fde990082a1e 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java
@@ -15,8 +15,16 @@ public class CraftFallingBlock extends CraftEntity implements FallingBlock {
@ -1107,7 +1107,7 @@ index 3c64461119391ec2e987fc936104e21ef0a95ce4..d702e34f65de28df677a9d3616f38b2c
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java
index f4591dfe799538ba8aea104793ceb9995dad0bb6..f74f4932a177ce38dc6339d9b16fca02db481444 100644
index bac5c30d3aae1b3a7dbfb78f6fd37c11038fc735..20b12701979daf1185332cffdc3895384da30fbb 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java
@@ -16,8 +16,16 @@ public class CraftFishHook extends CraftProjectile implements FishHook {
@ -1401,7 +1401,7 @@ index 1f474ef8f9e86da383206bd50ba00c7ed8352c5d..4dd82aef0d4e37336c076f74fe01cbd4
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
index 1b008e5217c5bbf566a213abb92e1c7c43a3a7c2..139f4e7fb1e9fa9b21b0eddbffb05a35d54cf5cf 100644
index cba6ead3f937f2b3d59c15a864e07e5cb2f2330c..773c07d78877e23f557e448b8d2631534a067350 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
@@ -293,8 +293,16 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
@ -1527,7 +1527,7 @@ index 9b7b98e21e757ab2caca68de20d0191d0011bc9d..06e2580653db00110b0f0a05cfda9d0e
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
index 2a297adc49129729751317817d959c2d2566fc3e..7352909a3f2b2f1dc0f96e452869014fa328fc1c 100644
index 5d6cb013fbc4e5bfe335211b3cc63d2788b5868b..1122ce7763988af38a1f48e789a626d87d8cb4fd 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
@@ -157,8 +157,16 @@ public class CraftItemFrame extends CraftHanging implements ItemFrame {
@ -1611,7 +1611,7 @@ index e515e819774bfb31ec03f05a5502921e66f2b0e2..80cac5d7362577e53ef5ca215ab32618
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
index d43859f8aa7beed82dd3a146bb1086982cd0cda7..3bc0bda187d0702694ad9d98b07aa62f5c44df8b 100644
index 42a4a45ece562d543cc4fab9d9e7c70573705f10..d065dfacb0980c7997b99ef7ed6e61a85a3c3055 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
@@ -419,8 +419,16 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
@ -1999,7 +1999,7 @@ index d7e9b1d7460c0479ff94a2cb52e6c572a464420a..caee1abf4c057afa08be8495bf742f87
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java
index 9a3734c670972ee91a0d44a1b1fa8493de854a9c..e1ae90699fdc13f6f2bb9ebf227b3803e13e2b88 100644
index 63e31c237dc3cf37a5e06c1cf3f030c9f1df3d38..8c02dabbfee88ee6c3315c8174da7236eecdd8cc 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java
@@ -10,8 +10,16 @@ public class CraftPhantom extends CraftFlying implements Phantom, CraftEnemy {
@ -2146,7 +2146,7 @@ index beea227855f0b978e655efc298024120df8f4945..e1b7922ed298b6b3068c3f5fbe3b4030
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index b0d9ab8088ee8b894fbee4c4b88bf081a419cb1f..a381e6a83762af08ed2005383d188313c12612ff 100644
index 93e0c4fd626455db1e14785a0b6750ba07fa0fe0..bc59a9d2f965c8e8638a2eeb3fcf661ed81f1727 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -593,7 +593,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@ -2158,7 +2158,7 @@ index b0d9ab8088ee8b894fbee4c4b88bf081a419cb1f..a381e6a83762af08ed2005383d188313
final ServerGamePacketListenerImpl connection = this.getHandle().connection;
if (connection != null) {
connection.disconnect(message == null ? net.kyori.adventure.text.Component.empty() : message, cause);
@@ -2017,9 +2017,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@@ -2023,9 +2023,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
return this;
}
@ -2176,11 +2176,11 @@ index b0d9ab8088ee8b894fbee4c4b88bf081a419cb1f..a381e6a83762af08ed2005383d188313
}
public void setHandle(final ServerPlayer entity) {
@@ -3028,7 +3035,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@@ -3024,7 +3031,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
{
if ( CraftPlayer.this.getHealth() <= 0 && CraftPlayer.this.isOnline() )
{
- server.getServer().getPlayerList().respawn( CraftPlayer.this.getHandle(), false, RespawnReason.PLUGIN );
- server.getServer().getPlayerList().respawn( CraftPlayer.this.getHandle(), false, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason.PLUGIN );
+ CraftPlayer.this.getHandle().respawn(null); // Folia - region threading
}
}
@ -2271,7 +2271,7 @@ index 3cb4860fea30bfaf2147b4f29a34336b6e417d6c..5d852e0a34004b877555157dd45020e5
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftRaider.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftRaider.java
index ad10ba1dbaf81e767441aa8e1babed1d3e654121..55113e60883e2d74f46ffd2b7d273a517baa7584 100644
index f0b061979f9acdce6d06f70b651692c841418d96..6fb31c15d65885614b3878b5bcce08567d5b2089 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftRaider.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftRaider.java
@@ -14,8 +14,16 @@ public abstract class CraftRaider extends CraftMonster implements Raider {
@ -2523,7 +2523,7 @@ index d8b4df1300791aaf310465ec1577b1b8c202901a..17b83eb8563586f1ddf252f438d52d55
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSniffer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSniffer.java
index d9b40521e0597f4e2236a34c25cc4625552be66f..1517b557a62fe5f68217d343953ac0cb44531e01 100644
index ca3bffd07e0e8f3b2409917cf561d4755c8fba21..79de600fbc424d80b5f37a60ad60a8f18e746ff6 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftSniffer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSniffer.java
@@ -17,8 +17,16 @@ public class CraftSniffer extends CraftAnimals implements Sniffer {
@ -2923,7 +2923,7 @@ index 4352af0a76ce4a4cd4afbea96f4851ef2b64ac7d..f8aa5bc3d846d2fd785c612dd3906b84
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java
index a14d0a688b9054988b5c86c94738e4aaca9f9cfd..dd5b95c809e2bbdac17981af0010f55403713c87 100644
index d3546fb082a56b4ce077ded5d25909e15f7eb593..319efb68aebd6d1182dede7b2b6f368b3d7b62c2 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java
@@ -10,8 +10,16 @@ public class CraftTurtle extends CraftAnimals implements Turtle {
@ -2965,7 +2965,7 @@ index bcfca66c7b99b9d514fe850d6cc6abd7e36ccaf7..90c295ff95bbee4f51fda1be2f58312d
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
index 4e880409b06086568627f3e930159f1abb979984..db774dac486fd0a4b49a45c8248b594f877b141e 100644
index e986767316a717bdbdff7a9ccaaeba068ab2a6d8..ca6a6507cadc6c3ada1a9d8ce35cd48d295e2884 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
@@ -31,8 +31,16 @@ public class CraftVillager extends CraftAbstractVillager implements Villager {
@ -3028,7 +3028,7 @@ index bea22e002a9d41b0e364eff1109d5a67c9824a00..fc7a87ed98801914d5cf3e6784f0025d
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java
index ecf0c4a7d1ce2b254d91b3276fa24c149329737a..fadc797d3c298c6ad4b2e6abc70b59e5ffc58e92 100644
index f9a3d060ff4da6047d11f2b024d144572c513399..824e2c7aa9263195cb173da2b5612b3b8bc3a046 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java
@@ -10,8 +10,16 @@ public class CraftWanderingTrader extends CraftAbstractVillager implements Wande
@ -3112,7 +3112,7 @@ index 9039db1a72009342063d4db08e18e6aee18836e8..c2bceaeabf13d37506eea540cb153d10
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java
index 1a21d30620f13a48976da5ead7edab201ea68b21..6eb758bed0d0e5394adfee3294aec2bf53daf3ed 100644
index 1477c2c04d8f5c5639ce94808fe2a7029cedaeb2..a6dc41a1eac7aa08a4e13489a7dabb61cd4eadd7 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java
@@ -22,8 +22,16 @@ public class CraftWither extends CraftMonster implements Wither, com.destroystok

View File

@ -1,119 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Thu, 2 Mar 2023 23:19:04 -0800
Subject: [PATCH] Cache whether region files do not exist
The repeated I/O of creating the directory for the regionfile
or for checking if the file exists can be heavy in
when pushing chunk generation extremely hard - as each chunk gen
request may effectively go through to the I/O thread.
diff --git a/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java b/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java
index a08cde4eefe879adcee7c4118bc38f98c5097ed0..8a11e10b01fa012b2f98b1c193c53251e848f909 100644
--- a/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java
+++ b/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java
@@ -819,8 +819,14 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread {
return file.hasChunk(chunkPos) ? Boolean.TRUE : Boolean.FALSE;
});
} else {
+ // first check if the region file for sure does not exist
+ if (taskController.doesRegionFileNotExist(chunkX, chunkZ)) {
+ return Boolean.FALSE;
+ } // else: it either exists or is not known, fall back to checking the loaded region file
+
return taskController.computeForRegionFileIfLoaded(chunkX, chunkZ, (final RegionFile file) -> {
if (file == null) { // null if not loaded
+ // not sure at this point, let the I/O thread figure it out
return Boolean.TRUE;
}
@@ -1116,6 +1122,10 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread {
return !this.tasks.isEmpty();
}
+ public boolean doesRegionFileNotExist(final int chunkX, final int chunkZ) {
+ return this.getCache().doesRegionFileNotExistNoIO(new ChunkPos(chunkX, chunkZ));
+ }
+
public <T> T computeForRegionFile(final int chunkX, final int chunkZ, final boolean existingOnly, final Function<RegionFile, T> function) {
final RegionFileStorage cache = this.getCache();
final RegionFile regionFile;
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 bd502ca721de0cab438d995efa00ad0554c0d2fe..9633b01d2d961fd1403e353484d336376ef009eb 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
@@ -28,6 +28,35 @@ public class RegionFileStorage implements AutoCloseable {
private final boolean isChunkData; // Paper
+ // Paper start - cache regionfile does not exist state
+ static final int MAX_NON_EXISTING_CACHE = 1024 * 64;
+ private final it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet nonExistingRegionFiles = new it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet();
+ private synchronized boolean doesRegionFilePossiblyExist(long position) {
+ if (this.nonExistingRegionFiles.contains(position)) {
+ this.nonExistingRegionFiles.addAndMoveToFirst(position);
+ return false;
+ }
+ return true;
+ }
+
+ private synchronized void createRegionFile(long position) {
+ this.nonExistingRegionFiles.remove(position);
+ }
+
+ private synchronized void markNonExisting(long position) {
+ if (this.nonExistingRegionFiles.addAndMoveToFirst(position)) {
+ while (this.nonExistingRegionFiles.size() >= MAX_NON_EXISTING_CACHE) {
+ this.nonExistingRegionFiles.removeLastLong();
+ }
+ }
+ }
+
+ public synchronized boolean doesRegionFileNotExistNoIO(ChunkPos pos) {
+ long key = ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ());
+ return !this.doesRegionFilePossiblyExist(key);
+ }
+ // Paper end - cache regionfile does not exist state
+
protected RegionFileStorage(Path directory, boolean dsync) { // Paper - protected constructor
// Paper start - add isChunkData param
this(directory, dsync, false);
@@ -77,7 +106,7 @@ public class RegionFileStorage implements AutoCloseable {
}
public synchronized RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly, boolean lock) throws IOException {
// Paper end
- long i = ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ());
+ long i = ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()); final long regionPos = i; // Paper - OBFHELPER
RegionFile regionfile = (RegionFile) this.regionCache.getAndMoveToFirst(i);
if (regionfile != null) {
@@ -89,15 +118,27 @@ public class RegionFileStorage implements AutoCloseable {
// Paper end
return regionfile;
} else {
+ // Paper start - cache regionfile does not exist state
+ if (existingOnly && !this.doesRegionFilePossiblyExist(regionPos)) {
+ return null;
+ }
+ // Paper end - cache regionfile does not exist state
if (this.regionCache.size() >= io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize) { // Paper - configurable
((RegionFile) this.regionCache.removeLast()).close();
}
- FileUtil.createDirectoriesSafe(this.folder);
+ // Paper - only create directory if not existing only - moved down
Path path = this.folder;
int j = chunkcoordintpair.getRegionX();
Path path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca"); // Paper - diff on change
- if (existingOnly && !java.nio.file.Files.exists(path1)) return null; // CraftBukkit
+ if (existingOnly && !java.nio.file.Files.exists(path1)) { // Paper start - cache regionfile does not exist state
+ this.markNonExisting(regionPos);
+ return null; // CraftBukkit
+ } else {
+ this.createRegionFile(regionPos);
+ }
+ // Paper end - cache regionfile does not exist state
+ FileUtil.createDirectoriesSafe(this.folder); // Paper - only create directory if not existing only - moved from above
RegionFile regionfile1 = new RegionFile(path1, this.folder, this.sync, this.isChunkData); // Paper - allow for chunk regionfiles to regen header
this.regionCache.putAndMoveToFirst(i, regionfile1);

View File

@ -10,10 +10,10 @@ the impact from scaling the region threads, but is not a fix
to the underlying issue.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 3478d9c1db9acf19165df7308b6ae4461fa8fef7..61bac6fda2d2f4b3db8a3f7e3003f47c84d5c4cd 100644
index 37121e7be9ed0056b50b8d01dff84ee8660bab47..32562030474e935b1da359b94028a10e39f1a5a4 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -2884,6 +2884,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2877,6 +2877,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
public final void executeMidTickTasks() {

View File

@ -5,7 +5,7 @@ Subject: [PATCH] Throw UnsupportedOperationException() for broken APIs
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 0fa6d701f4a16b3a74b55bdd660accb000f06565..fc3980d122eac0f1830375c113429135dc6d644c 100644
index d68e0ae2e6332a6a99630e2635b22d2cd509377f..dc7ff994269e2a41a3256936c6b46f80dd8c2e18 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1264,6 +1264,7 @@ public final class CraftServer implements Server {

View File

@ -5,7 +5,7 @@ Subject: [PATCH] Work around https://github.com/PaperMC/paperweight/issues/194
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index e1e5573b31ca071eac727383b21a085aead4d3eb..cebfc29c50c631d0f2636f600b1839b2c5515ba1 100644
index 502c64391db8d0dde3dce4edb976635cb9d0522a..7f109c441f360143fadbeafd51247867a1d77cac 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -504,7 +504,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic

View File

@ -41,10 +41,10 @@ index 89e6dc92bfbb28d20f252eca5257db1d3d042327..5f609782fceda0fdc856123ca8d40d3f
// connections
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
index 59a9a9633ac924c606564b75298fae22f1ffd4ec..33cc8db1892d6c876c2510bd14546766ab7e21e3 100644
index d9e622964b415b07caaa714d7d23649dcc62c677..56d115f4fdc6c7e3a748557eb8c052994c4521de 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -123,7 +123,7 @@ public class ServerPlayerGameMode {
@@ -124,7 +124,7 @@ public class ServerPlayerGameMode {
}
public void tick() {

View File

@ -9,20 +9,10 @@ add explicit block update suppression techniques, it's better
than the server crashing.
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 32b9358bacabedf4513bb17c68200ef84a95a91b..afb3d0fa48b7fd6d273361c6dc32764b5d35c356 100644
index f99b5a14717f87fe0629cbe7b6339a5ff2bb5fe9..8d15a9ff9fe61b97fa73957f3a2042859fe7d70e 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -1248,7 +1248,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
}
public int getSignal(BlockPos pos, Direction direction) {
- BlockState iblockdata = this.getBlockState(pos);
+ BlockState iblockdata = !io.papermc.paper.util.TickThread.isTickThreadFor((ServerLevel)this, pos) ? null : this.getBlockStateIfLoaded(pos); // Folia - block updates in unloaded chunks
+ if (iblockdata == null) return 0; // Folia - block updates in unloaded chunks
int i = iblockdata.getSignal(this, pos, direction);
return iblockdata.isRedstoneConductor(this, pos) ? Math.max(i, this.getDirectSignalTo(pos)) : i;
@@ -1418,7 +1419,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -1340,7 +1340,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
Direction enumdirection = (Direction) iterator.next();
BlockPos blockposition1 = pos.relative(enumdirection);
@ -31,20 +21,6 @@ index 32b9358bacabedf4513bb17c68200ef84a95a91b..afb3d0fa48b7fd6d273361c6dc32764b
BlockState iblockdata = this.getBlockState(blockposition1);
if (iblockdata.is(Blocks.COMPARATOR)) {
diff --git a/src/main/java/net/minecraft/world/level/LevelReader.java b/src/main/java/net/minecraft/world/level/LevelReader.java
index 07802d0a25e49519c3c9b33c217e05002cf05e31..11b1a260f530ea7244856174b193eb6ef4d16849 100644
--- a/src/main/java/net/minecraft/world/level/LevelReader.java
+++ b/src/main/java/net/minecraft/world/level/LevelReader.java
@@ -124,7 +124,8 @@ public interface LevelReader extends BlockAndTintGetter, CollisionGetter, BiomeM
}
default int getDirectSignal(BlockPos pos, Direction direction) {
- return this.getBlockState(pos).getDirectSignal(this, pos, direction);
+ BlockState blockstate = this instanceof net.minecraft.server.level.ServerLevel serverLevel && !io.papermc.paper.util.TickThread.isTickThreadFor(serverLevel, pos) ? null : this.getBlockStateIfLoaded(pos); // Folia - block updates in unloaded chunks
+ return blockstate == null ? 0 : blockstate.getDirectSignal(this, pos, direction); // Folia - block updates in unloaded chunks
}
default ChunkAccess getChunk(BlockPos pos) {
diff --git a/src/main/java/net/minecraft/world/level/block/DetectorRailBlock.java b/src/main/java/net/minecraft/world/level/block/DetectorRailBlock.java
index 05dfb1790a292f9f85b641377c2ca3675726c127..971fcb61915f8b5f3fc4cbe3f3e4c8d639abdee6 100644
--- a/src/main/java/net/minecraft/world/level/block/DetectorRailBlock.java
@ -78,7 +54,7 @@ index 7fddb6fa8fd30ef88346a59f7867aae792f13772..b963f364b4763091c3b5d0f938eaa679
} else {
RailShape blockpropertytrackposition1 = (RailShape) iblockdata.getValue(PoweredRailBlock.SHAPE);
diff --git a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
index cc019091a2decbc1e5c760129778b42ea1d449d6..068823de0f7adf7d9d6c90694e10401d704ecbad 100644
index 858f81db707a73edeec2bb4ee62e69ad2f64200c..7b479d0c093732a5a8899b8f3d49a77139ef8826 100644
--- a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
@@ -193,8 +193,9 @@ public class RedStoneWireBlock extends Block {
@ -93,7 +69,7 @@ index cc019091a2decbc1e5c760129778b42ea1d449d6..068823de0f7adf7d9d6c90694e10401d
BlockState iblockdata1 = world.getBlockState(blockposition_mutableblockposition);
diff --git a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
index 7c1768452fa0f7278ccc84470ef0965a3f96b0df..7b5de18daeba5945c82887768611353a5b003f7f 100644
index d50c7dd008af14fce9073666e0fd1b609f89559c..5ddd8108e077bc7c1a9524854ac591c6ab99c174 100644
--- a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
+++ b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
@@ -119,7 +119,8 @@ public class CollectingNeighborUpdater implements NeighborUpdater {
@ -115,11 +91,11 @@ index 7c1768452fa0f7278ccc84470ef0965a3f96b0df..7b5de18daeba5945c82887768611353a
++this.idx;
}
@@ -156,7 +158,9 @@ public class CollectingNeighborUpdater implements NeighborUpdater {
static record ShapeUpdate(Direction direction, BlockState state, BlockPos pos, BlockPos neighborPos, int updateFlags) implements CollectingNeighborUpdater.NeighborUpdates {
static record ShapeUpdate(Direction direction, BlockState state, BlockPos pos, BlockPos neighborPos, int updateFlags, int updateLimit) implements CollectingNeighborUpdater.NeighborUpdates {
@Override
public boolean runNext(Level world) {
+ if (io.papermc.paper.util.TickThread.isTickThreadFor((net.minecraft.server.level.ServerLevel)world, this.pos) && world.getChunkIfLoaded(this.pos) != null) { // Folia - block updates in unloaded chunks
NeighborUpdater.executeShapeUpdate(world, this.direction, this.state, this.pos, this.neighborPos, this.updateFlags, 512);
NeighborUpdater.executeShapeUpdate(world, this.direction, this.state, this.pos, this.neighborPos, this.updateFlags, this.updateLimit);
+ } // Folia - block updates in unloaded chunks
return false;
}

View File

@ -7,10 +7,10 @@ The returned TE may be in the world, in which case it is unsafe
for the current thread to modify or access its contents.
diff --git a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
index 9a1cffd51aaf97f759a9057aefbf50bd6f5ed028..d5ad8f210cd545bf394f94c443652e6b12091879 100644
index 4a3ac7dedf5cb1e76f16ec4f18e82afc717d0ced..44609fb7965a03283e2bb50e483a8f60254de510 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
@@ -82,6 +82,11 @@ public class ImposterProtoChunk extends ProtoChunk {
@@ -84,6 +84,11 @@ public class ImposterProtoChunk extends ProtoChunk {
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos) {

View File

@ -9,10 +9,10 @@ data deserialization and is racey even in Vanilla. But in Folia,
some accesses may throw and as such we need to fix this directly.
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index c4970eefb48baf24cc51ba3cb27997984be872db..987ccd4ccdc50241e70aae681de7de2a7a6edc19 100644
index 6d3d8fd7d8e21fe9ba47574b764b04f627887d56..3a22b5516f8be5b7ec13887f7fae7c21b2366684 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -572,7 +572,7 @@ public class ServerPlayer extends Player {
@@ -571,7 +571,7 @@ public class ServerPlayer extends Player {
this.getBukkitEntity().readExtraData(nbt); // CraftBukkit
if (this.isSleeping()) {
@ -22,10 +22,10 @@ index c4970eefb48baf24cc51ba3cb27997984be872db..987ccd4ccdc50241e70aae681de7de2a
// CraftBukkit start
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 4f8062432cfe6480664e674fde0255f07b15c73a..49716190b784339b80c8a3ac8e5b13bc450284ee 100644
index 7da79a21f5ff0bd546cfc602099916dac2ab0e51..5b5473ff1a87b5f82d9179e54ae448120eb763a3 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -4225,6 +4225,11 @@ public abstract class LivingEntity extends Entity implements Attackable {
@@ -4273,6 +4273,11 @@ public abstract class LivingEntity extends Entity implements Attackable {
}
});

View File

@ -1,39 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 1 Apr 2023 00:34:29 -0700
Subject: [PATCH] Acquire scheduling lock in
NewChunkHolder#onFullChunkLoadChange
It modifies data that should be held by the lock: pending
chunk status
diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java
index 3b70ccd8e0b1ada943f57faf99c23b2935249cf6..12feb739a784a0108256451a37d94d041b7a5cdc 100644
--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java
+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java
@@ -1203,8 +1203,12 @@ public final class NewChunkHolder {
}
}
- // only call on main thread, must hold ticket level and scheduling lock
+ // only call on main thread // Folia - update comment
private void onFullChunkLoadChange(final boolean loaded, final List<NewChunkHolder> changedFullStatus) {
+ // Folia start - chunk system fix - acquire scheduling lock
+ this.scheduler.schedulingLock.lock();
+ try {
+ // Folia end - chunk system fix - acquire scheduling lock
for (int dz = -NEIGHBOUR_RADIUS; dz <= NEIGHBOUR_RADIUS; ++dz) {
for (int dx = -NEIGHBOUR_RADIUS; dx <= NEIGHBOUR_RADIUS; ++dx) {
final NewChunkHolder holder = (dx | dz) == 0 ? this : this.scheduler.chunkHolderManager.getChunkHolder(dx + this.chunkX, dz + this.chunkZ);
@@ -1219,6 +1223,11 @@ public final class NewChunkHolder {
}
}
}
+ // Folia start - chunk system fix - acquire scheduling lock
+ } finally {
+ this.scheduler.schedulingLock.unlock();
+ }
+ // Folia end - chunk system fix - acquire scheduling lock
}
private ChunkHolder.FullChunkStatus updateCurrentState(final ChunkHolder.FullChunkStatus to) {

View File

@ -0,0 +1,482 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Fri, 12 May 2023 20:37:56 -0700
Subject: [PATCH] Use coordinate-based locking to increase chunk system
parallelism
A significant overhead in Folia comes from the chunk system's
locks, the ticket lock and the scheduling lock. The public
test server, which had ~330 players, had signficant performance
problems with these locks: ~80% of the time spent ticking
was _waiting_ for the locks to free. Given that it used
around 15 cores total at peak, this is a complete and utter loss
of potential.
To address this issue, I have replaced the ticket lock and scheduling
lock with two ReentrantAreaLocks. The ReentrantAreaLock takes a
shift, which is used internally to group positions into sections.
This grouping is neccessary, as the possible radius of area that
needs to be acquired for any given lock usage is up to 64. As such,
the shift is critical to reduce the number of areas required to lock
for any lock operation. Currently, it is set to a shift of 6, which
is identical to the ticket level propagation shift (and, it must be
at least the ticket level propagation shift AND the region shift).
The chunk system locking changes required a complete rewrite of the
chunk system tick, chunk system unload, and chunk system ticket level
propagation - as all of the previous logic only works with a single
global lock.
This does introduce two other section shifts: the lock shift, and the
ticket shift. The lock shift is simply what shift the area locks use,
and the ticket shift represents the size of the ticket sections.
Currently, these values are just set to the region shift for simplicity.
However, they are not arbitrary: the lock shift must be at least the size
of the ticket shift and must be at least the size of the region shift.
The ticket shift must also be >= the ceil(log2(max ticket level source)).
The chunk system's ticket propagator is now global state, instead of
region state. This cleans up the logic for ticket levels significantly,
and removes usage of the region lock in this area, but it also means
that the addition of a ticket no longer creates a region. To alleviate
the side effects of this change, the global tick thread now processes
ticket level updates for each world every tick to guarantee eventual
ticket level processing. The chunk system also provides a hook to
process ticket level changes in a given _section_, so that the
region queue can guarantee that after adding its reference counter
that the region section is created/exists/wont be destroyed.
The ticket propagator operates by updating the sources in a single ticket
section, and propagating the updates to its 1 radius neighbours. This
allows the ticket updates to occur in parallel or selectively (see above).
Currently, the process ticket level update function operates by
polling from a concurrent queue of sections to update and simply
invoking the single section update logic. This allows the function
to operate completely in parallel, provided the queue is ordered right.
Additionally, this limits the area used in the ticket/scheduling lock
when processing updates, which should massively increase parallelism compared
to before.
The chunk system ticket addition for expirable ticket types has been modified
to no longer track exact tick deadlines, as this relies on what region the
ticket is in. Instead, the chunk system tracks a map of
lock section -> (chunk coordinate -> expire ticket count) and every ticket
has been changed to have a removeDelay count that is decremented each tick.
Each region searches its own sections to find tickets to try to expire.
Chunk system unloading has been modified to track unloads by lock section.
The ordering is determined by which section a chunk resides in.
The unload process now removes from unload sections and processes
the full unload stages (1, 2, 3) before moving to the next section, if possible.
This allows the unload logic to only hold one lock section at a time for
each lock, which is a massive parallelism increase.
In stress testing, these changes lowered the locking overhead to only 5%
from ~70%, which completely fix the original problem as described.
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/lock/AreaLock.java b/src/main/java/ca/spottedleaf/concurrentutil/lock/AreaLock.java
deleted file mode 100644
index 6a155b779914828a0d4199bdfcb0d6fca25e1581..0000000000000000000000000000000000000000
--- a/src/main/java/ca/spottedleaf/concurrentutil/lock/AreaLock.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package ca.spottedleaf.concurrentutil.lock;
-
-import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
-import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.locks.LockSupport;
-
-public final class AreaLock {
-
- private final int coordinateShift;
-
- private final Long2ReferenceOpenHashMap<Node> nodesByPosition = new Long2ReferenceOpenHashMap<>(1024, 0.10f);
-
- public AreaLock(final int coordinateShift) {
- this.coordinateShift = coordinateShift;
- }
-
- private static long key(final int x, final int z) {
- return ((long)z << 32) | (x & 0xFFFFFFFFL);
- }
-
- public Node lock(final int x, final int z, final int radius) {
- final Thread thread = Thread.currentThread();
- final int minX = (x - radius) >> this.coordinateShift;
- final int minZ = (z - radius) >> this.coordinateShift;
- final int maxX = (x + radius) >> this.coordinateShift;
- final int maxZ = (z + radius) >> this.coordinateShift;
-
- final Node node = new Node(x, z, radius, thread);
-
- synchronized (this) {
- ReferenceOpenHashSet<Node> parents = null;
- for (int currZ = minZ; currZ <= maxZ; ++currZ) {
- for (int currX = minX; currX <= maxX; ++currX) {
- final Node dependency = this.nodesByPosition.put(key(currX, currZ), node);
- if (dependency == null) {
- continue;
- }
-
- if (parents == null) {
- parents = new ReferenceOpenHashSet<>();
- }
-
- if (parents.add(dependency)) {
- // added a dependency, so we need to add as a child to the dependency
- if (dependency.children == null) {
- dependency.children = new ArrayList<>();
- }
- dependency.children.add(node);
- }
- }
- }
-
- if (parents == null) {
- // no dependencies, so we can just return immediately
- return node;
- } // else: we need to lock
-
- node.parents = parents;
- }
-
- while (!node.unlocked) {
- LockSupport.park(node);
- }
-
- return node;
- }
-
- public void unlock(final Node node) {
- List<Node> toUnpark = null;
-
- final int x = node.x;
- final int z = node.z;
- final int radius = node.radius;
-
- final int minX = (x - radius) >> this.coordinateShift;
- final int minZ = (z - radius) >> this.coordinateShift;
- final int maxX = (x + radius) >> this.coordinateShift;
- final int maxZ = (z + radius) >> this.coordinateShift;
-
- synchronized (this) {
- final List<Node> children = node.children;
- if (children != null) {
- // try to unlock children
- for (int i = 0, len = children.size(); i < len; ++i) {
- final Node child = children.get(i);
- if (!child.parents.remove(node)) {
- throw new IllegalStateException();
- }
- if (child.parents.isEmpty()) {
- // we can unlock, as it now has no dependencies in front
- child.parents = null;
- if (toUnpark == null) {
- toUnpark = new ArrayList<>();
- toUnpark.add(child);
- } else {
- toUnpark.add(child);
- }
- }
- }
- }
-
- // remove node from dependency map
- for (int currZ = minZ; currZ <= maxZ; ++currZ) {
- for (int currX = minX; currX <= maxX; ++currX) {
- // node: we only remove if we match, as a mismatch indicates a child node which of course has not
- // yet been unlocked
- this.nodesByPosition.remove(key(currX, currZ), node);
- }
- }
- }
-
- if (toUnpark == null) {
- return;
- }
-
- // we move the unpark / unlock logic here because we want to avoid performing work while holding the lock
-
- for (int i = 0, len = toUnpark.size(); i < len; ++i) {
- final Node toUnlock = toUnpark.get(i);
- toUnlock.unlocked = true; // must be volatile and before unpark()
- LockSupport.unpark(toUnlock.thread);
- }
- }
-
- public static final class Node {
-
- public final int x;
- public final int z;
- public final int radius;
- public final Thread thread;
-
- private List<Node> children;
- private ReferenceOpenHashSet<Node> parents;
-
- private volatile boolean unlocked;
-
- public Node(final int x, final int z, final int radius, final Thread thread) {
- this.x = x;
- this.z = z;
- this.radius = radius;
- this.thread = thread;
- }
- }
-}
diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java
index 8a5e3138c0f5e3f275c7352faf07bf39c04d00ca..daeed35877ffd0c110b2ec259030b038cf60cefb 100644
--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java
+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java
@@ -191,7 +191,7 @@ public final class ChunkTaskScheduler {
// it must be >= ticket propagator section shift so that the ticket propagator can assume that owning a position implies owning
// the entire section
// we just take the max, as we want the smallest shift that satifies these properties
- private static final int LOCK_SHIFT = ThreadedTicketLevelPropagator.SECTION_SHIFT;
+ private static final int LOCK_SHIFT = Math.max(io.papermc.paper.threadedregions.ThreadedTicketLevelPropagator.SECTION_SHIFT, io.papermc.paper.threadedregions.TickRegions.getRegionChunkShift()); // Folia - region threading
public static int getChunkSystemLockShift() {
return LOCK_SHIFT;
}
diff --git a/src/main/java/io/papermc/paper/threadedregions/RegionizedServer.java b/src/main/java/io/papermc/paper/threadedregions/RegionizedServer.java
index 6c1d55144f044f39926ddf998104950b9efe3ee1..8e31c6ee9ee16aff699e124a9b0554eaafa5c1ac 100644
--- a/src/main/java/io/papermc/paper/threadedregions/RegionizedServer.java
+++ b/src/main/java/io/papermc/paper/threadedregions/RegionizedServer.java
@@ -185,7 +185,96 @@ public final class RegionizedServer {
private long lastServerStatus;
private long tickCount;
+ /*
+ private final java.util.Random random = new java.util.Random(4L);
+ private final List<io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.SingleUserAreaMap<Void>> walkers =
+ new java.util.ArrayList<>();
+ static final int PLAYERS = 100;
+ static final int RAD_BLOCKS = 10000;
+ static final int RAD = RAD_BLOCKS >> 4;
+ static final int RAD_BIG_BLOCKS = 100_000;
+ static final int RAD_BIG = RAD_BIG_BLOCKS >> 4;
+ static final int VD = 4;
+ static final int BIG_PLAYERS = 50;
+ static final double WALK_CHANCE = 0.10;
+ static final double TP_CHANCE = 0.01;
+
+ private ServerLevel getWorld() {
+ return this.worlds.get(0);
+ }
+
+ private void init2() {
+ for (int i = 0; i < PLAYERS; ++i) {
+ int rad = i < BIG_PLAYERS ? RAD_BIG : RAD;
+ int posX = this.random.nextInt(-rad, rad + 1);
+ int posZ = this.random.nextInt(-rad, rad + 1);
+
+ io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.SingleUserAreaMap<Void> map = new io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.SingleUserAreaMap<>(null) {
+ @Override
+ protected void addCallback(Void parameter, int chunkX, int chunkZ) {
+ ServerLevel world = RegionizedServer.this.getWorld();
+ world.chunkTaskScheduler.chunkHolderManager.addTicketAtLevel(
+ net.minecraft.server.level.TicketType.PLAYER, chunkX, chunkZ, io.papermc.paper.chunk.system.scheduling.ChunkHolderManager.ENTITY_TICKING_TICKET_LEVEL, new net.minecraft.world.level.ChunkPos(posX, posZ)
+ );
+ }
+
+ @Override
+ protected void removeCallback(Void parameter, int chunkX, int chunkZ) {
+ ServerLevel world = RegionizedServer.this.getWorld();
+ world.chunkTaskScheduler.chunkHolderManager.removeTicketAtLevel(
+ net.minecraft.server.level.TicketType.PLAYER, chunkX, chunkZ, io.papermc.paper.chunk.system.scheduling.ChunkHolderManager.ENTITY_TICKING_TICKET_LEVEL, new net.minecraft.world.level.ChunkPos(posX, posZ)
+ );
+ }
+ };
+
+ map.add(posX, posZ, VD);
+
+ walkers.add(map);
+ }
+ }
+
+ private void randomWalk() {
+ if (this.walkers.isEmpty()) {
+ this.init2();
+ return;
+ }
+
+ for (int i = 0; i < PLAYERS; ++i) {
+ if (this.random.nextDouble() > WALK_CHANCE) {
+ continue;
+ }
+
+ io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.SingleUserAreaMap<Void> map = this.walkers.get(i);
+
+ int updateX = this.random.nextInt(-1, 2);
+ int updateZ = this.random.nextInt(-1, 2);
+
+ map.update(map.lastChunkX + updateX, map.lastChunkZ + updateZ, VD);
+ }
+
+ for (int i = 0; i < PLAYERS; ++i) {
+ if (random.nextDouble() >= TP_CHANCE) {
+ continue;
+ }
+
+ int rad = i < BIG_PLAYERS ? RAD_BIG : RAD;
+ int posX = random.nextInt(-rad, rad + 1);
+ int posZ = random.nextInt(-rad, rad + 1);
+
+ io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.SingleUserAreaMap<Void> map = walkers.get(i);
+
+ map.update(posX, posZ, VD);
+ }
+ }
+ */
+
private void globalTick(final int tickCount) {
+ /*
+ if (false) {
+ io.papermc.paper.threadedregions.ThreadedTicketLevelPropagator.main(null);
+ }
+ this.randomWalk();
+ */
++this.tickCount;
// expire invalid click command callbacks
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue((int)this.tickCount);
@@ -311,6 +400,8 @@ public final class RegionizedServer {
this.tickTime(world, tickCount);
world.updateTickData();
+
+ world.chunkTaskScheduler.chunkHolderManager.processTicketUpdates(); // Folia - use area based lock to reduce contention - required now to eventually process ticket updates
}
private void updateRaids(final ServerLevel world) {
diff --git a/src/main/java/io/papermc/paper/threadedregions/RegionizedTaskQueue.java b/src/main/java/io/papermc/paper/threadedregions/RegionizedTaskQueue.java
index 4a095e69584d7dbbefafe6e0a4a1a1090172ac9e..2e4514e5a45db6e625ef7799b63a9285a3bc1030 100644
--- a/src/main/java/io/papermc/paper/threadedregions/RegionizedTaskQueue.java
+++ b/src/main/java/io/papermc/paper/threadedregions/RegionizedTaskQueue.java
@@ -69,7 +69,7 @@ public final class RegionizedTaskQueue {
public static final class WorldRegionTaskData {
private final ServerLevel world;
private final MultiThreadedQueue<Runnable> globalChunkTask = new MultiThreadedQueue<>();
- private final SWMRLong2ObjectHashTable<AtomicLong> referenceCounters = new SWMRLong2ObjectHashTable<>();
+ private final java.util.concurrent.ConcurrentHashMap<io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkCoordinate, AtomicLong> referenceCounters = new java.util.concurrent.ConcurrentHashMap<>(); // Folia - use area based lock to reduce contention
public WorldRegionTaskData(final ServerLevel world) {
this.world = world;
@@ -115,17 +115,25 @@ public final class RegionizedTaskQueue {
);
}
+ // Folia start - use area based lock to reduce contention
+ private void processTicketUpdates(final long coord) {
+ this.world.chunkTaskScheduler.chunkHolderManager.processTicketUpdates(CoordinateUtils.getChunkX(coord), CoordinateUtils.getChunkZ(coord));
+ }
+ // Folia end - use area based lock to reduce contention
+
private void decrementReference(final AtomicLong reference, final long coord) {
final long val = reference.decrementAndGet();
if (val == 0L) {
- final ReentrantLock ticketLock = this.world.chunkTaskScheduler.chunkHolderManager.ticketLock;
- ticketLock.lock();
+ final int chunkX = CoordinateUtils.getChunkX(coord); // Folia - use area based lock to reduce contention
+ final int chunkZ = CoordinateUtils.getChunkZ(coord); // Folia - use area based lock to reduce contention
+ final io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkCoordinate key = new io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkCoordinate(coord); // Folia - use area based lock to reduce contention
+ final ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock.Node ticketLock = this.world.chunkTaskScheduler.chunkHolderManager.ticketLockArea.lock(chunkX, chunkZ); // Folia - use area based lock to reduce contention
try {
- if (this.referenceCounters.remove(coord, reference)) {
+ if (this.referenceCounters.remove(key, reference)) { // Folia - use area based lock to reduce contention
WorldRegionTaskData.this.removeTicket(coord);
} // else: race condition, something replaced our reference - not our issue anymore
} finally {
- ticketLock.unlock();
+ this.world.chunkTaskScheduler.chunkHolderManager.ticketLockArea.unlock(ticketLock); // Folia - use area based lock to reduce contention
}
} else if (val < 0L) {
throw new IllegalStateException("Reference count < 0: " + val);
@@ -133,7 +141,8 @@ public final class RegionizedTaskQueue {
}
private AtomicLong incrementReference(final long coord) {
- final AtomicLong ret = this.referenceCounters.get(coord);
+ final io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkCoordinate key = new io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkCoordinate(coord); // Folia - use area based lock to reduce contention
+ final AtomicLong ret = this.referenceCounters.get(key); // Folia - use area based lock to reduce contention
if (ret != null) {
// try to fast acquire counter
int failures = 0;
@@ -156,41 +165,54 @@ public final class RegionizedTaskQueue {
}
// slow acquire
- final ReentrantLock ticketLock = this.world.chunkTaskScheduler.chunkHolderManager.ticketLock;
- ticketLock.lock();
+ final int chunkX = CoordinateUtils.getChunkX(coord); // Folia - use area based lock to reduce contention
+ final int chunkZ = CoordinateUtils.getChunkZ(coord); // Folia - use area based lock to reduce contention
+ final ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock.Node ticketLock = this.world.chunkTaskScheduler.chunkHolderManager.ticketLockArea.lock(chunkX, chunkZ); // Folia - use area based lock to reduce contention
+ final AtomicLong ret2;
+ final boolean processTicketUpdates;
try {
final AtomicLong replace = new AtomicLong(1L);
- final AtomicLong valueInMap = this.referenceCounters.putIfAbsent(coord, replace);
+ final AtomicLong valueInMap = this.referenceCounters.putIfAbsent(key, replace); // Folia - use area based lock to reduce contention
if (valueInMap == null) {
// replaced, we should usually be here
this.addTicket(coord);
- return replace;
- } // else: need to attempt to acquire the reference
+ ret2 = replace;
+ processTicketUpdates = true;
+ } else {
+ processTicketUpdates = false;
+ int failures = 0;
+ for (long curr = valueInMap.get();;) {
+ if (curr == 0L) {
+ // don't need to add ticket here, since ticket is only removed during the lock
+ // we just need to replace the value in the map so that the thread removing fails and doesn't
+ // remove the ticket (see decrementReference)
+ this.referenceCounters.put(key, replace); // Folia - use area based lock to reduce contention
+ ret2 = replace;
+ break;
+ }
- int failures = 0;
- for (long curr = valueInMap.get();;) {
- if (curr == 0L) {
- // don't need to add ticket here, since ticket is only removed during the lock
- // we just need to replace the value in the map so that the thread removing fails and doesn't
- // remove the ticket (see decrementReference)
- this.referenceCounters.put(coord, replace);
- return replace;
- }
+ for (int i = 0; i < failures; ++i) {
+ ConcurrentUtil.backoff();
+ }
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
+ if (curr == (curr = valueInMap.compareAndExchange(curr, curr + 1L))) {
+ // acquired
+ ret2 = valueInMap;
+ break;
+ }
- if (curr == (curr = valueInMap.compareAndExchange(curr, curr + 1L))) {
- // acquired
- return valueInMap;
+ ++failures;
}
-
- ++failures;
}
} finally {
- ticketLock.unlock();
+ this.world.chunkTaskScheduler.chunkHolderManager.ticketLockArea.unlock(ticketLock); // Folia - use area based lock to reduce contention
+ }
+
+ if (processTicketUpdates) {
+ this.processTicketUpdates(coord);
}
+
+ return ret2;
}
}

View File

@ -9,7 +9,7 @@ raid before it's completion, it would throw an exception due to not being on the
same region thread anymore.
diff --git a/src/main/java/net/minecraft/world/entity/raid/Raid.java b/src/main/java/net/minecraft/world/entity/raid/Raid.java
index 359f1690497eac00899eb26c17308e0a6fe943ad..2289de057dd3c09cbe76f114e4bcc78b737bb6a3 100644
index 113583d0d9de744e314bc7ee15cb8e21ec4a92f9..ddec5251b335a25cb9d8726396d90d94572dbd6b 100644
--- a/src/main/java/net/minecraft/world/entity/raid/Raid.java
+++ b/src/main/java/net/minecraft/world/entity/raid/Raid.java
@@ -407,12 +407,25 @@ public class Raid {

View File

@ -1,110 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 15 May 2023 11:34:28 -0700
Subject: [PATCH] Mark POI/Entity load tasks as completed before releasing
scheduling lock
It must be marked as completed during that lock hold since the
waiters field is set to null. Thus, any other thread attempting
a cancellation will fail to remove from waiters. Also, any
other thread attempting to cancel may set the completed field
to true which would cause accept() to fail as well.
Completion was always designed to happen while holding the
scheduling lock to prevent these race conditions. The code
was originally set up to complete while not holding the
scheduling lock to avoid invoking callbacks while holding the
lock, however the access to the completion field was not
considered.
Resolve this by marking the callback as completed during the
lock, but invoking the accept() function after releasing
the lock. This will prevent any cancellation attempts to be
blocked, and allow the current thread to complete the callback
without any issues.
diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java
index 1ff6b138ccf4a1cefa719cd0b2b3af02d18a26fb..dc60f4af918c2bd73873cadfb69e5572173b8e46 100644
--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java
+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java
@@ -156,6 +156,12 @@ public final class NewChunkHolder {
LOGGER.error("Unhandled entity data load exception, data data will be lost: ", result.right());
}
+ // Folia start - mark these tasks as completed before releasing the scheduling lock
+ for (final GenericDataLoadTaskCallback callback : waiters) {
+ callback.markCompleted();
+ }
+ // Folia end - mark these tasks as completed before releasing the scheduling lock
+
completeWaiters = waiters;
} else {
// cancelled
@@ -187,7 +193,7 @@ public final class NewChunkHolder {
// avoid holding the scheduling lock while completing
if (completeWaiters != null) {
for (final GenericDataLoadTaskCallback callback : completeWaiters) {
- callback.accept(result);
+ callback.acceptCompleted(result); // Folia - mark these tasks as completed before releasing the scheduling lock
}
}
@@ -273,6 +279,12 @@ public final class NewChunkHolder {
LOGGER.error("Unhandled poi load exception, poi data will be lost: ", result.right());
}
+ // Folia start - mark these tasks as completed before releasing the scheduling lock
+ for (final GenericDataLoadTaskCallback callback : waiters) {
+ callback.markCompleted();
+ }
+ // Folia end - mark these tasks as completed before releasing the scheduling lock
+
completeWaiters = waiters;
} else {
// cancelled
@@ -304,7 +316,7 @@ public final class NewChunkHolder {
// avoid holding the scheduling lock while completing
if (completeWaiters != null) {
for (final GenericDataLoadTaskCallback callback : completeWaiters) {
- callback.accept(result);
+ callback.acceptCompleted(result); // Folia - mark these tasks as completed before releasing the scheduling lock
}
}
schedulingLock = this.scheduler.schedulingLockArea.lock(this.chunkX, this.chunkZ); // Folia - use area based lock to reduce contention
@@ -357,7 +369,7 @@ public final class NewChunkHolder {
}
}
- public static abstract class GenericDataLoadTaskCallback implements Cancellable, Consumer<GenericDataLoadTask.TaskResult<?, Throwable>> {
+ public static abstract class GenericDataLoadTaskCallback implements Cancellable { // Folia - mark callbacks as completed before unlocking scheduling lock
protected final Consumer<GenericDataLoadTask.TaskResult<?, Throwable>> consumer;
protected final NewChunkHolder chunkHolder;
@@ -393,13 +405,23 @@ public final class NewChunkHolder {
return this.completed = true;
}
- @Override
- public void accept(final GenericDataLoadTask.TaskResult<?, Throwable> result) {
+ // Folia start - mark callbacks as completed before unlocking scheduling lock
+ // must hold scheduling lock
+ void markCompleted() {
+ if (this.completed) {
+ throw new IllegalStateException("May not be completed here");
+ }
+ this.completed = true;
+ }
+ // Folia end - mark callbacks as completed before unlocking scheduling lock
+
+ // Folia - mark callbacks as completed before unlocking scheduling lock
+ void acceptCompleted(final GenericDataLoadTask.TaskResult<?, Throwable> result) {
if (result != null) {
- if (this.setCompleted()) {
+ if (this.completed) { // Folia - mark callbacks as completed before unlocking scheduling lock
this.consumer.accept(result);
} else {
- throw new IllegalStateException("Cannot be cancelled at this point");
+ throw new IllegalStateException("Cannot be uncompleted at this point"); // Folia - mark callbacks as completed before unlocking scheduling lock
}
} else {
throw new NullPointerException("Result cannot be null (cancelled)");

View File

@ -1,63 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 15 May 2023 12:24:17 -0700
Subject: [PATCH] Properly cancel chunk load tasks that were not scheduled
Since the chunk load task was not scheduled, the entity/poi load
task fields will not be set, but the task complete counter
will not be adjusted. Thus, the chunk load task will not complete.
To resolve this, detect when the entity/poi tasks were not scheduled
and decrement the task complete counter in such cases.
diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkLoadTask.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkLoadTask.java
index 7b8362625b48f1829ed4fd3c7fde6a4bec8e4099..be53c4b4a10463ef27f6fa178f17f92ca866e2e6 100644
--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkLoadTask.java
+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkLoadTask.java
@@ -126,7 +126,9 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
public void cancel() {
// must be before load task access, so we can synchronise with the writes to the fields
final ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock.Node schedulingLock = this.scheduler.schedulingLockArea.lock(this.chunkX, this.chunkZ); // Folia - use area based lock to reduce contention
+ final boolean scheduled; // Folia - fix cancellation of chunk load task
try {
+ scheduled = this.scheduled; // Folia - fix cancellation of chunk load task - must read field here, as it may be written later conucrrently - we need to know if we scheduled _before_ cancellation
this.cancelled = true;
} finally {
this.scheduler.schedulingLockArea.unlock(schedulingLock); // Folia - use area based lock to reduce contention
@@ -139,16 +141,29 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
the chunk load task attempts to complete with a non-null value
*/
- if (this.entityLoadTask != null) {
- if (this.entityLoadTask.cancel()) {
- this.tryCompleteLoad();
+ // Folia start - fix cancellation of chunk load task
+ if (scheduled) {
+ // since we scheduled, we need to cancel the tasks
+ if (this.entityLoadTask != null) {
+ if (this.entityLoadTask.cancel()) {
+ this.tryCompleteLoad();
+ }
}
- }
- if (this.poiLoadTask != null) {
- if (this.poiLoadTask.cancel()) {
- this.tryCompleteLoad();
+ if (this.poiLoadTask != null) {
+ if (this.poiLoadTask.cancel()) {
+ this.tryCompleteLoad();
+ }
}
+ } else {
+ // since nothing was scheduled, we need to decrement the task count here ourselves
+
+ // for entity load task
+ this.tryCompleteLoad();
+
+ // for poi load task
+ this.tryCompleteLoad();
}
+ // Folia end - fix cancellation of chunk load task
this.loadTask.cancel();
}

View File

@ -1,68 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 15 May 2023 20:31:36 -0700
Subject: [PATCH] Always recalculate light list on protochunk deserialize
I noticed during my stress testing that the total size of the
light list was far too large, which indicates many duplicates.
For me, this caused many GC problems which made stress testing
harder.
It turns out, it was possible for the light list recalculation
logic to occur _and_ the addition of the light list data from
the NBT data. Since there is no logic to de-duplicate this list,
every chunk load would re-add all light sources into the light
list and the light list would grow uncontrollably.
Since the recalculation logic would often run, I have
decided to solve this by discarding the data on disk and always
just calculating the list from the chunk data alone. Additionally,
I have applied an optimization from Vanilla 1.20 to avoid
searching sections without light sources by first checking the
palette for possible block sources.
Now my stress tests do not have issues with GC at all.
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
index 3c9ae42290c2e0cb70bb08e97f3ea2c3fb594c7d..200915f5accdb26c634cc77300a7d3275a8b1e0a 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
@@ -321,7 +321,7 @@ public class ChunkSerializer {
BelowZeroRetrogen belowzeroretrogen = protochunk.getBelowZeroRetrogen();
boolean flag5 = chunkstatus.isOrAfter(ChunkStatus.LIGHT) || belowzeroretrogen != null && belowzeroretrogen.targetStatus().isOrAfter(ChunkStatus.LIGHT);
- if (!flag) { // Paper - fix incorrect parsing of blocks that emit light - it should always parse it, unless the chunk is marked as lit
+ if (true) { // Paper - fix incorrect parsing of blocks that emit light - it should always parse it, unless the chunk is marked as lit // Folia - always recalculate light data to eliminate duplicates
// Paper start - let's make sure the implementation isn't as slow as possible
int offX = chunkPos.x << 4;
int offZ = chunkPos.z << 4;
@@ -332,7 +332,7 @@ public class ChunkSerializer {
LevelChunkSection[] sections = achunksection;
for (int sectionY = minChunkSection; sectionY <= maxChunkSection; ++sectionY) {
LevelChunkSection section = sections[sectionY - minChunkSection];
- if (section == null || section.hasOnlyAir()) {
+ if (section == null || section.hasOnlyAir() || !section.maybeHas((BlockState state) -> state.getLightEmission() > 0)) { // Folia - always recalculate light data to eliminate duplicates - skip sections that definitely have no sources
// no sources in empty sections
continue;
}
@@ -416,19 +416,7 @@ public class ChunkSerializer {
((ChunkAccess) object1).setBlockEntityNbt(nbttagcompound4);
}
- ListTag nbttaglist4 = nbt.getList("Lights", 9);
-
- for (int l1 = 0; l1 < nbttaglist4.size(); ++l1) {
- LevelChunkSection chunksection1 = achunksection[l1];
-
- if (chunksection1 != null && !chunksection1.hasOnlyAir()) {
- ListTag nbttaglist5 = nbttaglist4.getList(l1);
-
- for (int i2 = 0; i2 < nbttaglist5.size(); ++i2) {
- protochunk1.addLight(nbttaglist5.getShort(i2), l1);
- }
- }
- }
+ // Folia - always recalculate light data to eliminate duplicates
nbttagcompound4 = nbt.getCompound("CarvingMasks");
Iterator iterator2 = nbttagcompound4.getAllKeys().iterator();

0
rb.bat Normal file → Executable file
View File