mirror of
https://github.com/PaperMC/Folia.git
synced 2024-11-22 12:05:12 +01:00
Initial patch apply
This commit is contained in:
parent
bf7ba50dcd
commit
1b0a5071d0
@ -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"
|
||||
}
|
||||
|
||||
|
@ -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
0
gradlew.bat
vendored
Normal file → Executable file
0
install.bat
Normal file → Executable file
0
install.bat
Normal file → Executable 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
|
||||
|
@ -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
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
File diff suppressed because it is too large
Load Diff
@ -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
|
@ -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);
|
@ -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() {
|
@ -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 {
|
@ -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
|
@ -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() {
|
@ -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;
|
||||
}
|
@ -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) {
|
@ -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 {
|
||||
}
|
||||
|
||||
});
|
@ -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) {
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
File diff suppressed because it is too large
Load Diff
@ -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)");
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
Loading…
Reference in New Issue
Block a user