mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-27 06:50:12 +01:00
net/minecraft/server/level
This commit is contained in:
parent
0262d9a165
commit
35afd218f5
19 changed files with 3854 additions and 4329 deletions
|
@ -1,42 +1,20 @@
|
|||
--- a/net/minecraft/server/level/ChunkHolder.java
|
||||
+++ b/net/minecraft/server/level/ChunkHolder.java
|
||||
@@ -28,14 +28,18 @@
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
|
||||
+// CraftBukkit start
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+// CraftBukkit end
|
||||
+
|
||||
public class ChunkHolder extends GenerationChunkHolder {
|
||||
|
||||
@@ -33,9 +_,9 @@
|
||||
public static final ChunkResult<LevelChunk> UNLOADED_LEVEL_CHUNK = ChunkResult.error("Unloaded level chunk");
|
||||
private static final CompletableFuture<ChunkResult<LevelChunk>> UNLOADED_LEVEL_CHUNK_FUTURE = CompletableFuture.completedFuture(ChunkHolder.UNLOADED_LEVEL_CHUNK);
|
||||
private static final CompletableFuture<ChunkResult<LevelChunk>> UNLOADED_LEVEL_CHUNK_FUTURE = CompletableFuture.completedFuture(UNLOADED_LEVEL_CHUNK);
|
||||
private final LevelHeightAccessor levelHeightAccessor;
|
||||
- private volatile CompletableFuture<ChunkResult<LevelChunk>> fullChunkFuture;
|
||||
- private volatile CompletableFuture<ChunkResult<LevelChunk>> tickingChunkFuture;
|
||||
- private volatile CompletableFuture<ChunkResult<LevelChunk>> entityTickingChunkFuture;
|
||||
- private volatile CompletableFuture<ChunkResult<LevelChunk>> fullChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE;
|
||||
- private volatile CompletableFuture<ChunkResult<LevelChunk>> tickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE;
|
||||
- private volatile CompletableFuture<ChunkResult<LevelChunk>> entityTickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE;
|
||||
+ private volatile CompletableFuture<ChunkResult<LevelChunk>> fullChunkFuture; private int fullChunkCreateCount; private volatile boolean isFullChunkReady; // Paper - cache chunk ticking stage
|
||||
+ private volatile CompletableFuture<ChunkResult<LevelChunk>> tickingChunkFuture; private volatile boolean isTickingReady; // Paper - cache chunk ticking stage
|
||||
+ private volatile CompletableFuture<ChunkResult<LevelChunk>> entityTickingChunkFuture; private volatile boolean isEntityTickingReady; // Paper - cache chunk ticking stage
|
||||
public int oldTicketLevel;
|
||||
private int ticketLevel;
|
||||
private int queueLevel;
|
||||
@@ -58,9 +62,9 @@
|
||||
this.entityTickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
|
||||
this.blockChangedLightSectionFilter = new BitSet();
|
||||
this.skyChangedLightSectionFilter = new BitSet();
|
||||
- this.pendingFullStateConfirmation = CompletableFuture.completedFuture((Object) null);
|
||||
- this.sendSync = CompletableFuture.completedFuture((Object) null);
|
||||
- this.saveSync = CompletableFuture.completedFuture((Object) null);
|
||||
+ this.pendingFullStateConfirmation = CompletableFuture.completedFuture(null); // CraftBukkit - decompile error
|
||||
+ this.sendSync = CompletableFuture.completedFuture(null); // CraftBukkit - decompile error
|
||||
+ this.saveSync = CompletableFuture.completedFuture(null); // CraftBukkit - decompile error
|
||||
this.levelHeightAccessor = world;
|
||||
this.lightEngine = lightingProvider;
|
||||
this.onLevelChange = levelUpdateListener;
|
||||
@@ -72,6 +76,18 @@
|
||||
this.changedBlocksPerSection = new ShortSet[world.getSectionsCount()];
|
||||
@@ -71,6 +_,18 @@
|
||||
this.changedBlocksPerSection = new ShortSet[levelHeightAccessor.getSectionsCount()];
|
||||
}
|
||||
|
||||
+ // CraftBukkit start
|
||||
|
@ -54,64 +32,31 @@
|
|||
public CompletableFuture<ChunkResult<LevelChunk>> getTickingChunkFuture() {
|
||||
return this.tickingChunkFuture;
|
||||
}
|
||||
@@ -85,8 +101,8 @@
|
||||
}
|
||||
|
||||
@Nullable
|
||||
- public LevelChunk getTickingChunk() {
|
||||
- return (LevelChunk) ((ChunkResult) this.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).orElse((Object) null);
|
||||
+ public final LevelChunk getTickingChunk() { // Paper - final for inline
|
||||
+ return (LevelChunk) ((ChunkResult) this.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).orElse(null); // CraftBukkit - decompile error
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -138,6 +154,7 @@
|
||||
@@ -129,6 +_,7 @@
|
||||
} else {
|
||||
boolean flag = this.hasChangedSections;
|
||||
int i = this.levelHeightAccessor.getSectionIndex(pos.getY());
|
||||
|
||||
+ if (i < 0 || i >= this.changedBlocksPerSection.length) return false; // CraftBukkit - SPIGOT-6086, SPIGOT-6296
|
||||
if (this.changedBlocksPerSection[i] == null) {
|
||||
int sectionIndex = this.levelHeightAccessor.getSectionIndex(pos.getY());
|
||||
+ if (sectionIndex < 0 || sectionIndex >= this.changedBlocksPerSection.length) return false; // CraftBukkit - SPIGOT-6086, SPIGOT-6296
|
||||
if (this.changedBlocksPerSection[sectionIndex] == null) {
|
||||
this.hasChangedSections = true;
|
||||
this.changedBlocksPerSection[i] = new ShortOpenHashSet();
|
||||
@@ -224,8 +241,11 @@
|
||||
ClientboundSectionBlocksUpdatePacket packetplayoutmultiblockchange = new ClientboundSectionBlocksUpdatePacket(sectionposition, shortset, chunksection);
|
||||
|
||||
this.broadcast(list, packetplayoutmultiblockchange);
|
||||
+ // CraftBukkit start
|
||||
+ List finalList = list;
|
||||
packetplayoutmultiblockchange.runUpdates((blockposition1, iblockdata1) -> {
|
||||
- this.broadcastBlockEntityIfNeeded(list, world, blockposition1, iblockdata1);
|
||||
+ this.broadcastBlockEntityIfNeeded(finalList, world, blockposition1, iblockdata1);
|
||||
+ // CraftBukkit end
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -291,7 +311,7 @@
|
||||
this.pendingFullStateConfirmation = completablefuture1;
|
||||
chunkFuture.thenAccept((chunkresult) -> {
|
||||
chunkresult.ifSuccess((chunk) -> {
|
||||
- completablefuture1.complete((Object) null);
|
||||
+ completablefuture1.complete(null); // CraftBukkit - decompile error
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -301,6 +321,38 @@
|
||||
chunkLoadingManager.onFullChunkStatusChange(this.pos, target);
|
||||
this.changedBlocksPerSection[sectionIndex] = new ShortOpenHashSet();
|
||||
@@ -274,6 +_,38 @@
|
||||
chunkMap.onFullChunkStatusChange(this.pos, fullChunkStatus);
|
||||
}
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ // ChunkUnloadEvent: Called before the chunk is unloaded: isChunkLoaded is still true and chunk can still be modified by plugins.
|
||||
+ // SPIGOT-7780: Moved out of updateFutures to call all chunk unload events before calling updateHighestAllowedStatus for all chunks
|
||||
+ protected void callEventIfUnloading(ChunkMap playerchunkmap) {
|
||||
+ protected void callEventIfUnloading(ChunkMap chunkMap) {
|
||||
+ FullChunkStatus oldFullChunkStatus = ChunkLevel.fullStatus(this.oldTicketLevel);
|
||||
+ FullChunkStatus newFullChunkStatus = ChunkLevel.fullStatus(this.ticketLevel);
|
||||
+ boolean oldIsFull = oldFullChunkStatus.isOrAfter(FullChunkStatus.FULL);
|
||||
+ boolean newIsFull = newFullChunkStatus.isOrAfter(FullChunkStatus.FULL);
|
||||
+ if (oldIsFull && !newIsFull) {
|
||||
+ this.getFullChunkFuture().thenAccept((either) -> {
|
||||
+ LevelChunk chunk = (LevelChunk) either.orElse(null);
|
||||
+ LevelChunk chunk = either.orElse(null);
|
||||
+ if (chunk != null) {
|
||||
+ playerchunkmap.callbackExecutor.execute(() -> {
|
||||
+ chunkMap.callbackExecutor.execute(() -> {
|
||||
+ // Minecraft will apply the chunks tick lists to the world once the chunk got loaded, and then store the tick
|
||||
+ // lists again inside the chunk once the chunk becomes inaccessible and set the chunk's needsSaving flag.
|
||||
+ // These actions may however happen deferred, so we manually set the needsSaving flag already here.
|
||||
|
@ -121,26 +66,26 @@
|
|||
+ }
|
||||
+ }).exceptionally((throwable) -> {
|
||||
+ // ensure exceptions are printed, by default this is not the case
|
||||
+ MinecraftServer.LOGGER.error("Failed to schedule unload callback for chunk " + ChunkHolder.this.pos, throwable);
|
||||
+ net.minecraft.server.MinecraftServer.LOGGER.error("Failed to schedule unload callback for chunk " + ChunkHolder.this.pos, throwable);
|
||||
+ return null;
|
||||
+ });
|
||||
+
|
||||
+ // Run callback right away if the future was already done
|
||||
+ playerchunkmap.callbackExecutor.run();
|
||||
+ chunkMap.callbackExecutor.run();
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
protected void updateFutures(ChunkMap chunkLoadingManager, Executor executor) {
|
||||
FullChunkStatus fullchunkstatus = ChunkLevel.fullStatus(this.oldTicketLevel);
|
||||
FullChunkStatus fullchunkstatus1 = ChunkLevel.fullStatus(this.ticketLevel);
|
||||
@@ -309,12 +361,28 @@
|
||||
|
||||
this.wasAccessibleSinceLastSave |= flag1;
|
||||
if (!flag && flag1) {
|
||||
protected void updateFutures(ChunkMap chunkMap, Executor executor) {
|
||||
FullChunkStatus fullChunkStatus = ChunkLevel.fullStatus(this.oldTicketLevel);
|
||||
FullChunkStatus fullChunkStatus1 = ChunkLevel.fullStatus(this.ticketLevel);
|
||||
@@ -281,12 +_,28 @@
|
||||
boolean isOrAfter1 = fullChunkStatus1.isOrAfter(FullChunkStatus.FULL);
|
||||
this.wasAccessibleSinceLastSave |= isOrAfter1;
|
||||
if (!isOrAfter && isOrAfter1) {
|
||||
+ int expectCreateCount = ++this.fullChunkCreateCount; // Paper
|
||||
this.fullChunkFuture = chunkLoadingManager.prepareAccessibleChunk(this);
|
||||
this.scheduleFullChunkPromotion(chunkLoadingManager, this.fullChunkFuture, executor, FullChunkStatus.FULL);
|
||||
this.fullChunkFuture = chunkMap.prepareAccessibleChunk(this);
|
||||
this.scheduleFullChunkPromotion(chunkMap, this.fullChunkFuture, executor, FullChunkStatus.FULL);
|
||||
+ // Paper start - cache ticking ready status
|
||||
+ this.fullChunkFuture.thenAccept(chunkResult -> {
|
||||
+ chunkResult.ifSuccess(chunk -> {
|
||||
|
@ -154,19 +99,19 @@
|
|||
this.addSaveDependency(this.fullChunkFuture);
|
||||
}
|
||||
|
||||
if (flag && !flag1) {
|
||||
if (isOrAfter && !isOrAfter1) {
|
||||
+ // Paper start
|
||||
+ if (this.isFullChunkReady) {
|
||||
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.onChunkNotBorder(this.fullChunkFuture.join().orElseThrow(IllegalStateException::new), this); // Paper
|
||||
+ }
|
||||
+ // Paper end
|
||||
this.fullChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK);
|
||||
this.fullChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
|
||||
this.fullChunkFuture.complete(UNLOADED_LEVEL_CHUNK);
|
||||
this.fullChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE;
|
||||
}
|
||||
@@ -325,11 +393,25 @@
|
||||
if (!flag2 && flag3) {
|
||||
this.tickingChunkFuture = chunkLoadingManager.prepareTickingChunk(this);
|
||||
this.scheduleFullChunkPromotion(chunkLoadingManager, this.tickingChunkFuture, executor, FullChunkStatus.BLOCK_TICKING);
|
||||
@@ -296,11 +_,25 @@
|
||||
if (!isOrAfter2 && isOrAfter3) {
|
||||
this.tickingChunkFuture = chunkMap.prepareTickingChunk(this);
|
||||
this.scheduleFullChunkPromotion(chunkMap, this.tickingChunkFuture, executor, FullChunkStatus.BLOCK_TICKING);
|
||||
+ // Paper start - cache ticking ready status
|
||||
+ this.tickingChunkFuture.thenAccept(chunkResult -> {
|
||||
+ chunkResult.ifSuccess(chunk -> {
|
||||
|
@ -179,21 +124,21 @@
|
|||
this.addSaveDependency(this.tickingChunkFuture);
|
||||
}
|
||||
|
||||
if (flag2 && !flag3) {
|
||||
- this.tickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK);
|
||||
if (isOrAfter2 && !isOrAfter3) {
|
||||
- this.tickingChunkFuture.complete(UNLOADED_LEVEL_CHUNK);
|
||||
+ // Paper start
|
||||
+ if (this.isTickingReady) {
|
||||
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.onChunkNotTicking(this.tickingChunkFuture.join().orElseThrow(IllegalStateException::new), this); // Paper
|
||||
+ }
|
||||
+ // Paper end
|
||||
+ this.tickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); this.isTickingReady = false; // Paper - cache chunk ticking stage
|
||||
this.tickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
|
||||
this.tickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE;
|
||||
}
|
||||
|
||||
@@ -343,11 +425,24 @@
|
||||
@@ -313,11 +_,24 @@
|
||||
|
||||
this.entityTickingChunkFuture = chunkLoadingManager.prepareEntityTickingChunk(this);
|
||||
this.scheduleFullChunkPromotion(chunkLoadingManager, this.entityTickingChunkFuture, executor, FullChunkStatus.ENTITY_TICKING);
|
||||
this.entityTickingChunkFuture = chunkMap.prepareEntityTickingChunk(this);
|
||||
this.scheduleFullChunkPromotion(chunkMap, this.entityTickingChunkFuture, executor, FullChunkStatus.ENTITY_TICKING);
|
||||
+ // Paper start - cache ticking ready status
|
||||
+ this.entityTickingChunkFuture.thenAccept(chunkResult -> {
|
||||
+ chunkResult.ifSuccess(chunk -> {
|
||||
|
@ -205,39 +150,39 @@
|
|||
this.addSaveDependency(this.entityTickingChunkFuture);
|
||||
}
|
||||
|
||||
if (flag4 && !flag5) {
|
||||
- this.entityTickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK);
|
||||
if (isOrAfter4 && !isOrAfter5) {
|
||||
- this.entityTickingChunkFuture.complete(UNLOADED_LEVEL_CHUNK);
|
||||
+ // Paper start
|
||||
+ if (this.isEntityTickingReady) {
|
||||
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.onChunkNotEntityTicking(this.entityTickingChunkFuture.join().orElseThrow(IllegalStateException::new), this);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+ this.entityTickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); this.isEntityTickingReady = false; // Paper - cache chunk ticking stage
|
||||
this.entityTickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
|
||||
this.entityTickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE;
|
||||
}
|
||||
|
||||
@@ -357,6 +452,26 @@
|
||||
@@ -327,6 +_,26 @@
|
||||
|
||||
this.onLevelChange.onLevelChange(this.pos, this::getQueueLevel, this.ticketLevel, this::setQueueLevel);
|
||||
this.oldTicketLevel = this.ticketLevel;
|
||||
+ // CraftBukkit start
|
||||
+ // ChunkLoadEvent: Called after the chunk is loaded: isChunkLoaded returns true and chunk is ready to be modified by plugins.
|
||||
+ if (!fullchunkstatus.isOrAfter(FullChunkStatus.FULL) && fullchunkstatus1.isOrAfter(FullChunkStatus.FULL)) {
|
||||
+ if (!fullChunkStatus.isOrAfter(FullChunkStatus.FULL) && fullChunkStatus1.isOrAfter(FullChunkStatus.FULL)) {
|
||||
+ this.getFullChunkFuture().thenAccept((either) -> {
|
||||
+ LevelChunk chunk = (LevelChunk) either.orElse(null);
|
||||
+ if (chunk != null) {
|
||||
+ chunkLoadingManager.callbackExecutor.execute(() -> {
|
||||
+ chunkMap.callbackExecutor.execute(() -> {
|
||||
+ chunk.loadCallback();
|
||||
+ });
|
||||
+ }
|
||||
+ }).exceptionally((throwable) -> {
|
||||
+ // ensure exceptions are printed, by default this is not the case
|
||||
+ MinecraftServer.LOGGER.error("Failed to schedule load callback for chunk " + ChunkHolder.this.pos, throwable);
|
||||
+ net.minecraft.server.MinecraftServer.LOGGER.error("Failed to schedule load callback for chunk " + ChunkHolder.this.pos, throwable);
|
||||
+ return null;
|
||||
+ });
|
||||
+
|
||||
+ // Run callback right away if the future was already done
|
||||
+ chunkLoadingManager.callbackExecutor.run();
|
||||
+ chunkMap.callbackExecutor.run();
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
}
|
|
@ -0,0 +1,375 @@
|
|||
--- a/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -145,6 +_,33 @@
|
||||
public int serverViewDistance;
|
||||
private final WorldGenContext worldGenContext;
|
||||
|
||||
+ // CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback()
|
||||
+ public final CallbackExecutor callbackExecutor = new CallbackExecutor();
|
||||
+ public static final class CallbackExecutor implements java.util.concurrent.Executor, Runnable {
|
||||
+
|
||||
+ private final java.util.Queue<Runnable> queue = new java.util.ArrayDeque<>();
|
||||
+
|
||||
+ @Override
|
||||
+ public void execute(Runnable runnable) {
|
||||
+ this.queue.add(runnable);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void run() {
|
||||
+ Runnable task;
|
||||
+ while ((task = this.queue.poll()) != null) {
|
||||
+ task.run();
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
+ // Paper start
|
||||
+ public final ChunkHolder getUnloadingChunkHolder(int chunkX, int chunkZ) {
|
||||
+ return this.pendingUnloads.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public ChunkMap(
|
||||
ServerLevel level,
|
||||
LevelStorageSource.LevelStorageAccess levelStorageAccess,
|
||||
@@ -171,13 +_,19 @@
|
||||
this.level = level;
|
||||
RegistryAccess registryAccess = level.registryAccess();
|
||||
long seed = level.getSeed();
|
||||
- if (generator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
|
||||
+ // CraftBukkit start - SPIGOT-7051: It's a rigged game! Use delegate for random state creation, otherwise it is not so random.
|
||||
+ ChunkGenerator randomGenerator = generator;
|
||||
+ if (randomGenerator instanceof org.bukkit.craftbukkit.generator.CustomChunkGenerator customChunkGenerator) {
|
||||
+ randomGenerator = customChunkGenerator.getDelegate();
|
||||
+ }
|
||||
+ if (randomGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
|
||||
+ // CraftBukkit end
|
||||
this.randomState = RandomState.create(noiseBasedChunkGenerator.generatorSettings().value(), registryAccess.lookupOrThrow(Registries.NOISE), seed);
|
||||
} else {
|
||||
this.randomState = RandomState.create(NoiseGeneratorSettings.dummy(), registryAccess.lookupOrThrow(Registries.NOISE), seed);
|
||||
}
|
||||
|
||||
- this.chunkGeneratorState = generator.createState(registryAccess.lookupOrThrow(Registries.STRUCTURE_SET), this.randomState, seed);
|
||||
+ this.chunkGeneratorState = generator.createState(registryAccess.lookupOrThrow(Registries.STRUCTURE_SET), this.randomState, seed, level.spigotConfig); // Spigot
|
||||
this.mainThreadExecutor = mainThreadExecutor;
|
||||
ConsecutiveExecutor consecutiveExecutor = new ConsecutiveExecutor(dispatcher, "worldgen");
|
||||
this.progressListener = progressListener;
|
||||
@@ -207,6 +_,12 @@
|
||||
this.chunksToEagerlySave.add(chunkPos.toLong());
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
protected ChunkGenerator generator() {
|
||||
return this.worldGenContext.generator();
|
||||
}
|
||||
@@ -354,9 +_,9 @@
|
||||
}
|
||||
);
|
||||
stringBuilder.append("Updating:").append(System.lineSeparator());
|
||||
- this.updatingChunkMap.values().forEach(consumer);
|
||||
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.getUpdatingChunkHolders(this.level).forEach(consumer); // Paper
|
||||
stringBuilder.append("Visible:").append(System.lineSeparator());
|
||||
- this.visibleChunkMap.values().forEach(consumer);
|
||||
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level).forEach(consumer); // Paper
|
||||
CrashReport crashReport = CrashReport.forThrowable(exception, "Chunk loading");
|
||||
CrashReportCategory crashReportCategory = crashReport.addCategory("Chunk loading");
|
||||
crashReportCategory.setDetail("Details", details);
|
||||
@@ -392,6 +_,9 @@
|
||||
holder.setTicketLevel(newLevel);
|
||||
} else {
|
||||
holder = new ChunkHolder(new ChunkPos(chunkPos), newLevel, this.level, this.lightEngine, this::onLevelChange, this);
|
||||
+ // Paper start
|
||||
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.onChunkHolderCreate(this.level, holder);
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
this.updatingChunkMap.put(chunkPos, holder);
|
||||
@@ -420,8 +_,8 @@
|
||||
|
||||
protected void saveAllChunks(boolean flush) {
|
||||
if (flush) {
|
||||
- List<ChunkHolder> list = this.visibleChunkMap
|
||||
- .values()
|
||||
+ List<ChunkHolder> list = ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level) // Paper - moonrise
|
||||
+ //.values() // Paper - moonrise
|
||||
.stream()
|
||||
.filter(ChunkHolder::wasAccessibleSinceLastSave)
|
||||
.peek(ChunkHolder::refreshAccessibility)
|
||||
@@ -447,7 +_,7 @@
|
||||
this.nextChunkSaveTime.clear();
|
||||
long millis = Util.getMillis();
|
||||
|
||||
- for (ChunkHolder chunkHolder : this.visibleChunkMap.values()) {
|
||||
+ for (ChunkHolder chunkHolder : ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level)) { // Paper
|
||||
this.saveChunkIfNeeded(chunkHolder, millis);
|
||||
}
|
||||
}
|
||||
@@ -468,6 +_,7 @@
|
||||
public boolean hasWork() {
|
||||
return this.lightEngine.hasLightWork()
|
||||
|| !this.pendingUnloads.isEmpty()
|
||||
+ || ca.spottedleaf.moonrise.common.util.ChunkSystem.hasAnyChunkHolders(this.level) // Paper - moonrise
|
||||
|| !this.updatingChunkMap.isEmpty()
|
||||
|| this.poiManager.hasWork()
|
||||
|| !this.toDrop.isEmpty()
|
||||
@@ -526,7 +_,11 @@
|
||||
this.scheduleUnload(chunkPos, chunkHolder);
|
||||
} else {
|
||||
ChunkAccess latestChunk = chunkHolder.getLatestChunk();
|
||||
- if (this.pendingUnloads.remove(chunkPos, chunkHolder) && latestChunk != null) {
|
||||
+ // Paper start
|
||||
+ boolean removed;
|
||||
+ if ((removed = this.pendingUnloads.remove(chunkPos, chunkHolder)) && latestChunk != null) {
|
||||
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.onChunkHolderDelete(this.level, chunkHolder);
|
||||
+ // Paper end
|
||||
if (latestChunk instanceof LevelChunk levelChunk) {
|
||||
levelChunk.setLoaded(false);
|
||||
}
|
||||
@@ -540,7 +_,9 @@
|
||||
this.lightEngine.tryScheduleUpdate();
|
||||
this.progressListener.onStatusChange(latestChunk.getPos(), null);
|
||||
this.nextChunkSaveTime.remove(latestChunk.getPos().toLong());
|
||||
- }
|
||||
+ } else if (removed) { // Paper start
|
||||
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.onChunkHolderDelete(this.level, chunkHolder);
|
||||
+ } // Paper end
|
||||
}
|
||||
}, this.unloadQueue::add).whenComplete((_void, error) -> {
|
||||
if (error != null) {
|
||||
@@ -818,7 +_,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- protected void setServerViewDistance(int viewDistance) {
|
||||
+ public void setServerViewDistance(int viewDistance) { // Paper - publi
|
||||
int i = Mth.clamp(viewDistance, 2, 32);
|
||||
if (i != this.serverViewDistance) {
|
||||
this.serverViewDistance = i;
|
||||
@@ -856,7 +_,7 @@
|
||||
}
|
||||
|
||||
public int size() {
|
||||
- return this.visibleChunkMap.size();
|
||||
+ return ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolderCount(this.level); // Paper
|
||||
}
|
||||
|
||||
public net.minecraft.server.level.DistanceManager getDistanceManager() {
|
||||
@@ -864,7 +_,7 @@
|
||||
}
|
||||
|
||||
protected Iterable<ChunkHolder> getChunks() {
|
||||
- return Iterables.unmodifiableIterable(this.visibleChunkMap.values());
|
||||
+ return Iterables.unmodifiableIterable(ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level)); // Paper
|
||||
}
|
||||
|
||||
void dumpChunks(Writer writer) throws IOException {
|
||||
@@ -888,10 +_,10 @@
|
||||
.build(writer);
|
||||
TickingTracker tickingTracker = this.distanceManager.tickingTracker();
|
||||
|
||||
- for (Entry<ChunkHolder> entry : this.visibleChunkMap.long2ObjectEntrySet()) {
|
||||
- long longKey = entry.getLongKey();
|
||||
+ for (ChunkHolder entry : ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level)) { // Paper - Moonrise
|
||||
+ long longKey = entry.pos.toLong(); // Paper - Moonrise
|
||||
ChunkPos chunkPos = new ChunkPos(longKey);
|
||||
- ChunkHolder chunkHolder = entry.getValue();
|
||||
+ ChunkHolder chunkHolder = entry; // Paper - Moonrise
|
||||
Optional<ChunkAccess> optional = Optional.ofNullable(chunkHolder.getLatestChunk());
|
||||
Optional<LevelChunk> optional1 = optional.flatMap(chunk -> chunk instanceof LevelChunk ? Optional.of((LevelChunk)chunk) : Optional.empty());
|
||||
csvOutput.writeRow(
|
||||
@@ -931,11 +_,13 @@
|
||||
}
|
||||
|
||||
private CompletableFuture<Optional<CompoundTag>> readChunk(ChunkPos pos) {
|
||||
- return this.read(pos).thenApplyAsync(optional -> optional.map(this::upgradeChunkTag), Util.backgroundExecutor().forName("upgradeChunk"));
|
||||
+ return this.read(pos).thenApplyAsync(optional -> optional.map(tag -> upgradeChunkTag(tag, pos)), Util.backgroundExecutor().forName("upgradeChunk")); // CraftBukkit
|
||||
}
|
||||
|
||||
- private CompoundTag upgradeChunkTag(CompoundTag tag) {
|
||||
- return this.upgradeChunkTag(this.level.dimension(), this.overworldDataStorage, tag, this.generator().getTypeNameForDataFixer());
|
||||
+ // CraftBukkit start
|
||||
+ private CompoundTag upgradeChunkTag(CompoundTag tag, ChunkPos pos) {
|
||||
+ return this.upgradeChunkTag(this.level.dimension(), this.overworldDataStorage, tag, this.generator().getTypeNameForDataFixer(), pos, this.level);
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
|
||||
void forEachSpawnCandidateChunk(Consumer<ChunkHolder> action) {
|
||||
@@ -951,12 +_,34 @@
|
||||
}
|
||||
|
||||
public boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkPos) {
|
||||
- return this.distanceManager.hasPlayersNearby(chunkPos.toLong()) && this.anyPlayerCloseEnoughForSpawningInternal(chunkPos);
|
||||
+ // Spigot start
|
||||
+ return this.anyPlayerCloseEnoughForSpawning(chunkPos, false);
|
||||
+ }
|
||||
+
|
||||
+ boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkPos, boolean reducedRange) {
|
||||
+ return this.distanceManager.hasPlayersNearby(chunkPos.toLong()) && this.anyPlayerCloseEnoughForSpawningInternal(chunkPos, reducedRange);
|
||||
+ // Spigot end
|
||||
}
|
||||
|
||||
private boolean anyPlayerCloseEnoughForSpawningInternal(ChunkPos chunkPos) {
|
||||
+ // Spigot start
|
||||
+ return this.anyPlayerCloseEnoughForSpawningInternal(chunkPos, false);
|
||||
+ }
|
||||
+
|
||||
+ private boolean anyPlayerCloseEnoughForSpawningInternal(ChunkPos chunkPos, boolean reducedRange) {
|
||||
+ double blockRange; // Paper - use from event
|
||||
+ // Spigot end
|
||||
for (ServerPlayer serverPlayer : this.playerMap.getAllPlayers()) {
|
||||
- if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos)) {
|
||||
+ // Paper start - PlayerNaturallySpawnCreaturesEvent
|
||||
+ com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event;
|
||||
+ blockRange = 16384.0D;
|
||||
+ if (reducedRange) {
|
||||
+ event = serverPlayer.playerNaturallySpawnedEvent;
|
||||
+ if (event == null || event.isCancelled()) continue;
|
||||
+ blockRange = (double) ((event.getSpawnRadius() << 4) * (event.getSpawnRadius() << 4));
|
||||
+ }
|
||||
+ // Paper end - PlayerNaturallySpawnCreaturesEvent
|
||||
+ if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos, blockRange)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -972,7 +_,7 @@
|
||||
Builder<ServerPlayer> builder = ImmutableList.builder();
|
||||
|
||||
for (ServerPlayer serverPlayer : this.playerMap.getAllPlayers()) {
|
||||
- if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos)) {
|
||||
+ if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos, 16384.0D)) { // Spigot
|
||||
builder.add(serverPlayer);
|
||||
}
|
||||
}
|
||||
@@ -981,12 +_,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
- private boolean playerIsCloseEnoughForSpawning(ServerPlayer player, ChunkPos chunkPos) {
|
||||
+ private boolean playerIsCloseEnoughForSpawning(ServerPlayer player, ChunkPos chunkPos, double range) { // Spigot
|
||||
if (player.isSpectator()) {
|
||||
return false;
|
||||
} else {
|
||||
double d = euclideanDistanceSquared(chunkPos, player);
|
||||
- return d < 16384.0;
|
||||
+ return d < range; // Spigot
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1100,9 +_,19 @@
|
||||
}
|
||||
|
||||
public void addEntity(Entity entity) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot
|
||||
+ // Paper start - ignore and warn about illegal addEntity calls instead of crashing server
|
||||
+ if (!entity.valid || entity.level() != this.level || this.entityMap.containsKey(entity.getId())) {
|
||||
+ LOGGER.error("Illegal ChunkMap::addEntity for world " + this.level.getWorld().getName()
|
||||
+ + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""), new Throwable());
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - ignore and warn about illegal addEntity calls instead of crashing server
|
||||
+ if (entity instanceof ServerPlayer && ((ServerPlayer) entity).supressTrackerForLogin) return; // Paper - Fire PlayerJoinEvent when Player is actually ready; Delayadding to tracker until after list packets
|
||||
if (!(entity instanceof EnderDragonPart)) {
|
||||
EntityType<?> type = entity.getType();
|
||||
int i = type.clientTrackingRange() * 16;
|
||||
+ i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot
|
||||
if (i != 0) {
|
||||
int updateInterval = type.updateInterval();
|
||||
if (this.entityMap.containsKey(entity.getId())) {
|
||||
@@ -1126,6 +_,7 @@
|
||||
}
|
||||
|
||||
protected void removeEntity(Entity entity) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("entity untrack"); // Spigot
|
||||
if (entity instanceof ServerPlayer serverPlayer) {
|
||||
this.updatePlayerStatus(serverPlayer, false);
|
||||
|
||||
@@ -1230,7 +_,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
- class DistanceManager extends net.minecraft.server.level.DistanceManager {
|
||||
+ public class DistanceManager extends net.minecraft.server.level.DistanceManager { // Paper - public
|
||||
protected DistanceManager(final Executor dispatcher, final Executor mainThreadExecutor) {
|
||||
super(dispatcher, mainThreadExecutor);
|
||||
}
|
||||
@@ -1258,10 +_,10 @@
|
||||
final Entity entity;
|
||||
private final int range;
|
||||
SectionPos lastSectionPos;
|
||||
- public final Set<ServerPlayerConnection> seenBy = Sets.newIdentityHashSet();
|
||||
+ public final Set<ServerPlayerConnection> seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl
|
||||
|
||||
public TrackedEntity(final Entity entity, final int range, final int updateInterval, final boolean trackDelta) {
|
||||
- this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, updateInterval, trackDelta, this::broadcast);
|
||||
+ this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, updateInterval, trackDelta, this::broadcast, this.seenBy); // CraftBukkit
|
||||
this.entity = entity;
|
||||
this.range = range;
|
||||
this.lastSectionPos = SectionPos.of(entity);
|
||||
@@ -1297,24 +_,47 @@
|
||||
}
|
||||
|
||||
public void removePlayer(ServerPlayer player) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot
|
||||
if (this.seenBy.remove(player.connection)) {
|
||||
this.serverEntity.removePairing(player);
|
||||
}
|
||||
}
|
||||
|
||||
public void updatePlayer(ServerPlayer player) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
|
||||
if (player != this.entity) {
|
||||
- Vec3 vec3 = player.position().subtract(this.entity.position());
|
||||
+ // Paper start - remove allocation of Vec3D here
|
||||
+ // Vec3 vec3d = player.position().subtract(this.entity.position());
|
||||
+ double vec3d_dx = player.getX() - this.entity.getX();
|
||||
+ double vec3d_dz = player.getZ() - this.entity.getZ();
|
||||
+ // Paper end - remove allocation of Vec3D here
|
||||
int playerViewDistance = ChunkMap.this.getPlayerViewDistance(player);
|
||||
double d = Math.min(this.getEffectiveRange(), playerViewDistance * 16);
|
||||
- double d1 = vec3.x * vec3.x + vec3.z * vec3.z;
|
||||
+ double d1 = vec3d_dx * vec3d_dx + vec3d_dz * vec3d_dz; // Paper
|
||||
double d2 = d * d;
|
||||
- boolean flag = d1 <= d2
|
||||
- && this.entity.broadcastToPlayer(player)
|
||||
- && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z);
|
||||
+ // Paper start - Configurable entity tracking range by Y
|
||||
+ boolean flag = d1 <= d2;
|
||||
+ if (flag && level.paperConfig().entities.trackingRangeY.enabled) {
|
||||
+ double rangeY = level.paperConfig().entities.trackingRangeY.get(this.entity, -1);
|
||||
+ if (rangeY != -1) {
|
||||
+ double vec3d_dy = player.getY() - this.entity.getY();
|
||||
+ flag = vec3d_dy * vec3d_dy <= rangeY * rangeY;
|
||||
+ }
|
||||
+ }
|
||||
+ flag = flag && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z);
|
||||
+ // Paper end - Configurable entity tracking range by Y
|
||||
+ // CraftBukkit start - respect vanish API
|
||||
+ if (flag && !player.getBukkitEntity().canSee(this.entity.getBukkitEntity())) { // Paper - only consider hits
|
||||
+ flag = false;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
if (flag) {
|
||||
if (this.seenBy.add(player.connection)) {
|
||||
+ // Paper start - entity tracking events
|
||||
+ if (io.papermc.paper.event.player.PlayerTrackEntityEvent.getHandlerList().getRegisteredListeners().length == 0 || new io.papermc.paper.event.player.PlayerTrackEntityEvent(player.getBukkitEntity(), this.entity.getBukkitEntity()).callEvent()) {
|
||||
this.serverEntity.addPairing(player);
|
||||
+ }
|
||||
+ // Paper end - entity tracking events
|
||||
}
|
||||
} else if (this.seenBy.remove(player.connection)) {
|
||||
this.serverEntity.removePairing(player);
|
||||
@@ -1331,6 +_,7 @@
|
||||
|
||||
for (Entity entity : this.entity.getIndirectPassengers()) {
|
||||
int i1 = entity.getType().clientTrackingRange() * 16;
|
||||
+ i1 = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i1); // Paper
|
||||
if (i1 > i) {
|
||||
i = i1;
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
--- a/net/minecraft/server/level/DistanceManager.java
|
||||
+++ b/net/minecraft/server/level/DistanceManager.java
|
||||
@@ -107,6 +_,12 @@
|
||||
}
|
||||
|
||||
if (!this.chunksToUpdateFutures.isEmpty()) {
|
||||
+ // CraftBukkit start - SPIGOT-7780: Call chunk unload events before updateHighestAllowedStatus
|
||||
+ for (final ChunkHolder chunkHolder : this.chunksToUpdateFutures) {
|
||||
+ chunkHolder.callEventIfUnloading(chunkMap);
|
||||
+ }
|
||||
+ // CraftBukkit end - SPIGOT-7780: Call chunk unload events before updateHighestAllowedStatus
|
||||
+
|
||||
for (ChunkHolder chunkHolder : this.chunksToUpdateFutures) {
|
||||
chunkHolder.updateHighestAllowedStatus(chunkMap);
|
||||
}
|
||||
@@ -143,7 +_,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- void addTicket(long chunkPos, Ticket<?> ticket) {
|
||||
+ boolean addTicket(long chunkPos, Ticket<?> ticket) { // CraftBukkit - void -> boolean
|
||||
SortedArraySet<Ticket<?>> tickets = this.getTickets(chunkPos);
|
||||
int ticketLevelAt = getTicketLevelAt(tickets);
|
||||
Ticket<?> ticket1 = tickets.addOrGet(ticket);
|
||||
@@ -151,11 +_,14 @@
|
||||
if (ticket.getTicketLevel() < ticketLevelAt) {
|
||||
this.ticketTracker.update(chunkPos, ticket.getTicketLevel(), true);
|
||||
}
|
||||
+ return ticket == ticket1; // CraftBukkit
|
||||
}
|
||||
|
||||
- void removeTicket(long chunkPos, Ticket<?> ticket) {
|
||||
+ boolean removeTicket(long chunkPos, Ticket<?> ticket) { // CraftBukkit - void -> boolean
|
||||
SortedArraySet<Ticket<?>> tickets = this.getTickets(chunkPos);
|
||||
+ boolean removed = false; // CraftBukkit
|
||||
if (tickets.remove(ticket)) {
|
||||
+ removed = true; // CraftBukkit
|
||||
}
|
||||
|
||||
if (tickets.isEmpty()) {
|
||||
@@ -163,6 +_,7 @@
|
||||
}
|
||||
|
||||
this.ticketTracker.update(chunkPos, getTicketLevelAt(tickets), false);
|
||||
+ return removed; // CraftBukkit
|
||||
}
|
||||
|
||||
public <T> void addTicket(TicketType<T> type, ChunkPos pos, int level, T value) {
|
||||
@@ -175,17 +_,29 @@
|
||||
}
|
||||
|
||||
public <T> void addRegionTicket(TicketType<T> type, ChunkPos pos, int distance, T value) {
|
||||
+ // CraftBukkit start
|
||||
+ this.addRegionTicketAtDistance(type, pos, distance, value);
|
||||
+ }
|
||||
+ public <T> boolean addRegionTicketAtDistance(TicketType<T> type, ChunkPos pos, int distance, T value) {
|
||||
+ // CraftBukkit end
|
||||
Ticket<T> ticket = new Ticket<>(type, ChunkLevel.byStatus(FullChunkStatus.FULL) - distance, value);
|
||||
long packedChunkPos = pos.toLong();
|
||||
- this.addTicket(packedChunkPos, ticket);
|
||||
+ final boolean addded = this.addTicket(packedChunkPos, ticket); // CraftBukkit
|
||||
this.tickingTicketsTracker.addTicket(packedChunkPos, ticket);
|
||||
+ return addded; // CraftBukkit
|
||||
}
|
||||
|
||||
public <T> void removeRegionTicket(TicketType<T> type, ChunkPos pos, int distance, T value) {
|
||||
+ // CraftBukkit start
|
||||
+ removeRegionTicketAtDistance(type, pos, distance, value);
|
||||
+ }
|
||||
+ public <T> boolean removeRegionTicketAtDistance(TicketType<T> type, ChunkPos pos, int distance, T value) {
|
||||
+ // CraftBukkit end
|
||||
Ticket<T> ticket = new Ticket<>(type, ChunkLevel.byStatus(FullChunkStatus.FULL) - distance, value);
|
||||
long packedChunkPos = pos.toLong();
|
||||
- this.removeTicket(packedChunkPos, ticket);
|
||||
+ final boolean removed = this.removeTicket(packedChunkPos, ticket); // CraftBukkit
|
||||
this.tickingTicketsTracker.removeTicket(packedChunkPos, ticket);
|
||||
+ return removed; // CraftBukkit
|
||||
}
|
||||
|
||||
private SortedArraySet<Ticket<?>> getTickets(long chunkPos) {
|
||||
@@ -217,8 +_,9 @@
|
||||
ChunkPos chunkPos = sectionPos.chunk();
|
||||
long packedChunkPos = chunkPos.toLong();
|
||||
ObjectSet<ServerPlayer> set = this.playersPerChunk.get(packedChunkPos);
|
||||
- set.remove(player);
|
||||
- if (set.isEmpty()) {
|
||||
+ if (set == null) return; // CraftBukkit - SPIGOT-6208
|
||||
+ if (set != null) set.remove(player); // Paper - some state corruption happens here, don't crash, clean up gracefully
|
||||
+ if (set == null || set.isEmpty()) { // Paper
|
||||
this.playersPerChunk.remove(packedChunkPos);
|
||||
this.naturalSpawnChunkCounter.update(packedChunkPos, Integer.MAX_VALUE, false);
|
||||
this.playerTicketManager.update(packedChunkPos, Integer.MAX_VALUE, false);
|
||||
@@ -299,7 +_,7 @@
|
||||
}
|
||||
|
||||
public void removeTicketsOnClosing() {
|
||||
- ImmutableSet<TicketType<?>> set = ImmutableSet.of(TicketType.UNKNOWN);
|
||||
+ ImmutableSet<TicketType<?>> set = ImmutableSet.of(TicketType.UNKNOWN, TicketType.POST_TELEPORT, TicketType.FUTURE_AWAIT); // Paper - add additional tickets to preserve
|
||||
ObjectIterator<Entry<SortedArraySet<Ticket<?>>>> objectIterator = this.tickets.long2ObjectEntrySet().fastIterator();
|
||||
|
||||
while (objectIterator.hasNext()) {
|
||||
@@ -329,6 +_,26 @@
|
||||
public boolean hasTickets() {
|
||||
return !this.tickets.isEmpty();
|
||||
}
|
||||
+
|
||||
+ // CraftBukkit start
|
||||
+ public <T> void removeAllTicketsFor(TicketType<T> ticketType, int ticketLevel, T ticketIdentifier) {
|
||||
+ Ticket<T> target = new Ticket<>(ticketType, ticketLevel, ticketIdentifier);
|
||||
+
|
||||
+ for (java.util.Iterator<Entry<SortedArraySet<Ticket<?>>>> iterator = this.tickets.long2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
|
||||
+ Entry<SortedArraySet<Ticket<?>>> entry = iterator.next();
|
||||
+ SortedArraySet<Ticket<?>> tickets = entry.getValue();
|
||||
+ if (tickets.remove(target)) {
|
||||
+ // copied from removeTicket
|
||||
+ this.ticketTracker.update(entry.getLongKey(), DistanceManager.getTicketLevelAt(tickets), false);
|
||||
+
|
||||
+ // can't use entry after it's removed
|
||||
+ if (tickets.isEmpty()) {
|
||||
+ iterator.remove();
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
|
||||
class ChunkTicketTracker extends ChunkTracker {
|
||||
private static final int MAX_LEVEL = ChunkLevel.MAX_LEVEL + 1;
|
|
@ -1,6 +1,6 @@
|
|||
--- a/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -74,6 +74,13 @@
|
||||
@@ -73,6 +_,13 @@
|
||||
@Nullable
|
||||
@VisibleForDebug
|
||||
private NaturalSpawner.SpawnState lastSpawnState;
|
||||
|
@ -12,9 +12,9 @@
|
|||
+ long chunkFutureAwaitCounter;
|
||||
+ // Paper end
|
||||
|
||||
public ServerChunkCache(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor workerExecutor, ChunkGenerator chunkGenerator, int viewDistance, int simulationDistance, boolean dsync, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory) {
|
||||
this.level = world;
|
||||
@@ -95,6 +102,64 @@
|
||||
public ServerChunkCache(
|
||||
ServerLevel level,
|
||||
@@ -121,6 +_,64 @@
|
||||
this.clearCache();
|
||||
}
|
||||
|
||||
|
@ -79,53 +79,50 @@
|
|||
@Override
|
||||
public ThreadedLevelLightEngine getLightEngine() {
|
||||
return this.lightEngine;
|
||||
@@ -138,7 +203,7 @@
|
||||
if (k == this.lastChunkPos[l] && leastStatus == this.lastChunkStatus[l]) {
|
||||
ChunkAccess ichunkaccess = this.lastChunk[l];
|
||||
|
||||
- if (ichunkaccess != null || !create) {
|
||||
+ if (ichunkaccess != null) { // CraftBukkit - the chunk can become accessible in the meantime TODO for non-null chunks it might also make sense to check that the chunk's state hasn't changed in the meantime
|
||||
return ichunkaccess;
|
||||
@@ -160,7 +_,7 @@
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (packedChunkPos == this.lastChunkPos[i] && chunkStatus == this.lastChunkStatus[i]) {
|
||||
ChunkAccess chunkAccess = this.lastChunk[i];
|
||||
- if (chunkAccess != null || !requireChunk) {
|
||||
+ if (chunkAccess != null) { // CraftBukkit - the chunk can become accessible in the meantime TODO for non-null chunks it might also make sense to check that the chunk's state hasn't changed in the meantime
|
||||
return chunkAccess;
|
||||
}
|
||||
}
|
||||
@@ -150,8 +215,9 @@
|
||||
|
||||
Objects.requireNonNull(completablefuture);
|
||||
chunkproviderserver_b.managedBlock(completablefuture::isDone);
|
||||
@@ -169,6 +_,7 @@
|
||||
profilerFiller.incrementCounter("getChunkCacheMiss");
|
||||
CompletableFuture<ChunkResult<ChunkAccess>> chunkFutureMainThread = this.getChunkFutureMainThread(x, z, chunkStatus, requireChunk);
|
||||
this.mainThreadProcessor.managedBlock(chunkFutureMainThread::isDone);
|
||||
+ // com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x, z); // Paper - Add debug for sync chunk loads
|
||||
ChunkResult<ChunkAccess> chunkresult = (ChunkResult) completablefuture.join();
|
||||
- ChunkAccess ichunkaccess1 = (ChunkAccess) chunkresult.orElse((Object) null);
|
||||
+ ChunkAccess ichunkaccess1 = (ChunkAccess) chunkresult.orElse(null); // CraftBukkit - decompile error
|
||||
|
||||
if (ichunkaccess1 == null && create) {
|
||||
throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("Chunk not there when requested: " + chunkresult.getError()));
|
||||
@@ -231,7 +297,15 @@
|
||||
int l = ChunkLevel.byStatus(leastStatus);
|
||||
ChunkHolder playerchunk = this.getVisibleChunkIfPresent(k);
|
||||
|
||||
- if (create) {
|
||||
ChunkResult<ChunkAccess> chunkResult = chunkFutureMainThread.join();
|
||||
ChunkAccess chunkAccess1 = chunkResult.orElse(null);
|
||||
if (chunkAccess1 == null && requireChunk) {
|
||||
@@ -240,7 +_,15 @@
|
||||
long packedChunkPos = chunkPos.toLong();
|
||||
int i = ChunkLevel.byStatus(chunkStatus);
|
||||
ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(packedChunkPos);
|
||||
- if (requireChunk) {
|
||||
+ // CraftBukkit start - don't add new ticket for currently unloading chunk
|
||||
+ boolean currentlyUnloading = false;
|
||||
+ if (playerchunk != null) {
|
||||
+ FullChunkStatus oldChunkState = ChunkLevel.fullStatus(playerchunk.oldTicketLevel);
|
||||
+ FullChunkStatus currentChunkState = ChunkLevel.fullStatus(playerchunk.getTicketLevel());
|
||||
+ if (visibleChunkIfPresent != null) {
|
||||
+ FullChunkStatus oldChunkState = ChunkLevel.fullStatus(visibleChunkIfPresent.oldTicketLevel);
|
||||
+ FullChunkStatus currentChunkState = ChunkLevel.fullStatus(visibleChunkIfPresent.getTicketLevel());
|
||||
+ currentlyUnloading = (oldChunkState.isOrAfter(FullChunkStatus.FULL) && !currentChunkState.isOrAfter(FullChunkStatus.FULL));
|
||||
+ }
|
||||
+ if (create && !currentlyUnloading) {
|
||||
+ // CraftBukkit end
|
||||
this.distanceManager.addTicket(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair);
|
||||
if (this.chunkAbsent(playerchunk, l)) {
|
||||
ProfilerFiller gameprofilerfiller = Profiler.get();
|
||||
@@ -250,7 +324,7 @@
|
||||
+ if (requireChunk && !currentlyUnloading) {
|
||||
+ // CraftBukkit end
|
||||
this.distanceManager.addTicket(TicketType.UNKNOWN, chunkPos, i, chunkPos);
|
||||
if (this.chunkAbsent(visibleChunkIfPresent, i)) {
|
||||
ProfilerFiller profilerFiller = Profiler.get();
|
||||
@@ -260,7 +_,7 @@
|
||||
}
|
||||
|
||||
private boolean chunkAbsent(@Nullable ChunkHolder holder, int maxLevel) {
|
||||
- return holder == null || holder.getTicketLevel() > maxLevel;
|
||||
+ return holder == null || holder.oldTicketLevel > maxLevel; // CraftBukkit using oldTicketLevel for isLoaded checks
|
||||
private boolean chunkAbsent(@Nullable ChunkHolder chunkHolder, int status) {
|
||||
- return chunkHolder == null || chunkHolder.getTicketLevel() > status;
|
||||
+ return chunkHolder == null || chunkHolder.oldTicketLevel > status; // CraftBukkit using oldTicketLevel for isLoaded checks
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -279,7 +353,7 @@
|
||||
@@ -287,7 +_,7 @@
|
||||
return this.mainThreadProcessor.pollTask();
|
||||
}
|
||||
|
||||
|
@ -133,13 +130,12 @@
|
|||
+ public boolean runDistanceManagerUpdates() { // Paper - public
|
||||
boolean flag = this.distanceManager.runAllUpdates(this.chunkMap);
|
||||
boolean flag1 = this.chunkMap.promoteChunkMap();
|
||||
|
||||
@@ -309,18 +383,40 @@
|
||||
this.chunkMap.runGenerationTasks();
|
||||
@@ -315,17 +_,38 @@
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
- this.save(true);
|
||||
+ // CraftBukkit start
|
||||
+ this.close(true);
|
||||
+ }
|
||||
+
|
||||
|
@ -168,26 +164,24 @@
|
|||
+ // CraftBukkit end
|
||||
+
|
||||
@Override
|
||||
public void tick(BooleanSupplier shouldKeepTicking, boolean tickChunks) {
|
||||
ProfilerFiller gameprofilerfiller = Profiler.get();
|
||||
|
||||
gameprofilerfiller.push("purge");
|
||||
public void tick(BooleanSupplier hasTimeLeft, boolean tickChunks) {
|
||||
ProfilerFiller profilerFiller = Profiler.get();
|
||||
profilerFiller.push("purge");
|
||||
- if (this.level.tickRateManager().runsNormally() || !tickChunks) {
|
||||
+ if (this.level.tickRateManager().runsNormally() || !tickChunks || this.level.spigotConfig.unloadFrozenChunks) { // Spigot
|
||||
this.distanceManager.purgeStaleTickets();
|
||||
}
|
||||
|
||||
@@ -401,14 +497,22 @@
|
||||
|
||||
this.lastSpawnState = spawnercreature_d;
|
||||
@@ -400,11 +_,19 @@
|
||||
);
|
||||
this.lastSpawnState = spawnState;
|
||||
profiler.popPush("spawnAndTick");
|
||||
- boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING);
|
||||
+ boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
|
||||
int k = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
|
||||
List list1;
|
||||
|
||||
if (flag && (this.spawnEnemies || this.spawnFriendlies)) {
|
||||
- boolean flag1 = this.level.getLevelData().getGameTime() % 400L == 0L;
|
||||
- boolean _boolean = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING);
|
||||
+ boolean _boolean = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit;
|
||||
int _int = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
|
||||
List<MobCategory> filteredSpawningCategories;
|
||||
if (_boolean && (this.spawnEnemies || this.spawnFriendlies)) {
|
||||
- boolean flag = this.level.getLevelData().getGameTime() % 400L == 0L;
|
||||
+ // Paper start - PlayerNaturallySpawnCreaturesEvent
|
||||
+ for (ServerPlayer entityPlayer : this.level.players()) {
|
||||
+ int chunkRange = Math.min(level.spigotConfig.mobSpawnRange, entityPlayer.getBukkitEntity().getViewDistance());
|
||||
|
@ -196,60 +190,51 @@
|
|||
+ entityPlayer.playerNaturallySpawnedEvent.callEvent();
|
||||
+ }
|
||||
+ // Paper end - PlayerNaturallySpawnCreaturesEvent
|
||||
+ boolean flag1 = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getLevelData().getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit
|
||||
|
||||
- list1 = NaturalSpawner.getFilteredSpawningCategories(spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1);
|
||||
+ list1 = NaturalSpawner.getFilteredSpawningCategories(spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1, this.level); // CraftBukkit
|
||||
+ boolean flag = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getLevelData().getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit
|
||||
filteredSpawningCategories = NaturalSpawner.getFilteredSpawningCategories(spawnState, this.spawnFriendlies, this.spawnEnemies, flag);
|
||||
} else {
|
||||
list1 = List.of();
|
||||
}
|
||||
@@ -420,7 +524,7 @@
|
||||
ChunkPos chunkcoordintpair = chunk.getPos();
|
||||
|
||||
chunk.incrementInhabitedTime(timeDelta);
|
||||
- if (!list1.isEmpty() && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair)) {
|
||||
+ if (!list1.isEmpty() && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair, true)) { // Spigot
|
||||
NaturalSpawner.spawnForChunk(this.level, chunk, spawnercreature_d, list1);
|
||||
filteredSpawningCategories = List.of();
|
||||
@@ -413,7 +_,7 @@
|
||||
for (LevelChunk levelChunk : chunks) {
|
||||
ChunkPos pos = levelChunk.getPos();
|
||||
levelChunk.incrementInhabitedTime(timeInhabited);
|
||||
- if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos)) {
|
||||
+ if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot
|
||||
NaturalSpawner.spawnForChunk(this.level, levelChunk, spawnState, filteredSpawningCategories);
|
||||
}
|
||||
|
||||
@@ -541,10 +645,16 @@
|
||||
@@ -526,8 +_,13 @@
|
||||
|
||||
@Override
|
||||
public void setSpawnSettings(boolean spawnMonsters) {
|
||||
- this.spawnEnemies = spawnMonsters;
|
||||
public void setSpawnSettings(boolean spawnSettings) {
|
||||
- this.spawnEnemies = spawnSettings;
|
||||
- this.spawnFriendlies = this.spawnFriendlies;
|
||||
+ // CraftBukkit start
|
||||
+ this.setSpawnSettings(spawnMonsters, this.spawnFriendlies);
|
||||
}
|
||||
|
||||
+ public void setSpawnSettings(boolean flag, boolean spawnFriendlies) {
|
||||
+ this.spawnEnemies = flag;
|
||||
+ this.setSpawnSettings(spawnSettings, this.spawnFriendlies);
|
||||
+ }
|
||||
+ public void setSpawnSettings(boolean spawnEnemies, boolean spawnFriendlies) {
|
||||
+ this.spawnEnemies = spawnEnemies;
|
||||
+ this.spawnFriendlies = spawnFriendlies;
|
||||
+ // CraftBukkit end
|
||||
+ }
|
||||
+
|
||||
public String getChunkDebugData(ChunkPos pos) {
|
||||
return this.chunkMap.getChunkDebugData(pos);
|
||||
}
|
||||
@@ -618,14 +728,20 @@
|
||||
}
|
||||
|
||||
public String getChunkDebugData(ChunkPos chunkPos) {
|
||||
@@ -603,12 +_,18 @@
|
||||
|
||||
@Override
|
||||
- protected boolean pollTask() {
|
||||
+ // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task
|
||||
+ public boolean pollTask() {
|
||||
+ try {
|
||||
public boolean pollTask() {
|
||||
+ try { // CraftBukkit - process pending Chunk loadCallback() and unloadCallback() after each run task
|
||||
if (ServerChunkCache.this.runDistanceManagerUpdates()) {
|
||||
return true;
|
||||
} else {
|
||||
ServerChunkCache.this.lightEngine.tryScheduleUpdate();
|
||||
return super.pollTask();
|
||||
}
|
||||
+ } finally {
|
||||
+ ServerChunkCache.this.chunkMap.callbackExecutor.run();
|
||||
+ // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task
|
||||
+ } finally {
|
||||
+ ServerChunkCache.this.chunkMap.callbackExecutor.run();
|
||||
+ }
|
||||
+ // CraftBukkit end - process pending Chunk loadCallback() and unloadCallback() after each run task
|
||||
}
|
||||
+ // CraftBukkit end
|
||||
+ }
|
||||
}
|
||||
|
||||
private static record ChunkAndHolder(LevelChunk chunk, ChunkHolder holder) {
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
--- a/net/minecraft/server/level/ServerEntity.java
|
||||
+++ b/net/minecraft/server/level/ServerEntity.java
|
||||
@@ -65,13 +_,17 @@
|
||||
private Vec3 lastSentMovement;
|
||||
private int tickCount;
|
||||
private int teleportDelay;
|
||||
- private List<Entity> lastPassengers = Collections.emptyList();
|
||||
+ private List<Entity> lastPassengers = com.google.common.collect.ImmutableList.of(); // Paper - optimize passenger checks
|
||||
private boolean wasRiding;
|
||||
private boolean wasOnGround;
|
||||
@Nullable
|
||||
private List<SynchedEntityData.DataValue<?>> trackedDataValues;
|
||||
|
||||
- public ServerEntity(ServerLevel level, Entity entity, int updateInterval, boolean trackDelta, Consumer<Packet<?>> broadcast) {
|
||||
+ // CraftBukkit start
|
||||
+ private final Set<net.minecraft.server.network.ServerPlayerConnection> trackedPlayers;
|
||||
+ public ServerEntity(ServerLevel level, Entity entity, int updateInterval, boolean trackDelta, Consumer<Packet<?>> broadcast, final Set<net.minecraft.server.network.ServerPlayerConnection> trackedPlayers) {
|
||||
+ this.trackedPlayers = trackedPlayers;
|
||||
+ // CraftBukkit end
|
||||
this.level = level;
|
||||
this.broadcast = broadcast;
|
||||
this.entity = entity;
|
||||
@@ -89,7 +_,7 @@
|
||||
public void sendChanges() {
|
||||
List<Entity> passengers = this.entity.getPassengers();
|
||||
if (!passengers.equals(this.lastPassengers)) {
|
||||
- this.broadcast.accept(new ClientboundSetPassengersPacket(this.entity));
|
||||
+ this.broadcastAndSend(new ClientboundSetPassengersPacket(this.entity)); // CraftBukkit
|
||||
removedPassengers(passengers, this.lastPassengers)
|
||||
.forEach(
|
||||
removedPassenger -> {
|
||||
@@ -102,10 +_,10 @@
|
||||
this.lastPassengers = passengers;
|
||||
}
|
||||
|
||||
- if (this.entity instanceof ItemFrame itemFrame && this.tickCount % 10 == 0) {
|
||||
+ if (!this.trackedPlayers.isEmpty() && this.entity instanceof ItemFrame itemFrame /*&& this.tickCount % 10 == 0*/) { // CraftBukkit - moved tickCount below // Paper - Perf: Only tick item frames if players can see it
|
||||
ItemStack item = itemFrame.getItem();
|
||||
- if (item.getItem() instanceof MapItem) {
|
||||
- MapId mapId = item.get(DataComponents.MAP_ID);
|
||||
+ if (this.level.paperConfig().maps.itemFrameCursorUpdateInterval > 0 && this.tickCount % this.level.paperConfig().maps.itemFrameCursorUpdateInterval == 0 && item.getItem() instanceof MapItem) { // CraftBukkit - Moved this.tickCounter % 10 logic here so item frames do not enter the other blocks // Paper - Make item frame map cursor update interval configurable
|
||||
+ MapId mapId = itemFrame.cachedMapId; // Paper - Perf: Cache map ids on item frames
|
||||
MapItemSavedData savedData = MapItem.getSavedData(mapId, this.level);
|
||||
if (savedData != null) {
|
||||
for (ServerPlayer serverPlayer : this.level.players()) {
|
||||
@@ -141,7 +_,13 @@
|
||||
} else {
|
||||
this.teleportDelay++;
|
||||
Vec3 vec3 = this.entity.trackingPosition();
|
||||
- boolean flag1 = this.positionCodec.delta(vec3).lengthSqr() >= 7.6293945E-6F;
|
||||
+ // Paper start - reduce allocation of Vec3D here
|
||||
+ Vec3 base = this.positionCodec.base;
|
||||
+ double vec3_dx = vec3.x - base.x;
|
||||
+ double vec3_dy = vec3.y - base.y;
|
||||
+ double vec3_dz = vec3.z - base.z;
|
||||
+ boolean flag1 = (vec3_dx * vec3_dx + vec3_dy * vec3_dy + vec3_dz * vec3_dz) >= 7.62939453125E-6D;
|
||||
+ // Paper end - reduce allocation of Vec3D here
|
||||
Packet<?> packet = null;
|
||||
boolean flag2 = flag1 || this.tickCount % 60 == 0;
|
||||
boolean flag3 = false;
|
||||
@@ -219,6 +_,25 @@
|
||||
|
||||
this.tickCount++;
|
||||
if (this.entity.hurtMarked) {
|
||||
+ // CraftBukkit start - Create PlayerVelocity event
|
||||
+ boolean cancelled = false;
|
||||
+
|
||||
+ if (this.entity instanceof ServerPlayer) {
|
||||
+ org.bukkit.entity.Player player = (org.bukkit.entity.Player) this.entity.getBukkitEntity();
|
||||
+ org.bukkit.util.Vector velocity = player.getVelocity();
|
||||
+
|
||||
+ org.bukkit.event.player.PlayerVelocityEvent event = new org.bukkit.event.player.PlayerVelocityEvent(player, velocity.clone());
|
||||
+ if (!event.callEvent()) {
|
||||
+ cancelled = true;
|
||||
+ } else if (!velocity.equals(event.getVelocity())) {
|
||||
+ player.setVelocity(event.getVelocity());
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (cancelled) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
this.entity.hurtMarked = false;
|
||||
this.broadcastAndSend(new ClientboundSetEntityMotionPacket(this.entity));
|
||||
}
|
||||
@@ -273,7 +_,10 @@
|
||||
|
||||
public void sendPairingData(ServerPlayer player, Consumer<Packet<ClientGamePacketListener>> consumer) {
|
||||
if (this.entity.isRemoved()) {
|
||||
- LOGGER.warn("Fetching packet for removed entity {}", this.entity);
|
||||
+ // CraftBukkit start - Remove useless error spam, just return
|
||||
+ // LOGGER.warn("Fetching packet for removed entity {}", this.entity);
|
||||
+ return;
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
|
||||
Packet<ClientGamePacketListener> addEntityPacket = this.entity.getAddEntityPacket(this);
|
||||
@@ -285,6 +_,12 @@
|
||||
boolean flag = this.trackDelta;
|
||||
if (this.entity instanceof LivingEntity) {
|
||||
Collection<AttributeInstance> syncableAttributes = ((LivingEntity)this.entity).getAttributes().getSyncableAttributes();
|
||||
+
|
||||
+ // CraftBukkit start - If sending own attributes send scaled health instead of current maximum health
|
||||
+ if (this.entity.getId() == player.getId()) {
|
||||
+ ((ServerPlayer) this.entity).getBukkitEntity().injectScaledMaxHealth(syncableAttributes, false);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
if (!syncableAttributes.isEmpty()) {
|
||||
consumer.accept(new ClientboundUpdateAttributesPacket(this.entity.getId(), syncableAttributes));
|
||||
}
|
||||
@@ -309,8 +_,9 @@
|
||||
}
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
- consumer.accept(new ClientboundSetEquipmentPacket(this.entity.getId(), list));
|
||||
+ consumer.accept(new ClientboundSetEquipmentPacket(this.entity.getId(), list, true)); // Paper - data sanitization
|
||||
}
|
||||
+ ((LivingEntity) this.entity).detectEquipmentUpdatesPublic(); // CraftBukkit - SPIGOT-3789: sync again immediately after sending
|
||||
}
|
||||
|
||||
if (!this.entity.getPassengers().isEmpty()) {
|
||||
@@ -357,6 +_,11 @@
|
||||
if (this.entity instanceof LivingEntity) {
|
||||
Set<AttributeInstance> attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync();
|
||||
if (!attributesToSync.isEmpty()) {
|
||||
+ // CraftBukkit start - Send scaled max health
|
||||
+ if (this.entity instanceof ServerPlayer) {
|
||||
+ ((ServerPlayer) this.entity).getBukkitEntity().injectScaledMaxHealth(attributesToSync, false);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync));
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
--- a/net/minecraft/server/level/ServerPlayerGameMode.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayerGameMode.java
|
||||
@@ -13,6 +13,7 @@
|
||||
@@ -13,6 +_,7 @@
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
|
@ -8,75 +8,51 @@
|
|||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
||||
@@ -20,12 +21,30 @@
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.GameMasterBlock;
|
||||
+import net.minecraft.world.level.block.TrapDoorBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
+import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
+// CraftBukkit start
|
||||
+import java.util.ArrayList;
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+import net.minecraft.world.level.block.Blocks;
|
||||
+import net.minecraft.world.level.block.CakeBlock;
|
||||
+import net.minecraft.world.level.block.DoorBlock;
|
||||
+import org.bukkit.GameMode;
|
||||
+import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
+import org.bukkit.event.block.BlockBreakEvent;
|
||||
+import org.bukkit.craftbukkit.event.CraftEventFactory;
|
||||
+import org.bukkit.event.Event;
|
||||
+import org.bukkit.event.block.Action;
|
||||
+import org.bukkit.event.player.PlayerGameModeChangeEvent;
|
||||
+import org.bukkit.event.player.PlayerInteractEvent;
|
||||
+// CraftBukkit end
|
||||
+
|
||||
public class ServerPlayerGameMode {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
@@ -42,6 +61,8 @@
|
||||
private BlockPos delayedDestroyPos;
|
||||
@@ -41,6 +_,8 @@
|
||||
private BlockPos delayedDestroyPos = BlockPos.ZERO;
|
||||
private int delayedTickStart;
|
||||
private int lastSentState;
|
||||
private int lastSentState = -1;
|
||||
+ public boolean captureSentBlockEntities = false; // Paper - Send block entities after destroy prediction
|
||||
+ public boolean capturedBlockEntity = false; // Paper - Send block entities after destroy prediction
|
||||
|
||||
public ServerPlayerGameMode(ServerPlayer player) {
|
||||
this.gameModeForPlayer = GameType.DEFAULT_MODE;
|
||||
@@ -53,18 +74,32 @@
|
||||
this.player = player;
|
||||
@@ -48,21 +_,39 @@
|
||||
}
|
||||
|
||||
public boolean changeGameModeForPlayer(GameType gameMode) {
|
||||
public boolean changeGameModeForPlayer(GameType gameModeForPlayer) {
|
||||
+ // Paper start - Expand PlayerGameModeChangeEvent
|
||||
+ PlayerGameModeChangeEvent event = this.changeGameModeForPlayer(gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.UNKNOWN, null);
|
||||
+ org.bukkit.event.player.PlayerGameModeChangeEvent event = this.changeGameModeForPlayer(gameModeForPlayer, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.UNKNOWN, null);
|
||||
+ return event != null && event.isCancelled();
|
||||
+ }
|
||||
+ @Nullable
|
||||
+ public PlayerGameModeChangeEvent changeGameModeForPlayer(GameType gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause cause, @Nullable net.kyori.adventure.text.Component cancelMessage) {
|
||||
+ public org.bukkit.event.player.PlayerGameModeChangeEvent changeGameModeForPlayer(GameType gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause playerGameModeChangeCause, @Nullable net.kyori.adventure.text.Component cancelMessage) {
|
||||
+ // Paper end - Expand PlayerGameModeChangeEvent
|
||||
if (gameMode == this.gameModeForPlayer) {
|
||||
if (gameModeForPlayer == this.gameModeForPlayer) {
|
||||
- return false;
|
||||
+ return null; // Paper - Expand PlayerGameModeChangeEvent
|
||||
} else {
|
||||
- this.setGameModeForPlayer(gameMode, this.previousGameModeForPlayer);
|
||||
- this.setGameModeForPlayer(gameModeForPlayer, this.previousGameModeForPlayer);
|
||||
+ // CraftBukkit start
|
||||
+ PlayerGameModeChangeEvent event = new PlayerGameModeChangeEvent(this.player.getBukkitEntity(), GameMode.getByValue(gameMode.getId()), cause, cancelMessage); // Paper
|
||||
+ this.level.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ if (event.isCancelled()) {
|
||||
+ org.bukkit.event.player.PlayerGameModeChangeEvent event = new org.bukkit.event.player.PlayerGameModeChangeEvent(
|
||||
+ this.player.getBukkitEntity(),
|
||||
+ org.bukkit.GameMode.getByValue(gameMode.getId()),
|
||||
+ playerGameModeChangeCause, // Paper
|
||||
+ cancelMessage
|
||||
+ );
|
||||
+ if (!event.callEvent()) {
|
||||
+ return event; // Paper - Expand PlayerGameModeChangeEvent
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+ this.setGameModeForPlayer(gameMode, this.gameModeForPlayer); // Paper - Fix MC-259571
|
||||
this.player.onUpdateAbilities();
|
||||
- this.player.server.getPlayerList().broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, this.player));
|
||||
+ this.player.server.getPlayerList().broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, this.player), this.player); // CraftBukkit
|
||||
this.player
|
||||
.server
|
||||
.getPlayerList()
|
||||
- .broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, this.player));
|
||||
+ .broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, this.player), this.player); // CraftBukkit
|
||||
this.level.updateSleepingPlayerList();
|
||||
if (gameMode == GameType.CREATIVE) {
|
||||
if (gameModeForPlayer == GameType.CREATIVE) {
|
||||
this.player.resetCurrentImpulseContext();
|
||||
}
|
||||
|
||||
|
@ -85,61 +61,61 @@
|
|||
}
|
||||
}
|
||||
|
||||
@@ -92,12 +127,12 @@
|
||||
@@ -90,10 +_,10 @@
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
- ++this.gameTicks;
|
||||
+ this.gameTicks = MinecraftServer.currentTick; // CraftBukkit;
|
||||
BlockState iblockdata;
|
||||
|
||||
- this.gameTicks++;
|
||||
+ this.gameTicks = net.minecraft.server.MinecraftServer.currentTick; // CraftBukkit;
|
||||
if (this.hasDelayedDestroy) {
|
||||
- iblockdata = this.level.getBlockState(this.delayedDestroyPos);
|
||||
- if (iblockdata.isAir()) {
|
||||
+ iblockdata = this.level.getBlockStateIfLoaded(this.delayedDestroyPos); // Paper - Don't allow digging into unloaded chunks
|
||||
+ if (iblockdata == null || iblockdata.isAir()) { // Paper - Don't allow digging into unloaded chunks
|
||||
- BlockState blockState = this.level.getBlockState(this.delayedDestroyPos);
|
||||
- if (blockState.isAir()) {
|
||||
+ BlockState blockState = this.level.getBlockStateIfLoaded(this.delayedDestroyPos); // Paper - Don't allow digging into unloaded chunks
|
||||
+ if (blockState == null || blockState.isAir()) { // Paper - Don't allow digging into unloaded chunks
|
||||
this.hasDelayedDestroy = false;
|
||||
} else {
|
||||
float f = this.incrementDestroyProgress(iblockdata, this.delayedDestroyPos, this.delayedTickStart);
|
||||
@@ -108,7 +143,13 @@
|
||||
float f = this.incrementDestroyProgress(blockState, this.delayedDestroyPos, this.delayedTickStart);
|
||||
@@ -103,7 +_,13 @@
|
||||
}
|
||||
}
|
||||
} else if (this.isDestroyingBlock) {
|
||||
- iblockdata = this.level.getBlockState(this.destroyPos);
|
||||
- BlockState blockState = this.level.getBlockState(this.destroyPos);
|
||||
+ // Paper start - Don't allow digging into unloaded chunks; don't want to do same logic as above, return instead
|
||||
+ iblockdata = this.level.getBlockStateIfLoaded(this.destroyPos);
|
||||
+ if (iblockdata == null) {
|
||||
+ BlockState blockState = this.level.getBlockStateIfLoaded(this.destroyPos);
|
||||
+ if (blockState == null) {
|
||||
+ this.isDestroyingBlock = false;
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - Don't allow digging into unloaded chunks
|
||||
if (iblockdata.isAir()) {
|
||||
if (blockState.isAir()) {
|
||||
this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1);
|
||||
this.lastSentState = -1;
|
||||
@@ -137,6 +178,7 @@
|
||||
@@ -131,6 +_,7 @@
|
||||
|
||||
public void handleBlockBreakAction(BlockPos pos, ServerboundPlayerActionPacket.Action action, Direction direction, int worldHeight, int sequence) {
|
||||
if (!this.player.canInteractWithBlock(pos, 1.0D)) {
|
||||
public void handleBlockBreakAction(BlockPos pos, ServerboundPlayerActionPacket.Action action, Direction face, int maxBuildHeight, int sequence) {
|
||||
if (!this.player.canInteractWithBlock(pos, 1.0)) {
|
||||
+ if (true) return; // Paper - Don't allow digging into unloaded chunks; Don't notify if unreasonably far away
|
||||
this.debugLogging(pos, false, sequence, "too far");
|
||||
} else if (pos.getY() > worldHeight) {
|
||||
} else if (pos.getY() > maxBuildHeight) {
|
||||
this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos)));
|
||||
@@ -146,16 +188,40 @@
|
||||
|
||||
@@ -138,16 +_,40 @@
|
||||
} else {
|
||||
if (action == ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK) {
|
||||
if (!this.level.mayInteract(this.player, pos)) {
|
||||
+ // CraftBukkit start - fire PlayerInteractEvent
|
||||
+ CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, pos, direction, this.player.getInventory().getSelected(), InteractionHand.MAIN_HAND);
|
||||
+ org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(this.player, org.bukkit.event.block.Action.LEFT_CLICK_BLOCK, pos, face, this.player.getInventory().getSelected(), InteractionHand.MAIN_HAND);
|
||||
this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos)));
|
||||
this.debugLogging(pos, false, sequence, "may not interact");
|
||||
- return;
|
||||
- }
|
||||
+ // Update any tile entity data for this block
|
||||
+ capturedBlockEntity = true; // Paper - Send block entities after destroy prediction
|
||||
+ // CraftBukkit end
|
||||
return;
|
||||
}
|
||||
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ // CraftBukkit start
|
||||
+ PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, pos, direction, this.player.getInventory().getSelected(), InteractionHand.MAIN_HAND);
|
||||
+ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(this.player, org.bukkit.event.block.Action.LEFT_CLICK_BLOCK, pos, face, this.player.getInventory().getSelected(), InteractionHand.MAIN_HAND);
|
||||
+ if (event.isCancelled()) {
|
||||
+ // Let the client know the block still exists
|
||||
+ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); // Paper - Don't resync blocks
|
||||
|
@ -148,7 +124,7 @@
|
|||
+ return;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
|
||||
if (this.isCreative()) {
|
||||
this.destroyAndAck(pos, sequence, "creative destroy");
|
||||
return;
|
||||
|
@ -156,7 +132,7 @@
|
|||
|
||||
+ // Spigot start - handle debug stick left click for non-creative
|
||||
+ if (this.player.getMainHandItem().is(net.minecraft.world.item.Items.DEBUG_STICK)
|
||||
+ && ((net.minecraft.world.item.DebugStickItem) net.minecraft.world.item.Items.DEBUG_STICK).handleInteraction(this.player, this.level.getBlockState(pos), this.level, pos, false, this.player.getMainHandItem())) {
|
||||
+ && ((net.minecraft.world.item.DebugStickItem) net.minecraft.world.item.Items.DEBUG_STICK).handleInteraction(this.player, this.level.getBlockState(pos), this.level, pos, false, this.player.getMainHandItem())) {
|
||||
+ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); // Paper - Don't resync block
|
||||
+ return;
|
||||
+ }
|
||||
|
@ -165,13 +141,13 @@
|
|||
if (this.player.blockActionRestricted(this.level, pos, this.gameModeForPlayer)) {
|
||||
this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos)));
|
||||
this.debugLogging(pos, false, sequence, "block action restricted");
|
||||
@@ -166,7 +232,21 @@
|
||||
@@ -157,7 +_,21 @@
|
||||
this.destroyProgressStart = this.gameTicks;
|
||||
float f = 1.0F;
|
||||
|
||||
iblockdata = this.level.getBlockState(pos);
|
||||
- if (!iblockdata.isAir()) {
|
||||
BlockState blockState = this.level.getBlockState(pos);
|
||||
- if (!blockState.isAir()) {
|
||||
+ // CraftBukkit start - Swings at air do *NOT* exist.
|
||||
+ if (event.useInteractedBlock() == Event.Result.DENY) {
|
||||
+ if (event.useInteractedBlock() == org.bukkit.event.Event.Result.DENY) {
|
||||
+ // If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door.
|
||||
+ // Paper start - Don't resync blocks
|
||||
+ //BlockState data = this.level.getBlockState(pos);
|
||||
|
@ -184,22 +160,22 @@
|
|||
+ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos));
|
||||
+ //}
|
||||
+ // Paper end - Don't resync blocks
|
||||
+ } else if (!iblockdata.isAir()) {
|
||||
EnchantmentHelper.onHitBlock(this.level, this.player.getMainHandItem(), this.player, this.player, EquipmentSlot.MAINHAND, Vec3.atCenterOf(pos), iblockdata, (item) -> {
|
||||
this.player.onEquippedItemBroken(item, EquipmentSlot.MAINHAND);
|
||||
});
|
||||
@@ -174,6 +254,26 @@
|
||||
f = iblockdata.getDestroyProgress(this.player, this.player.level(), pos);
|
||||
+ } else if (!blockState.isAir()) {
|
||||
EnchantmentHelper.onHitBlock(
|
||||
this.level,
|
||||
this.player.getMainHandItem(),
|
||||
@@ -172,6 +_,26 @@
|
||||
f = blockState.getDestroyProgress(this.player, this.player.level(), pos);
|
||||
}
|
||||
|
||||
+ if (event.useItemInHand() == Event.Result.DENY) {
|
||||
+ if (event.useItemInHand() == org.bukkit.event.Event.Result.DENY) {
|
||||
+ // If we 'insta destroyed' then the client needs to be informed.
|
||||
+ if (f > 1.0f) {
|
||||
+ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); // Paper - Don't resync blocks
|
||||
+ }
|
||||
+ return;
|
||||
+ }
|
||||
+ org.bukkit.event.block.BlockDamageEvent blockEvent = CraftEventFactory.callBlockDamageEvent(this.player, pos, direction, this.player.getInventory().getSelected(), f >= 1.0f); // Paper - Add BlockFace to BlockDamageEvent
|
||||
+ org.bukkit.event.block.BlockDamageEvent blockEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDamageEvent(this.player, pos, face, this.player.getInventory().getSelected(), f >= 1.0f); // Paper - Add BlockFace to BlockDamageEvent
|
||||
+
|
||||
+ if (blockEvent.isCancelled()) {
|
||||
+ // Let the client know the block still exists
|
||||
|
@ -212,15 +188,15 @@
|
|||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
if (!iblockdata.isAir() && f >= 1.0F) {
|
||||
if (!blockState.isAir() && f >= 1.0F) {
|
||||
this.destroyAndAck(pos, sequence, "insta mine");
|
||||
} else {
|
||||
@@ -217,14 +317,18 @@
|
||||
@@ -212,14 +_,18 @@
|
||||
this.debugLogging(pos, true, sequence, "stopped destroying");
|
||||
} else if (action == ServerboundPlayerActionPacket.Action.ABORT_DESTROY_BLOCK) {
|
||||
this.isDestroyingBlock = false;
|
||||
- if (!Objects.equals(this.destroyPos, pos)) {
|
||||
- ServerPlayerGameMode.LOGGER.warn("Mismatch in destroy block pos: {} {}", this.destroyPos, pos);
|
||||
- LOGGER.warn("Mismatch in destroy block pos: {} {}", this.destroyPos, pos);
|
||||
- this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1);
|
||||
- this.debugLogging(pos, true, sequence, "aborted mismatched destroying");
|
||||
+ if (!Objects.equals(this.destroyPos, pos) && !BlockPos.ZERO.equals(this.destroyPos)) { // Paper
|
||||
|
@ -234,31 +210,30 @@
|
|||
this.level.destroyBlockProgress(this.player.getId(), pos, -1);
|
||||
this.debugLogging(pos, true, sequence, "aborted destroying");
|
||||
+
|
||||
+ CraftEventFactory.callBlockDamageAbortEvent(this.player, pos, this.player.getInventory().getSelected()); // CraftBukkit
|
||||
+ org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDamageAbortEvent(this.player, pos, this.player.getInventory().getSelected()); // CraftBukkit
|
||||
}
|
||||
|
||||
}
|
||||
@@ -242,19 +346,82 @@
|
||||
}
|
||||
@@ -235,36 +_,125 @@
|
||||
|
||||
public boolean destroyBlock(BlockPos pos) {
|
||||
BlockState iblockdata = this.level.getBlockState(pos);
|
||||
BlockState blockState = this.level.getBlockState(pos);
|
||||
- if (!this.player.getMainHandItem().getItem().canAttackBlock(blockState, this.level, pos, this.player)) {
|
||||
+ // CraftBukkit start - fire BlockBreakEvent
|
||||
+ org.bukkit.block.Block bblock = CraftBlock.at(this.level, pos);
|
||||
+ BlockBreakEvent event = null;
|
||||
|
||||
- if (!this.player.getMainHandItem().getItem().canAttackBlock(iblockdata, this.level, pos, this.player)) {
|
||||
+ org.bukkit.block.Block bblock = org.bukkit.craftbukkit.block.CraftBlock.at(this.level, pos);
|
||||
+ org.bukkit.event.block.BlockBreakEvent event = null;
|
||||
+ if (this.player instanceof ServerPlayer) {
|
||||
+ // Sword + Creative mode pre-cancel
|
||||
+ boolean isSwordNoBreak = !this.player.getMainHandItem().getItem().canAttackBlock(iblockdata, this.level, pos, this.player);
|
||||
+ boolean isSwordNoBreak = !this.player.getMainHandItem().getItem().canAttackBlock(blockState, this.level, pos, this.player);
|
||||
+
|
||||
+ // Tell client the block is gone immediately then process events
|
||||
+ // Don't tell the client if its a creative sword break because its not broken!
|
||||
+ if (false && this.level.getBlockEntity(pos) == null && !isSwordNoBreak) { // Paper - Don't resync block
|
||||
+ ClientboundBlockUpdatePacket packet = new ClientboundBlockUpdatePacket(pos, Blocks.AIR.defaultBlockState());
|
||||
+ ClientboundBlockUpdatePacket packet = new ClientboundBlockUpdatePacket(pos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState());
|
||||
+ this.player.connection.send(packet);
|
||||
+ }
|
||||
+
|
||||
+ event = new BlockBreakEvent(bblock, this.player.getBukkitEntity());
|
||||
+ event = new org.bukkit.event.block.BlockBreakEvent(bblock, this.player.getBukkitEntity());
|
||||
+
|
||||
+ // Sword + Creative mode pre-cancel
|
||||
+ event.setCancelled(isSwordNoBreak);
|
||||
|
@ -291,40 +266,38 @@
|
|||
+
|
||||
+ // Update any tile entity data for this block
|
||||
+ if (!captureSentBlockEntities) { // Paper - Send block entities after destroy prediction
|
||||
+ BlockEntity tileentity = this.level.getBlockEntity(pos);
|
||||
+ if (tileentity != null) {
|
||||
+ this.player.connection.send(tileentity.getUpdatePacket());
|
||||
+ }
|
||||
+ BlockEntity tileentity = this.level.getBlockEntity(pos);
|
||||
+ if (tileentity != null) {
|
||||
+ this.player.connection.send(tileentity.getUpdatePacket());
|
||||
+ }
|
||||
+ } else {capturedBlockEntity = true;} // Paper - Send block entities after destroy prediction
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
+ if (false && !this.player.getMainHandItem().getItem().canAttackBlock(iblockdata, this.level, pos, this.player)) { // CraftBukkit - false
|
||||
+ if (false && !this.player.getMainHandItem().getItem().canAttackBlock(blockState, this.level, pos, this.player)) { // CraftBukkit - false
|
||||
return false;
|
||||
} else {
|
||||
+ iblockdata = this.level.getBlockState(pos); // CraftBukkit - update state from plugins
|
||||
+ if (iblockdata.isAir()) return false; // CraftBukkit - A plugin set block to air without cancelling
|
||||
BlockEntity tileentity = this.level.getBlockEntity(pos);
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
+ blockState = this.level.getBlockState(pos); // CraftBukkit - update state from plugins
|
||||
+ if (blockState.isAir()) return false; // CraftBukkit - A plugin set block to air without cancelling
|
||||
BlockEntity blockEntity = this.level.getBlockEntity(pos);
|
||||
Block block = blockState.getBlock();
|
||||
- if (block instanceof GameMasterBlock && !this.player.canUseGameMasterBlocks()) {
|
||||
+ if (block instanceof GameMasterBlock && !this.player.canUseGameMasterBlocks() && !(block instanceof net.minecraft.world.level.block.CommandBlock && (this.player.isCreative() && this.player.getBukkitEntity().hasPermission("minecraft.commandblock")))) { // Paper - command block permission
|
||||
this.level.sendBlockUpdated(pos, iblockdata, iblockdata, 3);
|
||||
this.level.sendBlockUpdated(pos, blockState, blockState, 3);
|
||||
return false;
|
||||
} else if (this.player.blockActionRestricted(this.level, pos, this.gameModeForPlayer)) {
|
||||
return false;
|
||||
} else {
|
||||
+ // CraftBukkit start
|
||||
+ org.bukkit.block.BlockState state = bblock.getState();
|
||||
+ this.level.captureDrops = new ArrayList<>();
|
||||
+ this.level.captureDrops = new java.util.ArrayList<>();
|
||||
+ // CraftBukkit end
|
||||
BlockState iblockdata1 = block.playerWillDestroy(this.level, pos, iblockdata, this.player);
|
||||
BlockState blockState1 = block.playerWillDestroy(this.level, pos, blockState, this.player);
|
||||
boolean flag = this.level.removeBlock(pos, false);
|
||||
|
||||
@@ -262,20 +429,46 @@
|
||||
block.destroy(this.level, pos, iblockdata1);
|
||||
if (flag) {
|
||||
block.destroy(this.level, pos, blockState1);
|
||||
}
|
||||
|
||||
+ ItemStack mainHandStack = null; // Paper - Trigger bee_nest_destroyed trigger in the correct place
|
||||
|
@ -333,22 +306,24 @@
|
|||
- return true;
|
||||
+ // return true; // CraftBukkit
|
||||
} else {
|
||||
ItemStack itemstack = this.player.getMainHandItem();
|
||||
ItemStack itemstack1 = itemstack.copy();
|
||||
boolean flag1 = this.player.hasCorrectToolForDrops(iblockdata1);
|
||||
+ mainHandStack = itemstack1; // Paper - Trigger bee_nest_destroyed trigger in the correct place
|
||||
+ isCorrectTool = flag1; // Paper - Trigger bee_nest_destroyed trigger in the correct place
|
||||
|
||||
itemstack.mineBlock(this.level, iblockdata1, pos, this.player);
|
||||
- if (flag && flag1) {
|
||||
- block.playerDestroy(this.level, this.player, pos, iblockdata1, tileentity, itemstack1);
|
||||
+ if (flag && flag1/* && event.isDropItems() */) { // CraftBukkit - Check if block should drop items // Paper - fix drops not preventing stats/food exhaustion
|
||||
+ block.playerDestroy(this.level, this.player, pos, iblockdata1, tileentity, itemstack1, event.isDropItems(), false); // Paper - fix drops not preventing stats/food exhaustion
|
||||
}
|
||||
|
||||
ItemStack mainHandItem = this.player.getMainHandItem();
|
||||
ItemStack itemStack = mainHandItem.copy();
|
||||
boolean hasCorrectToolForDrops = this.player.hasCorrectToolForDrops(blockState1);
|
||||
+ mainHandStack = itemStack; // Paper - Trigger bee_nest_destroyed trigger in the correct place
|
||||
+ isCorrectTool = hasCorrectToolForDrops; // Paper - Trigger bee_nest_destroyed trigger in the correct place
|
||||
mainHandItem.mineBlock(this.level, blockState1, pos, this.player);
|
||||
- if (flag && hasCorrectToolForDrops) {
|
||||
- block.playerDestroy(this.level, this.player, pos, blockState1, blockEntity, itemStack);
|
||||
- }
|
||||
-
|
||||
- return true;
|
||||
- }
|
||||
+ if (flag && hasCorrectToolForDrops/* && event.isDropItems() */) { // CraftBukkit - Check if block should drop items // Paper - fix drops not preventing stats/food exhaustion
|
||||
+ block.playerDestroy(this.level, this.player, pos, blockState1, blockEntity, itemStack, event.isDropItems(), false); // Paper - fix drops not preventing stats/food exhaustion
|
||||
+ }
|
||||
+
|
||||
+ // return true; // CraftBukkit
|
||||
}
|
||||
+ }
|
||||
+ // CraftBukkit start
|
||||
+ java.util.List<net.minecraft.world.entity.item.ItemEntity> itemsToDrop = this.level.captureDrops; // Paper - capture all item additions to the world
|
||||
+ this.level.captureDrops = null; // Paper - capture all item additions to the world; Remove this earlier so that we can actually drop stuff
|
||||
|
@ -359,12 +334,12 @@
|
|||
+
|
||||
+ // Drop event experience
|
||||
+ if (flag && event != null) {
|
||||
+ iblockdata.getBlock().popExperience(this.level, pos, event.getExpToDrop(), this.player); // Paper
|
||||
+ blockState.getBlock().popExperience(this.level, pos, event.getExpToDrop(), this.player); // Paper
|
||||
+ }
|
||||
+ // Paper start - Trigger bee_nest_destroyed trigger in the correct place (check impls of block#playerDestroy)
|
||||
+ if (mainHandStack != null) {
|
||||
+ if (flag && isCorrectTool && event.isDropItems() && block instanceof net.minecraft.world.level.block.BeehiveBlock && tileentity instanceof net.minecraft.world.level.block.entity.BeehiveBlockEntity beehiveBlockEntity) { // simulates the guard on block#playerDestroy above
|
||||
+ CriteriaTriggers.BEE_NEST_DESTROYED.trigger(player, iblockdata, mainHandStack, beehiveBlockEntity.getOccupantCount());
|
||||
+ if (flag && isCorrectTool && event.isDropItems() && block instanceof net.minecraft.world.level.block.BeehiveBlock && blockEntity instanceof net.minecraft.world.level.block.entity.BeehiveBlockEntity beehiveBlockEntity) { // simulates the guard on block#playerDestroy above
|
||||
+ CriteriaTriggers.BEE_NEST_DESTROYED.trigger(player, blockState, mainHandStack, beehiveBlockEntity.getOccupantCount());
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Trigger bee_nest_destroyed trigger in the correct place
|
||||
|
@ -374,7 +349,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
@@ -321,17 +514,63 @@
|
||||
@@ -307,15 +_,61 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -384,40 +359,38 @@
|
|||
+ public BlockPos interactPosition;
|
||||
+ public InteractionHand interactHand;
|
||||
+ public ItemStack interactItemStack;
|
||||
public InteractionResult useItemOn(ServerPlayer player, Level world, ItemStack stack, InteractionHand hand, BlockHitResult hitResult) {
|
||||
BlockPos blockposition = hitResult.getBlockPos();
|
||||
BlockState iblockdata = world.getBlockState(blockposition);
|
||||
public InteractionResult useItemOn(ServerPlayer player, Level level, ItemStack stack, InteractionHand hand, BlockHitResult hitResult) {
|
||||
BlockPos blockPos = hitResult.getBlockPos();
|
||||
BlockState blockState = level.getBlockState(blockPos);
|
||||
+ boolean cancelledBlock = false;
|
||||
+ boolean cancelledItem = false; // Paper - correctly handle items on cooldown
|
||||
|
||||
if (!iblockdata.getBlock().isEnabled(world.enabledFeatures())) {
|
||||
if (!blockState.getBlock().isEnabled(level.enabledFeatures())) {
|
||||
return InteractionResult.FAIL;
|
||||
} else if (this.gameModeForPlayer == GameType.SPECTATOR) {
|
||||
MenuProvider itileinventory = iblockdata.getMenuProvider(world, blockposition);
|
||||
+ cancelledBlock = !(itileinventory instanceof MenuProvider);
|
||||
MenuProvider menuProvider = blockState.getMenuProvider(level, blockPos);
|
||||
- if (menuProvider != null) {
|
||||
- player.openMenu(menuProvider);
|
||||
+ cancelledBlock = !(menuProvider instanceof MenuProvider);
|
||||
+ }
|
||||
|
||||
- if (itileinventory != null) {
|
||||
- player.openMenu(itileinventory);
|
||||
+
|
||||
+ if (player.getCooldowns().isOnCooldown(stack)) {
|
||||
+ cancelledItem = true; // Paper - correctly handle items on cooldown
|
||||
+ }
|
||||
+
|
||||
+ PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(player, Action.RIGHT_CLICK_BLOCK, blockposition, hitResult.getDirection(), stack, cancelledBlock, cancelledItem, hand, hitResult.getLocation()); // Paper - correctly handle items on cooldown
|
||||
+ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(player, org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK, blockPos, hitResult.getDirection(), stack, cancelledBlock, cancelledItem, hand, hitResult.getLocation()); // Paper - correctly handle items on cooldown
|
||||
+ this.firedInteract = true;
|
||||
+ this.interactResult = event.useItemInHand() == Event.Result.DENY;
|
||||
+ this.interactPosition = blockposition.immutable();
|
||||
+ this.interactResult = event.useItemInHand() == org.bukkit.event.Event.Result.DENY;
|
||||
+ this.interactPosition = blockPos.immutable();
|
||||
+ this.interactHand = hand;
|
||||
+ this.interactItemStack = stack.copy();
|
||||
+
|
||||
+ if (event.useInteractedBlock() == Event.Result.DENY) {
|
||||
+ if (event.useInteractedBlock() == org.bukkit.event.Event.Result.DENY) {
|
||||
+ // If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door.
|
||||
+ if (iblockdata.getBlock() instanceof DoorBlock) {
|
||||
+ if (blockState.getBlock() instanceof net.minecraft.world.level.block.DoorBlock) {
|
||||
+ // Paper start - Don't resync blocks
|
||||
+ // boolean bottom = iblockdata.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER;
|
||||
+ // player.connection.send(new ClientboundBlockUpdatePacket(world, bottom ? blockposition.above() : blockposition.below()));
|
||||
+ // Paper end - Don't resync blocks
|
||||
+ } else if (iblockdata.getBlock() instanceof CakeBlock) {
|
||||
+ } else if (blockState.getBlock() instanceof net.minecraft.world.level.block.CakeBlock) {
|
||||
+ player.getBukkitEntity().sendHealthUpdate(); // SPIGOT-1341 - reset health for cake
|
||||
+ } else if (this.interactItemStack.getItem() instanceof DoubleHighBlockItem) {
|
||||
+ // send a correcting update to the client, as it already placed the upper half of the bisected item
|
||||
|
@ -425,33 +398,33 @@
|
|||
+
|
||||
+ // send a correcting update to the client for the block above as well, this because of replaceable blocks (such as grass, sea grass etc)
|
||||
+ //player.connection.send(new ClientboundBlockUpdatePacket(world, blockposition.above())); // Paper - Don't resync blocks
|
||||
+ // Paper start - extend Player Interact cancellation // TODO: consider merging this into the extracted method
|
||||
+ } else if (iblockdata.is(Blocks.JIGSAW) || iblockdata.is(Blocks.STRUCTURE_BLOCK) || iblockdata.getBlock() instanceof net.minecraft.world.level.block.CommandBlock) {
|
||||
+ // Paper start - extend Player Interact cancellation // TODO: consider merging this into the extracted method
|
||||
+ } else if (blockState.is(net.minecraft.world.level.block.Blocks.JIGSAW) || blockState.is(net.minecraft.world.level.block.Blocks.STRUCTURE_BLOCK) || blockState.getBlock() instanceof net.minecraft.world.level.block.CommandBlock) {
|
||||
+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundContainerClosePacket(this.player.containerMenu.containerId));
|
||||
+ }
|
||||
+ // Paper end - extend Player Interact cancellation
|
||||
+ player.getBukkitEntity().updateInventory(); // SPIGOT-2867
|
||||
+ this.player.resyncUsingItem(this.player); // Paper - Properly cancel usable items
|
||||
+ return (event.useItemInHand() != Event.Result.ALLOW) ? InteractionResult.SUCCESS : InteractionResult.PASS;
|
||||
+ return (event.useItemInHand() != org.bukkit.event.Event.Result.ALLOW) ? InteractionResult.SUCCESS : InteractionResult.PASS;
|
||||
+ } else if (this.gameModeForPlayer == GameType.SPECTATOR) {
|
||||
+ MenuProvider itileinventory = iblockdata.getMenuProvider(world, blockposition);
|
||||
+ MenuProvider itileinventory = blockState.getMenuProvider(level, blockPos);
|
||||
+
|
||||
+ if (itileinventory != null && player.openMenu(itileinventory).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
|
||||
return InteractionResult.CONSUME;
|
||||
} else {
|
||||
return InteractionResult.PASS;
|
||||
@@ -359,7 +598,7 @@
|
||||
@@ -340,7 +_,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- if (!stack.isEmpty() && !player.getCooldowns().isOnCooldown(stack)) {
|
||||
+ if (!stack.isEmpty() && !this.interactResult) { // add !interactResult SPIGOT-764
|
||||
UseOnContext itemactioncontext = new UseOnContext(player, hand, hitResult);
|
||||
|
||||
UseOnContext useOnContext = new UseOnContext(player, hand, hitResult);
|
||||
InteractionResult interactionResult1;
|
||||
if (this.isCreative()) {
|
||||
@@ -377,6 +616,11 @@
|
||||
@@ -357,6 +_,11 @@
|
||||
|
||||
return enuminteractionresult;
|
||||
return interactionResult1;
|
||||
} else {
|
||||
+ // Paper start - Properly cancel usable items; Cancel only if cancelled + if the interact result is different from default response
|
||||
+ if (this.interactResult && this.interactResult != cancelledItem) {
|
|
@ -1,20 +1,20 @@
|
|||
--- a/net/minecraft/server/level/TicketType.java
|
||||
+++ b/net/minecraft/server/level/TicketType.java
|
||||
@@ -7,6 +7,7 @@
|
||||
@@ -7,6 +_,7 @@
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
|
||||
public class TicketType<T> {
|
||||
+ public static final TicketType<Long> FUTURE_AWAIT = create("future_await", Long::compareTo); // Paper
|
||||
|
||||
private final String name;
|
||||
private final Comparator<T> comparator;
|
||||
@@ -22,6 +23,9 @@
|
||||
public static final TicketType<BlockPos> PORTAL = TicketType.create("portal", Vec3i::compareTo, 300);
|
||||
public static final TicketType<ChunkPos> ENDER_PEARL = TicketType.create("ender_pearl", Comparator.comparingLong(ChunkPos::toLong), 40);
|
||||
public static final TicketType<ChunkPos> UNKNOWN = TicketType.create("unknown", Comparator.comparingLong(ChunkPos::toLong), 1);
|
||||
public long timeout;
|
||||
@@ -17,6 +_,9 @@
|
||||
public static final TicketType<BlockPos> PORTAL = create("portal", Vec3i::compareTo, 300);
|
||||
public static final TicketType<ChunkPos> ENDER_PEARL = create("ender_pearl", Comparator.comparingLong(ChunkPos::toLong), 40);
|
||||
public static final TicketType<ChunkPos> UNKNOWN = create("unknown", Comparator.comparingLong(ChunkPos::toLong), 1);
|
||||
+ public static final TicketType<Unit> PLUGIN = TicketType.create("plugin", (a, b) -> 0); // CraftBukkit
|
||||
+ public static final TicketType<org.bukkit.plugin.Plugin> PLUGIN_TICKET = TicketType.create("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit
|
||||
+ public static final TicketType<Integer> POST_TELEPORT = TicketType.create("post_teleport", Integer::compare, 5); // Paper - post teleport ticket type
|
||||
|
||||
public static <T> TicketType<T> create(String name, Comparator<T> argumentComparator) {
|
||||
return new TicketType<>(name, argumentComparator, 0L);
|
||||
public static <T> TicketType<T> create(String name, Comparator<T> comparator) {
|
||||
return new TicketType<>(name, comparator, 0L);
|
|
@ -1,11 +1,9 @@
|
|||
--- a/net/minecraft/server/level/WorldGenRegion.java
|
||||
+++ b/net/minecraft/server/level/WorldGenRegion.java
|
||||
@@ -167,7 +167,27 @@
|
||||
int k = this.center.getPos().getChessboardDistance(chunkX, chunkZ);
|
||||
@@ -151,6 +_,26 @@
|
||||
return chessboardDistance < this.generatingStep.directDependencies().size();
|
||||
}
|
||||
|
||||
return k < this.generatingStep.directDependencies().size();
|
||||
+ }
|
||||
+
|
||||
+ // Paper start - if loaded util
|
||||
+ @Nullable
|
||||
+ @Override
|
||||
|
@ -23,36 +21,43 @@
|
|||
+ public final FluidState getFluidIfLoaded(BlockPos blockposition) {
|
||||
+ ChunkAccess chunk = this.getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4);
|
||||
+ return chunk == null ? null : chunk.getFluidState(blockposition);
|
||||
}
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
+
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos pos) {
|
||||
@@ -217,7 +237,8 @@
|
||||
if (iblockdata.isAir()) {
|
||||
return this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())).getBlockState(pos);
|
||||
@@ -198,7 +_,8 @@
|
||||
if (blockState.isAir()) {
|
||||
return false;
|
||||
} else {
|
||||
- if (drop) {
|
||||
+ if (drop) LOGGER.warn("Potential async entity add during worldgen", new Throwable()); // Paper - Fix async entity add due to fungus trees; log when this happens
|
||||
- if (dropBlock) {
|
||||
+ if (dropBlock) LOGGER.warn("Potential async entity add during worldgen", new Throwable()); // Paper - Fix async entity add due to fungus trees; log when this happens
|
||||
+ if (false) { // CraftBukkit - SPIGOT-6833: Do not drop during world generation
|
||||
BlockEntity tileentity = iblockdata.hasBlockEntity() ? this.getBlockEntity(pos) : null;
|
||||
|
||||
Block.dropResources(iblockdata, this.level, pos, tileentity, breakingEntity, ItemStack.EMPTY);
|
||||
@@ -264,6 +285,7 @@
|
||||
BlockEntity blockEntity = blockState.hasBlockEntity() ? this.getBlockEntity(pos) : null;
|
||||
Block.dropResources(blockState, this.level, pos, blockEntity, entity, ItemStack.EMPTY);
|
||||
}
|
||||
@@ -242,6 +_,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ private boolean hasSetFarWarned = false; // Paper - Buffer OOB setBlock calls
|
||||
@Override
|
||||
public boolean ensureCanWrite(BlockPos pos) {
|
||||
int i = SectionPos.blockToSectionCoord(pos.getX());
|
||||
@@ -283,7 +305,15 @@
|
||||
int sectionPosX = SectionPos.blockToSectionCoord(pos.getX());
|
||||
@@ -259,6 +_,8 @@
|
||||
|
||||
return true;
|
||||
} else {
|
||||
+ // Paper start - Buffer OOB setBlock calls
|
||||
+ if (!hasSetFarWarned) {
|
||||
Util.logAndPauseIfInIde("Detected setBlock in a far chunk [" + i + ", " + j + "], pos: " + String.valueOf(pos) + ", status: " + String.valueOf(this.generatingStep.targetStatus()) + (this.currentlyGenerating == null ? "" : ", currently generating: " + (String) this.currentlyGenerating.get()));
|
||||
Util.logAndPauseIfInIde(
|
||||
"Detected setBlock in a far chunk ["
|
||||
+ sectionPosX
|
||||
@@ -270,6 +_,12 @@
|
||||
+ this.generatingStep.targetStatus()
|
||||
+ (this.currentlyGenerating == null ? "" : ", currently generating: " + this.currentlyGenerating.get())
|
||||
);
|
||||
+ hasSetFarWarned = true;
|
||||
+ if (this.getServer() != null && this.getServer().isDebugging()) {
|
||||
+ io.papermc.paper.util.TraceUtil.dumpTraceForThread("far setBlock call");
|
||||
|
@ -62,17 +67,17 @@
|
|||
return false;
|
||||
}
|
||||
}
|
||||
@@ -294,7 +324,7 @@
|
||||
@@ -280,7 +_,7 @@
|
||||
return false;
|
||||
} else {
|
||||
ChunkAccess ichunkaccess = this.getChunk(pos);
|
||||
- BlockState iblockdata1 = ichunkaccess.setBlockState(pos, state, false);
|
||||
+ BlockState iblockdata1 = ichunkaccess.setBlockState(pos, state, false); final BlockState previousBlockState = iblockdata1; // Paper - Clear block entity before setting up a DUMMY block entity - obfhelper
|
||||
|
||||
if (iblockdata1 != null) {
|
||||
this.level.onBlockStateChange(pos, iblockdata1, state);
|
||||
@@ -310,6 +340,17 @@
|
||||
ichunkaccess.removeBlockEntity(pos);
|
||||
ChunkAccess chunk = this.getChunk(pos);
|
||||
- BlockState blockState = chunk.setBlockState(pos, state, false);
|
||||
+ BlockState blockState = chunk.setBlockState(pos, state, false); final BlockState previousBlockState = blockState; // Paper - Clear block entity before setting up a DUMMY block entity - obfhelper
|
||||
if (blockState != null) {
|
||||
this.level.onBlockStateChange(pos, blockState, state);
|
||||
}
|
||||
@@ -294,6 +_,17 @@
|
||||
chunk.removeBlockEntity(pos);
|
||||
}
|
||||
} else {
|
||||
+ // Paper start - Clear block entity before setting up a DUMMY block entity
|
||||
|
@ -83,13 +88,13 @@
|
|||
+ // be waterlogged would remove its existing block entity (see PaperMC/Paper#10750)
|
||||
+ // This logic is *also* found in LevelChunk#setBlockState.
|
||||
+ if (previousBlockState != null && !java.util.Objects.equals(previousBlockState.getBlock(), state.getBlock())) {
|
||||
+ ichunkaccess.removeBlockEntity(pos);
|
||||
+ chunk.removeBlockEntity(pos);
|
||||
+ }
|
||||
+ // Paper end - Clear block entity before setting up a DUMMY block entity
|
||||
CompoundTag nbttagcompound = new CompoundTag();
|
||||
|
||||
nbttagcompound.putInt("x", pos.getX());
|
||||
@@ -336,6 +377,13 @@
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putInt("x", pos.getX());
|
||||
compoundTag.putInt("y", pos.getY());
|
||||
@@ -319,6 +_,13 @@
|
||||
|
||||
@Override
|
||||
public boolean addFreshEntity(Entity entity) {
|
||||
|
@ -100,6 +105,6 @@
|
|||
+ @Override
|
||||
+ public boolean addFreshEntity(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
|
||||
+ // CraftBukkit end
|
||||
int i = SectionPos.blockToSectionCoord(entity.getBlockX());
|
||||
int j = SectionPos.blockToSectionCoord(entity.getBlockZ());
|
||||
|
||||
int sectionPosX = SectionPos.blockToSectionCoord(entity.getBlockX());
|
||||
int sectionPosZ = SectionPos.blockToSectionCoord(entity.getBlockZ());
|
||||
this.getChunk(sectionPosX, sectionPosZ).addEntity(entity);
|
|
@ -1,433 +0,0 @@
|
|||
--- a/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -104,6 +104,10 @@
|
||||
import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
+// CraftBukkit start
|
||||
+import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
|
||||
+// CraftBukkit end
|
||||
+
|
||||
public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider, GeneratingChunkMap {
|
||||
|
||||
private static final ChunkResult<List<ChunkAccess>> UNLOADED_CHUNK_LIST_RESULT = ChunkResult.error("Unloaded chunks found in range");
|
||||
@@ -149,6 +153,33 @@
|
||||
public int serverViewDistance;
|
||||
private final WorldGenContext worldGenContext;
|
||||
|
||||
+ // CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback()
|
||||
+ public final CallbackExecutor callbackExecutor = new CallbackExecutor();
|
||||
+ public static final class CallbackExecutor implements java.util.concurrent.Executor, Runnable {
|
||||
+
|
||||
+ private final java.util.Queue<Runnable> queue = new java.util.ArrayDeque<>();
|
||||
+
|
||||
+ @Override
|
||||
+ public void execute(Runnable runnable) {
|
||||
+ this.queue.add(runnable);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void run() {
|
||||
+ Runnable task;
|
||||
+ while ((task = this.queue.poll()) != null) {
|
||||
+ task.run();
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
+ // Paper start
|
||||
+ public final ChunkHolder getUnloadingChunkHolder(int chunkX, int chunkZ) {
|
||||
+ return this.pendingUnloads.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
|
||||
super(new RegionStorageInfo(session.getLevelId(), world.dimension(), "chunk"), session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
|
||||
this.visibleChunkMap = this.updatingChunkMap.clone();
|
||||
@@ -170,13 +201,19 @@
|
||||
RegistryAccess iregistrycustom = world.registryAccess();
|
||||
long j = world.getSeed();
|
||||
|
||||
- if (chunkGenerator instanceof NoiseBasedChunkGenerator chunkgeneratorabstract) {
|
||||
+ // CraftBukkit start - SPIGOT-7051: It's a rigged game! Use delegate for random state creation, otherwise it is not so random.
|
||||
+ ChunkGenerator randomGenerator = chunkGenerator;
|
||||
+ if (randomGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
||||
+ randomGenerator = customChunkGenerator.getDelegate();
|
||||
+ }
|
||||
+ if (randomGenerator instanceof NoiseBasedChunkGenerator chunkgeneratorabstract) {
|
||||
+ // CraftBukkit end
|
||||
this.randomState = RandomState.create((NoiseGeneratorSettings) chunkgeneratorabstract.generatorSettings().value(), (HolderGetter) iregistrycustom.lookupOrThrow(Registries.NOISE), j);
|
||||
} else {
|
||||
this.randomState = RandomState.create(NoiseGeneratorSettings.dummy(), (HolderGetter) iregistrycustom.lookupOrThrow(Registries.NOISE), j);
|
||||
}
|
||||
|
||||
- this.chunkGeneratorState = chunkGenerator.createState(iregistrycustom.lookupOrThrow(Registries.STRUCTURE_SET), this.randomState, j);
|
||||
+ this.chunkGeneratorState = chunkGenerator.createState(iregistrycustom.lookupOrThrow(Registries.STRUCTURE_SET), this.randomState, j, world.spigotConfig); // Spigot
|
||||
this.mainThreadExecutor = mainThreadExecutor;
|
||||
ConsecutiveExecutor consecutiveexecutor = new ConsecutiveExecutor(executor, "worldgen");
|
||||
|
||||
@@ -198,6 +235,12 @@
|
||||
this.chunksToEagerlySave.add(pos.toLong());
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
protected ChunkGenerator generator() {
|
||||
return this.worldGenContext.generator();
|
||||
}
|
||||
@@ -325,7 +368,7 @@
|
||||
throw this.debugFuturesAndCreateReportedException(new IllegalStateException("At least one of the chunk futures were null"), "n/a");
|
||||
}
|
||||
|
||||
- ChunkAccess ichunkaccess = (ChunkAccess) chunkresult.orElse((Object) null);
|
||||
+ ChunkAccess ichunkaccess = (ChunkAccess) chunkresult.orElse(null); // CraftBukkit - decompile error
|
||||
|
||||
if (ichunkaccess == null) {
|
||||
return ChunkMap.UNLOADED_CHUNK_LIST_RESULT;
|
||||
@@ -354,9 +397,9 @@
|
||||
};
|
||||
|
||||
stringbuilder.append("Updating:").append(System.lineSeparator());
|
||||
- this.updatingChunkMap.values().forEach(consumer);
|
||||
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.getUpdatingChunkHolders(this.level).forEach(consumer); // Paper
|
||||
stringbuilder.append("Visible:").append(System.lineSeparator());
|
||||
- this.visibleChunkMap.values().forEach(consumer);
|
||||
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level).forEach(consumer); // Paper
|
||||
CrashReport crashreport = CrashReport.forThrowable(exception, "Chunk loading");
|
||||
CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Chunk loading");
|
||||
|
||||
@@ -398,6 +441,9 @@
|
||||
holder.setTicketLevel(level);
|
||||
} else {
|
||||
holder = new ChunkHolder(new ChunkPos(pos), level, this.level, this.lightEngine, this::onLevelChange, this);
|
||||
+ // Paper start
|
||||
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.onChunkHolderCreate(this.level, holder);
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
this.updatingChunkMap.put(pos, holder);
|
||||
@@ -427,7 +473,7 @@
|
||||
|
||||
protected void saveAllChunks(boolean flush) {
|
||||
if (flush) {
|
||||
- List<ChunkHolder> list = this.visibleChunkMap.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).toList();
|
||||
+ List<ChunkHolder> list = ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level).stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).toList(); // Paper
|
||||
MutableBoolean mutableboolean = new MutableBoolean();
|
||||
|
||||
do {
|
||||
@@ -453,7 +499,7 @@
|
||||
} else {
|
||||
this.nextChunkSaveTime.clear();
|
||||
long i = Util.getMillis();
|
||||
- ObjectIterator objectiterator = this.visibleChunkMap.values().iterator();
|
||||
+ Iterator<ChunkHolder> objectiterator = ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level).iterator(); // Paper
|
||||
|
||||
while (objectiterator.hasNext()) {
|
||||
ChunkHolder playerchunk = (ChunkHolder) objectiterator.next();
|
||||
@@ -478,7 +524,7 @@
|
||||
}
|
||||
|
||||
public boolean hasWork() {
|
||||
- return this.lightEngine.hasLightWork() || !this.pendingUnloads.isEmpty() || !this.updatingChunkMap.isEmpty() || this.poiManager.hasWork() || !this.toDrop.isEmpty() || !this.unloadQueue.isEmpty() || this.worldgenTaskDispatcher.hasWork() || this.lightTaskDispatcher.hasWork() || this.distanceManager.hasTickets();
|
||||
+ return this.lightEngine.hasLightWork() || !this.pendingUnloads.isEmpty() || ca.spottedleaf.moonrise.common.util.ChunkSystem.hasAnyChunkHolders(this.level) || !this.updatingChunkMap.isEmpty() || this.poiManager.hasWork() || !this.toDrop.isEmpty() || !this.unloadQueue.isEmpty() || this.worldgenTaskDispatcher.hasWork() || this.lightTaskDispatcher.hasWork() || this.distanceManager.hasTickets();
|
||||
}
|
||||
|
||||
private void processUnloads(BooleanSupplier shouldKeepTicking) {
|
||||
@@ -537,8 +583,11 @@
|
||||
this.scheduleUnload(pos, chunk);
|
||||
} else {
|
||||
ChunkAccess ichunkaccess = chunk.getLatestChunk();
|
||||
-
|
||||
- if (this.pendingUnloads.remove(pos, chunk) && ichunkaccess != null) {
|
||||
+ // Paper start
|
||||
+ boolean removed;
|
||||
+ if ((removed = this.pendingUnloads.remove(pos, chunk)) && ichunkaccess != null) {
|
||||
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.onChunkHolderDelete(this.level, chunk);
|
||||
+ // Paper end
|
||||
LevelChunk chunk1;
|
||||
|
||||
if (ichunkaccess instanceof LevelChunk) {
|
||||
@@ -556,7 +605,9 @@
|
||||
this.lightEngine.tryScheduleUpdate();
|
||||
this.progressListener.onStatusChange(ichunkaccess.getPos(), (ChunkStatus) null);
|
||||
this.nextChunkSaveTime.remove(ichunkaccess.getPos().toLong());
|
||||
- }
|
||||
+ } else if (removed) { // Paper start
|
||||
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.onChunkHolderDelete(this.level, chunk);
|
||||
+ } // Paper end
|
||||
|
||||
}
|
||||
};
|
||||
@@ -905,7 +956,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- protected void setServerViewDistance(int watchDistance) {
|
||||
+ public void setServerViewDistance(int watchDistance) { // Paper - public
|
||||
int j = Mth.clamp(watchDistance, 2, 32);
|
||||
|
||||
if (j != this.serverViewDistance) {
|
||||
@@ -922,7 +973,7 @@
|
||||
|
||||
}
|
||||
|
||||
- int getPlayerViewDistance(ServerPlayer player) {
|
||||
+ public int getPlayerViewDistance(ServerPlayer player) { // Paper - public
|
||||
return Mth.clamp(player.requestedViewDistance(), 2, this.serverViewDistance);
|
||||
}
|
||||
|
||||
@@ -951,7 +1002,7 @@
|
||||
}
|
||||
|
||||
public int size() {
|
||||
- return this.visibleChunkMap.size();
|
||||
+ return ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolderCount(this.level); // Paper
|
||||
}
|
||||
|
||||
public DistanceManager getDistanceManager() {
|
||||
@@ -959,25 +1010,26 @@
|
||||
}
|
||||
|
||||
protected Iterable<ChunkHolder> getChunks() {
|
||||
- return Iterables.unmodifiableIterable(this.visibleChunkMap.values());
|
||||
+ return Iterables.unmodifiableIterable(ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level)); // Paper
|
||||
}
|
||||
|
||||
void dumpChunks(Writer writer) throws IOException {
|
||||
CsvOutput csvwriter = CsvOutput.builder().addColumn("x").addColumn("z").addColumn("level").addColumn("in_memory").addColumn("status").addColumn("full_status").addColumn("accessible_ready").addColumn("ticking_ready").addColumn("entity_ticking_ready").addColumn("ticket").addColumn("spawning").addColumn("block_entity_count").addColumn("ticking_ticket").addColumn("ticking_level").addColumn("block_ticks").addColumn("fluid_ticks").build(writer);
|
||||
TickingTracker tickingtracker = this.distanceManager.tickingTracker();
|
||||
- ObjectBidirectionalIterator objectbidirectionaliterator = this.visibleChunkMap.long2ObjectEntrySet().iterator();
|
||||
+ Iterator<ChunkHolder> objectbidirectionaliterator = ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level).iterator(); // Paper
|
||||
|
||||
while (objectbidirectionaliterator.hasNext()) {
|
||||
- Entry<ChunkHolder> entry = (Entry) objectbidirectionaliterator.next();
|
||||
- long i = entry.getLongKey();
|
||||
+ ChunkHolder playerchunk = objectbidirectionaliterator.next(); // Paper
|
||||
+ long i = playerchunk.pos.toLong(); // Paper
|
||||
ChunkPos chunkcoordintpair = new ChunkPos(i);
|
||||
- ChunkHolder playerchunk = (ChunkHolder) entry.getValue();
|
||||
+ // Paper - move up
|
||||
Optional<ChunkAccess> optional = Optional.ofNullable(playerchunk.getLatestChunk());
|
||||
Optional<LevelChunk> optional1 = optional.flatMap((ichunkaccess) -> {
|
||||
return ichunkaccess instanceof LevelChunk ? Optional.of((LevelChunk) ichunkaccess) : Optional.empty();
|
||||
});
|
||||
|
||||
- csvwriter.writeRow(chunkcoordintpair.x, chunkcoordintpair.z, playerchunk.getTicketLevel(), optional.isPresent(), optional.map(ChunkAccess::getPersistedStatus).orElse((Object) null), optional1.map(LevelChunk::getFullStatus).orElse((Object) null), ChunkMap.printFuture(playerchunk.getFullChunkFuture()), ChunkMap.printFuture(playerchunk.getTickingChunkFuture()), ChunkMap.printFuture(playerchunk.getEntityTickingChunkFuture()), this.distanceManager.getTicketDebugString(i), this.anyPlayerCloseEnoughForSpawning(chunkcoordintpair), optional1.map((chunk) -> {
|
||||
+ // CraftBukkit - decompile error
|
||||
+ csvwriter.writeRow(chunkcoordintpair.x, chunkcoordintpair.z, playerchunk.getTicketLevel(), optional.isPresent(), optional.map(ChunkAccess::getPersistedStatus).orElse(null), optional1.map(LevelChunk::getFullStatus).orElse(null), ChunkMap.printFuture(playerchunk.getFullChunkFuture()), ChunkMap.printFuture(playerchunk.getTickingChunkFuture()), ChunkMap.printFuture(playerchunk.getEntityTickingChunkFuture()), this.distanceManager.getTicketDebugString(i), this.anyPlayerCloseEnoughForSpawning(chunkcoordintpair), optional1.map((chunk) -> {
|
||||
return chunk.getBlockEntities().size();
|
||||
}).orElse(0), tickingtracker.getTicketDebugString(i), tickingtracker.getLevel(i), optional1.map((chunk) -> {
|
||||
return chunk.getBlockTicks().count();
|
||||
@@ -990,7 +1042,7 @@
|
||||
|
||||
private static String printFuture(CompletableFuture<ChunkResult<LevelChunk>> future) {
|
||||
try {
|
||||
- ChunkResult<LevelChunk> chunkresult = (ChunkResult) future.getNow((Object) null);
|
||||
+ ChunkResult<LevelChunk> chunkresult = (ChunkResult) future.getNow(null); // CraftBukkit - decompile error
|
||||
|
||||
return chunkresult != null ? (chunkresult.isSuccess() ? "done" : "unloaded") : "not completed";
|
||||
} catch (CompletionException completionexception) {
|
||||
@@ -1002,12 +1054,14 @@
|
||||
|
||||
private CompletableFuture<Optional<CompoundTag>> readChunk(ChunkPos chunkPos) {
|
||||
return this.read(chunkPos).thenApplyAsync((optional) -> {
|
||||
- return optional.map(this::upgradeChunkTag);
|
||||
+ return optional.map((nbttagcompound) -> this.upgradeChunkTag(nbttagcompound, chunkPos)); // CraftBukkit
|
||||
}, Util.backgroundExecutor().forName("upgradeChunk"));
|
||||
}
|
||||
|
||||
- private CompoundTag upgradeChunkTag(CompoundTag nbt) {
|
||||
- return this.upgradeChunkTag(this.level.dimension(), this.overworldDataStorage, nbt, this.generator().getTypeNameForDataFixer());
|
||||
+ // CraftBukkit start
|
||||
+ private CompoundTag upgradeChunkTag(CompoundTag nbttagcompound, ChunkPos chunkcoordintpair) {
|
||||
+ return this.upgradeChunkTag(this.level.getTypeKey(), this.overworldDataStorage, nbttagcompound, this.generator().getTypeNameForDataFixer(), chunkcoordintpair, this.level);
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
|
||||
void forEachSpawnCandidateChunk(Consumer<ChunkHolder> callback) {
|
||||
@@ -1025,10 +1079,23 @@
|
||||
}
|
||||
|
||||
public boolean anyPlayerCloseEnoughForSpawning(ChunkPos pos) {
|
||||
- return !this.distanceManager.hasPlayersNearby(pos.toLong()) ? false : this.anyPlayerCloseEnoughForSpawningInternal(pos);
|
||||
+ // Spigot start
|
||||
+ return this.anyPlayerCloseEnoughForSpawning(pos, false);
|
||||
}
|
||||
|
||||
+ boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkcoordintpair, boolean reducedRange) {
|
||||
+ return !this.distanceManager.hasPlayersNearby(chunkcoordintpair.toLong()) ? false : this.anyPlayerCloseEnoughForSpawningInternal(chunkcoordintpair, reducedRange);
|
||||
+ // Spigot end
|
||||
+ }
|
||||
+
|
||||
private boolean anyPlayerCloseEnoughForSpawningInternal(ChunkPos pos) {
|
||||
+ // Spigot start
|
||||
+ return this.anyPlayerCloseEnoughForSpawningInternal(pos, false);
|
||||
+ }
|
||||
+
|
||||
+ private boolean anyPlayerCloseEnoughForSpawningInternal(ChunkPos chunkcoordintpair, boolean reducedRange) {
|
||||
+ double blockRange; // Paper - use from event
|
||||
+ // Spigot end
|
||||
Iterator iterator = this.playerMap.getAllPlayers().iterator();
|
||||
|
||||
ServerPlayer entityplayer;
|
||||
@@ -1039,7 +1106,16 @@
|
||||
}
|
||||
|
||||
entityplayer = (ServerPlayer) iterator.next();
|
||||
- } while (!this.playerIsCloseEnoughForSpawning(entityplayer, pos));
|
||||
+ // Paper start - PlayerNaturallySpawnCreaturesEvent
|
||||
+ com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event;
|
||||
+ blockRange = 16384.0D;
|
||||
+ if (reducedRange) {
|
||||
+ event = entityplayer.playerNaturallySpawnedEvent;
|
||||
+ if (event == null || event.isCancelled()) continue;
|
||||
+ blockRange = (double) ((event.getSpawnRadius() << 4) * (event.getSpawnRadius() << 4));
|
||||
+ }
|
||||
+ // Paper end - PlayerNaturallySpawnCreaturesEvent
|
||||
+ } while (!this.playerIsCloseEnoughForSpawning(entityplayer, chunkcoordintpair, blockRange)); // Spigot
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1056,7 +1132,7 @@
|
||||
while (iterator.hasNext()) {
|
||||
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
|
||||
|
||||
- if (this.playerIsCloseEnoughForSpawning(entityplayer, pos)) {
|
||||
+ if (this.playerIsCloseEnoughForSpawning(entityplayer, pos, 16384.0D)) { // Spigot
|
||||
builder.add(entityplayer);
|
||||
}
|
||||
}
|
||||
@@ -1065,13 +1141,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
- private boolean playerIsCloseEnoughForSpawning(ServerPlayer player, ChunkPos pos) {
|
||||
- if (player.isSpectator()) {
|
||||
+ private boolean playerIsCloseEnoughForSpawning(ServerPlayer entityplayer, ChunkPos chunkcoordintpair, double range) { // Spigot
|
||||
+ if (entityplayer.isSpectator()) {
|
||||
return false;
|
||||
} else {
|
||||
- double d0 = ChunkMap.euclideanDistanceSquared(pos, player);
|
||||
+ double d0 = ChunkMap.euclideanDistanceSquared(chunkcoordintpair, entityplayer);
|
||||
|
||||
- return d0 < 16384.0D;
|
||||
+ return d0 < range; // Spigot
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1215,9 +1291,19 @@
|
||||
}
|
||||
|
||||
public void addEntity(Entity entity) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot
|
||||
+ // Paper start - ignore and warn about illegal addEntity calls instead of crashing server
|
||||
+ if (!entity.valid || entity.level() != this.level || this.entityMap.containsKey(entity.getId())) {
|
||||
+ LOGGER.error("Illegal ChunkMap::addEntity for world " + this.level.getWorld().getName()
|
||||
+ + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""), new Throwable());
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - ignore and warn about illegal addEntity calls instead of crashing server
|
||||
+ if (entity instanceof ServerPlayer && ((ServerPlayer) entity).supressTrackerForLogin) return; // Paper - Fire PlayerJoinEvent when Player is actually ready; Delay adding to tracker until after list packets
|
||||
if (!(entity instanceof EnderDragonPart)) {
|
||||
EntityType<?> entitytypes = entity.getType();
|
||||
int i = entitytypes.clientTrackingRange() * 16;
|
||||
+ i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot
|
||||
|
||||
if (i != 0) {
|
||||
int j = entitytypes.updateInterval();
|
||||
@@ -1250,6 +1336,7 @@
|
||||
}
|
||||
|
||||
protected void removeEntity(Entity entity) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("entity untrack"); // Spigot
|
||||
if (entity instanceof ServerPlayer entityplayer) {
|
||||
this.updatePlayerStatus(entityplayer, false);
|
||||
ObjectIterator objectiterator = this.entityMap.values().iterator();
|
||||
@@ -1391,7 +1478,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
- private class ChunkDistanceManager extends DistanceManager {
|
||||
+ public class ChunkDistanceManager extends DistanceManager { // Paper - public
|
||||
|
||||
protected ChunkDistanceManager(final Executor workerExecutor, final Executor mainThreadExecutor) {
|
||||
super(workerExecutor, mainThreadExecutor);
|
||||
@@ -1421,10 +1508,10 @@
|
||||
final Entity entity;
|
||||
private final int range;
|
||||
SectionPos lastSectionPos;
|
||||
- public final Set<ServerPlayerConnection> seenBy = Sets.newIdentityHashSet();
|
||||
+ public final Set<ServerPlayerConnection> seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl
|
||||
|
||||
public TrackedEntity(final Entity entity, final int i, final int j, final boolean flag) {
|
||||
- this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast);
|
||||
+ this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit
|
||||
this.entity = entity;
|
||||
this.range = i;
|
||||
this.lastSectionPos = SectionPos.of((EntityAccess) entity);
|
||||
@@ -1469,6 +1556,7 @@
|
||||
}
|
||||
|
||||
public void removePlayer(ServerPlayer player) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot
|
||||
if (this.seenBy.remove(player.connection)) {
|
||||
this.serverEntity.removePairing(player);
|
||||
}
|
||||
@@ -1476,17 +1564,41 @@
|
||||
}
|
||||
|
||||
public void updatePlayer(ServerPlayer player) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
|
||||
if (player != this.entity) {
|
||||
- Vec3 vec3d = player.position().subtract(this.entity.position());
|
||||
+ // Paper start - remove allocation of Vec3D here
|
||||
+ // Vec3 vec3d = player.position().subtract(this.entity.position());
|
||||
+ double vec3d_dx = player.getX() - this.entity.getX();
|
||||
+ double vec3d_dz = player.getZ() - this.entity.getZ();
|
||||
+ // Paper end - remove allocation of Vec3D here
|
||||
int i = ChunkMap.this.getPlayerViewDistance(player);
|
||||
double d0 = (double) Math.min(this.getEffectiveRange(), i * 16);
|
||||
- double d1 = vec3d.x * vec3d.x + vec3d.z * vec3d.z;
|
||||
+ double d1 = vec3d_dx * vec3d_dx + vec3d_dz * vec3d_dz; // Paper
|
||||
double d2 = d0 * d0;
|
||||
- boolean flag = d1 <= d2 && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z);
|
||||
+ // Paper start - Configurable entity tracking range by Y
|
||||
+ boolean flag = d1 <= d2;
|
||||
+ if (flag && level.paperConfig().entities.trackingRangeY.enabled) {
|
||||
+ double rangeY = level.paperConfig().entities.trackingRangeY.get(this.entity, -1);
|
||||
+ if (rangeY != -1) {
|
||||
+ double vec3d_dy = player.getY() - this.entity.getY();
|
||||
+ flag = vec3d_dy * vec3d_dy <= rangeY * rangeY;
|
||||
+ }
|
||||
+ }
|
||||
+ flag = flag && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z);
|
||||
+ // Paper end - Configurable entity tracking range by Y
|
||||
|
||||
+ // CraftBukkit start - respect vanish API
|
||||
+ if (flag && !player.getBukkitEntity().canSee(this.entity.getBukkitEntity())) { // Paper - only consider hits
|
||||
+ flag = false;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
if (flag) {
|
||||
if (this.seenBy.add(player.connection)) {
|
||||
+ // Paper start - entity tracking events
|
||||
+ if (io.papermc.paper.event.player.PlayerTrackEntityEvent.getHandlerList().getRegisteredListeners().length == 0 || new io.papermc.paper.event.player.PlayerTrackEntityEvent(player.getBukkitEntity(), this.entity.getBukkitEntity()).callEvent()) {
|
||||
this.serverEntity.addPairing(player);
|
||||
+ }
|
||||
+ // Paper end - entity tracking events
|
||||
}
|
||||
} else if (this.seenBy.remove(player.connection)) {
|
||||
this.serverEntity.removePairing(player);
|
||||
@@ -1506,6 +1618,7 @@
|
||||
while (iterator.hasNext()) {
|
||||
Entity entity = (Entity) iterator.next();
|
||||
int j = entity.getType().clientTrackingRange() * 16;
|
||||
+ j = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, j); // Paper
|
||||
|
||||
if (j > i) {
|
||||
i = j;
|
|
@ -1,152 +0,0 @@
|
|||
--- a/net/minecraft/server/level/DistanceManager.java
|
||||
+++ b/net/minecraft/server/level/DistanceManager.java
|
||||
@@ -117,8 +117,17 @@
|
||||
|
||||
ChunkHolder playerchunk;
|
||||
|
||||
+ // CraftBukkit start - SPIGOT-7780: Call chunk unload events before updateHighestAllowedStatus
|
||||
while (iterator.hasNext()) {
|
||||
playerchunk = (ChunkHolder) iterator.next();
|
||||
+ playerchunk.callEventIfUnloading(chunkLoadingManager);
|
||||
+ }
|
||||
+
|
||||
+ iterator = this.chunksToUpdateFutures.iterator();
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
+ while (iterator.hasNext()) {
|
||||
+ playerchunk = (ChunkHolder) iterator.next();
|
||||
playerchunk.updateHighestAllowedStatus(chunkLoadingManager);
|
||||
}
|
||||
|
||||
@@ -165,30 +174,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
- void addTicket(long position, Ticket<?> ticket) {
|
||||
- SortedArraySet<Ticket<?>> arraysetsorted = this.getTickets(position);
|
||||
+ boolean addTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
|
||||
+ SortedArraySet<Ticket<?>> arraysetsorted = this.getTickets(i);
|
||||
int j = DistanceManager.getTicketLevelAt(arraysetsorted);
|
||||
Ticket<?> ticket1 = (Ticket) arraysetsorted.addOrGet(ticket);
|
||||
|
||||
ticket1.setCreatedTick(this.ticketTickCounter);
|
||||
if (ticket.getTicketLevel() < j) {
|
||||
- this.ticketTracker.update(position, ticket.getTicketLevel(), true);
|
||||
+ this.ticketTracker.update(i, ticket.getTicketLevel(), true);
|
||||
}
|
||||
|
||||
+ return ticket == ticket1; // CraftBukkit
|
||||
}
|
||||
|
||||
- void removeTicket(long pos, Ticket<?> ticket) {
|
||||
- SortedArraySet<Ticket<?>> arraysetsorted = this.getTickets(pos);
|
||||
+ boolean removeTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
|
||||
+ SortedArraySet<Ticket<?>> arraysetsorted = this.getTickets(i);
|
||||
|
||||
+ boolean removed = false; // CraftBukkit
|
||||
if (arraysetsorted.remove(ticket)) {
|
||||
- ;
|
||||
+ removed = true; // CraftBukkit
|
||||
}
|
||||
|
||||
if (arraysetsorted.isEmpty()) {
|
||||
- this.tickets.remove(pos);
|
||||
+ this.tickets.remove(i);
|
||||
}
|
||||
|
||||
- this.ticketTracker.update(pos, DistanceManager.getTicketLevelAt(arraysetsorted), false);
|
||||
+ this.ticketTracker.update(i, DistanceManager.getTicketLevelAt(arraysetsorted), false);
|
||||
+ return removed; // CraftBukkit
|
||||
}
|
||||
|
||||
public <T> void addTicket(TicketType<T> type, ChunkPos pos, int level, T argument) {
|
||||
@@ -202,19 +214,33 @@
|
||||
}
|
||||
|
||||
public <T> void addRegionTicket(TicketType<T> type, ChunkPos pos, int radius, T argument) {
|
||||
- Ticket<T> ticket = new Ticket<>(type, ChunkLevel.byStatus(FullChunkStatus.FULL) - radius, argument);
|
||||
- long j = pos.toLong();
|
||||
+ // CraftBukkit start
|
||||
+ this.addRegionTicketAtDistance(type, pos, radius, argument);
|
||||
+ }
|
||||
|
||||
- this.addTicket(j, ticket);
|
||||
+ public <T> boolean addRegionTicketAtDistance(TicketType<T> tickettype, ChunkPos chunkcoordintpair, int i, T t0) {
|
||||
+ // CraftBukkit end
|
||||
+ Ticket<T> ticket = new Ticket<>(tickettype, ChunkLevel.byStatus(FullChunkStatus.FULL) - i, t0);
|
||||
+ long j = chunkcoordintpair.toLong();
|
||||
+
|
||||
+ boolean added = this.addTicket(j, ticket); // CraftBukkit
|
||||
this.tickingTicketsTracker.addTicket(j, ticket);
|
||||
+ return added; // CraftBukkit
|
||||
}
|
||||
|
||||
public <T> void removeRegionTicket(TicketType<T> type, ChunkPos pos, int radius, T argument) {
|
||||
- Ticket<T> ticket = new Ticket<>(type, ChunkLevel.byStatus(FullChunkStatus.FULL) - radius, argument);
|
||||
- long j = pos.toLong();
|
||||
+ // CraftBukkit start
|
||||
+ this.removeRegionTicketAtDistance(type, pos, radius, argument);
|
||||
+ }
|
||||
|
||||
- this.removeTicket(j, ticket);
|
||||
+ public <T> boolean removeRegionTicketAtDistance(TicketType<T> tickettype, ChunkPos chunkcoordintpair, int i, T t0) {
|
||||
+ // CraftBukkit end
|
||||
+ Ticket<T> ticket = new Ticket<>(tickettype, ChunkLevel.byStatus(FullChunkStatus.FULL) - i, t0);
|
||||
+ long j = chunkcoordintpair.toLong();
|
||||
+
|
||||
+ boolean removed = this.removeTicket(j, ticket); // CraftBukkit
|
||||
this.tickingTicketsTracker.removeTicket(j, ticket);
|
||||
+ return removed; // CraftBukkit
|
||||
}
|
||||
|
||||
private SortedArraySet<Ticket<?>> getTickets(long position) {
|
||||
@@ -253,9 +279,10 @@
|
||||
ChunkPos chunkcoordintpair = pos.chunk();
|
||||
long i = chunkcoordintpair.toLong();
|
||||
ObjectSet<ServerPlayer> objectset = (ObjectSet) this.playersPerChunk.get(i);
|
||||
+ if (objectset == null) return; // CraftBukkit - SPIGOT-6208
|
||||
|
||||
- objectset.remove(player);
|
||||
- if (objectset.isEmpty()) {
|
||||
+ if (objectset != null) objectset.remove(player); // Paper - some state corruption happens here, don't crash, clean up gracefully
|
||||
+ if (objectset == null || objectset.isEmpty()) { // Paper
|
||||
this.playersPerChunk.remove(i);
|
||||
this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false);
|
||||
this.playerTicketManager.update(i, Integer.MAX_VALUE, false);
|
||||
@@ -358,7 +385,7 @@
|
||||
}
|
||||
|
||||
public void removeTicketsOnClosing() {
|
||||
- ImmutableSet<TicketType<?>> immutableset = ImmutableSet.of(TicketType.UNKNOWN);
|
||||
+ ImmutableSet<TicketType<?>> immutableset = ImmutableSet.of(TicketType.UNKNOWN, TicketType.POST_TELEPORT, TicketType.FUTURE_AWAIT); // Paper - add additional tickets to preserve
|
||||
ObjectIterator<Entry<SortedArraySet<Ticket<?>>>> objectiterator = this.tickets.long2ObjectEntrySet().fastIterator();
|
||||
|
||||
while (objectiterator.hasNext()) {
|
||||
@@ -389,7 +416,27 @@
|
||||
|
||||
public boolean hasTickets() {
|
||||
return !this.tickets.isEmpty();
|
||||
+ }
|
||||
+
|
||||
+ // CraftBukkit start
|
||||
+ public <T> void removeAllTicketsFor(TicketType<T> ticketType, int ticketLevel, T ticketIdentifier) {
|
||||
+ Ticket<T> target = new Ticket<>(ticketType, ticketLevel, ticketIdentifier);
|
||||
+
|
||||
+ for (java.util.Iterator<Entry<SortedArraySet<Ticket<?>>>> iterator = this.tickets.long2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
|
||||
+ Entry<SortedArraySet<Ticket<?>>> entry = iterator.next();
|
||||
+ SortedArraySet<Ticket<?>> tickets = entry.getValue();
|
||||
+ if (tickets.remove(target)) {
|
||||
+ // copied from removeTicket
|
||||
+ this.ticketTracker.update(entry.getLongKey(), DistanceManager.getTicketLevelAt(tickets), false);
|
||||
+
|
||||
+ // can't use entry after it's removed
|
||||
+ if (tickets.isEmpty()) {
|
||||
+ iterator.remove();
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
+ // CraftBukkit end
|
||||
|
||||
private class ChunkTicketTracker extends ChunkTracker {
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
--- a/net/minecraft/server/level/ServerEntity.java
|
||||
+++ b/net/minecraft/server/level/ServerEntity.java
|
||||
@@ -31,7 +31,6 @@
|
||||
import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket;
|
||||
import net.minecraft.network.protocol.game.VecDeltaCodec;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
-import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.Leashable;
|
||||
@@ -50,6 +49,13 @@
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
+// CraftBukkit start
|
||||
+import net.minecraft.server.network.ServerPlayerConnection;
|
||||
+import net.minecraft.util.Mth;
|
||||
+import org.bukkit.entity.Player;
|
||||
+import org.bukkit.event.player.PlayerVelocityEvent;
|
||||
+// CraftBukkit end
|
||||
+
|
||||
public class ServerEntity {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
@@ -69,18 +75,22 @@
|
||||
private Vec3 lastSentMovement;
|
||||
private int tickCount;
|
||||
private int teleportDelay;
|
||||
- private List<Entity> lastPassengers = Collections.emptyList();
|
||||
+ private List<Entity> lastPassengers = com.google.common.collect.ImmutableList.of(); // Paper - optimize passenger checks
|
||||
private boolean wasRiding;
|
||||
private boolean wasOnGround;
|
||||
@Nullable
|
||||
private List<SynchedEntityData.DataValue<?>> trackedDataValues;
|
||||
+ // CraftBukkit start
|
||||
+ private final Set<ServerPlayerConnection> trackedPlayers;
|
||||
|
||||
- public ServerEntity(ServerLevel world, Entity entity, int tickInterval, boolean alwaysUpdateVelocity, Consumer<Packet<?>> receiver) {
|
||||
- this.level = world;
|
||||
- this.broadcast = receiver;
|
||||
+ public ServerEntity(ServerLevel worldserver, Entity entity, int i, boolean flag, Consumer<Packet<?>> consumer, Set<ServerPlayerConnection> trackedPlayers) {
|
||||
+ this.trackedPlayers = trackedPlayers;
|
||||
+ // CraftBukkit end
|
||||
+ this.level = worldserver;
|
||||
+ this.broadcast = consumer;
|
||||
this.entity = entity;
|
||||
- this.updateInterval = tickInterval;
|
||||
- this.trackDelta = alwaysUpdateVelocity;
|
||||
+ this.updateInterval = i;
|
||||
+ this.trackDelta = flag;
|
||||
this.positionCodec.setBase(entity.trackingPosition());
|
||||
this.lastSentMovement = entity.getDeltaMovement();
|
||||
this.lastSentYRot = Mth.packDegrees(entity.getYRot());
|
||||
@@ -94,7 +104,7 @@
|
||||
List<Entity> list = this.entity.getPassengers();
|
||||
|
||||
if (!list.equals(this.lastPassengers)) {
|
||||
- this.broadcast.accept(new ClientboundSetPassengersPacket(this.entity));
|
||||
+ this.broadcastAndSend(new ClientboundSetPassengersPacket(this.entity)); // CraftBukkit
|
||||
ServerEntity.removedPassengers(list, this.lastPassengers).forEach((entity) -> {
|
||||
if (entity instanceof ServerPlayer entityplayer) {
|
||||
entityplayer.connection.teleport(entityplayer.getX(), entityplayer.getY(), entityplayer.getZ(), entityplayer.getYRot(), entityplayer.getXRot());
|
||||
@@ -106,19 +116,19 @@
|
||||
|
||||
Entity entity = this.entity;
|
||||
|
||||
- if (entity instanceof ItemFrame entityitemframe) {
|
||||
- if (this.tickCount % 10 == 0) {
|
||||
+ if (!this.trackedPlayers.isEmpty() && entity instanceof ItemFrame entityitemframe) { // Paper - Perf: Only tick item frames if players can see it
|
||||
+ if (true || this.tickCount % 10 == 0) { // CraftBukkit - Moved below, should always enter this block
|
||||
ItemStack itemstack = entityitemframe.getItem();
|
||||
|
||||
- if (itemstack.getItem() instanceof MapItem) {
|
||||
- MapId mapid = (MapId) itemstack.get(DataComponents.MAP_ID);
|
||||
+ if (this.level.paperConfig().maps.itemFrameCursorUpdateInterval > 0 && this.tickCount % this.level.paperConfig().maps.itemFrameCursorUpdateInterval == 0 && itemstack.getItem() instanceof MapItem) { // CraftBukkit - Moved this.tickCounter % 10 logic here so item frames do not enter the other blocks // Paper - Make item frame map cursor update interval configurable
|
||||
+ MapId mapid = entityitemframe.cachedMapId; // Paper - Perf: Cache map ids on item frames
|
||||
MapItemSavedData worldmap = MapItem.getSavedData(mapid, this.level);
|
||||
|
||||
if (worldmap != null) {
|
||||
- Iterator iterator = this.level.players().iterator();
|
||||
+ Iterator<ServerPlayerConnection> iterator = this.trackedPlayers.iterator(); // CraftBukkit
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
- ServerPlayer entityplayer = (ServerPlayer) iterator.next();
|
||||
+ ServerPlayer entityplayer = iterator.next().getPlayer(); // CraftBukkit
|
||||
|
||||
worldmap.tickCarriedBy(entityplayer, itemstack);
|
||||
Packet<?> packet = worldmap.getUpdatePacket(mapid, entityplayer);
|
||||
@@ -168,7 +178,13 @@
|
||||
|
||||
++this.teleportDelay;
|
||||
Vec3 vec3d = this.entity.trackingPosition();
|
||||
- boolean flag1 = this.positionCodec.delta(vec3d).lengthSqr() >= 7.62939453125E-6D;
|
||||
+ // Paper start - reduce allocation of Vec3D here
|
||||
+ Vec3 base = this.positionCodec.base;
|
||||
+ double vec3d_dx = vec3d.x - base.x;
|
||||
+ double vec3d_dy = vec3d.y - base.y;
|
||||
+ double vec3d_dz = vec3d.z - base.z;
|
||||
+ boolean flag1 = (vec3d_dx * vec3d_dx + vec3d_dy * vec3d_dy + vec3d_dz * vec3d_dz) >= 7.62939453125E-6D;
|
||||
+ // Paper end - reduce allocation of Vec3D here
|
||||
Packet<?> packet1 = null;
|
||||
boolean flag2 = flag1 || this.tickCount % 60 == 0;
|
||||
boolean flag3 = false;
|
||||
@@ -248,6 +264,27 @@
|
||||
|
||||
++this.tickCount;
|
||||
if (this.entity.hurtMarked) {
|
||||
+ // CraftBukkit start - Create PlayerVelocity event
|
||||
+ boolean cancelled = false;
|
||||
+
|
||||
+ if (this.entity instanceof ServerPlayer) {
|
||||
+ Player player = (Player) this.entity.getBukkitEntity();
|
||||
+ org.bukkit.util.Vector velocity = player.getVelocity();
|
||||
+
|
||||
+ PlayerVelocityEvent event = new PlayerVelocityEvent(player, velocity.clone());
|
||||
+ this.entity.level().getCraftServer().getPluginManager().callEvent(event);
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ cancelled = true;
|
||||
+ } else if (!velocity.equals(event.getVelocity())) {
|
||||
+ player.setVelocity(event.getVelocity());
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (cancelled) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
this.entity.hurtMarked = false;
|
||||
this.broadcastAndSend(new ClientboundSetEntityMotionPacket(this.entity));
|
||||
}
|
||||
@@ -298,7 +335,10 @@
|
||||
|
||||
public void sendPairingData(ServerPlayer player, Consumer<Packet<ClientGamePacketListener>> sender) {
|
||||
if (this.entity.isRemoved()) {
|
||||
- ServerEntity.LOGGER.warn("Fetching packet for removed entity {}", this.entity);
|
||||
+ // CraftBukkit start - Remove useless error spam, just return
|
||||
+ // EntityTrackerEntry.LOGGER.warn("Fetching packet for removed entity {}", this.entity);
|
||||
+ return;
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
|
||||
Packet<ClientGamePacketListener> packet = this.entity.getAddEntityPacket(this);
|
||||
@@ -313,6 +353,12 @@
|
||||
if (this.entity instanceof LivingEntity) {
|
||||
Collection<AttributeInstance> collection = ((LivingEntity) this.entity).getAttributes().getSyncableAttributes();
|
||||
|
||||
+ // CraftBukkit start - If sending own attributes send scaled health instead of current maximum health
|
||||
+ if (this.entity.getId() == player.getId()) {
|
||||
+ ((ServerPlayer) this.entity).getBukkitEntity().injectScaledMaxHealth(collection, false);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
if (!collection.isEmpty()) {
|
||||
sender.accept(new ClientboundUpdateAttributesPacket(this.entity.getId(), collection));
|
||||
}
|
||||
@@ -342,8 +388,9 @@
|
||||
}
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
- sender.accept(new ClientboundSetEquipmentPacket(this.entity.getId(), list));
|
||||
+ sender.accept(new ClientboundSetEquipmentPacket(this.entity.getId(), list, true)); // Paper - data sanitization
|
||||
}
|
||||
+ ((LivingEntity) this.entity).detectEquipmentUpdatesPublic(); // CraftBukkit - SPIGOT-3789: sync again immediately after sending
|
||||
}
|
||||
|
||||
if (!this.entity.getPassengers().isEmpty()) {
|
||||
@@ -396,6 +443,11 @@
|
||||
Set<AttributeInstance> set = ((LivingEntity) this.entity).getAttributes().getAttributesToSync();
|
||||
|
||||
if (!set.isEmpty()) {
|
||||
+ // CraftBukkit start - Send scaled max health
|
||||
+ if (this.entity instanceof ServerPlayer) {
|
||||
+ ((ServerPlayer) this.entity).getBukkitEntity().injectScaledMaxHealth(set, false);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), set));
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -275,7 +275,7 @@ public abstract class Configurations<G, W> {
|
|||
}
|
||||
|
||||
public Path getWorldConfigFile(ServerLevel level) {
|
||||
return level.convertable.levelDirectory.path().resolve(this.worldConfigFileName);
|
||||
return level.levelStorageAccess.levelDirectory.path().resolve(this.worldConfigFileName);
|
||||
}
|
||||
|
||||
public static class ContextMap {
|
||||
|
|
|
@ -327,7 +327,7 @@ public class PaperConfigurations extends Configurations<GlobalConfiguration, Wor
|
|||
}
|
||||
|
||||
private static ContextMap createWorldContextMap(ServerLevel level) {
|
||||
return createWorldContextMap(level.convertable.levelDirectory.path(), level.serverLevelData.getLevelName(), level.dimension().location(), level.spigotConfig, level.registryAccess(), level.getGameRules());
|
||||
return createWorldContextMap(level.levelStorageAccess.levelDirectory.path(), level.serverLevelData.getLevelName(), level.dimension().location(), level.spigotConfig, level.registryAccess(), level.getGameRules());
|
||||
}
|
||||
|
||||
public static ContextMap createWorldContextMap(final Path dir, final String levelName, final ResourceLocation worldKey, final SpigotWorldConfig spigotConfig, final RegistryAccess registryAccess, final GameRules gameRules) {
|
||||
|
|
|
@ -1494,7 +1494,7 @@ public final class CraftServer implements Server {
|
|||
|
||||
handle.getChunkSource().close(save);
|
||||
handle.entityManager.close(save); // SPIGOT-6722: close entityManager
|
||||
handle.convertable.close();
|
||||
handle.levelStorageAccess.close();
|
||||
} catch (Exception ex) {
|
||||
this.getLogger().log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import com.google.common.collect.ImmutableList;
|
|||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import io.papermc.paper.FeatureHooks;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import java.io.File;
|
||||
|
@ -21,7 +20,6 @@ import java.util.Objects;
|
|||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -32,7 +30,6 @@ import net.minecraft.core.particles.ParticleTypes;
|
|||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelEventPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetTimePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket;
|
||||
|
@ -77,7 +74,6 @@ import org.bukkit.Chunk;
|
|||
import org.bukkit.ChunkSnapshot;
|
||||
import org.bukkit.Difficulty;
|
||||
import org.bukkit.Effect;
|
||||
import org.bukkit.FeatureFlag;
|
||||
import org.bukkit.FluidCollisionMode;
|
||||
import org.bukkit.GameRule;
|
||||
import org.bukkit.Instrument;
|
||||
|
@ -130,7 +126,6 @@ import org.bukkit.entity.TippedArrow;
|
|||
import org.bukkit.entity.Trident;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
||||
import org.bukkit.event.weather.LightningStrikeEvent;
|
||||
import org.bukkit.event.world.SpawnChangeEvent;
|
||||
import org.bukkit.event.world.TimeSkipEvent;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
|
@ -1631,7 +1626,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|||
|
||||
@Override
|
||||
public File getWorldFolder() {
|
||||
return this.world.convertable.getLevelPath(LevelResource.ROOT).toFile().getParentFile();
|
||||
return this.world.levelStorageAccess.getLevelPath(LevelResource.ROOT).toFile().getParentFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in a new issue