More more more more more more more patches
This commit is contained in:
parent
bacf389fc3
commit
1295869b43
131 changed files with 354 additions and 345 deletions
File diff suppressed because it is too large
Load diff
|
@ -1,215 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Thu, 7 May 2020 05:48:54 -0700
|
||||
Subject: [PATCH] Optimise chunk tick iteration
|
||||
|
||||
Use a dedicated list of entity ticking chunks to reduce the cost
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
index a61f55ed1fbe5aac5289014cb95cb6950b4c77fa..e11ec87e8007979a1c6932b414bcd70c10db746c 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
@@ -88,6 +88,11 @@ public class ChunkHolder {
|
||||
this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key);
|
||||
this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
|
||||
// Paper end - optimise anyPlayerCloseEnoughForSpawning
|
||||
+ // Paper start - optimise chunk tick iteration
|
||||
+ if (this.needsBroadcastChanges()) {
|
||||
+ this.chunkMap.needsChangeBroadcasting.add(this);
|
||||
+ }
|
||||
+ // Paper end - optimise chunk tick iteration
|
||||
}
|
||||
|
||||
public void onChunkRemove() {
|
||||
@@ -95,6 +100,11 @@ public class ChunkHolder {
|
||||
this.playersInMobSpawnRange = null;
|
||||
this.playersInChunkTickRange = null;
|
||||
// Paper end - optimise anyPlayerCloseEnoughForSpawning
|
||||
+ // Paper start - optimise chunk tick iteration
|
||||
+ if (this.needsBroadcastChanges()) {
|
||||
+ this.chunkMap.needsChangeBroadcasting.remove(this);
|
||||
+ }
|
||||
+ // Paper end - optimise chunk tick iteration
|
||||
}
|
||||
// Paper end
|
||||
|
||||
@@ -210,7 +220,7 @@ public class ChunkHolder {
|
||||
|
||||
if (i < 0 || i >= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296
|
||||
if (this.changedBlocksPerSection[i] == null) {
|
||||
- this.hasChangedSections = true;
|
||||
+ this.hasChangedSections = true; this.addToBroadcastMap(); // Paper - optimise chunk tick iteration
|
||||
this.changedBlocksPerSection[i] = new ShortOpenHashSet();
|
||||
}
|
||||
|
||||
@@ -234,6 +244,7 @@ public class ChunkHolder {
|
||||
int k = this.lightEngine.getMaxLightSection();
|
||||
|
||||
if (y >= j && y <= k) {
|
||||
+ this.addToBroadcastMap(); // Paper - optimise chunk tick iteration
|
||||
int l = y - j;
|
||||
|
||||
if (lightType == LightLayer.SKY) {
|
||||
@@ -248,8 +259,19 @@ public class ChunkHolder {
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - optimise chunk tick iteration
|
||||
+ public final boolean needsBroadcastChanges() {
|
||||
+ return this.hasChangedSections || !this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty();
|
||||
+ }
|
||||
+
|
||||
+ private void addToBroadcastMap() {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("ChunkHolder update");
|
||||
+ this.chunkMap.needsChangeBroadcasting.add(this);
|
||||
+ }
|
||||
+ // Paper end - optimise chunk tick iteration
|
||||
+
|
||||
public void broadcastChanges(LevelChunk chunk) {
|
||||
- if (this.hasChangedSections || !this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty()) {
|
||||
+ if (this.needsBroadcastChanges()) { // Paper - moved into above, other logic needs to call
|
||||
Level world = chunk.getLevel();
|
||||
int i = 0;
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index 81870f23ae70db3a1290428df6a47d76baa9a722..1b8f221360f5edb4d244ec97104d85ffecef204c 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -113,6 +113,8 @@ import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
|
||||
import org.bukkit.entity.Player;
|
||||
// CraftBukkit end
|
||||
|
||||
+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper
|
||||
+
|
||||
public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider {
|
||||
|
||||
private static final byte CHUNK_TYPE_REPLACEABLE = -1;
|
||||
@@ -151,6 +153,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
private final Queue<Runnable> unloadQueue;
|
||||
int viewDistance;
|
||||
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper
|
||||
+ public final ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new ReferenceOpenHashSet<>();
|
||||
|
||||
// Paper - rewrite chunk system
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index d1652c237b2b272f0dfe80f774cff16056f39046..b768767b92bf7691a6e57627c69818a1f5fd82c8 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -48,6 +48,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
|
||||
import net.minecraft.world.level.storage.DimensionDataStorage;
|
||||
import net.minecraft.world.level.storage.LevelData;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper
|
||||
|
||||
public class ServerChunkCache extends ChunkSource {
|
||||
|
||||
@@ -724,42 +725,59 @@ public class ServerChunkCache extends ChunkSource {
|
||||
|
||||
this.lastSpawnState = spawnercreature_d;
|
||||
gameprofilerfiller.popPush("filteringLoadedChunks");
|
||||
- List<ServerChunkCache.ChunkAndHolder> list = Lists.newArrayListWithCapacity(l);
|
||||
- Iterator iterator = this.chunkMap.getChunks().iterator();
|
||||
+ // Paper - moved down
|
||||
this.level.timings.chunkTicks.startTiming(); // Paper
|
||||
|
||||
- while (iterator.hasNext()) {
|
||||
- ChunkHolder playerchunk = (ChunkHolder) iterator.next();
|
||||
- LevelChunk chunk = playerchunk.getTickingChunk();
|
||||
-
|
||||
- if (chunk != null) {
|
||||
- list.add(new ServerChunkCache.ChunkAndHolder(chunk, playerchunk));
|
||||
- }
|
||||
- }
|
||||
+ // Paper - moved down
|
||||
|
||||
gameprofilerfiller.popPush("spawnAndTick");
|
||||
boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
|
||||
|
||||
- Collections.shuffle(list);
|
||||
+ // Paper - only shuffle if per-player mob spawning is disabled
|
||||
// Paper - moved natural spawn event up
|
||||
- Iterator iterator1 = list.iterator();
|
||||
+ // Paper start - optimise chunk tick iteration
|
||||
+ Iterator<LevelChunk> iterator1;
|
||||
+ if (this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
|
||||
+ iterator1 = this.entityTickingChunks.iterator();
|
||||
+ } else {
|
||||
+ iterator1 = this.entityTickingChunks.unsafeIterator();
|
||||
+ List<LevelChunk> shuffled = Lists.newArrayListWithCapacity(this.entityTickingChunks.size());
|
||||
+ while (iterator1.hasNext()) {
|
||||
+ shuffled.add(iterator1.next());
|
||||
+ }
|
||||
+ Collections.shuffle(shuffled);
|
||||
+ iterator1 = shuffled.iterator();
|
||||
+ }
|
||||
|
||||
+ try {
|
||||
while (iterator1.hasNext()) {
|
||||
- ServerChunkCache.ChunkAndHolder chunkproviderserver_a = (ServerChunkCache.ChunkAndHolder) iterator1.next();
|
||||
- LevelChunk chunk1 = chunkproviderserver_a.chunk;
|
||||
+ LevelChunk chunk1 = iterator1.next();
|
||||
+ ChunkHolder holder = chunk1.playerChunk;
|
||||
+ if (holder != null) {
|
||||
+ // Paper - move down
|
||||
+ // Paper end - optimise chunk tick iteration
|
||||
ChunkPos chunkcoordintpair = chunk1.getPos();
|
||||
|
||||
- if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning
|
||||
+ if ((true || this.level.isNaturalSpawningAllowed(chunkcoordintpair)) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning // Paper - the chunk is known ticking
|
||||
chunk1.incrementInhabitedTime(j);
|
||||
- if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning
|
||||
+ if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration
|
||||
NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1);
|
||||
}
|
||||
|
||||
- if (this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) {
|
||||
+ if (true || this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { // Paper - the chunk is known ticking
|
||||
this.level.tickChunk(chunk1, k);
|
||||
}
|
||||
}
|
||||
+ // Paper start - optimise chunk tick iteration
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ } finally {
|
||||
+ if (iterator1 instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator safeIterator) {
|
||||
+ safeIterator.finishedIterating();
|
||||
+ }
|
||||
}
|
||||
+ // Paper end - optimise chunk tick iteration
|
||||
this.level.timings.chunkTicks.stopTiming(); // Paper
|
||||
gameprofilerfiller.popPush("customSpawners");
|
||||
if (flag2) {
|
||||
@@ -767,15 +785,24 @@ public class ServerChunkCache extends ChunkSource {
|
||||
this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies);
|
||||
} // Paper - timings
|
||||
}
|
||||
-
|
||||
- gameprofilerfiller.popPush("broadcast");
|
||||
- list.forEach((chunkproviderserver_a1) -> {
|
||||
- this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
|
||||
- chunkproviderserver_a1.holder.broadcastChanges(chunkproviderserver_a1.chunk);
|
||||
- this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
|
||||
- });
|
||||
gameprofilerfiller.pop();
|
||||
+ // Paper start - use set of chunks requiring updates, rather than iterating every single one loaded
|
||||
+ gameprofilerfiller.popPush("broadcast");
|
||||
+ this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
|
||||
+ if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) {
|
||||
+ ReferenceOpenHashSet<ChunkHolder> copy = this.chunkMap.needsChangeBroadcasting.clone();
|
||||
+ this.chunkMap.needsChangeBroadcasting.clear();
|
||||
+ for (ChunkHolder holder : copy) {
|
||||
+ holder.broadcastChanges(holder.getFullChunkNowUnchecked()); // LevelChunks are NEVER unloaded
|
||||
+ if (holder.needsBroadcastChanges()) {
|
||||
+ // I DON'T want to KNOW what DUMB plugins might be doing.
|
||||
+ this.chunkMap.needsChangeBroadcasting.add(holder);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
|
||||
gameprofilerfiller.pop();
|
||||
+ // Paper end - use set of chunks requiring updates, rather than iterating every single one loaded
|
||||
this.chunkMap.tick();
|
||||
}
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Mon, 6 Apr 2020 04:20:44 -0700
|
||||
Subject: [PATCH] Execute chunk tasks mid-tick
|
||||
|
||||
This will help the server load chunks if tick times are high.
|
||||
|
||||
diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java
|
||||
index 23e564b05ba438924180c91f9b19a60731eedd1b..5ec241d49ff5e3a161a39006f05823a5de847c5e 100644
|
||||
--- a/src/main/java/co/aikar/timings/MinecraftTimings.java
|
||||
+++ b/src/main/java/co/aikar/timings/MinecraftTimings.java
|
||||
@@ -46,6 +46,8 @@ public final class MinecraftTimings {
|
||||
public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update");
|
||||
public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate");
|
||||
|
||||
+ public static final Timing midTickChunkTasks = Timings.ofSafe("Mid Tick Chunk Tasks");
|
||||
+
|
||||
private static final Map<Class<?>, String> taskNameCache = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
private MinecraftTimings() {}
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index 34619d107231fe188d2b4c8eeefba8b6eaf4d3a5..4fb05a2be3ff06ceddae37e1592db548c727a056 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1305,6 +1305,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
private boolean pollTaskInternal() {
|
||||
if (super.pollTask()) {
|
||||
+ this.executeMidTickTasks(); // Paper - execute chunk tasks mid tick
|
||||
return true;
|
||||
} else {
|
||||
if (this.haveTime()) {
|
||||
@@ -2685,4 +2686,74 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
+
|
||||
+ // Paper start - execute chunk tasks mid tick
|
||||
+ static final long CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME = 25L * 1000L; // 25us
|
||||
+ static final long MAX_CHUNK_EXEC_TIME = 1000L; // 1us
|
||||
+
|
||||
+ static final long TASK_EXECUTION_FAILURE_BACKOFF = 5L * 1000L; // 5us
|
||||
+
|
||||
+ private static long lastMidTickExecute;
|
||||
+ private static long lastMidTickExecuteFailure;
|
||||
+
|
||||
+ private boolean tickMidTickTasks() {
|
||||
+ // give all worlds a fair chance at by targetting them all.
|
||||
+ // if we execute too many tasks, that's fine - we have logic to correctly handle overuse of allocated time.
|
||||
+ boolean executed = false;
|
||||
+ for (ServerLevel world : this.getAllLevels()) {
|
||||
+ long currTime = System.nanoTime();
|
||||
+ if (currTime - world.lastMidTickExecuteFailure <= TASK_EXECUTION_FAILURE_BACKOFF) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (!world.getChunkSource().pollTask()) {
|
||||
+ // we need to back off if this fails
|
||||
+ world.lastMidTickExecuteFailure = currTime;
|
||||
+ } else {
|
||||
+ executed = true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return executed;
|
||||
+ }
|
||||
+
|
||||
+ public final void executeMidTickTasks() {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("mid tick chunk task execution");
|
||||
+ long startTime = System.nanoTime();
|
||||
+ if ((startTime - lastMidTickExecute) <= CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME || (startTime - lastMidTickExecuteFailure) <= TASK_EXECUTION_FAILURE_BACKOFF) {
|
||||
+ // it's shown to be bad to constantly hit the queue (chunk loads slow to a crawl), even if no tasks are executed.
|
||||
+ // so, backoff to prevent this
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ co.aikar.timings.MinecraftTimings.midTickChunkTasks.startTiming();
|
||||
+ try {
|
||||
+ for (;;) {
|
||||
+ boolean moreTasks = this.tickMidTickTasks();
|
||||
+ long currTime = System.nanoTime();
|
||||
+ long diff = currTime - startTime;
|
||||
+
|
||||
+ if (!moreTasks || diff >= MAX_CHUNK_EXEC_TIME) {
|
||||
+ if (!moreTasks) {
|
||||
+ lastMidTickExecuteFailure = currTime;
|
||||
+ }
|
||||
+
|
||||
+ // note: negative values reduce the time
|
||||
+ long overuse = diff - MAX_CHUNK_EXEC_TIME;
|
||||
+ if (overuse >= (10L * 1000L * 1000L)) { // 10ms
|
||||
+ // make sure something like a GC or dumb plugin doesn't screw us over...
|
||||
+ overuse = 10L * 1000L * 1000L; // 10ms
|
||||
+ }
|
||||
+
|
||||
+ double overuseCount = (double)overuse/(double)MAX_CHUNK_EXEC_TIME;
|
||||
+ long extraSleep = (long)Math.round(overuseCount*CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME);
|
||||
+
|
||||
+ lastMidTickExecute = currTime + extraSleep;
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+ } finally {
|
||||
+ co.aikar.timings.MinecraftTimings.midTickChunkTasks.stopTiming();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - execute chunk tasks mid tick
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index b768767b92bf7691a6e57627c69818a1f5fd82c8..ebd0da4f87c74f12d702e1ae4f3206885272e4f7 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -749,6 +749,7 @@ public class ServerChunkCache extends ChunkSource {
|
||||
iterator1 = shuffled.iterator();
|
||||
}
|
||||
|
||||
+ int chunksTicked = 0; // Paper
|
||||
try {
|
||||
while (iterator1.hasNext()) {
|
||||
LevelChunk chunk1 = iterator1.next();
|
||||
@@ -766,6 +767,7 @@ public class ServerChunkCache extends ChunkSource {
|
||||
|
||||
if (true || this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { // Paper - the chunk is known ticking
|
||||
this.level.tickChunk(chunk1, k);
|
||||
+ if ((chunksTicked++ & 1) == 0) net.minecraft.server.MinecraftServer.getServer().executeMidTickTasks(); // Paper
|
||||
}
|
||||
}
|
||||
// Paper start - optimise chunk tick iteration
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index 5ec15aa3005e7cc401b9f391423008e1b75fe73f..9b2c24a01fd09d9520f3c8baff7c56ae48f0c5de 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -214,6 +214,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
private final StructureManager structureManager;
|
||||
private final StructureCheck structureCheck;
|
||||
private final boolean tickTime;
|
||||
+ public long lastMidTickExecuteFailure; // Paper - execute chunk tasks mid tick
|
||||
|
||||
// CraftBukkit start
|
||||
public final LevelStorageSource.LevelStorageAccess convertable;
|
||||
@@ -1045,6 +1046,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
if (fluid1.is(fluid)) {
|
||||
fluid1.tick(this, pos);
|
||||
}
|
||||
+ MinecraftServer.getServer().executeMidTickTasks(); // Paper - exec chunk tasks during world tick
|
||||
|
||||
}
|
||||
|
||||
@@ -1054,6 +1056,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
if (iblockdata.is(block)) {
|
||||
iblockdata.tick(this, pos, this.random);
|
||||
}
|
||||
+ MinecraftServer.getServer().executeMidTickTasks(); // Paper - exec chunk tasks during world tick
|
||||
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index 81b94b2cda04cbb2bc89036448e48fef791b338b..b94736286adf22ea4ff5702fa7a45b5e212b232f 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -826,6 +826,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
// Spigot end
|
||||
} else if (this.shouldTickBlocksAt(tickingblockentity.getPos())) {
|
||||
tickingblockentity.tick();
|
||||
+ // Paper start - execute chunk tasks during tick
|
||||
+ if ((this.tileTickPosition & 7) == 0) {
|
||||
+ MinecraftServer.getServer().executeMidTickTasks();
|
||||
+ }
|
||||
+ // Paper end - execute chunk tasks during tick
|
||||
}
|
||||
}
|
||||
this.blockEntityTickers.removeAll(toRemove);
|
||||
@@ -840,6 +845,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
public <T extends Entity> void guardEntityTick(Consumer<T> tickConsumer, T entity) {
|
||||
try {
|
||||
tickConsumer.accept(entity);
|
||||
+ MinecraftServer.getServer().executeMidTickTasks(); // Paper - execute chunk tasks mid tick
|
||||
} catch (Throwable throwable) {
|
||||
if (throwable instanceof ThreadDeath) throw throwable; // Paper
|
||||
// Paper start - Prevent tile entity and entity crashes
|
|
@ -1,766 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sun, 2 Feb 2020 02:25:10 -0800
|
||||
Subject: [PATCH] Attempt to recalculate regionfile header if it is corrupt
|
||||
|
||||
Instead of trying to relocate the chunk, which is seems to never
|
||||
be the correct choice, so we end up duplicating or swapping chunks,
|
||||
we instead drop the current regionfile header and recalculate -
|
||||
hoping that at least then we don't swap chunks, and maybe recover
|
||||
them all.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
index f8edcc9697289c2cf43c9021d5518955d0d701e7..313a3319c630a4f26f9c53255a0ac67f3c15e410 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
@@ -70,6 +70,18 @@ import net.minecraft.world.ticks.ProtoChunkTicks;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class ChunkSerializer {
|
||||
+ // Paper start
|
||||
+ // TODO: Check on update
|
||||
+ public static long getLastWorldSaveTime(CompoundTag chunkData) {
|
||||
+ final int dataVersion = ChunkStorage.getVersion(chunkData);
|
||||
+ if (dataVersion < 2842) { // Level tag is removed after this version
|
||||
+ final CompoundTag levelData = chunkData.getCompound("Level");
|
||||
+ return levelData.getLong("LastUpdate");
|
||||
+ } else {
|
||||
+ return chunkData.getLong("LastUpdate");
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), null); // Paper - Anti-Xray - Add preset block states
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
@@ -486,7 +498,7 @@ public class ChunkSerializer {
|
||||
nbttagcompound.putInt("xPos", chunkcoordintpair.x);
|
||||
nbttagcompound.putInt("yPos", chunk.getMinSection());
|
||||
nbttagcompound.putInt("zPos", chunkcoordintpair.z);
|
||||
- nbttagcompound.putLong("LastUpdate", asyncsavedata != null ? asyncsavedata.worldTime : world.getGameTime()); // Paper - async chunk unloading
|
||||
+ nbttagcompound.putLong("LastUpdate", asyncsavedata != null ? asyncsavedata.worldTime : world.getGameTime()); // Paper - async chunk unloading // Paper - diff on change
|
||||
nbttagcompound.putLong("InhabitedTime", chunk.getInhabitedTime());
|
||||
nbttagcompound.putString("Status", chunk.getStatus().getName());
|
||||
BlendingData blendingdata = chunk.getBlendingData();
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
|
||||
index e276b2ceddbb269ef9a8625f26cc1847ded3862a..6289858d54a6affd504533b1a280718a01446ebb 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
|
||||
@@ -41,7 +41,7 @@ public class ChunkStorage implements AutoCloseable {
|
||||
this.fixerUpper = dataFixer;
|
||||
// Paper start - async chunk io
|
||||
// remove IO worker
|
||||
- this.regionFileCache = new RegionFileStorage(directory, dsync); // Paper - nuke IOWorker
|
||||
+ this.regionFileCache = new RegionFileStorage(directory, dsync, true); // Paper - nuke IOWorker // Paper
|
||||
// Paper end - async chunk io
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java
|
||||
index c8298a597818227de33a4afce4698ec0666cf758..6baceb6ce9021c489be6e79d338a9704285afa26 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java
|
||||
@@ -9,6 +9,27 @@ import java.util.BitSet;
|
||||
public class RegionBitmap {
|
||||
private final BitSet used = new BitSet();
|
||||
|
||||
+ // Paper start
|
||||
+ public final void copyFrom(RegionBitmap other) {
|
||||
+ BitSet thisBitset = this.used;
|
||||
+ BitSet otherBitset = other.used;
|
||||
+
|
||||
+ for (int i = 0; i < Math.max(thisBitset.size(), otherBitset.size()); ++i) {
|
||||
+ thisBitset.set(i, otherBitset.get(i));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public final boolean tryAllocate(int from, int length) {
|
||||
+ BitSet bitset = this.used;
|
||||
+ int firstSet = bitset.nextSetBit(from);
|
||||
+ if (firstSet > 0 && firstSet < (from + length)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ bitset.set(from, from + length);
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public void force(int start, int size) {
|
||||
this.used.set(start, start + size);
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
index cda87a66fe80bf910f629c64e36c1fecbad81d77..9bae47f99336c377beb72c4b50b7f01cb4db15da 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
@@ -51,6 +51,355 @@ public class RegionFile implements AutoCloseable {
|
||||
public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(true); // Paper
|
||||
public final Path regionFile; // Paper
|
||||
|
||||
+ // Paper start - try to recover from RegionFile header corruption
|
||||
+ private static long roundToSectors(long bytes) {
|
||||
+ long sectors = bytes >>> 12; // 4096 = 2^12
|
||||
+ long remainingBytes = bytes & 4095;
|
||||
+ long sign = -remainingBytes; // sign is 1 if nonzero
|
||||
+ return sectors + (sign >>> 63);
|
||||
+ }
|
||||
+
|
||||
+ private static final CompoundTag OVERSIZED_COMPOUND = new CompoundTag();
|
||||
+
|
||||
+ private CompoundTag attemptRead(long sector, int chunkDataLength, long fileLength) throws IOException {
|
||||
+ try {
|
||||
+ if (chunkDataLength < 0) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ long offset = sector * 4096L + 4L; // offset for chunk data
|
||||
+
|
||||
+ if ((offset + chunkDataLength) > fileLength) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ ByteBuffer chunkData = ByteBuffer.allocate(chunkDataLength);
|
||||
+ if (chunkDataLength != this.file.read(chunkData, offset)) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ ((java.nio.Buffer)chunkData).flip();
|
||||
+
|
||||
+ byte compressionType = chunkData.get();
|
||||
+ if (compressionType < 0) { // compressionType & 128 != 0
|
||||
+ // oversized chunk
|
||||
+ return OVERSIZED_COMPOUND;
|
||||
+ }
|
||||
+
|
||||
+ RegionFileVersion compression = RegionFileVersion.fromId(compressionType);
|
||||
+ if (compression == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ InputStream input = compression.wrap(new ByteArrayInputStream(chunkData.array(), chunkData.position(), chunkDataLength - chunkData.position()));
|
||||
+
|
||||
+ return NbtIo.read(new DataInputStream(input));
|
||||
+ } catch (Exception ex) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private int getLength(long sector) throws IOException {
|
||||
+ ByteBuffer length = ByteBuffer.allocate(4);
|
||||
+ if (4 != this.file.read(length, sector * 4096L)) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ return length.getInt(0);
|
||||
+ }
|
||||
+
|
||||
+ private void backupRegionFile() {
|
||||
+ Path backup = this.regionFile.getParent().resolve(this.regionFile.getFileName() + "." + new java.util.Random().nextLong() + ".backup");
|
||||
+ this.backupRegionFile(backup);
|
||||
+ }
|
||||
+
|
||||
+ private void backupRegionFile(Path to) {
|
||||
+ try {
|
||||
+ this.file.force(true);
|
||||
+ LOGGER.warn("Backing up regionfile \"" + this.regionFile.toAbsolutePath() + "\" to " + to.toAbsolutePath());
|
||||
+ java.nio.file.Files.copy(this.regionFile, to, java.nio.file.StandardCopyOption.COPY_ATTRIBUTES);
|
||||
+ LOGGER.warn("Backed up the regionfile to " + to.toAbsolutePath());
|
||||
+ } catch (IOException ex) {
|
||||
+ LOGGER.error("Failed to backup to " + to.toAbsolutePath(), ex);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static boolean inSameRegionfile(ChunkPos first, ChunkPos second) {
|
||||
+ return (first.x & ~31) == (second.x & ~31) && (first.z & ~31) == (second.z & ~31);
|
||||
+ }
|
||||
+
|
||||
+ // note: only call for CHUNK regionfiles
|
||||
+ boolean recalculateHeader() throws IOException {
|
||||
+ if (!this.canRecalcHeader) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ ChunkPos ourLowerLeftPosition = RegionFileStorage.getRegionFileCoordinates(this.regionFile);
|
||||
+ if (ourLowerLeftPosition == null) {
|
||||
+ LOGGER.error("Unable to get chunk location of regionfile " + this.regionFile.toAbsolutePath() + ", cannot recover header");
|
||||
+ return false;
|
||||
+ }
|
||||
+ synchronized (this) {
|
||||
+ LOGGER.warn("Corrupt regionfile header detected! Attempting to re-calculate header offsets for regionfile " + this.regionFile.toAbsolutePath(), new Throwable());
|
||||
+
|
||||
+ // try to backup file so maybe it could be sent to us for further investigation
|
||||
+
|
||||
+ this.backupRegionFile();
|
||||
+ CompoundTag[] compounds = new CompoundTag[32 * 32]; // only in the regionfile (i.e exclude mojang/aikar oversized data)
|
||||
+ int[] rawLengths = new int[32 * 32]; // length of chunk data including 4 byte length field, bytes
|
||||
+ int[] sectorOffsets = new int[32 * 32]; // in sectors
|
||||
+ boolean[] hasAikarOversized = new boolean[32 * 32];
|
||||
+
|
||||
+ long fileLength = this.file.size();
|
||||
+ long totalSectors = roundToSectors(fileLength);
|
||||
+
|
||||
+ // search the regionfile from start to finish for the most up-to-date chunk data
|
||||
+
|
||||
+ for (long i = 2, maxSector = Math.min((long)(Integer.MAX_VALUE >>> 8), totalSectors); i < maxSector; ++i) { // first two sectors are header, skip
|
||||
+ int chunkDataLength = this.getLength(i);
|
||||
+ CompoundTag compound = this.attemptRead(i, chunkDataLength, fileLength);
|
||||
+ if (compound == null || compound == OVERSIZED_COMPOUND) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ ChunkPos chunkPos = ChunkSerializer.getChunkCoordinate(compound);
|
||||
+ if (!inSameRegionfile(ourLowerLeftPosition, chunkPos)) {
|
||||
+ LOGGER.error("Ignoring absolute chunk " + chunkPos + " in regionfile as it is not contained in the bounds of the regionfile '" + this.regionFile.toAbsolutePath() + "'. It should be in regionfile (" + (chunkPos.x >> 5) + "," + (chunkPos.z >> 5) + ")");
|
||||
+ continue;
|
||||
+ }
|
||||
+ int location = (chunkPos.x & 31) | ((chunkPos.z & 31) << 5);
|
||||
+
|
||||
+ CompoundTag otherCompound = compounds[location];
|
||||
+
|
||||
+ if (otherCompound != null && ChunkSerializer.getLastWorldSaveTime(otherCompound) > ChunkSerializer.getLastWorldSaveTime(compound)) {
|
||||
+ continue; // don't overwrite newer data.
|
||||
+ }
|
||||
+
|
||||
+ // aikar oversized?
|
||||
+ Path aikarOversizedFile = this.getOversizedFile(chunkPos.x, chunkPos.z);
|
||||
+ boolean isAikarOversized = false;
|
||||
+ if (Files.exists(aikarOversizedFile)) {
|
||||
+ try {
|
||||
+ CompoundTag aikarOversizedCompound = this.getOversizedData(chunkPos.x, chunkPos.z);
|
||||
+ if (ChunkSerializer.getLastWorldSaveTime(compound) == ChunkSerializer.getLastWorldSaveTime(aikarOversizedCompound)) {
|
||||
+ // best we got for an id. hope it's good enough
|
||||
+ isAikarOversized = true;
|
||||
+ }
|
||||
+ } catch (Exception ex) {
|
||||
+ LOGGER.error("Failed to read aikar oversized data for absolute chunk (" + chunkPos.x + "," + chunkPos.z + ") in regionfile " + this.regionFile.toAbsolutePath() + ", oversized data for this chunk will be lost", ex);
|
||||
+ // fall through, if we can't read aikar oversized we can't risk corrupting chunk data
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ hasAikarOversized[location] = isAikarOversized;
|
||||
+ compounds[location] = compound;
|
||||
+ rawLengths[location] = chunkDataLength + 4;
|
||||
+ sectorOffsets[location] = (int)i;
|
||||
+
|
||||
+ int chunkSectorLength = (int)roundToSectors(rawLengths[location]);
|
||||
+ i += chunkSectorLength;
|
||||
+ --i; // gets incremented next iteration
|
||||
+ }
|
||||
+
|
||||
+ // forge style oversized data is already handled by the local search, and aikar data we just hope
|
||||
+ // we get it right as aikar data has no identifiers we could use to try and find its corresponding
|
||||
+ // local data compound
|
||||
+
|
||||
+ java.nio.file.Path containingFolder = this.externalFileDir;
|
||||
+ Path[] regionFiles = Files.list(containingFolder).toArray(Path[]::new);
|
||||
+ boolean[] oversized = new boolean[32 * 32];
|
||||
+ RegionFileVersion[] oversizedCompressionTypes = new RegionFileVersion[32 * 32];
|
||||
+
|
||||
+ if (regionFiles != null) {
|
||||
+ int lowerXBound = ourLowerLeftPosition.x; // inclusive
|
||||
+ int lowerZBound = ourLowerLeftPosition.z; // inclusive
|
||||
+ int upperXBound = lowerXBound + 32 - 1; // inclusive
|
||||
+ int upperZBound = lowerZBound + 32 - 1; // inclusive
|
||||
+
|
||||
+ // read mojang oversized data
|
||||
+ for (Path regionFile : regionFiles) {
|
||||
+ ChunkPos oversizedCoords = getOversizedChunkPair(regionFile);
|
||||
+ if (oversizedCoords == null) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if ((oversizedCoords.x < lowerXBound || oversizedCoords.x > upperXBound) || (oversizedCoords.z < lowerZBound || oversizedCoords.z > upperZBound)) {
|
||||
+ continue; // not in our regionfile
|
||||
+ }
|
||||
+
|
||||
+ // ensure oversized data is valid & is newer than data in the regionfile
|
||||
+
|
||||
+ int location = (oversizedCoords.x & 31) | ((oversizedCoords.z & 31) << 5);
|
||||
+
|
||||
+ byte[] chunkData;
|
||||
+ try {
|
||||
+ chunkData = Files.readAllBytes(regionFile);
|
||||
+ } catch (Exception ex) {
|
||||
+ LOGGER.error("Failed to read oversized chunk data in file " + regionFile.toAbsolutePath() + ", data will be lost", ex);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ CompoundTag compound = null;
|
||||
+
|
||||
+ // We do not know the compression type, as it's stored in the regionfile. So we need to try all of them
|
||||
+ RegionFileVersion compression = null;
|
||||
+ for (RegionFileVersion compressionType : RegionFileVersion.VERSIONS.values()) {
|
||||
+ try {
|
||||
+ DataInputStream in = new DataInputStream(compressionType.wrap(new ByteArrayInputStream(chunkData))); // typical java
|
||||
+ compound = NbtIo.read((java.io.DataInput)in);
|
||||
+ compression = compressionType;
|
||||
+ break; // reaches here iff readNBT does not throw
|
||||
+ } catch (Exception ex) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (compound == null) {
|
||||
+ LOGGER.error("Failed to read oversized chunk data in file " + regionFile.toAbsolutePath() + ", it's corrupt. Its data will be lost");
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (!ChunkSerializer.getChunkCoordinate(compound).equals(oversizedCoords)) {
|
||||
+ LOGGER.error("Can't use oversized chunk stored in " + regionFile.toAbsolutePath() + ", got absolute chunkpos: " + ChunkSerializer.getChunkCoordinate(compound) + ", expected " + oversizedCoords);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (compounds[location] == null || ChunkSerializer.getLastWorldSaveTime(compound) > ChunkSerializer.getLastWorldSaveTime(compounds[location])) {
|
||||
+ oversized[location] = true;
|
||||
+ oversizedCompressionTypes[location] = compression;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // now we need to calculate a new offset header
|
||||
+
|
||||
+ int[] calculatedOffsets = new int[32 * 32];
|
||||
+ RegionBitmap newSectorAllocations = new RegionBitmap();
|
||||
+ newSectorAllocations.force(0, 2); // make space for header
|
||||
+
|
||||
+ // allocate sectors for normal chunks
|
||||
+
|
||||
+ for (int chunkX = 0; chunkX < 32; ++chunkX) {
|
||||
+ for (int chunkZ = 0; chunkZ < 32; ++chunkZ) {
|
||||
+ int location = chunkX | (chunkZ << 5);
|
||||
+
|
||||
+ if (oversized[location]) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ int rawLength = rawLengths[location]; // bytes
|
||||
+ int sectorOffset = sectorOffsets[location]; // sectors
|
||||
+ int sectorLength = (int)roundToSectors(rawLength);
|
||||
+
|
||||
+ if (newSectorAllocations.tryAllocate(sectorOffset, sectorLength)) {
|
||||
+ calculatedOffsets[location] = sectorOffset << 8 | (sectorLength > 255 ? 255 : sectorLength); // support forge style oversized
|
||||
+ } else {
|
||||
+ LOGGER.error("Failed to allocate space for local chunk (overlapping data??) at (" + chunkX + "," + chunkZ + ") in regionfile " + this.regionFile.toAbsolutePath() + ", chunk will be regenerated");
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // allocate sectors for oversized chunks
|
||||
+
|
||||
+ for (int chunkX = 0; chunkX < 32; ++chunkX) {
|
||||
+ for (int chunkZ = 0; chunkZ < 32; ++chunkZ) {
|
||||
+ int location = chunkX | (chunkZ << 5);
|
||||
+
|
||||
+ if (!oversized[location]) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ int sectorOffset = newSectorAllocations.allocate(1);
|
||||
+ int sectorLength = 1;
|
||||
+
|
||||
+ try {
|
||||
+ this.file.write(this.createExternalStub(oversizedCompressionTypes[location]), sectorOffset * 4096);
|
||||
+ // only allocate in the new offsets if the write succeeds
|
||||
+ calculatedOffsets[location] = sectorOffset << 8 | (sectorLength > 255 ? 255 : sectorLength); // support forge style oversized
|
||||
+ } catch (IOException ex) {
|
||||
+ newSectorAllocations.free(sectorOffset, sectorLength);
|
||||
+ LOGGER.error("Failed to write new oversized chunk data holder, local chunk at (" + chunkX + "," + chunkZ + ") in regionfile " + this.regionFile.toAbsolutePath() + " will be regenerated");
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // rewrite aikar oversized data
|
||||
+
|
||||
+ this.oversizedCount = 0;
|
||||
+ for (int chunkX = 0; chunkX < 32; ++chunkX) {
|
||||
+ for (int chunkZ = 0; chunkZ < 32; ++chunkZ) {
|
||||
+ int location = chunkX | (chunkZ << 5);
|
||||
+ int isAikarOversized = hasAikarOversized[location] ? 1 : 0;
|
||||
+
|
||||
+ this.oversizedCount += isAikarOversized;
|
||||
+ this.oversized[location] = (byte)isAikarOversized;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (this.oversizedCount > 0) {
|
||||
+ try {
|
||||
+ this.writeOversizedMeta();
|
||||
+ } catch (Exception ex) {
|
||||
+ LOGGER.error("Failed to write aikar oversized chunk meta, all aikar style oversized chunk data will be lost for regionfile " + this.regionFile.toAbsolutePath(), ex);
|
||||
+ Files.deleteIfExists(this.getOversizedMetaFile());
|
||||
+ }
|
||||
+ } else {
|
||||
+ Files.deleteIfExists(this.getOversizedMetaFile());
|
||||
+ }
|
||||
+
|
||||
+ this.usedSectors.copyFrom(newSectorAllocations);
|
||||
+
|
||||
+ // before we overwrite the old sectors, print a summary of the chunks that got changed.
|
||||
+
|
||||
+ LOGGER.info("Starting summary of changes for regionfile " + this.regionFile.toAbsolutePath());
|
||||
+
|
||||
+ for (int chunkX = 0; chunkX < 32; ++chunkX) {
|
||||
+ for (int chunkZ = 0; chunkZ < 32; ++chunkZ) {
|
||||
+ int location = chunkX | (chunkZ << 5);
|
||||
+
|
||||
+ int oldOffset = this.offsets.get(location);
|
||||
+ int newOffset = calculatedOffsets[location];
|
||||
+
|
||||
+ if (oldOffset == newOffset) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ this.offsets.put(location, newOffset); // overwrite incorrect offset
|
||||
+
|
||||
+ if (oldOffset == 0) {
|
||||
+ // found lost data
|
||||
+ LOGGER.info("Found missing data for local chunk (" + chunkX + "," + chunkZ + ") in regionfile " + this.regionFile.toAbsolutePath());
|
||||
+ } else if (newOffset == 0) {
|
||||
+ LOGGER.warn("Data for local chunk (" + chunkX + "," + chunkZ + ") could not be recovered in regionfile " + this.regionFile.toAbsolutePath() + ", it will be regenerated");
|
||||
+ } else {
|
||||
+ LOGGER.info("Local chunk (" + chunkX + "," + chunkZ + ") changed to point to newer data or correct chunk in regionfile " + this.regionFile.toAbsolutePath());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ LOGGER.info("End of change summary for regionfile " + this.regionFile.toAbsolutePath());
|
||||
+
|
||||
+ // simply destroy the timestamp header, it's not used
|
||||
+
|
||||
+ for (int i = 0; i < 32 * 32; ++i) {
|
||||
+ this.timestamps.put(i, calculatedOffsets[i] != 0 ? (int)System.currentTimeMillis() : 0); // write a valid timestamp for valid chunks, I do not want to find out whatever dumb program actually checks this
|
||||
+ }
|
||||
+
|
||||
+ // write new header
|
||||
+ try {
|
||||
+ this.flush();
|
||||
+ this.file.force(true); // try to ensure it goes through...
|
||||
+ LOGGER.info("Successfully wrote new header to disk for regionfile " + this.regionFile.toAbsolutePath());
|
||||
+ } catch (IOException ex) {
|
||||
+ LOGGER.error("Failed to write new header to disk for regionfile " + this.regionFile.toAbsolutePath(), ex);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ final boolean canRecalcHeader; // final forces compile fail on new constructor
|
||||
+ // Paper end
|
||||
+
|
||||
// Paper start - Cache chunk status
|
||||
private final net.minecraft.world.level.chunk.ChunkStatus[] statuses = new net.minecraft.world.level.chunk.ChunkStatus[32 * 32];
|
||||
|
||||
@@ -78,8 +427,19 @@ public class RegionFile implements AutoCloseable {
|
||||
public RegionFile(Path file, Path directory, boolean dsync) throws IOException {
|
||||
this(file, directory, RegionFileVersion.VERSION_DEFLATE, dsync);
|
||||
}
|
||||
+ // Paper start - add can recalc flag
|
||||
+ public RegionFile(Path file, Path directory, boolean dsync, boolean canRecalcHeader) throws IOException {
|
||||
+ this(file, directory, RegionFileVersion.VERSION_DEFLATE, dsync, canRecalcHeader);
|
||||
+ }
|
||||
+ // Paper end - add can recalc flag
|
||||
|
||||
public RegionFile(Path file, Path directory, RegionFileVersion outputChunkStreamVersion, boolean dsync) throws IOException {
|
||||
+ // Paper start - add can recalc flag
|
||||
+ this(file, directory, outputChunkStreamVersion, dsync, false);
|
||||
+ }
|
||||
+ public RegionFile(Path file, Path directory, RegionFileVersion outputChunkStreamVersion, boolean dsync, boolean canRecalcHeader) throws IOException {
|
||||
+ this.canRecalcHeader = canRecalcHeader;
|
||||
+ // Paper end - add can recalc flag
|
||||
this.header = ByteBuffer.allocateDirect(8192);
|
||||
this.regionFile = file; // Paper
|
||||
initOversizedState(); // Paper
|
||||
@@ -108,14 +468,16 @@ public class RegionFile implements AutoCloseable {
|
||||
RegionFile.LOGGER.warn("Region file {} has truncated header: {}", file, i);
|
||||
}
|
||||
|
||||
- long j = Files.size(file);
|
||||
+ final long j = Files.size(file); final long regionFileSize = j; // Paper - recalculate header on header corruption
|
||||
|
||||
- for (int k = 0; k < 1024; ++k) {
|
||||
- int l = this.offsets.get(k);
|
||||
+ boolean needsHeaderRecalc = false; // Paper - recalculate header on header corruption
|
||||
+ boolean hasBackedUp = false; // Paper - recalculate header on header corruption
|
||||
+ for (int k = 0; k < 1024; ++k) { final int headerLocation = k; // Paper - we expect this to be the header location
|
||||
+ final int l = this.offsets.get(k);
|
||||
|
||||
if (l != 0) {
|
||||
- int i1 = RegionFile.getSectorNumber(l);
|
||||
- int j1 = RegionFile.getNumSectors(l);
|
||||
+ final int i1 = RegionFile.getSectorNumber(l); final int offset = i1; // Paper - we expect this to be offset in file in sectors
|
||||
+ int j1 = RegionFile.getNumSectors(l); final int sectorLength; // Paper - diff on change, we expect this to be sector length of region - watch out for reassignments
|
||||
// Spigot start
|
||||
if (j1 == 255) {
|
||||
// We're maxed out, so we need to read the proper length from the section
|
||||
@@ -124,32 +486,102 @@ public class RegionFile implements AutoCloseable {
|
||||
j1 = (realLen.getInt(0) + 4) / 4096 + 1;
|
||||
}
|
||||
// Spigot end
|
||||
+ sectorLength = j1; // Paper - diff on change, we expect this to be sector length of region
|
||||
|
||||
if (i1 < 2) {
|
||||
RegionFile.LOGGER.warn("Region file {} has invalid sector at index: {}; sector {} overlaps with header", new Object[]{file, k, i1});
|
||||
- this.offsets.put(k, 0);
|
||||
+ //this.offsets.put(k, 0); // Paper - we catch this, but need it in the header for the summary change
|
||||
} else if (j1 == 0) {
|
||||
RegionFile.LOGGER.warn("Region file {} has an invalid sector at index: {}; size has to be > 0", file, k);
|
||||
- this.offsets.put(k, 0);
|
||||
+ //this.offsets.put(k, 0); // Paper - we catch this, but need it in the header for the summary change
|
||||
} else if ((long) i1 * 4096L > j) {
|
||||
RegionFile.LOGGER.warn("Region file {} has an invalid sector at index: {}; sector {} is out of bounds", new Object[]{file, k, i1});
|
||||
- this.offsets.put(k, 0);
|
||||
+ //this.offsets.put(k, 0); // Paper - we catch this, but need it in the header for the summary change
|
||||
} else {
|
||||
- this.usedSectors.force(i1, j1);
|
||||
+ //this.usedSectors.force(i1, j1); // Paper - move this down so we can check if it fails to allocate
|
||||
+ }
|
||||
+ // Paper start - recalculate header on header corruption
|
||||
+ if (offset < 2 || sectorLength <= 0 || ((long)offset * 4096L) > regionFileSize) {
|
||||
+ if (canRecalcHeader) {
|
||||
+ LOGGER.error("Detected invalid header for regionfile " + this.regionFile.toAbsolutePath() + "! Recalculating header...");
|
||||
+ needsHeaderRecalc = true;
|
||||
+ break;
|
||||
+ } else {
|
||||
+ // location = chunkX | (chunkZ << 5);
|
||||
+ LOGGER.error("Detected invalid header for regionfile " + this.regionFile.toAbsolutePath() +
|
||||
+ "! Cannot recalculate, removing local chunk (" + (headerLocation & 31) + "," + (headerLocation >>> 5) + ") from header");
|
||||
+ if (!hasBackedUp) {
|
||||
+ hasBackedUp = true;
|
||||
+ this.backupRegionFile();
|
||||
+ }
|
||||
+ this.timestamps.put(headerLocation, 0); // be consistent, delete the timestamp too
|
||||
+ this.offsets.put(headerLocation, 0); // delete the entry from header
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+ boolean failedToAllocate = !this.usedSectors.tryAllocate(offset, sectorLength);
|
||||
+ if (failedToAllocate) {
|
||||
+ LOGGER.error("Overlapping allocation by local chunk (" + (headerLocation & 31) + "," + (headerLocation >>> 5) + ") in regionfile " + this.regionFile.toAbsolutePath());
|
||||
}
|
||||
+ if (failedToAllocate & !canRecalcHeader) {
|
||||
+ // location = chunkX | (chunkZ << 5);
|
||||
+ LOGGER.error("Detected invalid header for regionfile " + this.regionFile.toAbsolutePath() +
|
||||
+ "! Cannot recalculate, removing local chunk (" + (headerLocation & 31) + "," + (headerLocation >>> 5) + ") from header");
|
||||
+ if (!hasBackedUp) {
|
||||
+ hasBackedUp = true;
|
||||
+ this.backupRegionFile();
|
||||
+ }
|
||||
+ this.timestamps.put(headerLocation, 0); // be consistent, delete the timestamp too
|
||||
+ this.offsets.put(headerLocation, 0); // delete the entry from header
|
||||
+ continue;
|
||||
+ }
|
||||
+ needsHeaderRecalc |= failedToAllocate;
|
||||
+ // Paper end - recalculate header on header corruption
|
||||
}
|
||||
}
|
||||
+ // Paper start - recalculate header on header corruption
|
||||
+ // we move the recalc here so comparison to old header is correct when logging to console
|
||||
+ if (needsHeaderRecalc) { // true if header gave us overlapping allocations or had other issues
|
||||
+ LOGGER.error("Recalculating regionfile " + this.regionFile.toAbsolutePath() + ", header gave erroneous offsets & locations");
|
||||
+ this.recalculateHeader();
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private Path getExternalChunkPath(ChunkPos chunkPos) {
|
||||
- String s = "c." + chunkPos.x + "." + chunkPos.z + ".mcc";
|
||||
+ String s = "c." + chunkPos.x + "." + chunkPos.z + ".mcc"; // Paper - diff on change
|
||||
|
||||
return this.externalFileDir.resolve(s);
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ private static ChunkPos getOversizedChunkPair(Path file) {
|
||||
+ String fileName = file.getFileName().toString();
|
||||
+
|
||||
+ if (!fileName.startsWith("c.") || !fileName.endsWith(".mcc")) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ String[] split = fileName.split("\\.");
|
||||
+
|
||||
+ if (split.length != 4) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ try {
|
||||
+ int x = Integer.parseInt(split[1]);
|
||||
+ int z = Integer.parseInt(split[2]);
|
||||
+
|
||||
+ return new ChunkPos(x, z);
|
||||
+ } catch (NumberFormatException ex) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Nullable
|
||||
public synchronized DataInputStream getChunkDataInputStream(ChunkPos pos) throws IOException {
|
||||
int i = this.getOffset(pos);
|
||||
@@ -173,6 +605,11 @@ public class RegionFile implements AutoCloseable {
|
||||
((java.nio.Buffer) bytebuffer).flip(); // CraftBukkit - decompile error
|
||||
if (bytebuffer.remaining() < 5) {
|
||||
RegionFile.LOGGER.error("Chunk {} header is truncated: expected {} but read {}", new Object[]{pos, l, bytebuffer.remaining()});
|
||||
+ // Paper start - recalculate header on regionfile corruption
|
||||
+ if (this.canRecalcHeader && this.recalculateHeader()) {
|
||||
+ return this.getChunkDataInputStream(pos);
|
||||
+ }
|
||||
+ // Paper end - recalculate header on regionfile corruption
|
||||
return null;
|
||||
} else {
|
||||
int i1 = bytebuffer.getInt();
|
||||
@@ -180,6 +617,11 @@ public class RegionFile implements AutoCloseable {
|
||||
|
||||
if (i1 == 0) {
|
||||
RegionFile.LOGGER.warn("Chunk {} is allocated, but stream is missing", pos);
|
||||
+ // Paper start - recalculate header on regionfile corruption
|
||||
+ if (this.canRecalcHeader && this.recalculateHeader()) {
|
||||
+ return this.getChunkDataInputStream(pos);
|
||||
+ }
|
||||
+ // Paper end - recalculate header on regionfile corruption
|
||||
return null;
|
||||
} else {
|
||||
int j1 = i1 - 1;
|
||||
@@ -187,17 +629,44 @@ public class RegionFile implements AutoCloseable {
|
||||
if (RegionFile.isExternalStreamChunk(b0)) {
|
||||
if (j1 != 0) {
|
||||
RegionFile.LOGGER.warn("Chunk has both internal and external streams");
|
||||
+ // Paper start - recalculate header on regionfile corruption
|
||||
+ if (this.canRecalcHeader && this.recalculateHeader()) {
|
||||
+ return this.getChunkDataInputStream(pos);
|
||||
+ }
|
||||
+ // Paper end - recalculate header on regionfile corruption
|
||||
}
|
||||
|
||||
- return this.createExternalChunkInputStream(pos, RegionFile.getExternalChunkVersion(b0));
|
||||
+ // Paper start - recalculate header on regionfile corruption
|
||||
+ final DataInputStream ret = this.createExternalChunkInputStream(pos, RegionFile.getExternalChunkVersion(b0));
|
||||
+ if (ret == null && this.canRecalcHeader && this.recalculateHeader()) {
|
||||
+ return this.getChunkDataInputStream(pos);
|
||||
+ }
|
||||
+ return ret;
|
||||
+ // Paper end - recalculate header on regionfile corruption
|
||||
} else if (j1 > bytebuffer.remaining()) {
|
||||
RegionFile.LOGGER.error("Chunk {} stream is truncated: expected {} but read {}", new Object[]{pos, j1, bytebuffer.remaining()});
|
||||
+ // Paper start - recalculate header on regionfile corruption
|
||||
+ if (this.canRecalcHeader && this.recalculateHeader()) {
|
||||
+ return this.getChunkDataInputStream(pos);
|
||||
+ }
|
||||
+ // Paper end - recalculate header on regionfile corruption
|
||||
return null;
|
||||
} else if (j1 < 0) {
|
||||
RegionFile.LOGGER.error("Declared size {} of chunk {} is negative", i1, pos);
|
||||
+ // Paper start - recalculate header on regionfile corruption
|
||||
+ if (this.canRecalcHeader && this.recalculateHeader()) {
|
||||
+ return this.getChunkDataInputStream(pos);
|
||||
+ }
|
||||
+ // Paper end - recalculate header on regionfile corruption
|
||||
return null;
|
||||
} else {
|
||||
- return this.createChunkInputStream(pos, b0, RegionFile.createStream(bytebuffer, j1));
|
||||
+ // Paper start - recalculate header on regionfile corruption
|
||||
+ final DataInputStream ret = this.createChunkInputStream(pos, b0, RegionFile.createStream(bytebuffer, j1));
|
||||
+ if (ret == null && this.canRecalcHeader && this.recalculateHeader()) {
|
||||
+ return this.getChunkDataInputStream(pos);
|
||||
+ }
|
||||
+ return ret;
|
||||
+ // Paper end - recalculate header on regionfile corruption
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -372,10 +841,15 @@ public class RegionFile implements AutoCloseable {
|
||||
}
|
||||
|
||||
private ByteBuffer createExternalStub() {
|
||||
+ // Paper start - add version param
|
||||
+ return this.createExternalStub(this.version);
|
||||
+ }
|
||||
+ private ByteBuffer createExternalStub(RegionFileVersion version) {
|
||||
+ // Paper end - add version param
|
||||
ByteBuffer bytebuffer = ByteBuffer.allocate(5);
|
||||
|
||||
bytebuffer.putInt(1);
|
||||
- bytebuffer.put((byte) (this.version.getId() | 128));
|
||||
+ bytebuffer.put((byte) (version.getId() | 128)); // Paper - replace with version param
|
||||
((java.nio.Buffer) bytebuffer).flip(); // CraftBukkit - decompile error
|
||||
return bytebuffer;
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
index 65fd57609e45ccd49ebfc1ba80d25243da13ab6e..d5b501bcb33a785ef0301bf6bb8d396b9d4fbb6a 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
@@ -26,7 +26,15 @@ public class RegionFileStorage implements AutoCloseable {
|
||||
private final Path folder;
|
||||
private final boolean sync;
|
||||
|
||||
+ private final boolean isChunkData; // Paper
|
||||
+
|
||||
protected RegionFileStorage(Path directory, boolean dsync) { // Paper - protected constructor
|
||||
+ // Paper start - add isChunkData param
|
||||
+ this(directory, dsync, false);
|
||||
+ }
|
||||
+ RegionFileStorage(Path directory, boolean dsync, boolean isChunkData) {
|
||||
+ this.isChunkData = isChunkData;
|
||||
+ // Paper end - add isChunkData param
|
||||
this.folder = directory;
|
||||
this.sync = dsync;
|
||||
}
|
||||
@@ -88,9 +96,9 @@ public class RegionFileStorage implements AutoCloseable {
|
||||
FileUtil.createDirectoriesSafe(this.folder);
|
||||
Path path = this.folder;
|
||||
int j = chunkcoordintpair.getRegionX();
|
||||
- Path path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca");
|
||||
+ Path path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca"); // Paper - diff on change
|
||||
if (existingOnly && !java.nio.file.Files.exists(path1)) return null; // CraftBukkit
|
||||
- RegionFile regionfile1 = new RegionFile(path1, this.folder, this.sync);
|
||||
+ RegionFile regionfile1 = new RegionFile(path1, this.folder, this.sync, this.isChunkData); // Paper - allow for chunk regionfiles to regen header
|
||||
|
||||
this.regionCache.putAndMoveToFirst(i, regionfile1);
|
||||
// Paper start
|
||||
@@ -175,6 +183,13 @@ public class RegionFileStorage implements AutoCloseable {
|
||||
if (regionfile == null) {
|
||||
return null;
|
||||
}
|
||||
+ // Paper start - Add regionfile parameter
|
||||
+ return this.read(pos, regionfile);
|
||||
+ }
|
||||
+ public CompoundTag read(ChunkPos pos, RegionFile regionfile) throws IOException {
|
||||
+ // We add the regionfile parameter to avoid the potential deadlock (on fileLock) if we went back to obtain a regionfile
|
||||
+ // if we decide to re-read
|
||||
+ // Paper end
|
||||
// CraftBukkit end
|
||||
try { // Paper
|
||||
DataInputStream datainputstream = regionfile.getChunkDataInputStream(pos);
|
||||
@@ -191,6 +206,20 @@ public class RegionFileStorage implements AutoCloseable {
|
||||
try {
|
||||
if (datainputstream != null) {
|
||||
nbttagcompound = NbtIo.read((DataInput) datainputstream);
|
||||
+ // Paper start - recover from corrupt regionfile header
|
||||
+ if (this.isChunkData) {
|
||||
+ ChunkPos chunkPos = ChunkSerializer.getChunkCoordinate(nbttagcompound);
|
||||
+ if (!chunkPos.equals(pos)) {
|
||||
+ net.minecraft.server.MinecraftServer.LOGGER.error("Attempting to read chunk data at " + pos + " but got chunk data for " + chunkPos + " instead! Attempting regionfile recalculation for regionfile " + regionfile.regionFile.toAbsolutePath());
|
||||
+ if (regionfile.recalculateHeader()) {
|
||||
+ regionfile.fileLock.lock(); // otherwise we will unlock twice and only lock once.
|
||||
+ return this.read(pos, regionfile);
|
||||
+ }
|
||||
+ net.minecraft.server.MinecraftServer.LOGGER.error("Can't recalculate regionfile header, regenerating chunk " + pos + " for " + regionfile.regionFile.toAbsolutePath());
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - recover from corrupt regionfile header
|
||||
break label43;
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
index 5fa7a842431dd64c7a0dc5d8e940563a2aeef463..4411e427d3b6b592f8a18e61b6c59309cf699d3f 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
@@ -14,7 +14,7 @@ import javax.annotation.Nullable;
|
||||
import net.minecraft.util.FastBufferedInputStream;
|
||||
|
||||
public class RegionFileVersion {
|
||||
- private static final Int2ObjectMap<RegionFileVersion> VERSIONS = new Int2ObjectOpenHashMap<>();
|
||||
+ public static final Int2ObjectMap<RegionFileVersion> VERSIONS = new Int2ObjectOpenHashMap<>(); // Paper - private -> public
|
||||
public static final RegionFileVersion VERSION_GZIP = register(new RegionFileVersion(1, (stream) -> {
|
||||
return new FastBufferedInputStream(new GZIPInputStream(stream));
|
||||
}, (stream) -> {
|
|
@ -1,343 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Thu, 11 Mar 2021 20:05:44 -0800
|
||||
Subject: [PATCH] Custom table implementation for blockstate state lookups
|
||||
|
||||
Testing some redstone intensive machines showed to bring about a 10%
|
||||
improvement.
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java b/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..57d0cd3ad6f972e986c72a57f1a6e36003f190c2
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java
|
||||
@@ -0,0 +1,160 @@
|
||||
+package io.papermc.paper.util.table;
|
||||
+
|
||||
+import com.google.common.collect.Table;
|
||||
+import net.minecraft.world.level.block.state.StateHolder;
|
||||
+import net.minecraft.world.level.block.state.properties.Property;
|
||||
+import java.util.Collection;
|
||||
+import java.util.HashSet;
|
||||
+import java.util.Map;
|
||||
+import java.util.Set;
|
||||
+
|
||||
+public final class ZeroCollidingReferenceStateTable {
|
||||
+
|
||||
+ // upper 32 bits: starting index
|
||||
+ // lower 32 bits: bitset for contained ids
|
||||
+ protected final long[] this_index_table;
|
||||
+ protected final Comparable<?>[] this_table;
|
||||
+ protected final StateHolder<?, ?> this_state;
|
||||
+
|
||||
+ protected long[] index_table;
|
||||
+ protected StateHolder<?, ?>[][] value_table;
|
||||
+
|
||||
+ public ZeroCollidingReferenceStateTable(final StateHolder<?, ?> state, final Map<Property<?>, Comparable<?>> this_map) {
|
||||
+ this.this_state = state;
|
||||
+ this.this_index_table = this.create_table(this_map.keySet());
|
||||
+
|
||||
+ int max_id = -1;
|
||||
+ for (final Property<?> property : this_map.keySet()) {
|
||||
+ final int id = lookup_vindex(property, this.this_index_table);
|
||||
+ if (id > max_id) {
|
||||
+ max_id = id;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ this.this_table = new Comparable[max_id + 1];
|
||||
+ for (final Map.Entry<Property<?>, Comparable<?>> entry : this_map.entrySet()) {
|
||||
+ this.this_table[lookup_vindex(entry.getKey(), this.this_index_table)] = entry.getValue();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public void loadInTable(final Table<Property<?>, Comparable<?>, StateHolder<?, ?>> table,
|
||||
+ final Map<Property<?>, Comparable<?>> this_map) {
|
||||
+ final Set<Property<?>> combined = new HashSet<>(table.rowKeySet());
|
||||
+ combined.addAll(this_map.keySet());
|
||||
+
|
||||
+ this.index_table = this.create_table(combined);
|
||||
+
|
||||
+ int max_id = -1;
|
||||
+ for (final Property<?> property : combined) {
|
||||
+ final int id = lookup_vindex(property, this.index_table);
|
||||
+ if (id > max_id) {
|
||||
+ max_id = id;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ this.value_table = new StateHolder[max_id + 1][];
|
||||
+
|
||||
+ final Map<Property<?>, Map<Comparable<?>, StateHolder<?, ?>>> map = table.rowMap();
|
||||
+ for (final Property<?> property : map.keySet()) {
|
||||
+ final Map<Comparable<?>, StateHolder<?, ?>> propertyMap = map.get(property);
|
||||
+
|
||||
+ final int id = lookup_vindex(property, this.index_table);
|
||||
+ final StateHolder<?, ?>[] states = this.value_table[id] = new StateHolder[property.getPossibleValues().size()];
|
||||
+
|
||||
+ for (final Map.Entry<Comparable<?>, StateHolder<?, ?>> entry : propertyMap.entrySet()) {
|
||||
+ if (entry.getValue() == null) {
|
||||
+ // TODO what
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ states[((Property)property).getIdFor(entry.getKey())] = entry.getValue();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ for (final Map.Entry<Property<?>, Comparable<?>> entry : this_map.entrySet()) {
|
||||
+ final Property<?> property = entry.getKey();
|
||||
+ final int index = lookup_vindex(property, this.index_table);
|
||||
+
|
||||
+ if (this.value_table[index] == null) {
|
||||
+ this.value_table[index] = new StateHolder[property.getPossibleValues().size()];
|
||||
+ }
|
||||
+
|
||||
+ this.value_table[index][((Property)property).getIdFor(entry.getValue())] = this.this_state;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ protected long[] create_table(final Collection<Property<?>> collection) {
|
||||
+ int max_id = -1;
|
||||
+ for (final Property<?> property : collection) {
|
||||
+ final int id = property.getId();
|
||||
+ if (id > max_id) {
|
||||
+ max_id = id;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ final long[] ret = new long[((max_id + 1) + 31) >>> 5]; // ceil((max_id + 1) / 32)
|
||||
+
|
||||
+ for (final Property<?> property : collection) {
|
||||
+ final int id = property.getId();
|
||||
+
|
||||
+ ret[id >>> 5] |= (1L << (id & 31));
|
||||
+ }
|
||||
+
|
||||
+ int total = 0;
|
||||
+ for (int i = 1, len = ret.length; i < len; ++i) {
|
||||
+ ret[i] |= (long)(total += Long.bitCount(ret[i - 1] & 0xFFFFFFFFL)) << 32;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ public Comparable<?> get(final Property<?> state) {
|
||||
+ final Comparable<?>[] table = this.this_table;
|
||||
+ final int index = lookup_vindex(state, this.this_index_table);
|
||||
+
|
||||
+ if (index < 0 || index >= table.length) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ return table[index];
|
||||
+ }
|
||||
+
|
||||
+ public StateHolder<?, ?> get(final Property<?> property, final Comparable<?> with) {
|
||||
+ final int withId = ((Property)property).getIdFor(with);
|
||||
+ if (withId < 0) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ final int index = lookup_vindex(property, this.index_table);
|
||||
+ final StateHolder<?, ?>[][] table = this.value_table;
|
||||
+ if (index < 0 || index >= table.length) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ final StateHolder<?, ?>[] values = table[index];
|
||||
+
|
||||
+ if (withId >= values.length) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ return values[withId];
|
||||
+ }
|
||||
+
|
||||
+ protected static int lookup_vindex(final Property<?> property, final long[] index_table) {
|
||||
+ final int id = property.getId();
|
||||
+ final long bitset_mask = (1L << (id & 31));
|
||||
+ final long lower_mask = bitset_mask - 1;
|
||||
+ final int index = id >>> 5;
|
||||
+ if (index >= index_table.length) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+ final long index_value = index_table[index];
|
||||
+ final long contains_check = ((index_value & bitset_mask) - 1) >> (Long.SIZE - 1); // -1L if doesn't contain
|
||||
+
|
||||
+ // index = total bits set in lower table values (upper 32 bits of index_value) plus total bits set in lower indices below id
|
||||
+ // contains_check is 0 if the bitset had id set, else it's -1: so index is unaffected if contains_check == 0,
|
||||
+ // otherwise it comes out as -1.
|
||||
+ return (int)(((index_value >>> 32) + Long.bitCount(index_value & lower_mask)) | contains_check);
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/state/StateHolder.java b/src/main/java/net/minecraft/world/level/block/state/StateHolder.java
|
||||
index b617f7e7a27284fbbad06d7a9926b938ba87cf18..170f5cb3f01a57ad76e3bbeacd5b7c7e52f29959 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/state/StateHolder.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/state/StateHolder.java
|
||||
@@ -39,11 +39,13 @@ public abstract class StateHolder<O, S> {
|
||||
private final ImmutableMap<Property<?>, Comparable<?>> values;
|
||||
private Table<Property<?>, Comparable<?>, S> neighbours;
|
||||
protected final MapCodec<S> propertiesCodec;
|
||||
+ protected final io.papermc.paper.util.table.ZeroCollidingReferenceStateTable optimisedTable; // Paper - optimise state lookup
|
||||
|
||||
protected StateHolder(O owner, ImmutableMap<Property<?>, Comparable<?>> entries, MapCodec<S> codec) {
|
||||
this.owner = owner;
|
||||
this.values = entries;
|
||||
this.propertiesCodec = codec;
|
||||
+ this.optimisedTable = new io.papermc.paper.util.table.ZeroCollidingReferenceStateTable(this, entries); // Paper - optimise state lookup
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> S cycle(Property<T> property) {
|
||||
@@ -84,11 +86,11 @@ public abstract class StateHolder<O, S> {
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> boolean hasProperty(Property<T> property) {
|
||||
- return this.values.containsKey(property);
|
||||
+ return this.optimisedTable.get(property) != null; // Paper - optimise state lookup
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> T getValue(Property<T> property) {
|
||||
- Comparable<?> comparable = this.values.get(property);
|
||||
+ Comparable<?> comparable = this.optimisedTable.get(property); // Paper - optimise state lookup
|
||||
if (comparable == null) {
|
||||
throw new IllegalArgumentException("Cannot get property " + property + " as it does not exist in " + this.owner);
|
||||
} else {
|
||||
@@ -97,24 +99,18 @@ public abstract class StateHolder<O, S> {
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> Optional<T> getOptionalValue(Property<T> property) {
|
||||
- Comparable<?> comparable = this.values.get(property);
|
||||
+ Comparable<?> comparable = this.optimisedTable.get(property); // Paper - optimise state lookup
|
||||
return comparable == null ? Optional.empty() : Optional.of(property.getValueClass().cast(comparable));
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>, V extends T> S setValue(Property<T> property, V value) {
|
||||
- Comparable<?> comparable = this.values.get(property);
|
||||
- if (comparable == null) {
|
||||
- throw new IllegalArgumentException("Cannot set property " + property + " as it does not exist in " + this.owner);
|
||||
- } else if (comparable == value) {
|
||||
- return (S)this;
|
||||
- } else {
|
||||
- S object = this.neighbours.get(property, value);
|
||||
- if (object == null) {
|
||||
- throw new IllegalArgumentException("Cannot set property " + property + " to " + value + " on " + this.owner + ", it is not an allowed value");
|
||||
- } else {
|
||||
- return object;
|
||||
- }
|
||||
+ // Paper start - optimise state lookup
|
||||
+ final S ret = (S)this.optimisedTable.get(property, value);
|
||||
+ if (ret == null) {
|
||||
+ throw new IllegalArgumentException("Cannot set property " + property + " to " + value + " on " + this.owner + ", it is not an allowed value");
|
||||
}
|
||||
+ return ret;
|
||||
+ // Paper end - optimise state lookup
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>, V extends T> S trySetValue(Property<T> property, V value) {
|
||||
@@ -147,7 +143,7 @@ public abstract class StateHolder<O, S> {
|
||||
}
|
||||
}
|
||||
|
||||
- this.neighbours = (Table<Property<?>, Comparable<?>, S>)(table.isEmpty() ? table : ArrayTable.create(table));
|
||||
+ this.neighbours = (Table<Property<?>, Comparable<?>, S>)(table.isEmpty() ? table : ArrayTable.create(table)); this.optimisedTable.loadInTable((Table)this.neighbours, this.values); // Paper - optimise state lookup
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java
|
||||
index ff1a0d125edd2ea10c870cbb62ae9aa23644b6dc..233215280f8494dbc33a2fd0b14e37e59f1cb643 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java
|
||||
@@ -7,6 +7,13 @@ import java.util.Optional;
|
||||
public class BooleanProperty extends Property<Boolean> {
|
||||
private final ImmutableSet<Boolean> values = ImmutableSet.of(true, false);
|
||||
|
||||
+ // Paper start - optimise iblockdata state lookup
|
||||
+ @Override
|
||||
+ public final int getIdFor(final Boolean value) {
|
||||
+ return value.booleanValue() ? 1 : 0;
|
||||
+ }
|
||||
+ // Paper end - optimise iblockdata state lookup
|
||||
+
|
||||
protected BooleanProperty(String name) {
|
||||
super(name, Boolean.class);
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java
|
||||
index 0bca0f971dac994bd8b6ecd87e8b33e26c0f18f9..edd3c745efb40ee79a1393199c7a27ddaa2f8026 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java
|
||||
@@ -15,6 +15,15 @@ public class EnumProperty<T extends Enum<T> & StringRepresentable> extends Prope
|
||||
private final ImmutableSet<T> values;
|
||||
private final Map<String, T> names = Maps.newHashMap();
|
||||
|
||||
+ // Paper start - optimise iblockdata state lookup
|
||||
+ private int[] idLookupTable;
|
||||
+
|
||||
+ @Override
|
||||
+ public final int getIdFor(final T value) {
|
||||
+ return this.idLookupTable[value.ordinal()];
|
||||
+ }
|
||||
+ // Paper end - optimise iblockdata state lookup
|
||||
+
|
||||
protected EnumProperty(String name, Class<T> type, Collection<T> values) {
|
||||
super(name, type);
|
||||
this.values = ImmutableSet.copyOf(values);
|
||||
@@ -28,6 +37,14 @@ public class EnumProperty<T extends Enum<T> & StringRepresentable> extends Prope
|
||||
this.names.put(string, enum_);
|
||||
}
|
||||
|
||||
+ // Paper start - optimise iblockdata state lookup
|
||||
+ int id = 0;
|
||||
+ this.idLookupTable = new int[type.getEnumConstants().length];
|
||||
+ java.util.Arrays.fill(this.idLookupTable, -1);
|
||||
+ for (final T value : this.getPossibleValues()) {
|
||||
+ this.idLookupTable[value.ordinal()] = id++;
|
||||
+ }
|
||||
+ // Paper end - optimise iblockdata state lookup
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java
|
||||
index bdbe0362e49e73c05237f9f3143230e0b03e494e..8eb20ea852a8e89c431fea55a7b60833a6c8104f 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java
|
||||
@@ -11,6 +11,16 @@ public class IntegerProperty extends Property<Integer> {
|
||||
public final int min;
|
||||
public final int max;
|
||||
|
||||
+ // Paper start - optimise iblockdata state lookup
|
||||
+ @Override
|
||||
+ public final int getIdFor(final Integer value) {
|
||||
+ final int val = value.intValue();
|
||||
+ final int ret = val - this.min;
|
||||
+
|
||||
+ return ret | ((this.max - ret) >> 31);
|
||||
+ }
|
||||
+ // Paper end - optimise iblockdata state lookup
|
||||
+
|
||||
protected IntegerProperty(String name, int min, int max) {
|
||||
super(name, Integer.class);
|
||||
if (min < 0) {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/Property.java b/src/main/java/net/minecraft/world/level/block/state/properties/Property.java
|
||||
index a37424bbc6bee02354abaa793aa0865c556c6bbe..f923593bd336dd1a950ba61603d53edb3c9703eb 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/state/properties/Property.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/Property.java
|
||||
@@ -22,6 +22,17 @@ public abstract class Property<T extends Comparable<T>> {
|
||||
}, this::getName);
|
||||
private final Codec<Property.Value<T>> valueCodec = this.codec.xmap(this::value, Property.Value::value);
|
||||
|
||||
+ // Paper start - optimise iblockdata state lookup
|
||||
+ private static final java.util.concurrent.atomic.AtomicInteger ID_GENERATOR = new java.util.concurrent.atomic.AtomicInteger();
|
||||
+ private final int id = ID_GENERATOR.getAndIncrement();
|
||||
+
|
||||
+ public final int getId() {
|
||||
+ return this.id;
|
||||
+ }
|
||||
+
|
||||
+ public abstract int getIdFor(final T value);
|
||||
+ // Paper end - optimise state lookup
|
||||
+
|
||||
protected Property(String name, Class<T> type) {
|
||||
this.clazz = type;
|
||||
this.name = name;
|
|
@ -1,295 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Thu, 26 Mar 2020 21:59:32 -0700
|
||||
Subject: [PATCH] Detail more information in watchdog dumps
|
||||
|
||||
- Dump position, world, velocity, and uuid for currently ticking entities
|
||||
- Dump player name, player uuid, position, and world for packet handling
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
||||
index 3355d0d75675e2f9a62c4681871ba0f403073286..46153b8e5823959e17621bbebd414d8998b77fb5 100644
|
||||
--- a/src/main/java/net/minecraft/network/Connection.java
|
||||
+++ b/src/main/java/net/minecraft/network/Connection.java
|
||||
@@ -512,7 +512,13 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
if (!(this.packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginPacketListener)
|
||||
|| loginPacketListener.state != net.minecraft.server.network.ServerLoginPacketListenerImpl.State.READY_TO_ACCEPT
|
||||
|| Connection.joinAttemptsThisTick++ < MAX_PER_TICK) {
|
||||
+ // Paper start - detailed watchdog information
|
||||
+ net.minecraft.network.protocol.PacketUtils.packetProcessing.push(this.packetListener);
|
||||
+ try { // Paper end - detailed watchdog information
|
||||
tickablepacketlistener.tick();
|
||||
+ } finally { // Paper start - detailed watchdog information
|
||||
+ net.minecraft.network.protocol.PacketUtils.packetProcessing.pop();
|
||||
+ } // Paper end - detailed watchdog information
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/network/protocol/PacketUtils.java b/src/main/java/net/minecraft/network/protocol/PacketUtils.java
|
||||
index acfa1907bfc9c29d261cfccc00d65bad9ad1a002..d6f3869f5725c7f081efb7f486f74dbb99d4d005 100644
|
||||
--- a/src/main/java/net/minecraft/network/protocol/PacketUtils.java
|
||||
+++ b/src/main/java/net/minecraft/network/protocol/PacketUtils.java
|
||||
@@ -15,6 +15,24 @@ public class PacketUtils {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
+ // Paper start - detailed watchdog information
|
||||
+ public static final java.util.concurrent.ConcurrentLinkedDeque<PacketListener> packetProcessing = new java.util.concurrent.ConcurrentLinkedDeque<>();
|
||||
+ static final java.util.concurrent.atomic.AtomicLong totalMainThreadPacketsProcessed = new java.util.concurrent.atomic.AtomicLong();
|
||||
+
|
||||
+ public static long getTotalProcessedPackets() {
|
||||
+ return totalMainThreadPacketsProcessed.get();
|
||||
+ }
|
||||
+
|
||||
+ public static java.util.List<PacketListener> getCurrentPacketProcessors() {
|
||||
+ java.util.List<PacketListener> ret = new java.util.ArrayList<>(4);
|
||||
+ for (PacketListener listener : packetProcessing) {
|
||||
+ ret.add(listener);
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+ // Paper end - detailed watchdog information
|
||||
+
|
||||
public PacketUtils() {}
|
||||
|
||||
public static <T extends PacketListener> void ensureRunningOnSameThread(Packet<T> packet, T listener, ServerLevel world) throws RunningOnDifferentThreadException {
|
||||
@@ -24,6 +42,8 @@ public class PacketUtils {
|
||||
public static <T extends PacketListener> void ensureRunningOnSameThread(Packet<T> packet, T listener, BlockableEventLoop<?> engine) throws RunningOnDifferentThreadException {
|
||||
if (!engine.isSameThread()) {
|
||||
engine.executeIfPossible(() -> {
|
||||
+ packetProcessing.push(listener); // Paper - detailed watchdog information
|
||||
+ try { // Paper - detailed watchdog information
|
||||
if (MinecraftServer.getServer().hasStopped() || (listener instanceof ServerGamePacketListenerImpl && ((ServerGamePacketListenerImpl) listener).processedDisconnect)) return; // CraftBukkit, MC-142590
|
||||
if (listener.getConnection().isConnected()) {
|
||||
co.aikar.timings.Timing timing = co.aikar.timings.MinecraftTimings.getPacketTiming(packet); // Paper - timings
|
||||
@@ -43,6 +63,12 @@ public class PacketUtils {
|
||||
} else {
|
||||
PacketUtils.LOGGER.debug("Ignoring packet due to disconnection: {}", packet);
|
||||
}
|
||||
+ // Paper start - detailed watchdog information
|
||||
+ } finally {
|
||||
+ totalMainThreadPacketsProcessed.getAndIncrement();
|
||||
+ packetProcessing.pop();
|
||||
+ }
|
||||
+ // Paper end - detailed watchdog information
|
||||
|
||||
});
|
||||
throw RunningOnDifferentThreadException.RUNNING_ON_DIFFERENT_THREAD;
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index 9b2c24a01fd09d9520f3c8baff7c56ae48f0c5de..7c6b76399648d67e6726a13e1d29e1978bae641a 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -1060,7 +1060,26 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
|
||||
}
|
||||
|
||||
+ // Paper start - log detailed entity tick information
|
||||
+ // TODO replace with varhandle
|
||||
+ static final java.util.concurrent.atomic.AtomicReference<Entity> currentlyTickingEntity = new java.util.concurrent.atomic.AtomicReference<>();
|
||||
+
|
||||
+ public static List<Entity> getCurrentlyTickingEntities() {
|
||||
+ Entity ticking = currentlyTickingEntity.get();
|
||||
+ List<Entity> ret = java.util.Arrays.asList(ticking == null ? new Entity[0] : new Entity[] { ticking });
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+ // Paper end - log detailed entity tick information
|
||||
+
|
||||
public void tickNonPassenger(Entity entity) {
|
||||
+ // Paper start - log detailed entity tick information
|
||||
+ io.papermc.paper.util.TickThread.ensureTickThread("Cannot tick an entity off-main");
|
||||
+ try {
|
||||
+ if (currentlyTickingEntity.get() == null) {
|
||||
+ currentlyTickingEntity.lazySet(entity);
|
||||
+ }
|
||||
+ // Paper end - log detailed entity tick information
|
||||
++TimingHistory.entityTicks; // Paper - timings
|
||||
// Spigot start
|
||||
co.aikar.timings.Timing timer; // Paper
|
||||
@@ -1100,7 +1119,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
this.tickPassenger(entity, entity1);
|
||||
}
|
||||
// } finally { timer.stopTiming(); } // Paper - timings - move up
|
||||
-
|
||||
+ // Paper start - log detailed entity tick information
|
||||
+ } finally {
|
||||
+ if (currentlyTickingEntity.get() == entity) {
|
||||
+ currentlyTickingEntity.lazySet(null);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - log detailed entity tick information
|
||||
}
|
||||
|
||||
private void tickPassenger(Entity vehicle, Entity passenger) {
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index f19ece7738e493c3e69ea298f55e3f32da31204e..ef515ec320454fd6a54457ca419ddd42d0c3b5c8 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -971,7 +971,42 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
return this.onGround;
|
||||
}
|
||||
|
||||
+ // Paper start - detailed watchdog information
|
||||
+ public final Object posLock = new Object(); // Paper - log detailed entity tick information
|
||||
+
|
||||
+ private Vec3 moveVector;
|
||||
+ private double moveStartX;
|
||||
+ private double moveStartY;
|
||||
+ private double moveStartZ;
|
||||
+
|
||||
+ public final Vec3 getMoveVector() {
|
||||
+ return this.moveVector;
|
||||
+ }
|
||||
+
|
||||
+ public final double getMoveStartX() {
|
||||
+ return this.moveStartX;
|
||||
+ }
|
||||
+
|
||||
+ public final double getMoveStartY() {
|
||||
+ return this.moveStartY;
|
||||
+ }
|
||||
+
|
||||
+ public final double getMoveStartZ() {
|
||||
+ return this.moveStartZ;
|
||||
+ }
|
||||
+ // Paper end - detailed watchdog information
|
||||
+
|
||||
public void move(MoverType movementType, Vec3 movement) {
|
||||
+ // Paper start - detailed watchdog information
|
||||
+ io.papermc.paper.util.TickThread.ensureTickThread("Cannot move an entity off-main");
|
||||
+ synchronized (this.posLock) {
|
||||
+ this.moveStartX = this.getX();
|
||||
+ this.moveStartY = this.getY();
|
||||
+ this.moveStartZ = this.getZ();
|
||||
+ this.moveVector = movement;
|
||||
+ }
|
||||
+ try {
|
||||
+ // Paper end - detailed watchdog information
|
||||
if (this.noPhysics) {
|
||||
this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z);
|
||||
} else {
|
||||
@@ -1144,6 +1179,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
this.level.getProfiler().pop();
|
||||
}
|
||||
}
|
||||
+ // Paper start - detailed watchdog information
|
||||
+ } finally {
|
||||
+ synchronized (this.posLock) { // Paper
|
||||
+ this.moveVector = null;
|
||||
+ } // Paper
|
||||
+ }
|
||||
+ // Paper end - detailed watchdog information
|
||||
}
|
||||
|
||||
protected boolean isHorizontalCollisionMinor(Vec3 adjustedMovement) {
|
||||
@@ -4027,7 +4069,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
}
|
||||
|
||||
public void setDeltaMovement(Vec3 velocity) {
|
||||
+ synchronized (this.posLock) { // Paper
|
||||
this.deltaMovement = velocity;
|
||||
+ } // Paper
|
||||
}
|
||||
|
||||
public void addDeltaMovement(Vec3 velocity) {
|
||||
@@ -4113,7 +4157,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
}
|
||||
// Paper end - fix MC-4
|
||||
if (this.position.x != x || this.position.y != y || this.position.z != z) {
|
||||
+ synchronized (this.posLock) { // Paper
|
||||
this.position = new Vec3(x, y, z);
|
||||
+ } // Paper
|
||||
int i = Mth.floor(x);
|
||||
int j = Mth.floor(y);
|
||||
int k = Mth.floor(z);
|
||||
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
index e0a71bfc1498a517456b21747ab6ef3f1067a3f3..e9fa7faaa4451e36b3908cbcbbe0baf213abde96 100644
|
||||
--- a/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
@@ -22,6 +22,78 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
|
||||
private volatile long lastTick;
|
||||
private volatile boolean stopping;
|
||||
|
||||
+ // Paper start - log detailed tick information
|
||||
+ private void dumpEntity(net.minecraft.world.entity.Entity entity) {
|
||||
+ Logger log = Bukkit.getServer().getLogger();
|
||||
+ double posX, posY, posZ;
|
||||
+ net.minecraft.world.phys.Vec3 mot;
|
||||
+ double moveStartX, moveStartY, moveStartZ;
|
||||
+ net.minecraft.world.phys.Vec3 moveVec;
|
||||
+ synchronized (entity.posLock) {
|
||||
+ posX = entity.getX();
|
||||
+ posY = entity.getY();
|
||||
+ posZ = entity.getZ();
|
||||
+ mot = entity.getDeltaMovement();
|
||||
+ moveStartX = entity.getMoveStartX();
|
||||
+ moveStartY = entity.getMoveStartY();
|
||||
+ moveStartZ = entity.getMoveStartZ();
|
||||
+ moveVec = entity.getMoveVector();
|
||||
+ }
|
||||
+
|
||||
+ String entityType = net.minecraft.world.entity.EntityType.getKey(entity.getType()).toString();
|
||||
+ java.util.UUID entityUUID = entity.getUUID();
|
||||
+ net.minecraft.world.level.Level world = entity.level;
|
||||
+
|
||||
+ log.log(Level.SEVERE, "Ticking entity: " + entityType + ", entity class: " + entity.getClass().getName());
|
||||
+ log.log(Level.SEVERE, "Entity status: removed: " + entity.isRemoved() + ", valid: " + entity.valid + ", alive: " + entity.isAlive() + ", is passenger: " + entity.isPassenger());
|
||||
+ log.log(Level.SEVERE, "Entity UUID: " + entityUUID);
|
||||
+ log.log(Level.SEVERE, "Position: world: '" + (world == null ? "unknown world?" : world.getWorld().getName()) + "' at location (" + posX + ", " + posY + ", " + posZ + ")");
|
||||
+ log.log(Level.SEVERE, "Velocity: " + (mot == null ? "unknown velocity" : mot.toString()) + " (in blocks per tick)");
|
||||
+ log.log(Level.SEVERE, "Entity AABB: " + entity.getBoundingBox());
|
||||
+ if (moveVec != null) {
|
||||
+ log.log(Level.SEVERE, "Move call information: ");
|
||||
+ log.log(Level.SEVERE, "Start position: (" + moveStartX + ", " + moveStartY + ", " + moveStartZ + ")");
|
||||
+ log.log(Level.SEVERE, "Move vector: " + moveVec.toString());
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void dumpTickingInfo() {
|
||||
+ Logger log = Bukkit.getServer().getLogger();
|
||||
+
|
||||
+ // ticking entities
|
||||
+ for (net.minecraft.world.entity.Entity entity : net.minecraft.server.level.ServerLevel.getCurrentlyTickingEntities()) {
|
||||
+ this.dumpEntity(entity);
|
||||
+ net.minecraft.world.entity.Entity vehicle = entity.getVehicle();
|
||||
+ if (vehicle != null) {
|
||||
+ log.log(Level.SEVERE, "Detailing vehicle for above entity:");
|
||||
+ this.dumpEntity(vehicle);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // packet processors
|
||||
+ for (net.minecraft.network.PacketListener packetListener : net.minecraft.network.protocol.PacketUtils.getCurrentPacketProcessors()) {
|
||||
+ if (packetListener instanceof net.minecraft.server.network.ServerGamePacketListenerImpl) {
|
||||
+ net.minecraft.server.level.ServerPlayer player = ((net.minecraft.server.network.ServerGamePacketListenerImpl)packetListener).player;
|
||||
+ long totalPackets = net.minecraft.network.protocol.PacketUtils.getTotalProcessedPackets();
|
||||
+ if (player == null) {
|
||||
+ log.log(Level.SEVERE, "Handling packet for player connection or ticking player connection (null player): " + packetListener);
|
||||
+ log.log(Level.SEVERE, "Total packets processed on the main thread for all players: " + totalPackets);
|
||||
+ } else {
|
||||
+ this.dumpEntity(player);
|
||||
+ net.minecraft.world.entity.Entity vehicle = player.getVehicle();
|
||||
+ if (vehicle != null) {
|
||||
+ log.log(Level.SEVERE, "Detailing vehicle for above entity:");
|
||||
+ this.dumpEntity(vehicle);
|
||||
+ }
|
||||
+ log.log(Level.SEVERE, "Total packets processed on the main thread for all players: " + totalPackets);
|
||||
+ }
|
||||
+ } else {
|
||||
+ log.log(Level.SEVERE, "Handling packet for connection: " + packetListener);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - log detailed tick information
|
||||
+
|
||||
private WatchdogThread(long timeoutTime, boolean restart)
|
||||
{
|
||||
super( "Paper Watchdog Thread" );
|
||||
@@ -120,6 +192,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
|
||||
log.log( Level.SEVERE, "------------------------------" );
|
||||
log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
|
||||
io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(isLongTimeout); // Paper // Paper - rewrite chunk system
|
||||
+ this.dumpTickingInfo(); // Paper - log detailed tick information
|
||||
WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
|
||||
log.log( Level.SEVERE, "------------------------------" );
|
||||
//
|
|
@ -1,63 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Mon, 6 Jul 2020 22:48:48 -0700
|
||||
Subject: [PATCH] Manually inline methods in BlockPosition
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/core/BlockPos.java b/src/main/java/net/minecraft/core/BlockPos.java
|
||||
index 08fee16ab39d9da3c4262b356dd956fd5c967d06..b1d12c78edf21cc29a9f9ca54e7957ddc8875ffb 100644
|
||||
--- a/src/main/java/net/minecraft/core/BlockPos.java
|
||||
+++ b/src/main/java/net/minecraft/core/BlockPos.java
|
||||
@@ -488,9 +488,9 @@ public class BlockPos extends Vec3i {
|
||||
}
|
||||
|
||||
public BlockPos.MutableBlockPos set(int x, int y, int z) {
|
||||
- this.setX(x);
|
||||
- this.setY(y);
|
||||
- this.setZ(z);
|
||||
+ this.x = x; // Paper - force inline
|
||||
+ this.y = y; // Paper - force inline
|
||||
+ this.z = z; // Paper - force inline
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -554,19 +554,19 @@ public class BlockPos extends Vec3i {
|
||||
// Paper start - comment out useless overrides @Override - TODO figure out why this is suddenly important to keep
|
||||
@Override
|
||||
public BlockPos.MutableBlockPos setX(int i) {
|
||||
- super.setX(i);
|
||||
+ this.x = i; // Paper
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos.MutableBlockPos setY(int i) {
|
||||
- super.setY(i);
|
||||
+ this.y = i; // Paper
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos.MutableBlockPos setZ(int i) {
|
||||
- super.setZ(i);
|
||||
+ this.z = i; // Paper
|
||||
return this;
|
||||
}
|
||||
// Paper end
|
||||
diff --git a/src/main/java/net/minecraft/core/Vec3i.java b/src/main/java/net/minecraft/core/Vec3i.java
|
||||
index 9482d5ca34bbc098a5afa785054d6d60d6a2decb..cc73ce9f860d1ed3a746e08364a3be97ffa5c8c2 100644
|
||||
--- a/src/main/java/net/minecraft/core/Vec3i.java
|
||||
+++ b/src/main/java/net/minecraft/core/Vec3i.java
|
||||
@@ -19,9 +19,9 @@ public class Vec3i implements Comparable<Vec3i> {
|
||||
return IntStream.of(vec.getX(), vec.getY(), vec.getZ());
|
||||
});
|
||||
public static final Vec3i ZERO = new Vec3i(0, 0, 0);
|
||||
- private int x;
|
||||
- private int y;
|
||||
- private int z;
|
||||
+ protected int x; // Paper - protected
|
||||
+ protected int y; // Paper - protected
|
||||
+ protected int z; // Paper - protected
|
||||
|
||||
private static Function<Vec3i, DataResult<Vec3i>> checkOffsetAxes(int maxAbsValue) {
|
||||
return (vec) -> {
|
|
@ -1,34 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sat, 18 Jul 2020 16:03:57 -0700
|
||||
Subject: [PATCH] Distance manager tick timings
|
||||
|
||||
Recently this has been taking up more time, so add a timings to
|
||||
really figure out how much.
|
||||
|
||||
diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java
|
||||
index 5ec241d49ff5e3a161a39006f05823a5de847c5e..435b3b6d05e00803386d123c66f961c97da83d40 100644
|
||||
--- a/src/main/java/co/aikar/timings/MinecraftTimings.java
|
||||
+++ b/src/main/java/co/aikar/timings/MinecraftTimings.java
|
||||
@@ -45,6 +45,7 @@ public final class MinecraftTimings {
|
||||
|
||||
public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update");
|
||||
public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate");
|
||||
+ public static final Timing distanceManagerTick = Timings.ofSafe("Distance Manager Tick"); // Paper - add timings for distance manager
|
||||
|
||||
public static final Timing midTickChunkTasks = Timings.ofSafe("Mid Tick Chunk Tasks");
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
|
||||
index e4dcadc24b3d73178ee1a4b64b8c6343e5285e59..e5d9c6f2cbe11c2ded6d8ad111fa6a8b2086dfba 100644
|
||||
--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
|
||||
+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
|
||||
@@ -915,7 +915,9 @@ public final class ChunkHolderManager {
|
||||
}
|
||||
|
||||
public boolean processTicketUpdates() {
|
||||
+ co.aikar.timings.MinecraftTimings.distanceManagerTick.startTiming(); try { // Paper - add timings for distance manager
|
||||
return this.processTicketUpdates(true, true, null);
|
||||
+ } finally { co.aikar.timings.MinecraftTimings.distanceManagerTick.stopTiming(); } // Paper - add timings for distance manager
|
||||
}
|
||||
|
||||
private static final ThreadLocal<List<ChunkProgressionTask>> CURRENT_TICKET_UPDATE_SCHEDULING = new ThreadLocal<>();
|
|
@ -1,33 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sun, 19 Jul 2020 15:17:01 -0700
|
||||
Subject: [PATCH] Name craft scheduler threads according to the plugin using
|
||||
them
|
||||
|
||||
Provides quick access to culprits running far more threads than
|
||||
they should be
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java
|
||||
index 2f3e2a404f55f09ae4db8261e495275e31228034..6d66f83afbeb650b10669fd7eeb24a315951fa86 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java
|
||||
@@ -25,7 +25,10 @@ class CraftAsyncTask extends CraftTask {
|
||||
@Override
|
||||
public void run() {
|
||||
final Thread thread = Thread.currentThread();
|
||||
- synchronized (this.workers) {
|
||||
+ // Paper start - name threads according to running plugin
|
||||
+ final String nameBefore = thread.getName();
|
||||
+ thread.setName(nameBefore + " - " + this.getOwner().getName());
|
||||
+ try { synchronized (this.workers) { // Paper end - name threads according to running plugin
|
||||
if (getPeriod() == CraftTask.CANCEL) {
|
||||
// Never continue running after cancelled.
|
||||
// Checking this with the lock is important!
|
||||
@@ -92,6 +95,7 @@ class CraftAsyncTask extends CraftTask {
|
||||
}
|
||||
}
|
||||
}
|
||||
+ } finally { thread.setName(nameBefore); } // Paper - name worker thread according
|
||||
}
|
||||
|
||||
LinkedList<BukkitWorker> getWorkers() {
|
|
@ -1,34 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sun, 20 Sep 2020 16:10:49 -0700
|
||||
Subject: [PATCH] Make sure inlined getChunkAt has inlined logic for loaded
|
||||
chunks
|
||||
|
||||
Tux did some profiling some time ago and showed that the
|
||||
previous getChunkAt method which had inlined logic for loaded
|
||||
chunks did get inlined, but the standard CPS.getChunkAt
|
||||
method was not inlined.
|
||||
|
||||
Paper recently reverted this optimisation, so it's been reintroduced
|
||||
here.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index b94736286adf22ea4ff5702fa7a45b5e212b232f..8be0e1097f0490bc87f3eb87249daa6c0f761bb7 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -366,6 +366,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
|
||||
@Override
|
||||
public final LevelChunk getChunk(int chunkX, int chunkZ) { // Paper - final to help inline
|
||||
+ // Paper start - make sure loaded chunks get the inlined variant of this function
|
||||
+ net.minecraft.server.level.ServerChunkCache cps = ((ServerLevel)this).getChunkSource();
|
||||
+ if (cps.mainThread == Thread.currentThread()) {
|
||||
+ LevelChunk ifLoaded = cps.getChunkAtIfLoadedMainThread(chunkX, chunkZ);
|
||||
+ if (ifLoaded != null) {
|
||||
+ return ifLoaded;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - make sure loaded chunks get the inlined variant of this function
|
||||
return (LevelChunk) this.getChunk(chunkX, chunkZ, ChunkStatus.FULL, true); // Paper - avoid a method jump
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Fri, 30 Oct 2020 22:37:16 -0700
|
||||
Subject: [PATCH] Add packet limiter config
|
||||
|
||||
Example config:
|
||||
packet-limiter:
|
||||
kick-message: '&cSent too many packets'
|
||||
limits:
|
||||
all:
|
||||
interval: 7.0
|
||||
max-packet-rate: 500.0
|
||||
ServerboundPlaceRecipePacket:
|
||||
interval: 4.0
|
||||
max-packet-rate: 5.0
|
||||
action: DROP
|
||||
|
||||
all section refers to all incoming packets, the action for all is
|
||||
hard coded to KICK.
|
||||
|
||||
For specific limits, the section name is the class's name,
|
||||
and an action can be defined: DROP or KICK
|
||||
|
||||
If interval or rate are less-than 0, the limit is ignored
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
||||
index 46153b8e5823959e17621bbebd414d8998b77fb5..fa0845ea094714c320617d3a39efac759ddbbd36 100644
|
||||
--- a/src/main/java/net/minecraft/network/Connection.java
|
||||
+++ b/src/main/java/net/minecraft/network/Connection.java
|
||||
@@ -154,6 +154,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
}
|
||||
}
|
||||
// Paper end - allow controlled flushing
|
||||
+ // Paper start - packet limiter
|
||||
+ protected final Object PACKET_LIMIT_LOCK = new Object();
|
||||
+ protected final @Nullable io.papermc.paper.util.IntervalledCounter allPacketCounts = io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.allPackets.isEnabled() ? new io.papermc.paper.util.IntervalledCounter(
|
||||
+ (long)(io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.allPackets.interval() * 1.0e9)
|
||||
+ ) : null;
|
||||
+ protected final java.util.Map<Class<? extends net.minecraft.network.protocol.Packet<?>>, io.papermc.paper.util.IntervalledCounter> packetSpecificLimits = new java.util.HashMap<>();
|
||||
+
|
||||
+ private boolean stopReadingPackets;
|
||||
+ private void killForPacketSpam() {
|
||||
+ this.sendPacket(new ClientboundDisconnectPacket(io.papermc.paper.adventure.PaperAdventure.asVanilla(io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.kickMessage)), PacketSendListener.thenRun(() -> {
|
||||
+ this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.kickMessage));
|
||||
+ }));
|
||||
+ this.setReadOnly();
|
||||
+ this.stopReadingPackets = true;
|
||||
+ }
|
||||
+ // Paper end - packet limiter
|
||||
|
||||
public Connection(PacketFlow side) {
|
||||
this.receiving = side;
|
||||
@@ -234,6 +250,45 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet<?> packet) {
|
||||
if (this.channel.isOpen()) {
|
||||
+ // Paper start - packet limiter
|
||||
+ if (this.stopReadingPackets) {
|
||||
+ return;
|
||||
+ }
|
||||
+ if (this.allPacketCounts != null ||
|
||||
+ io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.overrides.containsKey(packet.getClass())) {
|
||||
+ long time = System.nanoTime();
|
||||
+ synchronized (PACKET_LIMIT_LOCK) {
|
||||
+ if (this.allPacketCounts != null) {
|
||||
+ this.allPacketCounts.updateAndAdd(1, time);
|
||||
+ if (this.allPacketCounts.getRate() >= io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.allPackets.maxPacketRate()) {
|
||||
+ this.killForPacketSpam();
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for (Class<?> check = packet.getClass(); check != Object.class; check = check.getSuperclass()) {
|
||||
+ io.papermc.paper.configuration.GlobalConfiguration.PacketLimiter.PacketLimit packetSpecificLimit =
|
||||
+ io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.overrides.get(check);
|
||||
+ if (packetSpecificLimit == null || !packetSpecificLimit.isEnabled()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ io.papermc.paper.util.IntervalledCounter counter = this.packetSpecificLimits.computeIfAbsent((Class)check, (clazz) -> {
|
||||
+ return new io.papermc.paper.util.IntervalledCounter((long)(packetSpecificLimit.interval() * 1.0e9));
|
||||
+ });
|
||||
+ counter.updateAndAdd(1, time);
|
||||
+ if (counter.getRate() >= packetSpecificLimit.maxPacketRate()) {
|
||||
+ switch (packetSpecificLimit.action()) {
|
||||
+ case DROP:
|
||||
+ return;
|
||||
+ case KICK:
|
||||
+ this.killForPacketSpam();
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - packet limiter
|
||||
try {
|
||||
Connection.genericsFtw(packet, this.packetListener);
|
||||
} catch (RunningOnDifferentThreadException cancelledpackethandleexception) {
|
|
@ -1,21 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sun, 11 Apr 2021 02:58:48 -0700
|
||||
Subject: [PATCH] Don't read neighbour chunk data off disk when converting
|
||||
chunks
|
||||
|
||||
Lighting is purged on update anyways, so let's not add more
|
||||
into the conversion process
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
|
||||
index 6289858d54a6affd504533b1a280718a01446ebb..5a425be023d77f0370d102dfb52427147849ac1a 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
|
||||
@@ -51,6 +51,7 @@ public class ChunkStorage implements AutoCloseable {
|
||||
|
||||
// CraftBukkit start
|
||||
private boolean check(ServerChunkCache cps, int x, int z) {
|
||||
+ if (true) return true; // Paper - this isn't even needed anymore, light is purged updating to 1.14+, why are we holding up the conversion process reading chunk data off disk - return true, we need to set light populated to true so the converter recognizes the chunk as being "full"
|
||||
ChunkPos pos = new ChunkPos(x, z);
|
||||
if (cps != null) {
|
||||
//com.google.common.base.Preconditions.checkState(org.bukkit.Bukkit.isPrimaryThread(), "primary thread"); // Paper - this function is now MT-Safe
|
|
@ -1,52 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sat, 4 Apr 2020 17:00:20 -0700
|
||||
Subject: [PATCH] Consolidate flush calls for entity tracker packets
|
||||
|
||||
Most server packets seem to be sent from here, so try to avoid
|
||||
expensive flush calls from them.
|
||||
|
||||
This change was motivated due to local testing:
|
||||
|
||||
- My server spawn has 130 cows in it (for testing a prev. patch)
|
||||
- Try to let 200 players join spawn
|
||||
|
||||
Without this change, I could only get 20 players on before they
|
||||
all started timing out due to the load put on the Netty I/O threads.
|
||||
|
||||
With this change I could get all 200 on at 0ms ping.
|
||||
|
||||
(one of the primary issues is that my CPU is kinda trash, and having
|
||||
4 extra threads at 100% is just too much for it).
|
||||
|
||||
So in general this patch should reduce Netty I/O thread load.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index ebd0da4f87c74f12d702e1ae4f3206885272e4f7..ca84eddbdb1e198b899750e5f6b3eafd25ce970f 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -805,7 +805,24 @@ public class ServerChunkCache extends ChunkSource {
|
||||
this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
|
||||
gameprofilerfiller.pop();
|
||||
// Paper end - use set of chunks requiring updates, rather than iterating every single one loaded
|
||||
+ // Paper start - controlled flush for entity tracker packets
|
||||
+ List<net.minecraft.network.Connection> disabledFlushes = new java.util.ArrayList<>(this.level.players.size());
|
||||
+ for (ServerPlayer player : this.level.players) {
|
||||
+ net.minecraft.server.network.ServerGamePacketListenerImpl connection = player.connection;
|
||||
+ if (connection != null) {
|
||||
+ connection.connection.disableAutomaticFlush();
|
||||
+ disabledFlushes.add(connection.connection);
|
||||
+ }
|
||||
+ }
|
||||
+ try { // Paper end - controlled flush for entity tracker packets
|
||||
this.chunkMap.tick();
|
||||
+ // Paper start - controlled flush for entity tracker packets
|
||||
+ } finally {
|
||||
+ for (net.minecraft.network.Connection networkManager : disabledFlushes) {
|
||||
+ networkManager.enableAutomaticFlush();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - controlled flush for entity tracker packets
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Fri, 28 Aug 2020 12:33:47 -0700
|
||||
Subject: [PATCH] Don't lookup fluid state when raytracing
|
||||
|
||||
Just use the iblockdata already retrieved, removes a getType call.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java
|
||||
index bca0838e40bc91d78e9b93df5318642d1c9f341e..9cf2f046d50a8a0e08189c9b4b5d2f323d1f790d 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/BlockGetter.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/BlockGetter.java
|
||||
@@ -84,7 +84,7 @@ public interface BlockGetter extends LevelHeightAccessor {
|
||||
return BlockHitResult.miss(raytrace1.getTo(), Direction.getNearest(vec3d.x, vec3d.y, vec3d.z), new BlockPos(raytrace1.getTo()));
|
||||
}
|
||||
// Paper end
|
||||
- FluidState fluid = this.getFluidState(blockposition);
|
||||
+ FluidState fluid = iblockdata.getFluidState(); // Paper - don't need to go to world state again
|
||||
Vec3 vec3d = raytrace1.getFrom();
|
||||
Vec3 vec3d1 = raytrace1.getTo();
|
||||
VoxelShape voxelshape = raytrace1.getBlockShape(iblockdata, this, blockposition);
|
|
@ -1,43 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Tue, 21 Apr 2020 01:53:22 -0700
|
||||
Subject: [PATCH] Time scoreboard search
|
||||
|
||||
Plugins leaking scoreboards will make this very expensive,
|
||||
let server owners debug it easily
|
||||
|
||||
diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java
|
||||
index 435b3b6d05e00803386d123c66f961c97da83d40..9da5a6086323ff4c4fd62a035fa8f7efc3d92e38 100644
|
||||
--- a/src/main/java/co/aikar/timings/MinecraftTimings.java
|
||||
+++ b/src/main/java/co/aikar/timings/MinecraftTimings.java
|
||||
@@ -46,6 +46,7 @@ public final class MinecraftTimings {
|
||||
public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update");
|
||||
public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate");
|
||||
public static final Timing distanceManagerTick = Timings.ofSafe("Distance Manager Tick"); // Paper - add timings for distance manager
|
||||
+ public static final Timing scoreboardScoreSearch = Timings.ofSafe("Scoreboard score search"); // Paper - add timings for scoreboard search
|
||||
|
||||
public static final Timing midTickChunkTasks = Timings.ofSafe("Mid Tick Chunk Tasks");
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java
|
||||
index 39e19bea16419b9cbe53016084b8bbf014dcb056..138407c2d4b0bc55ddb9aac5d2aa3edadda090fb 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java
|
||||
@@ -113,9 +113,18 @@ public final class CraftScoreboardManager implements ScoreboardManager {
|
||||
|
||||
// CraftBukkit method
|
||||
public void getScoreboardScores(ObjectiveCriteria criteria, String name, Consumer<Score> consumer) {
|
||||
+ // Paper start - add timings for scoreboard search
|
||||
+ // plugins leaking scoreboards will make this very expensive, let server owners debug it easily
|
||||
+ co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.startTimingIfSync();
|
||||
+ try {
|
||||
+ // Paper end - add timings for scoreboard search
|
||||
for (CraftScoreboard scoreboard : this.scoreboards) {
|
||||
Scoreboard board = scoreboard.board;
|
||||
board.forAllObjectives(criteria, name, (score) -> consumer.accept(score));
|
||||
}
|
||||
+ } finally { // Paper start - add timings for scoreboard search
|
||||
+ co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.stopTimingIfSync();
|
||||
+ }
|
||||
+ // Paper end - add timings for scoreboard search
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Tue, 16 Feb 2021 00:16:56 -0800
|
||||
Subject: [PATCH] Send full pos packets for hard colliding entities
|
||||
|
||||
Prevent collision problems due to desync (i.e boats)
|
||||
|
||||
Configurable under
|
||||
`send-full-pos-for-hard-colliding-entities`
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
index 6281ade15b2700d16dc1f98c2affea8c0ba936c9..64683e218c9c43cfd30514800cfa8da42e37563e 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
@@ -169,7 +169,7 @@ public class ServerEntity {
|
||||
long i1 = this.positionCodec.encodeZ(vec3d);
|
||||
boolean flag4 = k < -32768L || k > 32767L || l < -32768L || l > 32767L || i1 < -32768L || i1 > 32767L;
|
||||
|
||||
- if (!flag4 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.isOnGround()) {
|
||||
+ if (!flag4 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.isOnGround() && !(io.papermc.paper.configuration.GlobalConfiguration.get().collisions.sendFullPosForHardCollidingEntities && this.entity.hardCollides())) { // Paper - send full pos for hard colliding entities to prevent collision problems due to desync
|
||||
if ((!flag2 || !flag3) && !(this.entity instanceof AbstractArrow)) {
|
||||
if (flag2) {
|
||||
packet1 = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) ((int) k), (short) ((int) l), (short) ((int) i1), this.entity.isOnGround());
|
|
@ -1,22 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sun, 7 Mar 2021 13:15:04 -0800
|
||||
Subject: [PATCH] Do not run raytrace logic for AIR
|
||||
|
||||
Saves approx. 5% for the raytrace call, as most (expensive)
|
||||
raytracing tends to go through air and returning early is an
|
||||
easy win. The remaining problems with this function
|
||||
are mostly with the block getting itself.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java
|
||||
index 9cf2f046d50a8a0e08189c9b4b5d2f323d1f790d..d1eefa6ef3e9abfe7af4d8310aa64465fa2d5463 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/BlockGetter.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/BlockGetter.java
|
||||
@@ -84,6 +84,7 @@ public interface BlockGetter extends LevelHeightAccessor {
|
||||
return BlockHitResult.miss(raytrace1.getTo(), Direction.getNearest(vec3d.x, vec3d.y, vec3d.z), new BlockPos(raytrace1.getTo()));
|
||||
}
|
||||
// Paper end
|
||||
+ if (iblockdata.isAir()) return null; // Paper - optimise air cases
|
||||
FluidState fluid = iblockdata.getFluidState(); // Paper - don't need to go to world state again
|
||||
Vec3 vec3d = raytrace1.getFrom();
|
||||
Vec3 vec3d1 = raytrace1.getTo();
|
|
@ -1,21 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Fri, 19 Feb 2021 22:51:52 -0800
|
||||
Subject: [PATCH] Oprimise map impl for tracked players
|
||||
|
||||
Reference2BooleanOpenHashMap is going to have
|
||||
better lookups than HashMap.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index 1b8f221360f5edb4d244ec97104d85ffecef204c..57e13cb9d1460be34fe06f7c7e14330f60f0a51a 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -1395,7 +1395,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
final Entity entity;
|
||||
private final int range;
|
||||
SectionPos lastSectionPos;
|
||||
- public final Set<ServerPlayerConnection> seenBy = Sets.newIdentityHashSet();
|
||||
+ public final Set<ServerPlayerConnection> seenBy = new ReferenceOpenHashSet<>(); // Paper - optimise map impl
|
||||
|
||||
public TrackedEntity(Entity entity, int i, int j, boolean flag) {
|
||||
this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit
|
|
@ -1,51 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Thu, 10 Jun 2021 14:36:00 -0700
|
||||
Subject: [PATCH] Optimise BlockSoil nearby water lookup
|
||||
|
||||
Apparently the abstract block iteration was taking about
|
||||
75% of the method call.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/FarmBlock.java b/src/main/java/net/minecraft/world/level/block/FarmBlock.java
|
||||
index 94ea6b8986c8fe3aee220ef0c95b65c5cef21c72..d089887030ac7c7a79abca97134ba9291e244059 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java
|
||||
@@ -138,19 +138,27 @@ public class FarmBlock extends Block {
|
||||
}
|
||||
|
||||
private static boolean isNearWater(LevelReader world, BlockPos pos) {
|
||||
- Iterator iterator = BlockPos.betweenClosed(pos.offset(-4, 0, -4), pos.offset(4, 1, 4)).iterator();
|
||||
-
|
||||
- BlockPos blockposition1;
|
||||
-
|
||||
- do {
|
||||
- if (!iterator.hasNext()) {
|
||||
- return false;
|
||||
+ // Paper start - remove abstract block iteration
|
||||
+ int xOff = pos.getX();
|
||||
+ int yOff = pos.getY();
|
||||
+ int zOff = pos.getZ();
|
||||
+
|
||||
+ for (int dz = -4; dz <= 4; ++dz) {
|
||||
+ int z = dz + zOff;
|
||||
+ for (int dx = -4; dx <= 4; ++dx) {
|
||||
+ int x = xOff + dx;
|
||||
+ for (int dy = 0; dy <= 1; ++dy) {
|
||||
+ int y = dy + yOff;
|
||||
+ net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)world.getChunk(x >> 4, z >> 4);
|
||||
+ net.minecraft.world.level.material.FluidState fluid = chunk.getBlockStateFinal(x, y, z).getFluidState();
|
||||
+ if (fluid.is(FluidTags.WATER)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
+ }
|
||||
|
||||
- blockposition1 = (BlockPos) iterator.next();
|
||||
- } while (!world.getFluidState(blockposition1).is(FluidTags.WATER));
|
||||
-
|
||||
- return true;
|
||||
+ return false;
|
||||
}
|
||||
|
||||
@Override
|
|
@ -1,456 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sun, 20 Jun 2021 16:19:26 -0700
|
||||
Subject: [PATCH] Optimise random block ticking
|
||||
|
||||
Massive performance improvement for random block ticking.
|
||||
The performance increase comes from the fact that the vast
|
||||
majority of attempted block ticks (~95% in my testing) fail
|
||||
because the randomly selected block is not tickable.
|
||||
|
||||
Now only tickable blocks are targeted, however this means that
|
||||
the maximum number of block ticks occurs per chunk. However,
|
||||
not all chunks are going to be targeted. The percent chance
|
||||
of a chunk being targeted is based on how many tickable blocks
|
||||
are in the chunk.
|
||||
This means that while block ticks are spread out less, the
|
||||
total number of blocks ticked per world tick remains the same.
|
||||
Therefore, the chance of a random tickable block being ticked
|
||||
remains the same.
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/util/math/ThreadUnsafeRandom.java b/src/main/java/io/papermc/paper/util/math/ThreadUnsafeRandom.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..7d93652c1abbb6aee6eb7c26cf35d4d032ef7b69
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/util/math/ThreadUnsafeRandom.java
|
||||
@@ -0,0 +1,65 @@
|
||||
+package io.papermc.paper.util.math;
|
||||
+
|
||||
+import net.minecraft.util.RandomSource;
|
||||
+import net.minecraft.world.level.levelgen.LegacyRandomSource;
|
||||
+import net.minecraft.world.level.levelgen.PositionalRandomFactory;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class ThreadUnsafeRandom extends LegacyRandomSource {
|
||||
+
|
||||
+ // See javadoc and internal comments for java.util.Random where these values come from, how they are used, and the author for them.
|
||||
+ private static final long multiplier = 0x5DEECE66DL;
|
||||
+ private static final long addend = 0xBL;
|
||||
+ private static final long mask = (1L << 48) - 1;
|
||||
+
|
||||
+ private static long initialScramble(long seed) {
|
||||
+ return (seed ^ multiplier) & mask;
|
||||
+ }
|
||||
+
|
||||
+ private long seed;
|
||||
+
|
||||
+ public ThreadUnsafeRandom(long seed) {
|
||||
+ super(seed);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public RandomSource fork() {
|
||||
+ return new ThreadUnsafeRandom(this.nextLong());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public PositionalRandomFactory forkPositional() {
|
||||
+ throw new UnsupportedOperationException();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setSeed(long seed) {
|
||||
+ // note: called by Random constructor
|
||||
+ this.seed = initialScramble(seed);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int next(int bits) {
|
||||
+ // avoid the expensive CAS logic used by superclass
|
||||
+ return (int) (((this.seed = this.seed * multiplier + addend) & mask) >>> (48 - bits));
|
||||
+ }
|
||||
+
|
||||
+ // Taken from
|
||||
+ // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
|
||||
+ // https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2016/06/25/fastrange.c
|
||||
+ // Original license is public domain
|
||||
+ public static int fastRandomBounded(final long randomInteger, final long limit) {
|
||||
+ // randomInteger must be [0, pow(2, 32))
|
||||
+ // limit must be [0, pow(2, 32))
|
||||
+ return (int)((randomInteger * limit) >>> 32);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int nextInt(int bound) {
|
||||
+ // yes this breaks random's spec
|
||||
+ // however there's nothing that uses this class that relies on it
|
||||
+ return fastRandomBounded(this.next(32) & 0xFFFFFFFFL, bound);
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index 7c6b76399648d67e6726a13e1d29e1978bae641a..915dd3221124756fb94d60624cd552b1ab586ab5 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -704,6 +704,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
entityplayer.stopSleepInBed(false, false);
|
||||
});
|
||||
}
|
||||
+ // Paper start - optimise random block ticking
|
||||
+ private final BlockPos.MutableBlockPos chunkTickMutablePosition = new BlockPos.MutableBlockPos();
|
||||
+ private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(this.random.nextLong());
|
||||
+ // Paper end
|
||||
|
||||
public void tickChunk(LevelChunk chunk, int randomTickSpeed) {
|
||||
ChunkPos chunkcoordintpair = chunk.getPos();
|
||||
@@ -713,10 +717,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
ProfilerFiller gameprofilerfiller = this.getProfiler();
|
||||
|
||||
gameprofilerfiller.push("thunder");
|
||||
- BlockPos blockposition;
|
||||
+ final BlockPos.MutableBlockPos blockposition = this.chunkTickMutablePosition; // Paper - use mutable to reduce allocation rate, final to force compile fail on change
|
||||
|
||||
if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - disable thunder
|
||||
- blockposition = this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15));
|
||||
+ blockposition.set(this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15))); // Paper
|
||||
if (this.isRainingAt(blockposition)) {
|
||||
DifficultyInstance difficultydamagescaler = this.getCurrentDifficultyAt(blockposition);
|
||||
boolean flag1 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < (double) difficultydamagescaler.getEffectiveDifficulty() * this.paperConfig().entities.spawning.skeletonHorseThunderSpawnChance.or(0.01D) && !this.getBlockState(blockposition.below()).is(Blocks.LIGHTNING_ROD); // Paper
|
||||
@@ -746,18 +750,26 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
int l;
|
||||
|
||||
if (!this.paperConfig().environment.disableIceAndSnow && this.random.nextInt(16) == 0) { // Paper - Disable ice and snow
|
||||
- blockposition = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, this.getBlockRandomPos(j, 0, k, 15));
|
||||
- BlockPos blockposition1 = blockposition.below();
|
||||
+ // Paper start - optimise chunk ticking
|
||||
+ this.getRandomBlockPosition(j, 0, k, 15, blockposition);
|
||||
+ int normalY = chunk.getHeight(Heightmap.Types.MOTION_BLOCKING, blockposition.getX() & 15, blockposition.getZ() & 15) + 1;
|
||||
+ int downY = normalY - 1;
|
||||
+ blockposition.setY(normalY);
|
||||
+ // Paper end
|
||||
Biome biomebase = (Biome) this.getBiome(blockposition).value();
|
||||
|
||||
- if (biomebase.shouldFreeze(this, blockposition1)) {
|
||||
- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, Blocks.ICE.defaultBlockState(), null); // CraftBukkit
|
||||
+ // Paper start - optimise chunk ticking
|
||||
+ blockposition.setY(downY);
|
||||
+ if (biomebase.shouldFreeze(this, blockposition)) {
|
||||
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition, Blocks.ICE.defaultBlockState(), null); // CraftBukkit
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
int i1 = this.getGameRules().getInt(GameRules.RULE_SNOW_ACCUMULATION_HEIGHT);
|
||||
BlockState iblockdata;
|
||||
|
||||
+ blockposition.setY(normalY); // Paper
|
||||
if (i1 > 0 && biomebase.shouldSnow(this, blockposition)) {
|
||||
iblockdata = this.getBlockState(blockposition);
|
||||
if (iblockdata.is(Blocks.SNOW)) {
|
||||
@@ -772,52 +784,55 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition, Blocks.SNOW.defaultBlockState(), null); // CraftBukkit
|
||||
}
|
||||
}
|
||||
+ blockposition.setY(downY); // Paper
|
||||
|
||||
- iblockdata = this.getBlockState(blockposition1);
|
||||
+ iblockdata = this.getBlockState(blockposition); // Paper
|
||||
Biome.Precipitation biomebase_precipitation = biomebase.getPrecipitation();
|
||||
|
||||
- if (biomebase_precipitation == Biome.Precipitation.RAIN && biomebase.coldEnoughToSnow(blockposition1)) {
|
||||
+ if (biomebase_precipitation == Biome.Precipitation.RAIN && biomebase.coldEnoughToSnow(blockposition)) { // Paper
|
||||
biomebase_precipitation = Biome.Precipitation.SNOW;
|
||||
}
|
||||
|
||||
- iblockdata.getBlock().handlePrecipitation(iblockdata, this, blockposition1, biomebase_precipitation);
|
||||
+ iblockdata.getBlock().handlePrecipitation(iblockdata, this, blockposition, biomebase_precipitation); // Paper
|
||||
}
|
||||
}
|
||||
|
||||
- gameprofilerfiller.popPush("tickBlocks");
|
||||
+ // Paper start - optimise random block ticking
|
||||
+ gameprofilerfiller.popPush("randomTick");
|
||||
timings.chunkTicksBlocks.startTiming(); // Paper
|
||||
if (randomTickSpeed > 0) {
|
||||
- LevelChunkSection[] achunksection = chunk.getSections();
|
||||
- int j1 = achunksection.length;
|
||||
-
|
||||
- for (int k1 = 0; k1 < j1; ++k1) {
|
||||
- LevelChunkSection chunksection = achunksection[k1];
|
||||
-
|
||||
- if (chunksection.isRandomlyTicking()) {
|
||||
- int l1 = chunksection.bottomBlockY();
|
||||
-
|
||||
- for (l = 0; l < randomTickSpeed; ++l) {
|
||||
- BlockPos blockposition2 = this.getBlockRandomPos(j, l1, k, 15);
|
||||
-
|
||||
- gameprofilerfiller.push("randomTick");
|
||||
- BlockState iblockdata2 = chunksection.getBlockState(blockposition2.getX() - j, blockposition2.getY() - l1, blockposition2.getZ() - k);
|
||||
+ LevelChunkSection[] sections = chunk.getSections();
|
||||
+ int minSection = io.papermc.paper.util.WorldUtil.getMinSection(this);
|
||||
+ for (int sectionIndex = 0; sectionIndex < sections.length; ++sectionIndex) {
|
||||
+ LevelChunkSection section = sections[sectionIndex];
|
||||
+ if (section == null || section.tickingList.size() == 0) {
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
- if (iblockdata2.isRandomlyTicking()) {
|
||||
- iblockdata2.randomTick(this, blockposition2, this.random);
|
||||
- }
|
||||
+ int yPos = (sectionIndex + minSection) << 4;
|
||||
+ for (int a = 0; a < randomTickSpeed; ++a) {
|
||||
+ int tickingBlocks = section.tickingList.size();
|
||||
+ int index = this.randomTickRandom.nextInt(16 * 16 * 16);
|
||||
+ if (index >= tickingBlocks) {
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
- FluidState fluid = iblockdata2.getFluidState();
|
||||
+ long raw = section.tickingList.getRaw(index);
|
||||
+ int location = com.destroystokyo.paper.util.maplist.IBlockDataList.getLocationFromRaw(raw);
|
||||
+ int randomX = location & 15;
|
||||
+ int randomY = ((location >>> (4 + 4)) & 255) | yPos;
|
||||
+ int randomZ = (location >>> 4) & 15;
|
||||
|
||||
- if (fluid.isRandomlyTicking()) {
|
||||
- fluid.randomTick(this, blockposition2, this.random);
|
||||
- }
|
||||
+ BlockPos blockposition2 = blockposition.set(j + randomX, randomY, k + randomZ);
|
||||
+ BlockState iblockdata = com.destroystokyo.paper.util.maplist.IBlockDataList.getBlockDataFromRaw(raw);
|
||||
|
||||
- gameprofilerfiller.pop();
|
||||
- }
|
||||
+ iblockdata.randomTick(this, blockposition2, this.randomTickRandom);
|
||||
+ // We drop the fluid tick since LAVA is ALREADY TICKED by the above method (See LiquidBlock).
|
||||
+ // TODO CHECK ON UPDATE (ping the Canadian)
|
||||
}
|
||||
}
|
||||
}
|
||||
-
|
||||
+ // Paper end - optimise random block ticking
|
||||
timings.chunkTicksBlocks.stopTiming(); // Paper
|
||||
gameprofilerfiller.pop();
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/util/BitStorage.java b/src/main/java/net/minecraft/util/BitStorage.java
|
||||
index 62251727788d48a461ea6f7945771d7d6bdc7282..106610ccc74b70b557b01c61262d56c4f1147acf 100644
|
||||
--- a/src/main/java/net/minecraft/util/BitStorage.java
|
||||
+++ b/src/main/java/net/minecraft/util/BitStorage.java
|
||||
@@ -20,4 +20,15 @@ public interface BitStorage {
|
||||
void unpack(int[] is);
|
||||
|
||||
BitStorage copy();
|
||||
+
|
||||
+ // Paper start
|
||||
+ void forEach(DataBitConsumer consumer);
|
||||
+
|
||||
+ @FunctionalInterface
|
||||
+ interface DataBitConsumer {
|
||||
+
|
||||
+ void accept(int location, int data);
|
||||
+
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/util/SimpleBitStorage.java b/src/main/java/net/minecraft/util/SimpleBitStorage.java
|
||||
index 9b81ce9d85cba07e9752c29fb5a842c4b00aa873..36e33923bf48e56c743ed043bcbc66bc32f0422f 100644
|
||||
--- a/src/main/java/net/minecraft/util/SimpleBitStorage.java
|
||||
+++ b/src/main/java/net/minecraft/util/SimpleBitStorage.java
|
||||
@@ -124,6 +124,28 @@ public class SimpleBitStorage implements BitStorage {
|
||||
return this.bits;
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public final void forEach(DataBitConsumer consumer) {
|
||||
+ int i = 0;
|
||||
+ long[] along = this.data;
|
||||
+ int j = along.length;
|
||||
+
|
||||
+ for (int k = 0; k < j; ++k) {
|
||||
+ long l = along[k];
|
||||
+
|
||||
+ for (int i1 = 0; i1 < this.valuesPerLong; ++i1) {
|
||||
+ consumer.accept(i, (int) (l & this.mask));
|
||||
+ l >>= this.bits;
|
||||
+ ++i;
|
||||
+ if (i >= this.size) {
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public void getAll(IntConsumer action) {
|
||||
int i = 0;
|
||||
diff --git a/src/main/java/net/minecraft/util/ZeroBitStorage.java b/src/main/java/net/minecraft/util/ZeroBitStorage.java
|
||||
index 9686ce7536c9924b1b2aced4f013f46759cbc72e..5d8e9bdf5538b19681f21949368d862fab8a89ad 100644
|
||||
--- a/src/main/java/net/minecraft/util/ZeroBitStorage.java
|
||||
+++ b/src/main/java/net/minecraft/util/ZeroBitStorage.java
|
||||
@@ -46,6 +46,15 @@ public class ZeroBitStorage implements BitStorage {
|
||||
return 0;
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public void forEach(DataBitConsumer consumer) {
|
||||
+ for(int i = 0; i < this.size; ++i) {
|
||||
+ consumer.accept(i, 0);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public void getAll(IntConsumer action) {
|
||||
for(int i = 0; i < this.size; ++i) {
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
|
||||
index 30663713e198bfe40b95c48524b71ea65f39965e..25503678e7d049a8b3172cfad8a5606958c32302 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
|
||||
@@ -83,7 +83,7 @@ public class Turtle extends Animal {
|
||||
}
|
||||
|
||||
public void setHomePos(BlockPos pos) {
|
||||
- this.entityData.set(Turtle.HOME_POS, pos);
|
||||
+ this.entityData.set(Turtle.HOME_POS, pos.immutable()); // Paper - called with mutablepos...
|
||||
}
|
||||
|
||||
public BlockPos getHomePos() {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index 8be0e1097f0490bc87f3eb87249daa6c0f761bb7..158046d001cbd71b4cec2717f5e9266cc87cd2d0 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -1369,10 +1369,18 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
public abstract RecipeManager getRecipeManager();
|
||||
|
||||
public BlockPos getBlockRandomPos(int x, int y, int z, int l) {
|
||||
+ // Paper start - allow use of mutable pos
|
||||
+ BlockPos.MutableBlockPos ret = new BlockPos.MutableBlockPos();
|
||||
+ this.getRandomBlockPosition(x, y, z, l, ret);
|
||||
+ return ret.immutable();
|
||||
+ }
|
||||
+ public final BlockPos.MutableBlockPos getRandomBlockPosition(int x, int y, int z, int l, BlockPos.MutableBlockPos out) {
|
||||
+ // Paper end
|
||||
this.randValue = this.randValue * 3 + 1013904223;
|
||||
int i1 = this.randValue >> 2;
|
||||
|
||||
- return new BlockPos(x + (i1 & 15), y + (i1 >> 16 & l), z + (i1 >> 8 & 15));
|
||||
+ out.set(x + (i1 & 15), y + (i1 >> 16 & l), z + (i1 >> 8 & 15)); // Paper - change to setValues call
|
||||
+ return out; // Paper
|
||||
}
|
||||
|
||||
public boolean noSave() {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||
index 066874d27495dcaa3dea254b7328257e46920357..c3f1334b2bb97f0633f3ea43b97ee49adfd8bc0d 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||
@@ -27,6 +27,7 @@ public class LevelChunkSection {
|
||||
public final PalettedContainer<BlockState> states;
|
||||
// CraftBukkit start - read/write
|
||||
private PalettedContainer<Holder<Biome>> biomes;
|
||||
+ public final com.destroystokyo.paper.util.maplist.IBlockDataList tickingList = new com.destroystokyo.paper.util.maplist.IBlockDataList(); // Paper
|
||||
|
||||
public LevelChunkSection(int i, PalettedContainer<BlockState> datapaletteblock, PalettedContainer<Holder<Biome>> palettedcontainerro) {
|
||||
// CraftBukkit end
|
||||
@@ -85,6 +86,9 @@ public class LevelChunkSection {
|
||||
--this.nonEmptyBlockCount;
|
||||
if (iblockdata1.isRandomlyTicking()) {
|
||||
--this.tickingBlockCount;
|
||||
+ // Paper start
|
||||
+ this.tickingList.remove(x, y, z);
|
||||
+ // Paper end
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +100,9 @@ public class LevelChunkSection {
|
||||
++this.nonEmptyBlockCount;
|
||||
if (state.isRandomlyTicking()) {
|
||||
++this.tickingBlockCount;
|
||||
+ // Paper start
|
||||
+ this.tickingList.add(x, y, z, state);
|
||||
+ // Paper end
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,40 +134,31 @@ public class LevelChunkSection {
|
||||
}
|
||||
|
||||
public void recalcBlockCounts() {
|
||||
- class a implements PalettedContainer.CountConsumer<BlockState> {
|
||||
-
|
||||
- public int nonEmptyBlockCount;
|
||||
- public int tickingBlockCount;
|
||||
- public int tickingFluidCount;
|
||||
-
|
||||
- a() {}
|
||||
-
|
||||
- public void accept(BlockState iblockdata, int i) {
|
||||
- FluidState fluid = iblockdata.getFluidState();
|
||||
-
|
||||
- if (!iblockdata.isAir()) {
|
||||
- this.nonEmptyBlockCount += i;
|
||||
- if (iblockdata.isRandomlyTicking()) {
|
||||
- this.tickingBlockCount += i;
|
||||
- }
|
||||
+ // Paper start - unfuck this
|
||||
+ this.tickingList.clear();
|
||||
+ this.nonEmptyBlockCount = 0;
|
||||
+ this.tickingBlockCount = 0;
|
||||
+ this.tickingFluidCount = 0;
|
||||
+ this.states.forEachLocation((BlockState iblockdata, int i) -> {
|
||||
+ FluidState fluid = iblockdata.getFluidState();
|
||||
+
|
||||
+ if (!iblockdata.isAir()) {
|
||||
+ this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + 1);
|
||||
+ if (iblockdata.isRandomlyTicking()) {
|
||||
+ this.tickingBlockCount = (short)(this.tickingBlockCount + 1);
|
||||
+ this.tickingList.add(i, iblockdata);
|
||||
}
|
||||
+ }
|
||||
|
||||
- if (!fluid.isEmpty()) {
|
||||
- this.nonEmptyBlockCount += i;
|
||||
- if (fluid.isRandomlyTicking()) {
|
||||
- this.tickingFluidCount += i;
|
||||
- }
|
||||
+ if (!fluid.isEmpty()) {
|
||||
+ this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + 1);
|
||||
+ if (fluid.isRandomlyTicking()) {
|
||||
+ this.tickingFluidCount = (short) (this.tickingFluidCount + 1);
|
||||
}
|
||||
-
|
||||
}
|
||||
- }
|
||||
|
||||
- a a0 = new a();
|
||||
-
|
||||
- this.states.count(a0);
|
||||
- this.nonEmptyBlockCount = (short) a0.nonEmptyBlockCount;
|
||||
- this.tickingBlockCount = (short) a0.tickingBlockCount;
|
||||
- this.tickingFluidCount = (short) a0.tickingFluidCount;
|
||||
+ });
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
public PalettedContainer<BlockState> getStates() {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
||||
index 715830d27490572bbe963515f046205c6d7e834d..3c7ef1e8e338a84eee34f39ce73e64876632ea87 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
||||
@@ -383,6 +383,14 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public void forEachLocation(PalettedContainer.CountConsumer<T> consumer) {
|
||||
+ this.data.storage.forEach((int location, int data) -> {
|
||||
+ consumer.accept(this.data.palette.valueFor(data), location);
|
||||
+ });
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@FunctionalInterface
|
||||
public interface CountConsumer<T> {
|
||||
void accept(T object, int count);
|
|
@ -1,55 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Tue, 22 Sep 2020 01:49:19 -0700
|
||||
Subject: [PATCH] Optimise non-flush packet sending
|
||||
|
||||
Places like entity tracking make heavy use of packet sending,
|
||||
and internally netty will use some very expensive thread wakeup
|
||||
calls when scheduling.
|
||||
|
||||
Thanks to various hacks in ProtocolLib as well as other
|
||||
plugins, we cannot simply use a queue of packets to group
|
||||
send on execute. We have to call execute for each packet.
|
||||
|
||||
Tux's suggestion here is exactly what was needed - tag
|
||||
the Runnable indicating it should not make a wakeup call.
|
||||
|
||||
Big thanks to Tux for making this possible as I had given
|
||||
up on this optimisation before he came along.
|
||||
|
||||
Locally this patch drops the entity tracker tick by a full 1.5x.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
||||
index fa0845ea094714c320617d3a39efac759ddbbd36..1a077353811a0a233f1a7ee7b6afe6c535e49168 100644
|
||||
--- a/src/main/java/net/minecraft/network/Connection.java
|
||||
+++ b/src/main/java/net/minecraft/network/Connection.java
|
||||
@@ -46,6 +46,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.Marker;
|
||||
import org.slf4j.MarkerFactory;
|
||||
|
||||
+
|
||||
+import io.netty.util.concurrent.AbstractEventExecutor; // Paper
|
||||
public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
private static final float AVERAGE_PACKETS_SMOOTHING = 0.75F;
|
||||
@@ -418,9 +420,19 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
if (this.channel.eventLoop().inEventLoop()) {
|
||||
this.doSendPacket(packet, callbacks, enumprotocol, enumprotocol1, flush); // Paper
|
||||
} else {
|
||||
+ // Paper start - optimise packets that are not flushed
|
||||
+ // note: since the type is not dynamic here, we need to actually copy the old executor code
|
||||
+ // into two branches. On conflict, just re-copy - no changes were made inside the executor code.
|
||||
+ if (!flush) {
|
||||
+ AbstractEventExecutor.LazyRunnable run = () -> {
|
||||
+ this.doSendPacket(packet, callbacks, enumprotocol, enumprotocol1, flush); // Paper - add flush parameter
|
||||
+ };
|
||||
+ this.channel.eventLoop().execute(run);
|
||||
+ } else { // Paper end - optimise packets that are not flushed
|
||||
this.channel.eventLoop().execute(() -> {
|
||||
- this.doSendPacket(packet, callbacks, enumprotocol, enumprotocol1, flush); // Paper
|
||||
+ this.doSendPacket(packet, callbacks, enumprotocol, enumprotocol1, flush); // Paper - add flush parameter // Paper - diff on change
|
||||
});
|
||||
+ } // Paper
|
||||
}
|
||||
|
||||
}
|
|
@ -1,426 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Thu, 27 Aug 2020 16:22:52 -0700
|
||||
Subject: [PATCH] Optimise nearby player lookups
|
||||
|
||||
Use a distance map to map out close players.
|
||||
Note that it's important that we cache the distance map value per chunk
|
||||
since the penalty of a map lookup could outweigh the benefits of
|
||||
searching less players (as it basically did in the outside range patch).
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
index e11ec87e8007979a1c6932b414bcd70c10db746c..bc46479fd0622a90fd98ac88f92b2840a22a2d04 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
@@ -93,6 +93,12 @@ public class ChunkHolder {
|
||||
this.chunkMap.needsChangeBroadcasting.add(this);
|
||||
}
|
||||
// Paper end - optimise chunk tick iteration
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ LevelChunk chunk = this.getFullChunkNowUnchecked();
|
||||
+ if (chunk != null) {
|
||||
+ chunk.updateGeneralAreaCache();
|
||||
+ }
|
||||
+ // Paper end - optimise checkDespawn
|
||||
}
|
||||
|
||||
public void onChunkRemove() {
|
||||
@@ -105,6 +111,12 @@ public class ChunkHolder {
|
||||
this.chunkMap.needsChangeBroadcasting.remove(this);
|
||||
}
|
||||
// Paper end - optimise chunk tick iteration
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ LevelChunk chunk = this.getFullChunkNowUnchecked();
|
||||
+ if (chunk != null) {
|
||||
+ chunk.removeGeneralAreaCache();
|
||||
+ }
|
||||
+ // Paper end - optimise checkDespawn
|
||||
}
|
||||
// Paper end
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index 57e13cb9d1460be34fe06f7c7e14330f60f0a51a..7f68b05b4d0789c308b5f90a5d626a633c80191f 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -156,6 +156,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
public final ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new ReferenceOpenHashSet<>();
|
||||
|
||||
// Paper - rewrite chunk system
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ public static final int GENERAL_AREA_MAP_SQUARE_RADIUS = 40;
|
||||
+ public static final double GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE = 16.0 * (GENERAL_AREA_MAP_SQUARE_RADIUS - 1);
|
||||
+ public static final double GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE_SQUARED = GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE * GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE;
|
||||
+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerGeneralAreaMap;
|
||||
+ // Paper end - optimise checkDespawn
|
||||
|
||||
// Paper start - distance maps
|
||||
private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets<ServerPlayer> pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>();
|
||||
@@ -208,6 +214,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
trackMap.add(player, chunkX, chunkZ, Math.min(trackRange, io.papermc.paper.chunk.system.ChunkSystem.getSendViewDistance(player)));
|
||||
}
|
||||
// Paper end - use distance map to optimise entity tracker
|
||||
+ this.playerGeneralAreaMap.add(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS); // Paper - optimise checkDespawn
|
||||
}
|
||||
|
||||
void removePlayerFromDistanceMaps(ServerPlayer player) {
|
||||
@@ -217,6 +224,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
this.playerMobSpawnMap.remove(player);
|
||||
this.playerChunkTickRangeMap.remove(player);
|
||||
// Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
+ this.playerGeneralAreaMap.remove(player); // Paper - optimise checkDespawns
|
||||
// Paper start - per player mob spawning
|
||||
if (this.playerMobDistanceMap != null) {
|
||||
this.playerMobDistanceMap.remove(player);
|
||||
@@ -248,6 +256,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
trackMap.update(player, chunkX, chunkZ, Math.min(trackRange, io.papermc.paper.chunk.system.ChunkSystem.getSendViewDistance(player)));
|
||||
}
|
||||
// Paper end - use distance map to optimise entity tracker
|
||||
+ this.playerGeneralAreaMap.update(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS); // Paper - optimise checkDespawn
|
||||
}
|
||||
// Paper end
|
||||
// Paper start
|
||||
@@ -405,6 +414,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
}
|
||||
});
|
||||
// Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ this.playerGeneralAreaMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
|
||||
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
||||
+ LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfCachedImmediately(rangeX, rangeZ);
|
||||
+ if (chunk != null) {
|
||||
+ chunk.updateGeneralAreaCache(newState);
|
||||
+ }
|
||||
+ },
|
||||
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
||||
+ LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfCachedImmediately(rangeX, rangeZ);
|
||||
+ if (chunk != null) {
|
||||
+ chunk.updateGeneralAreaCache(newState);
|
||||
+ }
|
||||
+ });
|
||||
+ // Paper end - optimise checkDespawn
|
||||
}
|
||||
|
||||
protected ChunkGenerator generator() {
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index 915dd3221124756fb94d60624cd552b1ab586ab5..5570ff6fccda19343a5520dca2671045c4fc48b4 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -444,6 +444,84 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
}
|
||||
// Paper end
|
||||
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ public final List<ServerPlayer> playersAffectingSpawning = new java.util.ArrayList<>();
|
||||
+ // Paper end - optimise checkDespawn
|
||||
+ // Paper start - optimise get nearest players for entity AI
|
||||
+ @Override
|
||||
+ public final ServerPlayer getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions condition, @Nullable LivingEntity source,
|
||||
+ double centerX, double centerY, double centerZ) {
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> nearby;
|
||||
+ nearby = this.getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(Mth.floor(centerX) >> 4, Mth.floor(centerZ) >> 4);
|
||||
+
|
||||
+ if (nearby == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ Object[] backingSet = nearby.getBackingSet();
|
||||
+
|
||||
+ double closestDistanceSquared = Double.MAX_VALUE;
|
||||
+ ServerPlayer closest = null;
|
||||
+
|
||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
||||
+ Object _player = backingSet[i];
|
||||
+ if (!(_player instanceof ServerPlayer)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ ServerPlayer player = (ServerPlayer)_player;
|
||||
+
|
||||
+ double distanceSquared = player.distanceToSqr(centerX, centerY, centerZ);
|
||||
+ if (distanceSquared < closestDistanceSquared && condition.test(source, player)) {
|
||||
+ closest = player;
|
||||
+ closestDistanceSquared = distanceSquared;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return closest;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Player getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions pathfindertargetcondition, LivingEntity entityliving) {
|
||||
+ return this.getNearestPlayer(pathfindertargetcondition, entityliving, entityliving.getX(), entityliving.getY(), entityliving.getZ());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Player getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions pathfindertargetcondition,
|
||||
+ double d0, double d1, double d2) {
|
||||
+ return this.getNearestPlayer(pathfindertargetcondition, null, d0, d1, d2);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public List<Player> getNearbyPlayers(net.minecraft.world.entity.ai.targeting.TargetingConditions condition, LivingEntity source, AABB axisalignedbb) {
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> nearby;
|
||||
+ double centerX = (axisalignedbb.maxX + axisalignedbb.minX) * 0.5;
|
||||
+ double centerZ = (axisalignedbb.maxZ + axisalignedbb.minZ) * 0.5;
|
||||
+ nearby = this.getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(Mth.floor(centerX) >> 4, Mth.floor(centerZ) >> 4);
|
||||
+
|
||||
+ List<Player> ret = new java.util.ArrayList<>();
|
||||
+
|
||||
+ if (nearby == null) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ Object[] backingSet = nearby.getBackingSet();
|
||||
+
|
||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
||||
+ Object _player = backingSet[i];
|
||||
+ if (!(_player instanceof ServerPlayer)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ ServerPlayer player = (ServerPlayer)_player;
|
||||
+
|
||||
+ if (axisalignedbb.contains(player.getX(), player.getY(), player.getZ()) && condition.test(source, player)) {
|
||||
+ ret.add(player);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+ // Paper end - optimise get nearest players for entity AI
|
||||
+
|
||||
// Add env and gen to constructor, IWorldDataServer -> WorldDataServer
|
||||
public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey<Level> resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
|
||||
// Holder holder = worlddimension.type(); // CraftBukkit - decompile error
|
||||
@@ -546,6 +624,14 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
}
|
||||
|
||||
public void tick(BooleanSupplier shouldKeepTicking) {
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ this.playersAffectingSpawning.clear();
|
||||
+ for (ServerPlayer player : this.players) {
|
||||
+ if (net.minecraft.world.entity.EntitySelector.PLAYER_AFFECTS_SPAWNING.test(player)) {
|
||||
+ this.playersAffectingSpawning.add(player);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - optimise checkDespawn
|
||||
ProfilerFiller gameprofilerfiller = this.getProfiler();
|
||||
|
||||
this.handlingTick = true;
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
|
||||
index 082da4f8eaede9d7f70341c207a2aaa6f07b997e..49b983064ea810382b6112f5dc7f93ba4e5710bd 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Mob.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
|
||||
@@ -826,7 +826,12 @@ public abstract class Mob extends LivingEntity {
|
||||
if (this.level.getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) {
|
||||
this.discard();
|
||||
} else if (!this.isPersistenceRequired() && !this.requiresCustomPersistence()) {
|
||||
- Player entityhuman = this.level.findNearbyPlayer(this, -1.0D, EntitySelector.PLAYER_AFFECTS_SPAWNING); // Paper
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ Player entityhuman = this.level.findNearbyPlayer(this, level.paperConfig().entities.spawning.despawnRanges.get(this.getType().getCategory()).hard() + 1, EntitySelector.PLAYER_AFFECTS_SPAWNING); // Paper
|
||||
+ if (entityhuman == null) {
|
||||
+ entityhuman = ((ServerLevel)this.level).playersAffectingSpawning.isEmpty() ? null : ((ServerLevel)this.level).playersAffectingSpawning.get(0);
|
||||
+ }
|
||||
+ // Paper end - optimise checkDespawn
|
||||
|
||||
if (entityhuman != null) {
|
||||
double d0 = entityhuman.distanceToSqr((Entity) this);
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index 158046d001cbd71b4cec2717f5e9266cc87cd2d0..9d122994fb0587eab313ba522a2e9933aa2ddc8b 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -204,6 +204,69 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
return this.getChunkIfLoaded(chunkX, chunkZ) != null;
|
||||
}
|
||||
// Paper end
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ public final List<net.minecraft.server.level.ServerPlayer> getNearbyPlayers(@Nullable Entity source, double sourceX, double sourceY,
|
||||
+ double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) {
|
||||
+ LevelChunk chunk;
|
||||
+ if (maxRange < 0.0 || maxRange >= net.minecraft.server.level.ChunkMap.GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE ||
|
||||
+ (chunk = (LevelChunk)this.getChunkIfLoadedImmediately(Mth.floor(sourceX) >> 4, Mth.floor(sourceZ) >> 4)) == null) {
|
||||
+ return this.getNearbyPlayersSlow(source, sourceX, sourceY, sourceZ, maxRange, predicate);
|
||||
+ }
|
||||
+
|
||||
+ List<net.minecraft.server.level.ServerPlayer> ret = new java.util.ArrayList<>();
|
||||
+ chunk.getNearestPlayers(sourceX, sourceY, sourceZ, predicate, maxRange, ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ private List<net.minecraft.server.level.ServerPlayer> getNearbyPlayersSlow(@Nullable Entity source, double sourceX, double sourceY,
|
||||
+ double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) {
|
||||
+ List<net.minecraft.server.level.ServerPlayer> ret = new java.util.ArrayList<>();
|
||||
+ double maxRangeSquared = maxRange * maxRange;
|
||||
+
|
||||
+ for (net.minecraft.server.level.ServerPlayer player : (List<net.minecraft.server.level.ServerPlayer>)this.players()) {
|
||||
+ if ((maxRange < 0.0 || player.distanceToSqr(sourceX, sourceY, sourceZ) < maxRangeSquared)) {
|
||||
+ if (predicate == null || predicate.test(player)) {
|
||||
+ ret.add(player);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ private net.minecraft.server.level.ServerPlayer getNearestPlayerSlow(@Nullable Entity source, double sourceX, double sourceY,
|
||||
+ double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) {
|
||||
+ net.minecraft.server.level.ServerPlayer closest = null;
|
||||
+ double closestRangeSquared = maxRange < 0.0 ? Double.MAX_VALUE : maxRange * maxRange;
|
||||
+
|
||||
+ for (net.minecraft.server.level.ServerPlayer player : (List<net.minecraft.server.level.ServerPlayer>)this.players()) {
|
||||
+ double distanceSquared = player.distanceToSqr(sourceX, sourceY, sourceZ);
|
||||
+ if (distanceSquared < closestRangeSquared && (predicate == null || predicate.test(player))) {
|
||||
+ closest = player;
|
||||
+ closestRangeSquared = distanceSquared;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return closest;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ public final net.minecraft.server.level.ServerPlayer getNearestPlayer(@Nullable Entity source, double sourceX, double sourceY,
|
||||
+ double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) {
|
||||
+ LevelChunk chunk;
|
||||
+ if (maxRange < 0.0 || maxRange >= net.minecraft.server.level.ChunkMap.GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE ||
|
||||
+ (chunk = (LevelChunk)this.getChunkIfLoadedImmediately(Mth.floor(sourceX) >> 4, Mth.floor(sourceZ) >> 4)) == null) {
|
||||
+ return this.getNearestPlayerSlow(source, sourceX, sourceY, sourceZ, maxRange, predicate);
|
||||
+ }
|
||||
+
|
||||
+ return chunk.findNearestPlayer(sourceX, sourceY, sourceZ, maxRange, predicate);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @Nullable Player getNearestPlayer(double d0, double d1, double d2, double d3, @Nullable Predicate<Entity> predicate) {
|
||||
+ return this.getNearestPlayer(null, d0, d1, d2, d3, predicate);
|
||||
+ }
|
||||
+ // Paper end - optimise checkDespawn
|
||||
|
||||
public abstract ResourceKey<LevelStem> getTypeKey();
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
index 0a49769bfa83d0b9c435e3ab4bba85979d660ff8..01b21f520ef1c834b9bafc3de85c1fa4fcf539d6 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
@@ -260,7 +260,7 @@ public final class NaturalSpawner {
|
||||
blockposition_mutableblockposition.set(l, i, i1);
|
||||
double d0 = (double) l + 0.5D;
|
||||
double d1 = (double) i1 + 0.5D;
|
||||
- Player entityhuman = world.getNearestPlayer(d0, (double) i, d1, -1.0D, false);
|
||||
+ Player entityhuman = (chunk instanceof LevelChunk) ? ((LevelChunk)chunk).findNearestPlayer(d0, i, d1, 576.0D, net.minecraft.world.entity.EntitySelector.NO_SPECTATORS) : world.getNearestPlayer(d0, (double) i, d1, -1.0D, false); // Paper - use chunk's player cache to optimize search in range
|
||||
|
||||
if (entityhuman != null) {
|
||||
double d2 = entityhuman.distanceToSqr(d0, (double) i, d1);
|
||||
@@ -334,7 +334,7 @@ public final class NaturalSpawner {
|
||||
}
|
||||
|
||||
private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) {
|
||||
- return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerToCenterThan(new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isNaturalSpawningAllowed((BlockPos) pos));
|
||||
+ return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerToCenterThan(new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isNaturalSpawningAllowed((BlockPos) pos)); // Paper - diff on change, copy into caller
|
||||
}
|
||||
|
||||
private static Boolean isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) { // Paper
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
index a3c1bba1c1f795d203207776bab41b4b8f4e69d7..28e4b302284f955a73e75d0f4276d55fb51826f5 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -269,6 +269,98 @@ public class LevelChunk extends ChunkAccess {
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ private boolean playerGeneralAreaCacheSet;
|
||||
+ private com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> playerGeneralAreaCache;
|
||||
+
|
||||
+ public com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> getPlayerGeneralAreaCache() {
|
||||
+ if (!this.playerGeneralAreaCacheSet) {
|
||||
+ this.updateGeneralAreaCache();
|
||||
+ }
|
||||
+ return this.playerGeneralAreaCache;
|
||||
+ }
|
||||
+
|
||||
+ public void updateGeneralAreaCache() {
|
||||
+ this.updateGeneralAreaCache(((ServerLevel)this.level).getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(this.coordinateKey));
|
||||
+ }
|
||||
+
|
||||
+ public void removeGeneralAreaCache() {
|
||||
+ this.playerGeneralAreaCacheSet = false;
|
||||
+ this.playerGeneralAreaCache = null;
|
||||
+ }
|
||||
+
|
||||
+ public void updateGeneralAreaCache(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> value) {
|
||||
+ this.playerGeneralAreaCacheSet = true;
|
||||
+ this.playerGeneralAreaCache = value;
|
||||
+ }
|
||||
+
|
||||
+ public net.minecraft.server.level.ServerPlayer findNearestPlayer(double sourceX, double sourceY, double sourceZ,
|
||||
+ double maxRange, java.util.function.Predicate<Entity> predicate) {
|
||||
+ if (!this.playerGeneralAreaCacheSet) {
|
||||
+ this.updateGeneralAreaCache();
|
||||
+ }
|
||||
+
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> nearby = this.playerGeneralAreaCache;
|
||||
+
|
||||
+ if (nearby == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ Object[] backingSet = nearby.getBackingSet();
|
||||
+ double closestDistance = maxRange < 0.0 ? Double.MAX_VALUE : maxRange * maxRange;
|
||||
+ net.minecraft.server.level.ServerPlayer closest = null;
|
||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
||||
+ Object _player = backingSet[i];
|
||||
+ if (!(_player instanceof net.minecraft.server.level.ServerPlayer)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer)_player;
|
||||
+
|
||||
+ double distance = player.distanceToSqr(sourceX, sourceY, sourceZ);
|
||||
+ if (distance < closestDistance && predicate.test(player)) {
|
||||
+ closest = player;
|
||||
+ closestDistance = distance;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return closest;
|
||||
+ }
|
||||
+
|
||||
+ public void getNearestPlayers(double sourceX, double sourceY, double sourceZ, java.util.function.Predicate<Entity> predicate,
|
||||
+ double range, java.util.List<net.minecraft.server.level.ServerPlayer> ret) {
|
||||
+ if (!this.playerGeneralAreaCacheSet) {
|
||||
+ this.updateGeneralAreaCache();
|
||||
+ }
|
||||
+
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> nearby = this.playerGeneralAreaCache;
|
||||
+
|
||||
+ if (nearby == null) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ double rangeSquared = range * range;
|
||||
+
|
||||
+ Object[] backingSet = nearby.getBackingSet();
|
||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
||||
+ Object _player = backingSet[i];
|
||||
+ if (!(_player instanceof net.minecraft.server.level.ServerPlayer)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer)_player;
|
||||
+
|
||||
+ if (range >= 0.0) {
|
||||
+ double distanceSquared = player.distanceToSqr(sourceX, sourceY, sourceZ);
|
||||
+ if (distanceSquared > rangeSquared) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (predicate == null || predicate.test(player)) {
|
||||
+ ret.add(player);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - optimise checkDespawn
|
||||
|
||||
public LevelChunk(ServerLevel world, ProtoChunk protoChunk, @Nullable LevelChunk.PostLoadProcessor entityLoader) {
|
||||
this(world, protoChunk.getPos(), protoChunk.getUpgradeData(), protoChunk.unpackBlockTicks(), protoChunk.unpackFluidTicks(), protoChunk.getInhabitedTime(), protoChunk.getSections(), entityLoader, protoChunk.getBlendingData());
|
|
@ -1,187 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Thu, 27 Aug 2020 20:51:40 -0700
|
||||
Subject: [PATCH] Remove streams for villager AI
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
|
||||
index b45c4f50705f80163d44d9e588f86a5770f5be38..10cbb80c7cd9ba30150d8d935c0d115719c35509 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
|
||||
@@ -52,7 +52,7 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
||||
if (this.hasRequiredMemories(entity)) {
|
||||
this.status = Behavior.Status.RUNNING;
|
||||
this.orderPolicy.apply(this.behaviors);
|
||||
- this.runningPolicy.apply(this.behaviors.stream(), world, entity, time);
|
||||
+ this.runningPolicy.apply(this.behaviors.entries, world, entity, time);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -61,11 +61,13 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
||||
|
||||
@Override
|
||||
public final void tickOrStop(ServerLevel world, E entity, long time) {
|
||||
- this.behaviors.stream().filter((task) -> {
|
||||
- return task.getStatus() == Behavior.Status.RUNNING;
|
||||
- }).forEach((task) -> {
|
||||
- task.tickOrStop(world, entity, time);
|
||||
- });
|
||||
+ // Paper start
|
||||
+ for (BehaviorControl<? super E> task : this.behaviors) {
|
||||
+ if (task.getStatus() == Behavior.Status.RUNNING) {
|
||||
+ task.tickOrStop(world, entity, time);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
if (this.behaviors.stream().noneMatch((task) -> {
|
||||
return task.getStatus() == Behavior.Status.RUNNING;
|
||||
})) {
|
||||
@@ -77,11 +79,11 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
||||
@Override
|
||||
public final void doStop(ServerLevel world, E entity, long time) {
|
||||
this.status = Behavior.Status.STOPPED;
|
||||
- this.behaviors.stream().filter((task) -> {
|
||||
- return task.getStatus() == Behavior.Status.RUNNING;
|
||||
- }).forEach((task) -> {
|
||||
- task.doStop(world, entity, time);
|
||||
- });
|
||||
+ for (BehaviorControl<? super E> behavior : this.behaviors) {
|
||||
+ if (behavior.getStatus() == Behavior.Status.RUNNING) {
|
||||
+ behavior.doStop(world, entity, time);
|
||||
+ }
|
||||
+ }
|
||||
this.exitErasedMemories.forEach(entity.getBrain()::eraseMemory);
|
||||
}
|
||||
|
||||
@@ -117,25 +119,31 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
||||
public static enum RunningPolicy {
|
||||
RUN_ONE {
|
||||
@Override
|
||||
- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time) {
|
||||
- tasks.filter((task) -> {
|
||||
- return task.getStatus() == Behavior.Status.STOPPED;
|
||||
- }).filter((task) -> {
|
||||
- return task.tryStart(world, entity, time);
|
||||
- }).findFirst();
|
||||
+ // Paper start - remove streams
|
||||
+ public <E extends LivingEntity> void apply(List<ShufflingList.WeightedEntry<BehaviorControl<? super E>>> tasks, ServerLevel world, E entity, long time) {
|
||||
+ for (ShufflingList.WeightedEntry<BehaviorControl<? super E>> task : tasks) {
|
||||
+ final BehaviorControl<? super E> behavior = task.getData();
|
||||
+ if (behavior.getStatus() == Behavior.Status.STOPPED && behavior.tryStart(world, entity, time)) {
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - remove streams
|
||||
}
|
||||
},
|
||||
TRY_ALL {
|
||||
@Override
|
||||
- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time) {
|
||||
- tasks.filter((task) -> {
|
||||
- return task.getStatus() == Behavior.Status.STOPPED;
|
||||
- }).forEach((task) -> {
|
||||
- task.tryStart(world, entity, time);
|
||||
- });
|
||||
+ // Paper start - remove streams
|
||||
+ public <E extends LivingEntity> void apply(List<ShufflingList.WeightedEntry<BehaviorControl<? super E>>> tasks, ServerLevel world, E entity, long time) {
|
||||
+ for (ShufflingList.WeightedEntry<BehaviorControl<? super E>> task : tasks) {
|
||||
+ final BehaviorControl<? super E> behavior = task.getData();
|
||||
+ if (behavior.getStatus() == Behavior.Status.STOPPED) {
|
||||
+ behavior.tryStart(world, entity, time);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - remove streams
|
||||
}
|
||||
};
|
||||
|
||||
- public abstract <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time);
|
||||
+ public abstract <E extends LivingEntity> void apply(List<ShufflingList.WeightedEntry<BehaviorControl<? super E>>> tasks, ServerLevel world, E entity, long time); // Paper - remove streams
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java
|
||||
index 731ef21dbbd25d6924717de42f4569a9b5935643..fe3ab3d388f0481fb0db06b7f730f868dbf8e8a5 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java
|
||||
@@ -14,7 +14,7 @@ import java.util.stream.Stream;
|
||||
import net.minecraft.util.RandomSource;
|
||||
|
||||
public class ShufflingList<U> implements Iterable<U> {
|
||||
- protected final List<ShufflingList.WeightedEntry<U>> entries;
|
||||
+ public final List<ShufflingList.WeightedEntry<U>> entries; // Paper - public
|
||||
private final RandomSource random = RandomSource.create();
|
||||
private final boolean isUnsafe; // Paper
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
|
||||
index 1dfcc5cba6ffb463acf161a23fff1ca452184290..2c4517850a9692f1c2b1332b58e8312fe1166772 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
|
||||
@@ -25,13 +25,16 @@ public class NearestItemSensor extends Sensor<Mob> {
|
||||
protected void doTick(ServerLevel world, Mob entity) {
|
||||
Brain<?> brain = entity.getBrain();
|
||||
List<ItemEntity> list = world.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0D, 16.0D, 32.0D), (itemEntity) -> {
|
||||
- return true;
|
||||
+ return itemEntity.closerThan(entity, 9.0D) && entity.wantsToPickUp(itemEntity.getItem()); // Paper - move predicate into getEntities
|
||||
});
|
||||
- list.sort(Comparator.comparingDouble(entity::distanceToSqr));
|
||||
+ list.sort((e1, e2) -> Double.compare(entity.distanceToSqr(e1), entity.distanceToSqr(e2))); // better to take the sort perf hit than using line of sight more than we need to.
|
||||
+ // Paper start - remove streams
|
||||
// Paper start - remove streams in favour of lists
|
||||
ItemEntity nearest = null;
|
||||
- for (ItemEntity entityItem : list) {
|
||||
- if (entity.wantsToPickUp(entityItem.getItem()) && entityItem.closerThan(entity, 32.0D) && entity.hasLineOfSight(entityItem)) {
|
||||
+ for (int i = 0; i < list.size(); i++) {
|
||||
+ ItemEntity entityItem = list.get(i);
|
||||
+ if (entity.hasLineOfSight(entityItem)) {
|
||||
+ // Paper end - remove streams
|
||||
nearest = entityItem;
|
||||
break;
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
|
||||
index 312775d0430f793720211dc29bb293503e799d11..75d9c4f011b5a97def215784c92bb57bbb35d06b 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
|
||||
@@ -21,25 +21,30 @@ public class PlayerSensor extends Sensor<LivingEntity> {
|
||||
|
||||
@Override
|
||||
protected void doTick(ServerLevel world, LivingEntity entity) {
|
||||
- List<Player> players = new java.util.ArrayList<>(world.players());
|
||||
- players.removeIf(player -> !EntitySelector.NO_SPECTATORS.test(player) || !entity.closerThan(player, 16.0D));
|
||||
- players.sort(Comparator.comparingDouble(entity::distanceTo));
|
||||
+ // Paper start - remove streams
|
||||
+ List<Player> players = (List)world.getNearbyPlayers(entity, entity.getX(), entity.getY(), entity.getZ(), 16.0D, EntitySelector.NO_SPECTATORS);
|
||||
+ players.sort((e1, e2) -> Double.compare(entity.distanceToSqr(e1), entity.distanceToSqr(e2)));
|
||||
Brain<?> brain = entity.getBrain();
|
||||
|
||||
brain.setMemory(MemoryModuleType.NEAREST_PLAYERS, players);
|
||||
|
||||
- Player nearest = null, nearestTargetable = null;
|
||||
- for (Player player : players) {
|
||||
- if (Sensor.isEntityTargetable(entity, player)) {
|
||||
- if (nearest == null) nearest = player;
|
||||
- if (Sensor.isEntityAttackable(entity, player)) {
|
||||
- nearestTargetable = player;
|
||||
- break; // Both variables are assigned, no reason to loop further
|
||||
- }
|
||||
+ Player firstTargetable = null;
|
||||
+ Player firstAttackable = null;
|
||||
+ for (int index = 0, len = players.size(); index < len; ++index) {
|
||||
+ Player player = players.get(index);
|
||||
+ if (firstTargetable == null && isEntityTargetable(entity, player)) {
|
||||
+ firstTargetable = player;
|
||||
+ }
|
||||
+ if (firstAttackable == null && isEntityAttackable(entity, player)) {
|
||||
+ firstAttackable = player;
|
||||
+ }
|
||||
+
|
||||
+ if (firstAttackable != null && firstTargetable != null) {
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER, nearest);
|
||||
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, nearestTargetable);
|
||||
- // Paper end
|
||||
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER, firstTargetable);
|
||||
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, Optional.ofNullable(firstAttackable));
|
||||
+ // Paper end - remove streams
|
||||
}
|
||||
}
|
|
@ -1,364 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Andrew Steinborn <git@steinborn.me>
|
||||
Date: Mon, 26 Jul 2021 02:15:17 -0400
|
||||
Subject: [PATCH] Use Velocity compression and cipher natives
|
||||
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index dbaff3f2505f09e87bea314bb179a408eebd2e7c..dc61ebe468740b0b9359bf6e3a6050726136941c 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -28,6 +28,11 @@ dependencies {
|
||||
implementation("org.spongepowered:configurate-yaml:4.1.2") // Paper - config files
|
||||
implementation("commons-lang:commons-lang:2.6")
|
||||
implementation("net.fabricmc:mapping-io:0.3.0") // Paper - needed to read mappings for stacktrace deobfuscation
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ implementation("com.velocitypowered:velocity-native:3.1.2-SNAPSHOT") {
|
||||
+ isTransitive = false
|
||||
+ }
|
||||
+ // Paper end
|
||||
runtimeOnly("org.xerial:sqlite-jdbc:3.36.0.3")
|
||||
runtimeOnly("mysql:mysql-connector-java:8.0.29")
|
||||
runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
|
||||
diff --git a/src/main/java/net/minecraft/network/CipherDecoder.java b/src/main/java/net/minecraft/network/CipherDecoder.java
|
||||
index 778beb445eac5769b9e4e07b4d1294c50ae2602b..c712fb8193115e1ab71b5e40fb0ccb9413062b03 100644
|
||||
--- a/src/main/java/net/minecraft/network/CipherDecoder.java
|
||||
+++ b/src/main/java/net/minecraft/network/CipherDecoder.java
|
||||
@@ -7,13 +7,29 @@ import java.util.List;
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
public class CipherDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
- private final CipherBase cipher;
|
||||
+ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper
|
||||
|
||||
- public CipherDecoder(Cipher cipher) {
|
||||
- this.cipher = new CipherBase(cipher);
|
||||
+ public CipherDecoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper
|
||||
+ this.cipher = cipher; // Paper
|
||||
}
|
||||
|
||||
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
|
||||
- list.add(this.cipher.decipher(channelHandlerContext, byteBuf));
|
||||
+ // Paper start
|
||||
+ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), cipher, byteBuf);
|
||||
+ try {
|
||||
+ cipher.process(compatible);
|
||||
+ list.add(compatible);
|
||||
+ } catch (Exception e) {
|
||||
+ compatible.release(); // compatible will never be used if we throw an exception
|
||||
+ throw e;
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
||||
+ cipher.close();
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/network/CipherEncoder.java b/src/main/java/net/minecraft/network/CipherEncoder.java
|
||||
index 0f3d502a9680006bcdcd7d272240a2e5c3b46790..5dd7be70603e8754d2625bb9d16900cb01b9c730 100644
|
||||
--- a/src/main/java/net/minecraft/network/CipherEncoder.java
|
||||
+++ b/src/main/java/net/minecraft/network/CipherEncoder.java
|
||||
@@ -4,15 +4,32 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import javax.crypto.Cipher;
|
||||
+import java.util.List;
|
||||
|
||||
-public class CipherEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
- private final CipherBase cipher;
|
||||
+public class CipherEncoder extends io.netty.handler.codec.MessageToMessageEncoder<ByteBuf> { // Paper - change superclass
|
||||
+ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper
|
||||
|
||||
- public CipherEncoder(Cipher cipher) {
|
||||
- this.cipher = new CipherBase(cipher);
|
||||
+ public CipherEncoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper
|
||||
+ this.cipher = cipher; // Paper
|
||||
}
|
||||
|
||||
- protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception {
|
||||
- this.cipher.encipher(byteBuf, byteBuf2);
|
||||
+ protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
|
||||
+ // Paper start
|
||||
+ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), cipher, byteBuf);
|
||||
+ try {
|
||||
+ cipher.process(compatible);
|
||||
+ list.add(compatible);
|
||||
+ } catch (Exception e) {
|
||||
+ compatible.release(); // compatible will never be used if we throw an exception
|
||||
+ throw e;
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
||||
+ cipher.close();
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/network/CompressionDecoder.java b/src/main/java/net/minecraft/network/CompressionDecoder.java
|
||||
index b62be99c57b0a5bba0dc29809557d4d247698b13..f4d4ad983baf24d889441541d5a84dc1f49ea4d4 100644
|
||||
--- a/src/main/java/net/minecraft/network/CompressionDecoder.java
|
||||
+++ b/src/main/java/net/minecraft/network/CompressionDecoder.java
|
||||
@@ -12,13 +12,20 @@ public class CompressionDecoder extends ByteToMessageDecoder {
|
||||
public static final int MAXIMUM_COMPRESSED_LENGTH = 2097152;
|
||||
public static final int MAXIMUM_UNCOMPRESSED_LENGTH = 8388608;
|
||||
private final Inflater inflater;
|
||||
+ private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper
|
||||
private int threshold;
|
||||
private boolean validateDecompressed;
|
||||
|
||||
+ // Paper start
|
||||
public CompressionDecoder(int compressionThreshold, boolean rejectsBadPackets) {
|
||||
+ this(null, compressionThreshold, rejectsBadPackets);
|
||||
+ }
|
||||
+ public CompressionDecoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold, boolean rejectsBadPackets) {
|
||||
this.threshold = compressionThreshold;
|
||||
this.validateDecompressed = rejectsBadPackets;
|
||||
- this.inflater = new Inflater();
|
||||
+ this.inflater = compressor == null ? new Inflater() : null;
|
||||
+ this.compressor = compressor;
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
|
||||
@@ -38,6 +45,8 @@ public class CompressionDecoder extends ByteToMessageDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ if (this.inflater != null) {
|
||||
byte[] bs = new byte[friendlyByteBuf.readableBytes()];
|
||||
friendlyByteBuf.readBytes(bs);
|
||||
this.inflater.setInput(bs);
|
||||
@@ -45,10 +54,36 @@ public class CompressionDecoder extends ByteToMessageDecoder {
|
||||
this.inflater.inflate(cs);
|
||||
list.add(Unpooled.wrappedBuffer(cs));
|
||||
this.inflater.reset();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ int claimedUncompressedSize = i; // OBFHELPER
|
||||
+ ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), this.compressor, byteBuf);
|
||||
+ ByteBuf uncompressed = com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(channelHandlerContext.alloc(), this.compressor, claimedUncompressedSize);
|
||||
+ try {
|
||||
+ this.compressor.inflate(compatibleIn, uncompressed, claimedUncompressedSize);
|
||||
+ list.add(uncompressed);
|
||||
+ byteBuf.clear();
|
||||
+ } catch (Exception e) {
|
||||
+ uncompressed.release();
|
||||
+ throw e;
|
||||
+ } finally {
|
||||
+ compatibleIn.release();
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
|
||||
+ if (this.compressor != null) {
|
||||
+ this.compressor.close();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public void setThreshold(int compressionThreshold, boolean rejectsBadPackets) {
|
||||
this.threshold = compressionThreshold;
|
||||
this.validateDecompressed = rejectsBadPackets;
|
||||
diff --git a/src/main/java/net/minecraft/network/CompressionEncoder.java b/src/main/java/net/minecraft/network/CompressionEncoder.java
|
||||
index 792883afe53d2b7989c25a81c2f9a639d5e21d20..c04379ca8a4db0f4de46ad2b3b3384310eebddf3 100644
|
||||
--- a/src/main/java/net/minecraft/network/CompressionEncoder.java
|
||||
+++ b/src/main/java/net/minecraft/network/CompressionEncoder.java
|
||||
@@ -6,22 +6,37 @@ import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
- private final byte[] encodeBuf = new byte[8192];
|
||||
+ private final byte[] encodeBuf; // Paper
|
||||
private final Deflater deflater;
|
||||
+ private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper
|
||||
private int threshold;
|
||||
|
||||
+ // Paper start
|
||||
public CompressionEncoder(int compressionThreshold) {
|
||||
+ this(null, compressionThreshold);
|
||||
+ }
|
||||
+ public CompressionEncoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold) {
|
||||
this.threshold = compressionThreshold;
|
||||
- this.deflater = new Deflater();
|
||||
+ if (compressor == null) {
|
||||
+ this.encodeBuf = new byte[8192];
|
||||
+ this.deflater = new Deflater();
|
||||
+ } else {
|
||||
+ this.encodeBuf = null;
|
||||
+ this.deflater = null;
|
||||
+ }
|
||||
+ this.compressor = compressor;
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
- protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) {
|
||||
+ protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception { // Paper
|
||||
int i = byteBuf.readableBytes();
|
||||
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(byteBuf2);
|
||||
if (i < this.threshold) {
|
||||
friendlyByteBuf.writeVarInt(0);
|
||||
friendlyByteBuf.writeBytes(byteBuf);
|
||||
} else {
|
||||
+ // Paper start
|
||||
+ if (this.deflater != null) {
|
||||
byte[] bs = new byte[i];
|
||||
byteBuf.readBytes(bs);
|
||||
friendlyByteBuf.writeVarInt(bs.length);
|
||||
@@ -34,10 +49,48 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
}
|
||||
|
||||
this.deflater.reset();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ friendlyByteBuf.writeVarInt(i);
|
||||
+ ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), this.compressor, byteBuf);
|
||||
+ try {
|
||||
+ this.compressor.deflate(compatibleIn, byteBuf2);
|
||||
+ } finally {
|
||||
+ compatibleIn.release();
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception{
|
||||
+ if (this.compressor != null) {
|
||||
+ // We allocate bytes to be compressed plus 1 byte. This covers two cases:
|
||||
+ //
|
||||
+ // - Compression
|
||||
+ // According to https://github.com/ebiggers/libdeflate/blob/master/libdeflate.h#L103,
|
||||
+ // if the data compresses well (and we do not have some pathological case) then the maximum
|
||||
+ // size the compressed size will ever be is the input size minus one.
|
||||
+ // - Uncompressed
|
||||
+ // This is fairly obvious - we will then have one more than the uncompressed size.
|
||||
+ int initialBufferSize = msg.readableBytes() + 1;
|
||||
+ return com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(ctx.alloc(), this.compressor, initialBufferSize);
|
||||
+ }
|
||||
+
|
||||
+ return super.allocateBuffer(ctx, msg, preferDirect);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
||||
+ if (this.compressor != null) {
|
||||
+ this.compressor.close();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public int getThreshold() {
|
||||
return this.threshold;
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
||||
index 1a077353811a0a233f1a7ee7b6afe6c535e49168..38c09c65dfa4a7a0c80d36f726c1fd028cbe05f8 100644
|
||||
--- a/src/main/java/net/minecraft/network/Connection.java
|
||||
+++ b/src/main/java/net/minecraft/network/Connection.java
|
||||
@@ -689,11 +689,28 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
return networkmanager;
|
||||
}
|
||||
|
||||
- public void setEncryptionKey(Cipher decryptionCipher, Cipher encryptionCipher) {
|
||||
- this.encrypted = true;
|
||||
- this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher));
|
||||
- this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher));
|
||||
+ // Paper start
|
||||
+// public void setEncryptionKey(Cipher decryptionCipher, Cipher encryptionCipher) {
|
||||
+// this.encrypted = true;
|
||||
+// this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher));
|
||||
+// this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher));
|
||||
+// }
|
||||
+
|
||||
+ public void setupEncryption(javax.crypto.SecretKey key) throws net.minecraft.util.CryptException {
|
||||
+ if (!this.encrypted) {
|
||||
+ try {
|
||||
+ com.velocitypowered.natives.encryption.VelocityCipher decryption = com.velocitypowered.natives.util.Natives.cipher.get().forDecryption(key);
|
||||
+ com.velocitypowered.natives.encryption.VelocityCipher encryption = com.velocitypowered.natives.util.Natives.cipher.get().forEncryption(key);
|
||||
+
|
||||
+ this.encrypted = true;
|
||||
+ this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryption));
|
||||
+ this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryption));
|
||||
+ } catch (java.security.GeneralSecurityException e) {
|
||||
+ throw new net.minecraft.util.CryptException(e);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
+ // Paper end
|
||||
|
||||
public boolean isEncrypted() {
|
||||
return this.encrypted;
|
||||
@@ -722,16 +739,17 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
public void setupCompression(int compressionThreshold, boolean rejectsBadPackets) {
|
||||
if (compressionThreshold >= 0) {
|
||||
+ com.velocitypowered.natives.compression.VelocityCompressor compressor = com.velocitypowered.natives.util.Natives.compress.get().create(-1); // Paper
|
||||
if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder) {
|
||||
((CompressionDecoder) this.channel.pipeline().get("decompress")).setThreshold(compressionThreshold, rejectsBadPackets);
|
||||
} else {
|
||||
- this.channel.pipeline().addBefore("decoder", "decompress", new CompressionDecoder(compressionThreshold, rejectsBadPackets));
|
||||
+ this.channel.pipeline().addBefore("decoder", "decompress", new CompressionDecoder(compressor, compressionThreshold, rejectsBadPackets)); // Paper
|
||||
}
|
||||
|
||||
if (this.channel.pipeline().get("compress") instanceof CompressionEncoder) {
|
||||
((CompressionEncoder) this.channel.pipeline().get("compress")).setThreshold(compressionThreshold);
|
||||
} else {
|
||||
- this.channel.pipeline().addBefore("encoder", "compress", new CompressionEncoder(compressionThreshold));
|
||||
+ this.channel.pipeline().addBefore("encoder", "compress", new CompressionEncoder(compressor, compressionThreshold)); // Paper
|
||||
}
|
||||
this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_THRESHOLD_SET); // Paper
|
||||
} else {
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
index b80aedd2002959b4026c27ce76b3ed17f0acfb5b..2985271132c9ae822dcb0d7a7e6f0c268d1736cc 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
@@ -106,6 +106,11 @@ public class ServerConnectionListener {
|
||||
ServerConnectionListener.LOGGER.info("Using default channel type");
|
||||
}
|
||||
|
||||
+ // Paper start - indicate Velocity natives in use
|
||||
+ ServerConnectionListener.LOGGER.info("Paper: Using " + com.velocitypowered.natives.util.Natives.compress.getLoadedVariant() + " compression from Velocity.");
|
||||
+ ServerConnectionListener.LOGGER.info("Paper: Using " + com.velocitypowered.natives.util.Natives.cipher.getLoadedVariant() + " cipher from Velocity.");
|
||||
+ // Paper end
|
||||
+
|
||||
this.channels.add(((ServerBootstrap) ((ServerBootstrap) (new ServerBootstrap()).channel(oclass)).childHandler(new ChannelInitializer<Channel>() {
|
||||
protected void initChannel(Channel channel) {
|
||||
try {
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
index fb2502a6ccf421d658a805eacc8b4b3b86ff61df..acde45dcf605d9e2ce85002cd4d0d457c4afce38 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
@@ -265,12 +265,14 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
||||
}
|
||||
|
||||
SecretKey secretkey = packet.getSecretKey(privatekey);
|
||||
- Cipher cipher = Crypt.getCipher(2, secretkey);
|
||||
- Cipher cipher1 = Crypt.getCipher(1, secretkey);
|
||||
+ // Paper start
|
||||
+// Cipher cipher = Crypt.getCipher(2, secretkey);
|
||||
+// Cipher cipher1 = Crypt.getCipher(1, secretkey);
|
||||
+ // Paper end
|
||||
|
||||
s = (new BigInteger(Crypt.digestData("", this.server.getKeyPair().getPublic(), secretkey))).toString(16);
|
||||
this.state = ServerLoginPacketListenerImpl.State.AUTHENTICATING;
|
||||
- this.connection.setEncryptionKey(cipher, cipher1);
|
||||
+ this.connection.setupEncryption(secretkey); // Paper
|
||||
} catch (CryptException cryptographyexception) {
|
||||
throw new IllegalStateException("Protocol error", cryptographyexception);
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Mon, 30 Aug 2021 04:26:40 -0700
|
||||
Subject: [PATCH] Reduce worldgen thread worker count for low core count CPUs
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java
|
||||
index 439ae035d8b2787916e017e2636115b5455811f1..a54c0a4d0849087229d08ccc89872c3603f2f426 100644
|
||||
--- a/src/main/java/net/minecraft/Util.java
|
||||
+++ b/src/main/java/net/minecraft/Util.java
|
||||
@@ -146,7 +146,19 @@ public class Util {
|
||||
|
||||
private static ExecutorService makeExecutor(String s, int priorityModifier) { // Paper - add priority
|
||||
// Paper start - use simpler thread pool that allows 1 thread
|
||||
- int i = Math.min(8, Math.max(Runtime.getRuntime().availableProcessors() - 2, 1));
|
||||
+ // Paper start - also try to avoid suffocating the system with the worldgen workers
|
||||
+ int cpus = Runtime.getRuntime().availableProcessors() / 2;
|
||||
+ int i;
|
||||
+ if (cpus <= 4) {
|
||||
+ i = cpus <= 2 ? 1 : 2;
|
||||
+ } else if (cpus <= 8) {
|
||||
+ // [5, 8]
|
||||
+ i = Math.max(3, cpus - 2);
|
||||
+ } else {
|
||||
+ i = cpus * 2 / 3;
|
||||
+ }
|
||||
+ i = Math.min(8, i);
|
||||
+ // Paper end - also try to avoid suffocating the system with the worldgen workers
|
||||
i = Integer.getInteger("Paper.WorkerThreadCount", i);
|
||||
ExecutorService executorService;
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sun, 31 Oct 2021 21:34:00 -0700
|
||||
Subject: [PATCH] Async catch modifications to critical entity state
|
||||
|
||||
These used to be here from Spigot, but were dropped with 1.17.
|
||||
Now in 1.17, this state is _even more_ critical than it was before,
|
||||
so these must exist to catch stupid plugins.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
|
||||
index 038abf2ac104ceecaab11b10d466ea69ec86623e..a77985b2dd7137d8eea03909403fc08e89376d73 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
|
||||
@@ -77,6 +77,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
}
|
||||
|
||||
private boolean addEntityUuid(T entity) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity add by UUID"); // Paper
|
||||
if (!this.knownUuids.add(entity.getUUID())) {
|
||||
PersistentEntitySectionManager.LOGGER.warn("UUID of added entity already exists: {}", entity);
|
||||
return false;
|
||||
@@ -90,6 +91,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
}
|
||||
|
||||
private boolean addEntity(T entity, boolean existing) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity add"); // Paper
|
||||
// Paper start - chunk system hooks
|
||||
if (existing) {
|
||||
// I don't want to know why this is a generic type.
|
||||
@@ -145,19 +147,23 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
}
|
||||
|
||||
void startTicking(T entity) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity start ticking"); // Paper
|
||||
this.callbacks.onTickingStart(entity);
|
||||
}
|
||||
|
||||
void stopTicking(T entity) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity stop ticking"); // Paper
|
||||
this.callbacks.onTickingEnd(entity);
|
||||
}
|
||||
|
||||
void startTracking(T entity) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity start tracking"); // Paper
|
||||
this.visibleEntityStorage.add(entity);
|
||||
this.callbacks.onTrackingStart(entity);
|
||||
}
|
||||
|
||||
void stopTracking(T entity) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity stop tracking"); // Paper
|
||||
this.callbacks.onTrackingEnd(entity);
|
||||
this.visibleEntityStorage.remove(entity);
|
||||
}
|
||||
@@ -169,6 +175,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
}
|
||||
|
||||
public void updateChunkStatus(ChunkPos chunkPos, Visibility trackingStatus) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Update chunk status"); // Paper
|
||||
long i = chunkPos.toLong();
|
||||
|
||||
if (trackingStatus == Visibility.HIDDEN) {
|
||||
@@ -213,6 +220,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
}
|
||||
|
||||
public void ensureChunkQueuedForLoad(long chunkPos) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity chunk save"); // Paper
|
||||
PersistentEntitySectionManager.ChunkLoadStatus persistententitysectionmanager_b = (PersistentEntitySectionManager.ChunkLoadStatus) this.chunkLoadStatuses.get(chunkPos);
|
||||
|
||||
if (persistententitysectionmanager_b == PersistentEntitySectionManager.ChunkLoadStatus.FRESH) {
|
||||
@@ -257,6 +265,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
}
|
||||
|
||||
private void requestChunkLoad(long chunkPos) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity chunk load request"); // Paper
|
||||
this.chunkLoadStatuses.put(chunkPos, PersistentEntitySectionManager.ChunkLoadStatus.PENDING);
|
||||
ChunkPos chunkcoordintpair = new ChunkPos(chunkPos);
|
||||
CompletableFuture completablefuture = this.permanentStorage.loadEntities(chunkcoordintpair);
|
||||
@@ -270,6 +279,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
}
|
||||
|
||||
private boolean processChunkUnload(long chunkPos) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity chunk unload process"); // Paper
|
||||
boolean flag = this.storeChunkSections(chunkPos, (entityaccess) -> {
|
||||
entityaccess.getPassengersAndSelf().forEach(this::unloadEntity);
|
||||
}, true); // CraftBukkit - add boolean for event call
|
||||
@@ -294,6 +304,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
}
|
||||
|
||||
private void processPendingLoads() {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity chunk process pending loads"); // Paper
|
||||
ChunkEntities<T> chunkentities; // CraftBukkit - decompile error
|
||||
|
||||
while ((chunkentities = (ChunkEntities) this.loadingInbox.poll()) != null) {
|
||||
@@ -310,6 +321,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity manager tick"); // Paper
|
||||
this.processPendingLoads();
|
||||
this.processUnloads();
|
||||
}
|
||||
@@ -330,6 +342,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
}
|
||||
|
||||
public void autoSave() {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity manager autosave"); // Paper
|
||||
this.getAllChunksToSave().forEach((java.util.function.LongConsumer) (i) -> { // CraftBukkit - decompile error
|
||||
boolean flag = this.chunkVisibility.get(i) == Visibility.HIDDEN;
|
||||
|
||||
@@ -344,6 +357,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
}
|
||||
|
||||
public void saveAll() {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity manager save"); // Paper
|
||||
LongSet longset = this.getAllChunksToSave();
|
||||
|
||||
while (!longset.isEmpty()) {
|
||||
@@ -446,6 +460,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
long i = SectionPos.asLong(blockposition);
|
||||
|
||||
if (i != this.currentSectionKey) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity move"); // Paper
|
||||
Visibility visibility = this.currentSection.getStatus();
|
||||
|
||||
if (!this.currentSection.remove(this.entity)) {
|
||||
@@ -500,6 +515,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
|
||||
@Override
|
||||
public void onRemove(Entity.RemovalReason reason) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Entity remove"); // Paper
|
||||
if (!this.currentSection.remove(this.entity)) {
|
||||
PersistentEntitySectionManager.LOGGER.warn("Entity {} wasn't found in section {} (destroying due to {})", new Object[]{this.entity, SectionPos.of(this.currentSectionKey), reason});
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Nassim Jahnke <nassim@njahnke.dev>
|
||||
Date: Sun, 24 Oct 2021 15:49:35 +0200
|
||||
Subject: [PATCH] Fix Bukkit NamespacedKey shenanigans
|
||||
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperContainerEntityLootableInventory.java b/src/main/java/com/destroystokyo/paper/loottable/PaperContainerEntityLootableInventory.java
|
||||
index 88e32ed64f90bfd277dac84ba4bd84f0d943f5f8..d4a8c1bbb8fef27ac42bdf27dde495b4c649e6cb 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/loottable/PaperContainerEntityLootableInventory.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/loottable/PaperContainerEntityLootableInventory.java
|
||||
@@ -17,7 +17,7 @@ public class PaperContainerEntityLootableInventory implements PaperLootableEntit
|
||||
|
||||
@Override
|
||||
public org.bukkit.loot.LootTable getLootTable() {
|
||||
- return entity.getLootTable() != null ? Bukkit.getLootTable(CraftNamespacedKey.fromMinecraft(entity.getLootTable())) : null;
|
||||
+ return entity.getLootTable() != null && !entity.getLootTable().getPath().isEmpty() ? Bukkit.getLootTable(CraftNamespacedKey.fromMinecraft(entity.getLootTable())) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperTileEntityLootableInventory.java b/src/main/java/com/destroystokyo/paper/loottable/PaperTileEntityLootableInventory.java
|
||||
index 9cfa5d36a6991067a3866e0d437749fafcc0158e..2ee4ee14ab3345486dad6b24fd9a4fcc6c746b99 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/loottable/PaperTileEntityLootableInventory.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/loottable/PaperTileEntityLootableInventory.java
|
||||
@@ -15,7 +15,7 @@ public class PaperTileEntityLootableInventory implements PaperLootableBlockInven
|
||||
|
||||
@Override
|
||||
public org.bukkit.loot.LootTable getLootTable() {
|
||||
- return tileEntityLootable.lootTable != null ? Bukkit.getLootTable(CraftNamespacedKey.fromMinecraft(tileEntityLootable.lootTable)) : null;
|
||||
+ return tileEntityLootable.lootTable != null && !tileEntityLootable.lootTable.getPath().isEmpty() ? Bukkit.getLootTable(CraftNamespacedKey.fromMinecraft(tileEntityLootable.lootTable)) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftNamespacedKey.java b/src/main/java/org/bukkit/craftbukkit/util/CraftNamespacedKey.java
|
||||
index 5f40d240b879e3989897b6e45725a8e5a6a7f194..5014192edb9616ce725fc1592832034789527b6f 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftNamespacedKey.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftNamespacedKey.java
|
||||
@@ -13,7 +13,7 @@ public final class CraftNamespacedKey {
|
||||
return null;
|
||||
}
|
||||
ResourceLocation minecraft = ResourceLocation.tryParse(string);
|
||||
- return (minecraft == null) ? null : CraftNamespacedKey.fromMinecraft(minecraft);
|
||||
+ return (minecraft == null || minecraft.getPath().isEmpty()) ? null : CraftNamespacedKey.fromMinecraft(minecraft); // Paper - Bukkit's parser does not match Vanilla for empty paths
|
||||
}
|
||||
|
||||
public static NamespacedKey fromString(String string) {
|
|
@ -1,22 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Thu, 2 Sep 2021 00:24:06 -0700
|
||||
Subject: [PATCH] Fix merchant inventory not closing on entity removal
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index 5570ff6fccda19343a5520dca2671045c4fc48b4..6b17c008926287f2c9f828ba656c3a77aef0fb2c 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -2588,6 +2588,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
// Spigot end
|
||||
// Spigot Start
|
||||
if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message
|
||||
+ // Paper start
|
||||
+ if (entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) {
|
||||
+ merchant.getTrader().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED);
|
||||
+ }
|
||||
+ // Paper end
|
||||
for (org.bukkit.entity.HumanEntity h : Lists.newArrayList(((org.bukkit.inventory.InventoryHolder) entity.getBukkitEntity()).getInventory().getViewers())) {
|
||||
h.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); // Paper
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: stonar96 <minecraft.stonar96@gmail.com>
|
||||
Date: Sun, 12 Sep 2021 00:14:21 +0200
|
||||
Subject: [PATCH] Check requirement before suggesting root nodes
|
||||
|
||||
Child nodes are handled by CommandDispatcher#parse checking
|
||||
requirements.
|
||||
|
||||
Vanilla clients only send ServerboundCommandSuggestionPacket when
|
||||
encountering a command node with ASK_SERVER suggestions, however a
|
||||
modified client can send this packet whenever it wants.
|
||||
|
||||
diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java
|
||||
index e733a5657032d29e5a0d64375c9e36639360a7e0..b64c98c173e25055f4ff9d7124d0a3cb7ff6ab1d 100644
|
||||
--- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java
|
||||
+++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java
|
||||
@@ -595,10 +595,14 @@ public class CommandDispatcher<S> {
|
||||
int i = 0;
|
||||
for (final CommandNode<S> node : parent.getChildren()) {
|
||||
CompletableFuture<Suggestions> future = Suggestions.empty();
|
||||
+ // Paper start - Don't suggest if the requirement isn't met
|
||||
+ if (parent != this.root || node.canUse(context.getSource())) {
|
||||
try {
|
||||
- if (node.canUse(parse.getContext().getSource())) future = node.listSuggestions(context.build(truncatedInput), new SuggestionsBuilder(truncatedInput, truncatedInputLowerCase, start)); // CraftBukkit
|
||||
+ future = node.listSuggestions(context.build(truncatedInput), new SuggestionsBuilder(truncatedInput, truncatedInputLowerCase, start)); // CraftBukkit
|
||||
} catch (final CommandSyntaxException ignored) {
|
||||
}
|
||||
+ }
|
||||
+ // Paper end
|
||||
futures[i++] = future;
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: stonar96 <minecraft.stonar96@gmail.com>
|
||||
Date: Sun, 12 Sep 2021 00:14:21 +0200
|
||||
Subject: [PATCH] Don't respond to ServerboundCommandSuggestionPacket when
|
||||
tab-complete is disabled
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index ffd7d7e916a8217e4929f77c0252a6f4e3416cef..d9a44cc361b1e4a307d91e12a90986a2bbca550d 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -835,6 +835,11 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
}
|
||||
// Paper end
|
||||
// CraftBukkit end
|
||||
+ // Paper start - Don't suggest if tab-complete is disabled
|
||||
+ if (org.spigotmc.SpigotConfig.tabComplete < 0) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - Don't suggest if tab-complete is disabled
|
||||
// Paper start - async tab completion
|
||||
TAB_COMPLETE_EXECUTOR.execute(() -> {
|
||||
StringReader stringreader = new StringReader(packet.getCommand());
|
|
@ -1,71 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Bjarne Koll <lynxplay101@gmail.com>
|
||||
Date: Sat, 6 Nov 2021 23:15:20 +0100
|
||||
Subject: [PATCH] Fix setPatternColor on tropical fish bucket meta
|
||||
|
||||
Prior to this commit, the tropical fish bucket meta would set the body
|
||||
color to the previous metas pattern colour when updating the pattern
|
||||
colour.
|
||||
|
||||
This commit hence simply fixes this by using the proper body colour
|
||||
value when updating the pattern color.
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java
|
||||
index 581e0f4d68d6eb8eb04449586ffdba35e8b3ad2b..9a045a7793ec20334853a0e1c3529b31899214b3 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java
|
||||
@@ -107,7 +107,7 @@ class CraftMetaTropicalFishBucket extends CraftMetaItem implements TropicalFishB
|
||||
if (this.variant == null) {
|
||||
this.variant = 0;
|
||||
}
|
||||
- this.variant = CraftTropicalFish.getData(color, this.getPatternColor(), this.getPattern());
|
||||
+ this.variant = CraftTropicalFish.getData(color, this.getBodyColor(), this.getPattern()); // Paper - properly set tropical fish pattern color without mutating body color
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/src/test/java/io/papermc/paper/inventory/CraftMetaTropicalFishBucketTest.java b/src/test/java/io/papermc/paper/inventory/CraftMetaTropicalFishBucketTest.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..2e7f8ef88ae74c7cbfdb7f397951cbc8479a995f
|
||||
--- /dev/null
|
||||
+++ b/src/test/java/io/papermc/paper/inventory/CraftMetaTropicalFishBucketTest.java
|
||||
@@ -0,0 +1,40 @@
|
||||
+package io.papermc.paper.inventory;
|
||||
+
|
||||
+import org.bukkit.DyeColor;
|
||||
+import org.bukkit.Material;
|
||||
+import org.bukkit.entity.TropicalFish;
|
||||
+import org.bukkit.inventory.ItemStack;
|
||||
+import org.bukkit.inventory.meta.TropicalFishBucketMeta;
|
||||
+import org.bukkit.support.AbstractTestingBase;
|
||||
+import org.junit.Assert;
|
||||
+import org.junit.Test;
|
||||
+
|
||||
+public class CraftMetaTropicalFishBucketTest extends AbstractTestingBase {
|
||||
+
|
||||
+ @Test
|
||||
+ public void testAllCombinations() {
|
||||
+ final var rawMeta = new ItemStack(Material.TROPICAL_FISH_BUCKET).getItemMeta();
|
||||
+ Assert.assertTrue("Meta was not a tropical fish bucket", rawMeta instanceof TropicalFishBucketMeta);
|
||||
+
|
||||
+ final var meta = (TropicalFishBucketMeta) rawMeta;
|
||||
+
|
||||
+ for (final var bodyColor : DyeColor.values()) {
|
||||
+ for (final var pattern : TropicalFish.Pattern.values()) {
|
||||
+ for (final var patternColor : DyeColor.values()) {
|
||||
+ meta.setBodyColor(bodyColor);
|
||||
+ Assert.assertEquals("Body color did not match post body color!", bodyColor, meta.getBodyColor());
|
||||
+
|
||||
+ meta.setPattern(pattern);
|
||||
+ Assert.assertEquals("Pattern did not match post pattern!", pattern, meta.getPattern());
|
||||
+ Assert.assertEquals("Body color did not match post pattern!", bodyColor, meta.getBodyColor());
|
||||
+
|
||||
+ meta.setPatternColor(patternColor);
|
||||
+ Assert.assertEquals("Pattern did not match post pattern color!", pattern, meta.getPattern());
|
||||
+ Assert.assertEquals("Body color did not match post pattern color!", bodyColor, meta.getBodyColor());
|
||||
+ Assert.assertEquals("Pattern color did not match post pattern color!", patternColor, meta.getPatternColor());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+}
|
|
@ -1,19 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Nassim Jahnke <nassim@njahnke.dev>
|
||||
Date: Tue, 28 Sep 2021 09:47:47 +0200
|
||||
Subject: [PATCH] Ensure valid vehicle status
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
index 76ab004580f48fefdeec03b727f1589963c4684a..e7d299469652ebc24c2868d447916b5999bd140d 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -518,7 +518,7 @@ public class ServerPlayer extends Player {
|
||||
}
|
||||
}
|
||||
|
||||
- if (persistVehicle && entity1 != null && entity != this && entity.hasExactlyOnePlayerPassenger()) {
|
||||
+ if (persistVehicle && entity1 != null && entity != this && entity.hasExactlyOnePlayerPassenger() && !entity.isRemoved()) { // Paper
|
||||
// CraftBukkit end
|
||||
CompoundTag nbttagcompound2 = new CompoundTag();
|
||||
CompoundTag nbttagcompound3 = new CompoundTag();
|
|
@ -1,22 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Noah van der Aa <ndvdaa@gmail.com>
|
||||
Date: Mon, 30 Aug 2021 15:22:18 +0200
|
||||
Subject: [PATCH] Prevent softlocked end exit portal generation
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java b/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java
|
||||
index abd13cefd3d8409e1ef8ea70d92877f76f89da6c..230de1c71b0a6d6370df2fedb337cf0e332a7596 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java
|
||||
@@ -419,6 +419,11 @@ public class EndDragonFight {
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - Prevent "softlocked" exit portal generation
|
||||
+ if (this.portalLocation.getY() <= this.level.getMinBuildHeight()) {
|
||||
+ this.portalLocation = this.portalLocation.atY(this.level.getMinBuildHeight() + 1);
|
||||
+ }
|
||||
+ // Paper end
|
||||
endPodiumFeature.place(FeatureConfiguration.NONE, this.level, this.level.getChunkSource().getGenerator(), RandomSource.create(), this.portalLocation);
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shane Freeder <theboyetronic@gmail.com>
|
||||
Date: Tue, 7 Sep 2021 21:29:38 +0100
|
||||
Subject: [PATCH] Fix CocaoDecorator causing a crash when trying to generate
|
||||
without logs
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/levelgen/feature/treedecorators/CocoaDecorator.java b/src/main/java/net/minecraft/world/level/levelgen/feature/treedecorators/CocoaDecorator.java
|
||||
index 42b4b306ee89a9e422d234bdaa9b43b118f8bd0a..0fc355bd847749f7ce716b283dd571f143824795 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/levelgen/feature/treedecorators/CocoaDecorator.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/levelgen/feature/treedecorators/CocoaDecorator.java
|
||||
@@ -25,6 +25,7 @@ public class CocoaDecorator extends TreeDecorator {
|
||||
|
||||
@Override
|
||||
public void place(TreeDecorator.Context generator) {
|
||||
+ if (generator.logs().isEmpty()) return; // Paper
|
||||
RandomSource randomSource = generator.random();
|
||||
if (!(randomSource.nextFloat() >= this.probability)) {
|
||||
List<BlockPos> list = generator.logs();
|
|
@ -1,19 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Noah van der Aa <ndvdaa@gmail.com>
|
||||
Date: Tue, 14 Sep 2021 16:24:45 +0200
|
||||
Subject: [PATCH] Don't log debug logging being disabled
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java
|
||||
index 3ac48dafe2300ff4cf4591569fec9ce4916503cd..612c3169c3463d702b85975e1db79ae6e47d60d0 100644
|
||||
--- a/src/main/java/org/spigotmc/SpigotConfig.java
|
||||
+++ b/src/main/java/org/spigotmc/SpigotConfig.java
|
||||
@@ -382,7 +382,7 @@ public class SpigotConfig
|
||||
Bukkit.getLogger().info( "Debug logging is enabled" );
|
||||
} else
|
||||
{
|
||||
- Bukkit.getLogger().info( "Debug logging is disabled" );
|
||||
+ // Bukkit.getLogger().info( "Debug logging is disabled" ); // Paper - Don't log if debug logging isn't enabled.
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Sun, 11 Jul 2021 12:52:56 -0700
|
||||
Subject: [PATCH] fix various menus with empty level accesses
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/inventory/ContainerLevelAccess.java b/src/main/java/net/minecraft/world/inventory/ContainerLevelAccess.java
|
||||
index c96b04f045dda384cdee9254a1765ef97e5f7f03..f00a957a0f55e69f93e6d7dc80193304447c3dcb 100644
|
||||
--- a/src/main/java/net/minecraft/world/inventory/ContainerLevelAccess.java
|
||||
+++ b/src/main/java/net/minecraft/world/inventory/ContainerLevelAccess.java
|
||||
@@ -27,6 +27,12 @@ public interface ContainerLevelAccess {
|
||||
public <T> Optional<T> evaluate(BiFunction<Level, BlockPos, T> getter) {
|
||||
return Optional.empty();
|
||||
}
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public org.bukkit.Location getLocation() {
|
||||
+ return null;
|
||||
+ }
|
||||
+ // Paper end
|
||||
};
|
||||
|
||||
static ContainerLevelAccess create(final Level world, final BlockPos pos) {
|
|
@ -1,61 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: lexikiq <noellekiq@gmail.com>
|
||||
Date: Mon, 21 Jun 2021 23:21:58 -0400
|
||||
Subject: [PATCH] Preserve overstacked loot
|
||||
|
||||
Preserves overstacked items in loot tables, such as shulker box drops, to prevent the items
|
||||
from being deleted (as they'd overflow past the bounds of the container)-- or worse, causing
|
||||
chunk bans via the large amount of NBT created by unstacking the items.
|
||||
|
||||
Fixes GH-5140 and GH-4748.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/storage/loot/LootTable.java b/src/main/java/net/minecraft/world/level/storage/loot/LootTable.java
|
||||
index 1326539c88aabfbe1bbaf2905268abfa729d8167..3bc13092873609af9c6f412190dd989d39f1df23 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/storage/loot/LootTable.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/storage/loot/LootTable.java
|
||||
@@ -56,14 +56,22 @@ public class LootTable {
|
||||
this.compositeFunction = LootItemFunctions.compose(functions);
|
||||
}
|
||||
|
||||
+ // Paper start - preserve overstacked items
|
||||
+ @Deprecated
|
||||
public static Consumer<ItemStack> createStackSplitter(LootContext context, Consumer<ItemStack> consumer) {
|
||||
+ return createStackSplitter(context, consumer, null);
|
||||
+ }
|
||||
+ public static Consumer<ItemStack> createStackSplitter(LootContext context, Consumer<ItemStack> consumer, @org.jetbrains.annotations.Nullable net.minecraft.server.level.ServerLevel level) {
|
||||
+ boolean skipSplitter = level != null && !level.paperConfig().fixes.splitOverstackedLoot;
|
||||
+ // Paper end
|
||||
return (itemstack) -> {
|
||||
if (itemstack.isItemEnabled(context.getLevel().enabledFeatures())) {
|
||||
- if (itemstack.getCount() < itemstack.getMaxStackSize()) {
|
||||
+ if (skipSplitter || itemstack.getCount() < itemstack.getMaxStackSize()) { // Paper - preserve overstacked items
|
||||
consumer.accept(itemstack);
|
||||
} else {
|
||||
int i = itemstack.getCount();
|
||||
|
||||
+
|
||||
while (i > 0) {
|
||||
ItemStack itemstack1 = itemstack.copy();
|
||||
|
||||
@@ -97,7 +105,7 @@ public class LootTable {
|
||||
}
|
||||
|
||||
public void getRandomItems(LootContext context, Consumer<ItemStack> lootConsumer) {
|
||||
- this.getRandomItemsRaw(context, LootTable.createStackSplitter(context, lootConsumer));
|
||||
+ this.getRandomItemsRaw(context, LootTable.createStackSplitter(context, lootConsumer, context.getLevel())); // Paper - preserve overstacked items
|
||||
}
|
||||
|
||||
public ObjectArrayList<ItemStack> getRandomItems(LootContext context) {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/storage/loot/functions/SetContainerContents.java b/src/main/java/net/minecraft/world/level/storage/loot/functions/SetContainerContents.java
|
||||
index 880a0686519dc033b3c3b2bf0126f49af6fb48de..eddad9593bccd9e91fbb6d79fa2bdd766b004690 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/storage/loot/functions/SetContainerContents.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/storage/loot/functions/SetContainerContents.java
|
||||
@@ -46,7 +46,7 @@ public class SetContainerContents extends LootItemConditionalFunction {
|
||||
NonNullList<ItemStack> nonNullList = NonNullList.create();
|
||||
this.entries.forEach((entry) -> {
|
||||
entry.expand(context, (choice) -> {
|
||||
- choice.createItemStack(LootTable.createStackSplitter(context, nonNullList::add), context);
|
||||
+ choice.createItemStack(LootTable.createStackSplitter(context, nonNullList::add, context.getLevel()), context); // Paper - preserve overstacked items
|
||||
});
|
||||
});
|
||||
CompoundTag compoundTag = new CompoundTag();
|
|
@ -1,29 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
|
||||
Date: Mon, 21 Jun 2021 21:55:23 -0400
|
||||
Subject: [PATCH] Update head rotation in missing places
|
||||
|
||||
In certain areas the player's head rotation could be desynced when teleported/moved.
|
||||
This is because bukkit uses a separate head rotation field for yaw.
|
||||
This issue only applies to players.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index ef515ec320454fd6a54457ca419ddd42d0c3b5c8..7e19fff139c4a0151a2dafd63086362e41d0b098 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -1778,6 +1778,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
this.setXRot(Mth.clamp(pitch, -90.0F, 90.0F) % 360.0F);
|
||||
this.yRotO = this.getYRot();
|
||||
this.xRotO = this.getXRot();
|
||||
+ this.setYHeadRot(yaw); // Paper - Update head rotation
|
||||
}
|
||||
|
||||
public void absMoveTo(double x, double y, double z) {
|
||||
@@ -1816,6 +1817,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
this.setXRot(pitch);
|
||||
this.setOldPosAndRot();
|
||||
this.reapplyPosition();
|
||||
+ this.setYHeadRot(yaw); // Paper - Update head rotation
|
||||
}
|
||||
|
||||
public final void setOldPosAndRot() {
|
|
@ -1,18 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Mon, 13 Sep 2021 18:55:45 -0700
|
||||
Subject: [PATCH] prevent unintended light block manipulation
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/LightBlock.java b/src/main/java/net/minecraft/world/level/block/LightBlock.java
|
||||
index 6427302f53336fe035882d0a09e3e2d2d929d3b7..6eec44c484763f877aece6d9676ffc166bc10395 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/LightBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/LightBlock.java
|
||||
@@ -46,6 +46,7 @@ public class LightBlock extends Block implements SimpleWaterloggedBlock {
|
||||
@Override
|
||||
public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
||||
if (!world.isClientSide && player.canUseGameMasterBlocks()) {
|
||||
+ if (player.getItemInHand(hand).getItem() != Items.LIGHT || !player.mayInteract(world, pos) || !player.mayUseItemAt(pos, hit.getDirection(), player.getItemInHand(hand))) { return InteractionResult.FAIL; } // Paper
|
||||
world.setBlock(pos, state.cycle(LEVEL), 2);
|
||||
return InteractionResult.SUCCESS;
|
||||
} else {
|
|
@ -1,19 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Mon, 4 Oct 2021 22:31:51 -0700
|
||||
Subject: [PATCH] Fix CraftCriteria defaults map
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java
|
||||
index a8728102499ec8a0b4946bcc9b59c16193731f8c..d849ef9a51dc901c8045d63218b8ee5fa5c7ee7a 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java
|
||||
@@ -54,7 +54,7 @@ public final class CraftCriteria implements Criteria {
|
||||
}
|
||||
|
||||
static CraftCriteria getFromNMS(Objective objective) {
|
||||
- return CraftCriteria.DEFAULTS.get(objective.getCriteria().getName());
|
||||
+ return java.util.Objects.requireNonNullElseGet(CraftCriteria.DEFAULTS.get(objective.getCriteria().getName()), () -> new CraftCriteria(objective.getCriteria())); // Paper
|
||||
}
|
||||
|
||||
public static CraftCriteria getFromBukkit(String name) {
|
|
@ -1,405 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Wed, 6 Oct 2021 20:50:48 -0700
|
||||
Subject: [PATCH] Fix upstreams block state factories
|
||||
|
||||
Sometimes, blocks are changed and then logic is called before the associated
|
||||
tile entity is removed. When this happens, the factories were relying on the
|
||||
block at the position, not the tile entity. This change prioritizes using the
|
||||
tile entity type to determine the block state factory and falls back on
|
||||
the material type of the block at that location.
|
||||
|
||||
== AT ==
|
||||
public net.minecraft.world.level.block.entity.BlockEntityType validBlocks
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
||||
index e5e10c30fa9020e8dbbad708ef262eb6e1d559a6..9b1e2af71728c1a0842c690cee01161342dc35f1 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
||||
@@ -250,7 +250,7 @@ public abstract class BlockEntity {
|
||||
// Paper end
|
||||
if (this.level == null) return null;
|
||||
org.bukkit.block.Block block = this.level.getWorld().getBlockAt(this.worldPosition.getX(), this.worldPosition.getY(), this.worldPosition.getZ());
|
||||
- if (block.getType() == org.bukkit.Material.AIR) return null;
|
||||
+ // if (block.getType() == org.bukkit.Material.AIR) return null; // Paper - actually get the tile entity if it still exists
|
||||
org.bukkit.block.BlockState state = block.getState(useSnapshot); // Paper
|
||||
if (state instanceof InventoryHolder) return (InventoryHolder) state;
|
||||
return null;
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
|
||||
index 87c25170fbe8b0591d452612496ee1a627138de7..a2894f02ceb7c58f6eafe055e1ff47b197b100f2 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
|
||||
@@ -6,7 +6,7 @@ import org.bukkit.World;
|
||||
import org.bukkit.block.TileState;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
|
||||
-public class CraftBlockEntityState<T extends BlockEntity> extends CraftBlockState implements TileState {
|
||||
+public abstract class CraftBlockEntityState<T extends BlockEntity> extends CraftBlockState implements TileState { // Paper - revert upstream's revert of the block state changes
|
||||
|
||||
private final T tileEntity;
|
||||
private final T snapshot;
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
|
||||
index 812eb8c75bf5308a3a0771a1faabdb22a3d80c28..d3f66bc8c9fa18322bd025fac2793456a5200717 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
|
||||
@@ -19,6 +19,7 @@ import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BellBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlastFurnaceBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
+import net.minecraft.world.level.block.entity.BlockEntityType; // Paper
|
||||
import net.minecraft.world.level.block.entity.BrewingStandBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.CampfireBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.ChestBlockEntity;
|
||||
@@ -110,217 +111,59 @@ public final class CraftBlockStates {
|
||||
private static final BlockStateFactory<?> DEFAULT_FACTORY = new BlockStateFactory<CraftBlockState>(CraftBlockState.class) {
|
||||
@Override
|
||||
public CraftBlockState createBlockState(World world, BlockPos blockPosition, net.minecraft.world.level.block.state.BlockState blockData, BlockEntity tileEntity) {
|
||||
- // SPIGOT-6754, SPIGOT-6817: Restore previous behaviour for tile entities with removed blocks (loot generation post-destroy)
|
||||
- if (tileEntity != null) {
|
||||
- // block with unhandled TileEntity:
|
||||
- return new CraftBlockEntityState<>(world, tileEntity);
|
||||
- }
|
||||
+ // Paper - revert upstream's revert of the block state changes. Block entities that have already had the block type set to AIR are still valid, upstream decided to ignore them
|
||||
Preconditions.checkState(tileEntity == null, "Unexpected BlockState for %s", CraftMagicNumbers.getMaterial(blockData.getBlock()));
|
||||
return new CraftBlockState(world, blockPosition, blockData);
|
||||
}
|
||||
};
|
||||
+ // Paper start
|
||||
+ private static final Map<BlockEntityType<?>, BlockStateFactory<?>> FACTORIES_BY_BLOCK_ENTITY_TYPE = new HashMap<>();
|
||||
+ private static void register(BlockEntityType<?> type, BlockStateFactory<?> factory) {
|
||||
+ FACTORIES_BY_BLOCK_ENTITY_TYPE.put(type, factory);
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
static {
|
||||
- register(
|
||||
- Arrays.asList(
|
||||
- Material.ACACIA_SIGN,
|
||||
- Material.ACACIA_WALL_SIGN,
|
||||
- Material.BAMBOO_SIGN,
|
||||
- Material.BAMBOO_WALL_SIGN,
|
||||
- Material.BIRCH_SIGN,
|
||||
- Material.BIRCH_WALL_SIGN,
|
||||
- Material.CRIMSON_SIGN,
|
||||
- Material.CRIMSON_WALL_SIGN,
|
||||
- Material.DARK_OAK_SIGN,
|
||||
- Material.DARK_OAK_WALL_SIGN,
|
||||
- Material.JUNGLE_SIGN,
|
||||
- Material.JUNGLE_WALL_SIGN,
|
||||
- Material.MANGROVE_SIGN,
|
||||
- Material.MANGROVE_WALL_SIGN,
|
||||
- Material.OAK_SIGN,
|
||||
- Material.OAK_WALL_SIGN,
|
||||
- Material.SPRUCE_SIGN,
|
||||
- Material.SPRUCE_WALL_SIGN,
|
||||
- Material.WARPED_SIGN,
|
||||
- Material.WARPED_WALL_SIGN
|
||||
- ), CraftSign.class, CraftSign::new, SignBlockEntity::new
|
||||
- );
|
||||
-
|
||||
- register(
|
||||
- Arrays.asList(
|
||||
- Material.ACACIA_HANGING_SIGN,
|
||||
- Material.ACACIA_WALL_HANGING_SIGN,
|
||||
- Material.BAMBOO_HANGING_SIGN,
|
||||
- Material.BAMBOO_WALL_HANGING_SIGN,
|
||||
- Material.BIRCH_HANGING_SIGN,
|
||||
- Material.BIRCH_WALL_HANGING_SIGN,
|
||||
- Material.CRIMSON_HANGING_SIGN,
|
||||
- Material.CRIMSON_WALL_HANGING_SIGN,
|
||||
- Material.DARK_OAK_HANGING_SIGN,
|
||||
- Material.DARK_OAK_WALL_HANGING_SIGN,
|
||||
- Material.JUNGLE_HANGING_SIGN,
|
||||
- Material.JUNGLE_WALL_HANGING_SIGN,
|
||||
- Material.MANGROVE_HANGING_SIGN,
|
||||
- Material.MANGROVE_WALL_HANGING_SIGN,
|
||||
- Material.OAK_HANGING_SIGN,
|
||||
- Material.OAK_WALL_HANGING_SIGN,
|
||||
- Material.SPRUCE_HANGING_SIGN,
|
||||
- Material.SPRUCE_WALL_HANGING_SIGN,
|
||||
- Material.WARPED_HANGING_SIGN,
|
||||
- Material.WARPED_WALL_HANGING_SIGN
|
||||
- ), CraftHangingSign.class, CraftHangingSign::new, HangingSignBlockEntity::new
|
||||
- );
|
||||
-
|
||||
- register(
|
||||
- Arrays.asList(
|
||||
- Material.CREEPER_HEAD,
|
||||
- Material.CREEPER_WALL_HEAD,
|
||||
- Material.DRAGON_HEAD,
|
||||
- Material.DRAGON_WALL_HEAD,
|
||||
- Material.PIGLIN_HEAD,
|
||||
- Material.PIGLIN_WALL_HEAD,
|
||||
- Material.PLAYER_HEAD,
|
||||
- Material.PLAYER_WALL_HEAD,
|
||||
- Material.SKELETON_SKULL,
|
||||
- Material.SKELETON_WALL_SKULL,
|
||||
- Material.WITHER_SKELETON_SKULL,
|
||||
- Material.WITHER_SKELETON_WALL_SKULL,
|
||||
- Material.ZOMBIE_HEAD,
|
||||
- Material.ZOMBIE_WALL_HEAD
|
||||
- ), CraftSkull.class, CraftSkull::new, SkullBlockEntity::new
|
||||
- );
|
||||
-
|
||||
- register(
|
||||
- Arrays.asList(
|
||||
- Material.COMMAND_BLOCK,
|
||||
- Material.REPEATING_COMMAND_BLOCK,
|
||||
- Material.CHAIN_COMMAND_BLOCK
|
||||
- ), CraftCommandBlock.class, CraftCommandBlock::new, CommandBlockEntity::new
|
||||
- );
|
||||
-
|
||||
- register(
|
||||
- Arrays.asList(
|
||||
- Material.BLACK_BANNER,
|
||||
- Material.BLACK_WALL_BANNER,
|
||||
- Material.BLUE_BANNER,
|
||||
- Material.BLUE_WALL_BANNER,
|
||||
- Material.BROWN_BANNER,
|
||||
- Material.BROWN_WALL_BANNER,
|
||||
- Material.CYAN_BANNER,
|
||||
- Material.CYAN_WALL_BANNER,
|
||||
- Material.GRAY_BANNER,
|
||||
- Material.GRAY_WALL_BANNER,
|
||||
- Material.GREEN_BANNER,
|
||||
- Material.GREEN_WALL_BANNER,
|
||||
- Material.LIGHT_BLUE_BANNER,
|
||||
- Material.LIGHT_BLUE_WALL_BANNER,
|
||||
- Material.LIGHT_GRAY_BANNER,
|
||||
- Material.LIGHT_GRAY_WALL_BANNER,
|
||||
- Material.LIME_BANNER,
|
||||
- Material.LIME_WALL_BANNER,
|
||||
- Material.MAGENTA_BANNER,
|
||||
- Material.MAGENTA_WALL_BANNER,
|
||||
- Material.ORANGE_BANNER,
|
||||
- Material.ORANGE_WALL_BANNER,
|
||||
- Material.PINK_BANNER,
|
||||
- Material.PINK_WALL_BANNER,
|
||||
- Material.PURPLE_BANNER,
|
||||
- Material.PURPLE_WALL_BANNER,
|
||||
- Material.RED_BANNER,
|
||||
- Material.RED_WALL_BANNER,
|
||||
- Material.WHITE_BANNER,
|
||||
- Material.WHITE_WALL_BANNER,
|
||||
- Material.YELLOW_BANNER,
|
||||
- Material.YELLOW_WALL_BANNER
|
||||
- ), CraftBanner.class, CraftBanner::new, BannerBlockEntity::new
|
||||
- );
|
||||
-
|
||||
- register(
|
||||
- Arrays.asList(
|
||||
- Material.SHULKER_BOX,
|
||||
- Material.WHITE_SHULKER_BOX,
|
||||
- Material.ORANGE_SHULKER_BOX,
|
||||
- Material.MAGENTA_SHULKER_BOX,
|
||||
- Material.LIGHT_BLUE_SHULKER_BOX,
|
||||
- Material.YELLOW_SHULKER_BOX,
|
||||
- Material.LIME_SHULKER_BOX,
|
||||
- Material.PINK_SHULKER_BOX,
|
||||
- Material.GRAY_SHULKER_BOX,
|
||||
- Material.LIGHT_GRAY_SHULKER_BOX,
|
||||
- Material.CYAN_SHULKER_BOX,
|
||||
- Material.PURPLE_SHULKER_BOX,
|
||||
- Material.BLUE_SHULKER_BOX,
|
||||
- Material.BROWN_SHULKER_BOX,
|
||||
- Material.GREEN_SHULKER_BOX,
|
||||
- Material.RED_SHULKER_BOX,
|
||||
- Material.BLACK_SHULKER_BOX
|
||||
- ), CraftShulkerBox.class, CraftShulkerBox::new, ShulkerBoxBlockEntity::new
|
||||
- );
|
||||
-
|
||||
- register(
|
||||
- Arrays.asList(
|
||||
- Material.BLACK_BED,
|
||||
- Material.BLUE_BED,
|
||||
- Material.BROWN_BED,
|
||||
- Material.CYAN_BED,
|
||||
- Material.GRAY_BED,
|
||||
- Material.GREEN_BED,
|
||||
- Material.LIGHT_BLUE_BED,
|
||||
- Material.LIGHT_GRAY_BED,
|
||||
- Material.LIME_BED,
|
||||
- Material.MAGENTA_BED,
|
||||
- Material.ORANGE_BED,
|
||||
- Material.PINK_BED,
|
||||
- Material.PURPLE_BED,
|
||||
- Material.RED_BED,
|
||||
- Material.WHITE_BED,
|
||||
- Material.YELLOW_BED
|
||||
- ), CraftBed.class, CraftBed::new, BedBlockEntity::new
|
||||
- );
|
||||
-
|
||||
- register(
|
||||
- Arrays.asList(
|
||||
- Material.BEEHIVE,
|
||||
- Material.BEE_NEST
|
||||
- ), CraftBeehive.class, CraftBeehive::new, BeehiveBlockEntity::new
|
||||
- );
|
||||
-
|
||||
- register(
|
||||
- Arrays.asList(
|
||||
- Material.CAMPFIRE,
|
||||
- Material.SOUL_CAMPFIRE
|
||||
- ), CraftCampfire.class, CraftCampfire::new, CampfireBlockEntity::new
|
||||
- );
|
||||
-
|
||||
- register(Material.BARREL, CraftBarrel.class, CraftBarrel::new, BarrelBlockEntity::new);
|
||||
- register(Material.BEACON, CraftBeacon.class, CraftBeacon::new, BeaconBlockEntity::new);
|
||||
- register(Material.BELL, CraftBell.class, CraftBell::new, BellBlockEntity::new);
|
||||
- register(Material.BLAST_FURNACE, CraftBlastFurnace.class, CraftBlastFurnace::new, BlastFurnaceBlockEntity::new);
|
||||
- register(Material.BREWING_STAND, CraftBrewingStand.class, CraftBrewingStand::new, BrewingStandBlockEntity::new);
|
||||
- register(Material.CHEST, CraftChest.class, CraftChest::new, ChestBlockEntity::new);
|
||||
- register(Material.CHISELED_BOOKSHELF, CraftChiseledBookshelf.class, CraftChiseledBookshelf::new, ChiseledBookShelfBlockEntity::new);
|
||||
- register(Material.COMPARATOR, CraftComparator.class, CraftComparator::new, ComparatorBlockEntity::new);
|
||||
- register(Material.CONDUIT, CraftConduit.class, CraftConduit::new, ConduitBlockEntity::new);
|
||||
- register(Material.DAYLIGHT_DETECTOR, CraftDaylightDetector.class, CraftDaylightDetector::new, DaylightDetectorBlockEntity::new);
|
||||
- register(Material.DISPENSER, CraftDispenser.class, CraftDispenser::new, DispenserBlockEntity::new);
|
||||
- register(Material.DROPPER, CraftDropper.class, CraftDropper::new, DropperBlockEntity::new);
|
||||
- register(Material.ENCHANTING_TABLE, CraftEnchantingTable.class, CraftEnchantingTable::new, EnchantmentTableBlockEntity::new);
|
||||
- register(Material.ENDER_CHEST, CraftEnderChest.class, CraftEnderChest::new, EnderChestBlockEntity::new);
|
||||
- register(Material.END_GATEWAY, CraftEndGateway.class, CraftEndGateway::new, TheEndGatewayBlockEntity::new);
|
||||
- register(Material.END_PORTAL, CraftEndPortal.class, CraftEndPortal::new, TheEndPortalBlockEntity::new);
|
||||
- register(Material.FURNACE, CraftFurnaceFurnace.class, CraftFurnaceFurnace::new, FurnaceBlockEntity::new);
|
||||
- register(Material.HOPPER, CraftHopper.class, CraftHopper::new, HopperBlockEntity::new);
|
||||
- register(Material.JIGSAW, CraftJigsaw.class, CraftJigsaw::new, JigsawBlockEntity::new);
|
||||
- register(Material.JUKEBOX, CraftJukebox.class, CraftJukebox::new, JukeboxBlockEntity::new);
|
||||
- register(Material.LECTERN, CraftLectern.class, CraftLectern::new, LecternBlockEntity::new);
|
||||
- register(Material.MOVING_PISTON, CraftMovingPiston.class, CraftMovingPiston::new, PistonMovingBlockEntity::new);
|
||||
- register(Material.SCULK_CATALYST, CraftSculkCatalyst.class, CraftSculkCatalyst::new, SculkCatalystBlockEntity::new);
|
||||
- register(Material.SCULK_SENSOR, CraftSculkSensor.class, CraftSculkSensor::new, SculkSensorBlockEntity::new);
|
||||
- register(Material.SCULK_SHRIEKER, CraftSculkShrieker.class, CraftSculkShrieker::new, SculkShriekerBlockEntity::new);
|
||||
- register(Material.SMOKER, CraftSmoker.class, CraftSmoker::new, SmokerBlockEntity::new);
|
||||
- register(Material.SPAWNER, CraftCreatureSpawner.class, CraftCreatureSpawner::new, SpawnerBlockEntity::new);
|
||||
- register(Material.STRUCTURE_BLOCK, CraftStructureBlock.class, CraftStructureBlock::new, StructureBlockEntity::new);
|
||||
- register(Material.TRAPPED_CHEST, CraftChest.class, CraftChest::new, TrappedChestBlockEntity::new);
|
||||
+ // Paper start - simplify
|
||||
+ register(BlockEntityType.SIGN, CraftSign.class, CraftSign::new);
|
||||
+ register(BlockEntityType.HANGING_SIGN, CraftHangingSign.class, CraftHangingSign::new);
|
||||
+ register(BlockEntityType.SKULL, CraftSkull.class, CraftSkull::new);
|
||||
+ register(BlockEntityType.COMMAND_BLOCK, CraftCommandBlock.class, CraftCommandBlock::new);
|
||||
+ register(BlockEntityType.BANNER, CraftBanner.class, CraftBanner::new);
|
||||
+ register(BlockEntityType.SHULKER_BOX, CraftShulkerBox.class, CraftShulkerBox::new);
|
||||
+ register(BlockEntityType.BED, CraftBed.class, CraftBed::new);
|
||||
+ register(BlockEntityType.BEEHIVE, CraftBeehive.class, CraftBeehive::new);
|
||||
+ register(BlockEntityType.CAMPFIRE, CraftCampfire.class, CraftCampfire::new);
|
||||
+ register(BlockEntityType.BARREL, CraftBarrel.class, CraftBarrel::new);
|
||||
+ register(BlockEntityType.BEACON, CraftBeacon.class, CraftBeacon::new);
|
||||
+ register(BlockEntityType.BELL, CraftBell.class, CraftBell::new);
|
||||
+ register(BlockEntityType.BLAST_FURNACE, CraftBlastFurnace.class, CraftBlastFurnace::new);
|
||||
+ register(BlockEntityType.BREWING_STAND, CraftBrewingStand.class, CraftBrewingStand::new);
|
||||
+ register(BlockEntityType.CHEST, CraftChest.class, CraftChest::new);
|
||||
+ register(BlockEntityType.CHISELED_BOOKSHELF, CraftChiseledBookshelf.class, CraftChiseledBookshelf::new);
|
||||
+ register(BlockEntityType.COMPARATOR, CraftComparator.class, CraftComparator::new);
|
||||
+ register(BlockEntityType.CONDUIT, CraftConduit.class, CraftConduit::new);
|
||||
+ register(BlockEntityType.DAYLIGHT_DETECTOR, CraftDaylightDetector.class, CraftDaylightDetector::new);
|
||||
+ register(BlockEntityType.DISPENSER, CraftDispenser.class, CraftDispenser::new);
|
||||
+ register(BlockEntityType.DROPPER, CraftDropper.class, CraftDropper::new);
|
||||
+ register(BlockEntityType.ENCHANTING_TABLE, CraftEnchantingTable.class, CraftEnchantingTable::new);
|
||||
+ register(BlockEntityType.ENDER_CHEST, CraftEnderChest.class, CraftEnderChest::new);
|
||||
+ register(BlockEntityType.END_GATEWAY, CraftEndGateway.class, CraftEndGateway::new);
|
||||
+ register(BlockEntityType.END_PORTAL, CraftEndPortal.class, CraftEndPortal::new);
|
||||
+ register(BlockEntityType.FURNACE, CraftFurnaceFurnace.class, CraftFurnaceFurnace::new);
|
||||
+ register(BlockEntityType.HOPPER, CraftHopper.class, CraftHopper::new);
|
||||
+ register(BlockEntityType.JIGSAW, CraftJigsaw.class, CraftJigsaw::new);
|
||||
+ register(BlockEntityType.JUKEBOX, CraftJukebox.class, CraftJukebox::new);
|
||||
+ register(BlockEntityType.LECTERN, CraftLectern.class, CraftLectern::new);
|
||||
+ register(BlockEntityType.PISTON, CraftMovingPiston.class, CraftMovingPiston::new);
|
||||
+ register(BlockEntityType.SCULK_CATALYST, CraftSculkCatalyst.class, CraftSculkCatalyst::new);
|
||||
+ register(BlockEntityType.SCULK_SENSOR, CraftSculkSensor.class, CraftSculkSensor::new);
|
||||
+ register(BlockEntityType.SCULK_SHRIEKER, CraftSculkShrieker.class, CraftSculkShrieker::new);
|
||||
+ register(BlockEntityType.SMOKER, CraftSmoker.class, CraftSmoker::new);
|
||||
+ register(BlockEntityType.MOB_SPAWNER, CraftCreatureSpawner.class, CraftCreatureSpawner::new);
|
||||
+ register(BlockEntityType.STRUCTURE_BLOCK, CraftStructureBlock.class, CraftStructureBlock::new);
|
||||
+ register(BlockEntityType.TRAPPED_CHEST, CraftChest.class, CraftChest::new);
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
private static void register(Material blockType, BlockStateFactory<?> factory) {
|
||||
@@ -328,30 +171,33 @@ public final class CraftBlockStates {
|
||||
}
|
||||
|
||||
private static <T extends BlockEntity, B extends CraftBlockEntityState<T>> void register(
|
||||
- Material blockType,
|
||||
- Class<B> blockStateType,
|
||||
- BiFunction<World, T, B> blockStateConstructor,
|
||||
- BiFunction<BlockPos, net.minecraft.world.level.block.state.BlockState, T> tileEntityConstructor
|
||||
- ) {
|
||||
- CraftBlockStates.register(Collections.singletonList(blockType), blockStateType, blockStateConstructor, tileEntityConstructor);
|
||||
- }
|
||||
-
|
||||
- private static <T extends BlockEntity, B extends CraftBlockEntityState<T>> void register(
|
||||
- List<Material> blockTypes,
|
||||
+ net.minecraft.world.level.block.entity.BlockEntityType<? extends T> blockEntityType, // Paper
|
||||
Class<B> blockStateType,
|
||||
- BiFunction<World, T, B> blockStateConstructor,
|
||||
- BiFunction<BlockPos, net.minecraft.world.level.block.state.BlockState, T> tileEntityConstructor
|
||||
+ BiFunction<World, T, B> blockStateConstructor // Paper
|
||||
) {
|
||||
- BlockStateFactory<B> factory = new BlockEntityStateFactory<>(blockStateType, blockStateConstructor, tileEntityConstructor);
|
||||
- for (Material blockType : blockTypes) {
|
||||
- CraftBlockStates.register(blockType, factory);
|
||||
+ // Paper start
|
||||
+ BlockStateFactory<B> factory = new BlockEntityStateFactory<>(blockStateType, blockStateConstructor, blockEntityType::create);
|
||||
+ for (net.minecraft.world.level.block.Block block : blockEntityType.validBlocks) {
|
||||
+ CraftBlockStates.register(CraftMagicNumbers.getMaterial(block), factory);
|
||||
}
|
||||
+ CraftBlockStates.register(blockEntityType, factory);
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
private static BlockStateFactory<?> getFactory(Material material) {
|
||||
return CraftBlockStates.FACTORIES.getOrDefault(material, DEFAULT_FACTORY);
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ private static BlockStateFactory<?> getFactory(Material material, BlockEntityType<?> type) {
|
||||
+ if (type != null) {
|
||||
+ return CraftBlockStates.FACTORIES_BY_BLOCK_ENTITY_TYPE.getOrDefault(type, getFactory(material));
|
||||
+ } else {
|
||||
+ return getFactory(material);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public static Class<? extends CraftBlockState> getBlockStateType(Material material) {
|
||||
Preconditions.checkNotNull(material, "material is null");
|
||||
return CraftBlockStates.getFactory(material).blockStateType;
|
||||
@@ -367,6 +213,13 @@ public final class CraftBlockStates {
|
||||
return null;
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public static Class<? extends CraftBlockState> getBlockStateType(BlockEntityType<?> blockEntityType) {
|
||||
+ Preconditions.checkNotNull(blockEntityType, "blockEntityType is null");
|
||||
+ return CraftBlockStates.getFactory(null, blockEntityType).blockStateType;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public static BlockState getBlockState(Block block) {
|
||||
// Paper start
|
||||
return CraftBlockStates.getBlockState(block, true);
|
||||
@@ -424,7 +277,7 @@ public final class CraftBlockStates {
|
||||
if (world != null && tileEntity == null && CraftBlockStates.isTileEntityOptional(material)) {
|
||||
factory = CraftBlockStates.DEFAULT_FACTORY;
|
||||
} else {
|
||||
- factory = CraftBlockStates.getFactory(material);
|
||||
+ factory = CraftBlockStates.getFactory(material, tileEntity != null ? tileEntity.getType() : null); // Paper
|
||||
}
|
||||
return factory.createBlockState(world, blockPosition, blockData, tileEntity);
|
||||
}
|
||||
diff --git a/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java b/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java
|
||||
index a6dbd1919bce5b5092666372f4cc31d2e2190c42..04710abbfb50eaa6e3e4d96fca5f0355f940a452 100644
|
||||
--- a/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java
|
||||
+++ b/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java
|
||||
@@ -45,4 +45,11 @@ public class BlockStateTest extends AbstractTestingBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+ @Test
|
||||
+ public void testBlockEntityTypes() {
|
||||
+ for (var blockEntityType : BuiltInRegistries.BLOCK_ENTITY_TYPE) {
|
||||
+ org.junit.Assert.assertNotNull(CraftBlockStates.getBlockStateType(blockEntityType));
|
||||
+ }
|
||||
+ }
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Noah van der Aa <ndvdaa@gmail.com>
|
||||
Date: Tue, 5 Oct 2021 20:04:21 +0200
|
||||
Subject: [PATCH] Add config option for logging player ip addresses
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/network/protocol/PacketUtils.java b/src/main/java/net/minecraft/network/protocol/PacketUtils.java
|
||||
index d6f3869f5725c7f081efb7f486f74dbb99d4d005..8bc0cb9ad5bb4e76d962ff54305e2c08e279a17b 100644
|
||||
--- a/src/main/java/net/minecraft/network/protocol/PacketUtils.java
|
||||
+++ b/src/main/java/net/minecraft/network/protocol/PacketUtils.java
|
||||
@@ -51,10 +51,11 @@ public class PacketUtils {
|
||||
packet.handle(listener);
|
||||
} catch (Exception exception) {
|
||||
net.minecraft.network.Connection networkmanager = listener.getConnection();
|
||||
+ String playerIP = io.papermc.paper.configuration.GlobalConfiguration.get().logging.logPlayerIpAddresses ? String.valueOf(networkmanager.getRemoteAddress()) : "<ip address withheld>"; // Paper
|
||||
if (networkmanager.getPlayer() != null) {
|
||||
- LOGGER.error("Error whilst processing packet {} for {}[{}]", packet, networkmanager.getPlayer().getScoreboardName(), networkmanager.getRemoteAddress(), exception);
|
||||
+ LOGGER.error("Error whilst processing packet {} for {}[{}]", packet, networkmanager.getPlayer().getScoreboardName(), playerIP, exception); // Paper
|
||||
} else {
|
||||
- LOGGER.error("Error whilst processing packet {} for connection from {}", packet, networkmanager.getRemoteAddress(), exception);
|
||||
+ LOGGER.error("Error whilst processing packet {} for connection from {}", packet, playerIP, exception); // Paper
|
||||
}
|
||||
net.minecraft.network.chat.Component error = net.minecraft.network.chat.Component.literal("Packet processing error");
|
||||
networkmanager.send(new net.minecraft.network.protocol.game.ClientboundDisconnectPacket(error), net.minecraft.network.PacketSendListener.thenRun(() -> networkmanager.disconnect(error)));
|
||||
diff --git a/src/main/java/net/minecraft/server/network/LegacyQueryHandler.java b/src/main/java/net/minecraft/server/network/LegacyQueryHandler.java
|
||||
index 21393ad40095a4049e5b6871169b2db7aa92d13c..e6553b936dac1eb25a310d1a33acb0b1a5e646d2 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/LegacyQueryHandler.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/LegacyQueryHandler.java
|
||||
@@ -185,7 +185,7 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
|
||||
buf.release();
|
||||
this.buf = null;
|
||||
|
||||
- LOGGER.debug("Ping: (1.6) from {}", ctx.channel().remoteAddress());
|
||||
+ LOGGER.debug("Ping: (1.6) from {}", io.papermc.paper.configuration.GlobalConfiguration.get().logging.logPlayerIpAddresses ? ctx.channel().remoteAddress() : "<ip address withheld>"); // Paper
|
||||
|
||||
InetSocketAddress virtualHost = com.destroystokyo.paper.network.PaperNetworkClient.prepareVirtualHost(host, port);
|
||||
com.destroystokyo.paper.event.server.PaperServerListPingEvent event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
index 2985271132c9ae822dcb0d7a7e6f0c268d1736cc..cfdbcd024de6ad0f9d4e83b2f912b36ef3299458 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
@@ -206,7 +206,7 @@ public class ServerConnectionListener {
|
||||
throw new ReportedException(CrashReport.forThrowable(exception, "Ticking memory connection"));
|
||||
}
|
||||
|
||||
- ServerConnectionListener.LOGGER.warn("Failed to handle packet for {}", networkmanager.getRemoteAddress(), exception);
|
||||
+ ServerConnectionListener.LOGGER.warn("Failed to handle packet for {}", io.papermc.paper.configuration.GlobalConfiguration.get().logging.logPlayerIpAddresses ? String.valueOf(networkmanager.getRemoteAddress()) : "<ip address withheld>", exception); // Paper
|
||||
MutableComponent ichatmutablecomponent = Component.literal("Internal server error");
|
||||
|
||||
networkmanager.send(new ClientboundDisconnectPacket(ichatmutablecomponent), PacketSendListener.thenRun(() -> {
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
index acde45dcf605d9e2ce85002cd4d0d457c4afce38..b6dc42aef6458b0c09007bb0e13048b99531f8c0 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
@@ -206,7 +206,10 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
- return this.gameProfile != null ? this.gameProfile + " (" + this.connection.getRemoteAddress() + ")" : String.valueOf(this.connection.getRemoteAddress());
|
||||
+ // Paper start
|
||||
+ String ip = io.papermc.paper.configuration.GlobalConfiguration.get().logging.logPlayerIpAddresses ? String.valueOf(this.connection.getRemoteAddress()) : "<ip address withheld>";
|
||||
+ return this.gameProfile != null ? this.gameProfile + " (" + ip + ")" : String.valueOf(ip);
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
index c646570cfa543d56011733af984345f73dc63cb9..0693667c670bfd3431a9cef18fc1dfe39bcc7f9a 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
@@ -240,7 +240,7 @@ public abstract class PlayerList {
|
||||
String s1 = "local";
|
||||
|
||||
if (connection.getRemoteAddress() != null) {
|
||||
- s1 = connection.getRemoteAddress().toString();
|
||||
+ s1 = io.papermc.paper.configuration.GlobalConfiguration.get().logging.logPlayerIpAddresses ? connection.getRemoteAddress().toString() : "<ip address withheld>"; // Paper
|
||||
}
|
||||
|
||||
// Spigot start - spawn location event
|
|
@ -1,40 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Nassim Jahnke <nassim@njahnke.dev>
|
||||
Date: Tue, 31 Aug 2021 17:05:27 +0200
|
||||
Subject: [PATCH] Configurable feature seeds
|
||||
|
||||
Co-authored-by: Thonk <30448663+ExcessiveAmountsOfZombies@users.noreply.github.com>
|
||||
|
||||
diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java
|
||||
index 98171f6c8e23f6ef89b897e4b80e3afb2a1950a0..06bff37e4c1fddd3be6343049a66787c63fb420c 100644
|
||||
--- a/src/main/java/co/aikar/timings/TimingsExport.java
|
||||
+++ b/src/main/java/co/aikar/timings/TimingsExport.java
|
||||
@@ -287,7 +287,7 @@ public class TimingsExport extends Thread {
|
||||
JSONObject object = new JSONObject();
|
||||
for (String key : config.getKeys(false)) {
|
||||
String fullKey = (parentKey != null ? parentKey + "." + key : key);
|
||||
- if (fullKey.equals("database") || fullKey.equals("settings.bungeecord-addresses") || TimingsManager.hiddenConfigs.contains(fullKey) || key.startsWith("seed-") || key.equals("worldeditregentempworld")) {
|
||||
+ if (fullKey.equals("database") || fullKey.equals("settings.bungeecord-addresses") || TimingsManager.hiddenConfigs.contains(fullKey) || key.startsWith("seed-") || key.equals("worldeditregentempworld") || key.equals("feature-seeds")) {
|
||||
continue;
|
||||
}
|
||||
final Object val = config.get(key);
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
|
||||
index aecdbbcc688aad308f81f48d50773a9866ded7b2..9ef0937b7292ec118d2b65e9b098f5538410dbac 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
|
||||
@@ -431,7 +431,14 @@ public abstract class ChunkGenerator {
|
||||
return (String) optional.orElseGet(placedfeature::toString);
|
||||
};
|
||||
|
||||
- seededrandom.setFeatureSeed(i, l1, l);
|
||||
+ // Paper start - change populationSeed used in random
|
||||
+ long featurePopulationSeed = i;
|
||||
+ final long configFeatureSeed = generatoraccessseed.getMinecraftWorld().paperConfig().featureSeeds.features.getLong(placedfeature.feature());
|
||||
+ if (configFeatureSeed != -1) {
|
||||
+ featurePopulationSeed = seededrandom.setDecorationSeed(configFeatureSeed, blockposition.getX(), blockposition.getZ()); // See seededrandom.setDecorationSeed from above
|
||||
+ }
|
||||
+ seededrandom.setFeatureSeed(featurePopulationSeed, l1, l);
|
||||
+ // Paper end
|
||||
|
||||
try {
|
||||
generatoraccessseed.setCurrentlyGenerating(supplier1);
|
|
@ -1,23 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Fri, 10 Sep 2021 15:49:12 -0700
|
||||
Subject: [PATCH] VanillaCommandWrapper didnt account for entity senders
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
|
||||
index 8dca2ad7d25f740941187698d77819af8ebc2805..6df44aa60d2b41b95fe79ed4cf92a6d3369400ea 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
|
||||
@@ -64,8 +64,10 @@ public final class VanillaCommandWrapper extends BukkitCommand {
|
||||
}
|
||||
|
||||
public static CommandSourceStack getListener(CommandSender sender) {
|
||||
- if (sender instanceof Player) {
|
||||
- return ((CraftPlayer) sender).getHandle().createCommandSourceStack();
|
||||
+ // Paper start - account for other entity command senders
|
||||
+ if (sender instanceof org.bukkit.craftbukkit.entity.CraftEntity craftEntity) {
|
||||
+ return craftEntity.getHandle().createCommandSourceStack();
|
||||
+ // Paper end
|
||||
}
|
||||
if (sender instanceof BlockCommandSender) {
|
||||
return ((CraftBlockCommandSender) sender).getWrapper();
|
|
@ -1,79 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: egg82 <eggys82@gmail.com>
|
||||
Date: Sat, 11 Sep 2021 22:55:14 +0200
|
||||
Subject: [PATCH] Add root/admin user detection
|
||||
|
||||
This patch detects whether or not the server is currently executing as a privileged user and spits out a warning.
|
||||
The warning serves as a sort-of PSA for newer server admins who don't understand the risks of running as root.
|
||||
We've seen plenty of bad/malicious plugins hit markets, and there's been a few close-calls with exploits in the past.
|
||||
Hopefully this helps mitigate some potential damage to servers, even if it is just a warning.
|
||||
|
||||
Co-authored-by: Noah van der Aa <ndvdaa@gmail.com>
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/util/ServerEnvironment.java b/src/main/java/io/papermc/paper/util/ServerEnvironment.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..6bd0afddbcc461149dfe9a5c7a86fff6ea13a5f1
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/util/ServerEnvironment.java
|
||||
@@ -0,0 +1,40 @@
|
||||
+package io.papermc.paper.util;
|
||||
+
|
||||
+import com.sun.security.auth.module.NTSystem;
|
||||
+import com.sun.security.auth.module.UnixSystem;
|
||||
+import org.apache.commons.lang.SystemUtils;
|
||||
+
|
||||
+import java.io.IOException;
|
||||
+import java.io.InputStream;
|
||||
+import java.util.Set;
|
||||
+
|
||||
+public class ServerEnvironment {
|
||||
+ private static final boolean RUNNING_AS_ROOT_OR_ADMIN;
|
||||
+ private static final String WINDOWS_HIGH_INTEGRITY_LEVEL = "S-1-16-12288";
|
||||
+
|
||||
+ static {
|
||||
+ if (SystemUtils.IS_OS_WINDOWS) {
|
||||
+ RUNNING_AS_ROOT_OR_ADMIN = Set.of(new NTSystem().getGroupIDs()).contains(WINDOWS_HIGH_INTEGRITY_LEVEL);
|
||||
+ } else {
|
||||
+ boolean isRunningAsRoot = false;
|
||||
+ if (new UnixSystem().getUid() == 0) {
|
||||
+ // Due to an OpenJDK bug (https://bugs.openjdk.java.net/browse/JDK-8274721), UnixSystem#getUid incorrectly
|
||||
+ // returns 0 when the user doesn't have a username. Because of this, we'll have to double-check if the user ID is
|
||||
+ // actually 0 by running the id -u command.
|
||||
+ try {
|
||||
+ Process process = new ProcessBuilder("id", "-u").start();
|
||||
+ process.waitFor();
|
||||
+ InputStream inputStream = process.getInputStream();
|
||||
+ isRunningAsRoot = new String(inputStream.readAllBytes()).trim().equals("0");
|
||||
+ } catch (InterruptedException | IOException ignored) {
|
||||
+ isRunningAsRoot = false;
|
||||
+ }
|
||||
+ }
|
||||
+ RUNNING_AS_ROOT_OR_ADMIN = isRunningAsRoot;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static boolean userIsRootOrAdmin() {
|
||||
+ return RUNNING_AS_ROOT_OR_ADMIN;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index 7dcffb49a982a8db00d66441a03e2951b1d29ff9..5b46cabf4f2e2a1f7feaad378dd98d64aeef8671 100644
|
||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -179,6 +179,16 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
DedicatedServer.LOGGER.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\"");
|
||||
}
|
||||
|
||||
+ // Paper start - detect running as root
|
||||
+ if (io.papermc.paper.util.ServerEnvironment.userIsRootOrAdmin()) {
|
||||
+ DedicatedServer.LOGGER.warn("****************************");
|
||||
+ DedicatedServer.LOGGER.warn("YOU ARE RUNNING THIS SERVER AS AN ADMINISTRATIVE OR ROOT USER. THIS IS NOT ADVISED.");
|
||||
+ DedicatedServer.LOGGER.warn("YOU ARE OPENING YOURSELF UP TO POTENTIAL RISKS WHEN DOING THIS.");
|
||||
+ DedicatedServer.LOGGER.warn("FOR MORE INFORMATION, SEE https://madelinemiller.dev/blog/root-minecraft-server/");
|
||||
+ DedicatedServer.LOGGER.warn("****************************");
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
DedicatedServer.LOGGER.info("Loading properties");
|
||||
DedicatedServerProperties dedicatedserverproperties = this.settings.getProperties();
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
|
||||
Date: Mon, 21 Jun 2021 22:12:53 -0400
|
||||
Subject: [PATCH] Always allow item changing in Fireball
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/projectile/Fireball.java b/src/main/java/net/minecraft/world/entity/projectile/Fireball.java
|
||||
index 838ba52969550f783d26e626267c556ab09b5f3e..7f4e3dfab421591151fda7ec39d9c00b464d62de 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/projectile/Fireball.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/projectile/Fireball.java
|
||||
@@ -28,7 +28,7 @@ public abstract class Fireball extends AbstractHurtingProjectile implements Item
|
||||
}
|
||||
|
||||
public void setItem(ItemStack stack) {
|
||||
- if (!stack.is(Items.FIRE_CHARGE) || stack.hasTag()) {
|
||||
+ if (true || !stack.is(Items.FIRE_CHARGE) || stack.hasTag()) { // Paper - always allow item changing
|
||||
this.getEntityData().set(Fireball.DATA_ITEM_STACK, (ItemStack) Util.make(stack.copy(), (itemstack1) -> {
|
||||
itemstack1.setCount(1);
|
||||
}));
|
|
@ -1,19 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: sulu5890 <sulu@sulu.me>
|
||||
Date: Sun, 24 Oct 2021 22:48:14 -0500
|
||||
Subject: [PATCH] don't attempt to teleport dead entities
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index 7e19fff139c4a0151a2dafd63086362e41d0b098..b284a9fd506f30256b8194603c12865086eebdaa 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -779,7 +779,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
// CraftBukkit start
|
||||
public void postTick() {
|
||||
// No clean way to break out of ticking once the entity has been copied to a new world, so instead we move the portalling later in the tick cycle
|
||||
- if (!(this instanceof ServerPlayer)) {
|
||||
+ if (!(this instanceof ServerPlayer) && this.isAlive()) { // Paper - don't attempt to teleport dead entities
|
||||
this.handleNetherPortal();
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Nassim Jahnke <nassim@njahnke.dev>
|
||||
Date: Thu, 25 Nov 2021 10:25:09 +0100
|
||||
Subject: [PATCH] Prevent excessive velocity through repeated crits
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
index b7cc5fb5798d703124e08afc850c24afb2d6ee69..f833d578009bb470855e7ccdc017567a2df8f580 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -2672,14 +2672,27 @@ public abstract class LivingEntity extends Entity {
|
||||
return this.hasEffect(MobEffects.JUMP) ? (double) (0.1F * (float) (this.getEffect(MobEffects.JUMP).getAmplifier() + 1)) : 0.0D;
|
||||
}
|
||||
|
||||
+ protected long lastJumpTime = 0L; // Paper
|
||||
protected void jumpFromGround() {
|
||||
double d0 = (double) this.getJumpPower() + this.getJumpBoostPower();
|
||||
Vec3 vec3d = this.getDeltaMovement();
|
||||
+ // Paper start
|
||||
+ long time = System.nanoTime();
|
||||
+ boolean canCrit = true;
|
||||
+ if (this instanceof net.minecraft.world.entity.player.Player) {
|
||||
+ canCrit = false;
|
||||
+ if (time - this.lastJumpTime > (long)(0.250e9)) {
|
||||
+ this.lastJumpTime = time;
|
||||
+ canCrit = true;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
this.setDeltaMovement(vec3d.x, d0, vec3d.z);
|
||||
if (this.isSprinting()) {
|
||||
float f = this.getYRot() * 0.017453292F;
|
||||
|
||||
+ if (canCrit) // Paper
|
||||
this.setDeltaMovement(this.getDeltaMovement().add((double) (-Mth.sin(f) * 0.2F), 0.0D, (double) (Mth.cos(f) * 0.2F)));
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Fri, 26 Nov 2021 15:09:58 -0800
|
||||
Subject: [PATCH] Remove client-side code using deprecated for removal
|
||||
AccessController
|
||||
|
||||
Fixes warnings on build
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java
|
||||
index a54c0a4d0849087229d08ccc89872c3603f2f426..5ef58831a857fd8aa4ac30147762dc17d773a53e 100644
|
||||
--- a/src/main/java/net/minecraft/Util.java
|
||||
+++ b/src/main/java/net/minecraft/Util.java
|
||||
@@ -892,17 +892,7 @@ public class Util {
|
||||
}
|
||||
|
||||
public void openUrl(URL url) {
|
||||
- try {
|
||||
- Process process = AccessController.doPrivileged((PrivilegedExceptionAction<Process>)(() -> {
|
||||
- return Runtime.getRuntime().exec(this.getOpenUrlArguments(url));
|
||||
- }));
|
||||
- process.getInputStream().close();
|
||||
- process.getErrorStream().close();
|
||||
- process.getOutputStream().close();
|
||||
- } catch (IOException | PrivilegedActionException var3) {
|
||||
- Util.LOGGER.error("Couldn't open url '{}'", url, var3);
|
||||
- }
|
||||
-
|
||||
+ throw new IllegalStateException("This method is not useful on dedicated servers."); // Paper
|
||||
}
|
||||
|
||||
public void openUri(URI uri) {
|
|
@ -1,53 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Mon, 23 Aug 2021 04:50:05 -0700
|
||||
Subject: [PATCH] Always parse protochunk light sources unless it is marked as
|
||||
non-lit
|
||||
|
||||
Chunks not marked as lit will always go through the light engine,
|
||||
so they should always have their block sources parsed.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
index 313a3319c630a4f26f9c53255a0ac67f3c15e410..d4c4d37bcef14e392739d9aae9e20b7d69b05c12 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
@@ -327,16 +327,33 @@ public class ChunkSerializer {
|
||||
BelowZeroRetrogen belowzeroretrogen = protochunk.getBelowZeroRetrogen();
|
||||
boolean flag5 = chunkstatus.isOrAfter(ChunkStatus.LIGHT) || belowzeroretrogen != null && belowzeroretrogen.targetStatus().isOrAfter(ChunkStatus.LIGHT);
|
||||
|
||||
- if (!flag && flag5) {
|
||||
- Iterator iterator = BlockPos.betweenClosed(chunkPos.getMinBlockX(), world.getMinBuildHeight(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), world.getMaxBuildHeight() - 1, chunkPos.getMaxBlockZ()).iterator();
|
||||
+ if (!flag) { // Paper - fix incorrect parsing of blocks that emit light - it should always parse it, unless the chunk is marked as lit
|
||||
+ // Paper start - let's make sure the implementation isn't as slow as possible
|
||||
+ int offX = chunkPos.x << 4;
|
||||
+ int offZ = chunkPos.z << 4;
|
||||
+
|
||||
+ int minChunkSection = io.papermc.paper.util.WorldUtil.getMinSection(world);
|
||||
+ int maxChunkSection = io.papermc.paper.util.WorldUtil.getMaxSection(world);
|
||||
+
|
||||
+ LevelChunkSection[] sections = achunksection;
|
||||
+ for (int sectionY = minChunkSection; sectionY <= maxChunkSection; ++sectionY) {
|
||||
+ LevelChunkSection section = sections[sectionY - minChunkSection];
|
||||
+ if (section == null || section.hasOnlyAir()) {
|
||||
+ // no sources in empty sections
|
||||
+ continue;
|
||||
+ }
|
||||
+ int offY = sectionY << 4;
|
||||
|
||||
- while (iterator.hasNext()) {
|
||||
- BlockPos blockposition = (BlockPos) iterator.next();
|
||||
+ for (int index = 0; index < (16 * 16 * 16); ++index) {
|
||||
+ if (section.states.get(index).getLightEmission() <= 0) {
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
- if (((ChunkAccess) object1).getBlockState(blockposition).getLightEmission() != 0) {
|
||||
- protochunk.addLight(blockposition);
|
||||
+ // index = x | (z << 4) | (y << 8)
|
||||
+ protochunk.addLight(new BlockPos(offX | (index & 15), offY | (index >>> 8), offZ | ((index >>> 4) & 15)));
|
||||
}
|
||||
}
|
||||
+ // Paper end
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake <jake.m.potrebic@gmail.com>
|
||||
Date: Tue, 30 Nov 2021 12:01:56 -0800
|
||||
Subject: [PATCH] Fix removing recipes from RecipeIterator
|
||||
|
||||
== AT ==
|
||||
public net.minecraft.world.item.crafting.RecipeManager byName
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/RecipeIterator.java b/src/main/java/org/bukkit/craftbukkit/inventory/RecipeIterator.java
|
||||
index 24f336c89b548c5ba2a95372db0d7c83b5c4ec47..91895c639c33a1cafd2a35bab7b5fd83e558468d 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/RecipeIterator.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/RecipeIterator.java
|
||||
@@ -11,6 +11,7 @@ import org.bukkit.inventory.Recipe;
|
||||
public class RecipeIterator implements Iterator<Recipe> {
|
||||
private final Iterator<Map.Entry<RecipeType<?>, Object2ObjectLinkedOpenHashMap<ResourceLocation, net.minecraft.world.item.crafting.Recipe<?>>>> recipes;
|
||||
private Iterator<net.minecraft.world.item.crafting.Recipe<?>> current;
|
||||
+ private Recipe currentRecipe; // Paper - fix removing recipes
|
||||
|
||||
public RecipeIterator() {
|
||||
this.recipes = MinecraftServer.getServer().getRecipeManager().recipes.entrySet().iterator();
|
||||
@@ -34,10 +35,16 @@ public class RecipeIterator implements Iterator<Recipe> {
|
||||
public Recipe next() {
|
||||
if (this.current == null || !this.current.hasNext()) {
|
||||
this.current = this.recipes.next().getValue().values().iterator();
|
||||
- return this.next();
|
||||
+ // Paper start - fix removing recipes
|
||||
+ this.currentRecipe = this.next();
|
||||
+ return this.currentRecipe;
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
- return this.current.next().toBukkitRecipe();
|
||||
+ // Paper start - fix removing recipes
|
||||
+ this.currentRecipe = this.current.next().toBukkitRecipe();
|
||||
+ return this.currentRecipe;
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,6 +53,11 @@ public class RecipeIterator implements Iterator<Recipe> {
|
||||
throw new IllegalStateException("next() not yet called");
|
||||
}
|
||||
|
||||
+ // Paper start - fix removing recipes
|
||||
+ if (this.currentRecipe instanceof org.bukkit.Keyed keyed) {
|
||||
+ MinecraftServer.getServer().getRecipeManager().byName.remove(org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(keyed.getKey()));
|
||||
+ }
|
||||
+ // Paper end
|
||||
this.current.remove();
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Nassim Jahnke <nassim@njahnke.dev>
|
||||
Date: Wed, 1 Dec 2021 12:36:25 +0100
|
||||
Subject: [PATCH] Prevent sending oversized item data in equipment and metadata
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/network/syncher/EntityDataSerializers.java b/src/main/java/net/minecraft/network/syncher/EntityDataSerializers.java
|
||||
index 6c8505d5d989a2f528a1311a562cd59f4f16fc6d..c5e82ec453a431486a9aed1d6c2637e8b335d0d6 100644
|
||||
--- a/src/main/java/net/minecraft/network/syncher/EntityDataSerializers.java
|
||||
+++ b/src/main/java/net/minecraft/network/syncher/EntityDataSerializers.java
|
||||
@@ -39,7 +39,7 @@ public class EntityDataSerializers {
|
||||
public static final EntityDataSerializer<ItemStack> ITEM_STACK = new EntityDataSerializer<ItemStack>() {
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf, ItemStack value) {
|
||||
- buf.writeItem(value);
|
||||
+ buf.writeItem(net.minecraft.world.entity.LivingEntity.sanitizeItemStack(value, false)); // Paper - prevent oversized data
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
index 64683e218c9c43cfd30514800cfa8da42e37563e..d2fed123ed56eaf550ac2c7a3fcc1678a127bba3 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
@@ -314,7 +314,10 @@ public class ServerEntity {
|
||||
ItemStack itemstack = ((LivingEntity) this.entity).getItemBySlot(enumitemslot);
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
- list.add(Pair.of(enumitemslot, itemstack.copy()));
|
||||
+ // Paper start - prevent oversized data
|
||||
+ final ItemStack sanitized = LivingEntity.sanitizeItemStack(itemstack.copy(), false);
|
||||
+ list.add(Pair.of(enumitemslot, sanitized));
|
||||
+ // Paper end
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
index f833d578009bb470855e7ccdc017567a2df8f580..cd053c211c893f9b9c83204eccf9f40d7910ab7f 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -3158,7 +3158,10 @@ public abstract class LivingEntity extends Entity {
|
||||
equipmentChanges.forEach((enumitemslot, itemstack) -> {
|
||||
ItemStack itemstack1 = itemstack.copy();
|
||||
|
||||
- list.add(Pair.of(enumitemslot, itemstack1));
|
||||
+ // Paper start - prevent oversized data
|
||||
+ ItemStack toSend = sanitizeItemStack(itemstack1, true);
|
||||
+ list.add(Pair.of(enumitemslot, toSend));
|
||||
+ // Paper end
|
||||
switch (enumitemslot.getType()) {
|
||||
case HAND:
|
||||
this.setLastHandItem(enumitemslot, itemstack1);
|
||||
@@ -3171,6 +3174,34 @@ public abstract class LivingEntity extends Entity {
|
||||
((ServerLevel) this.level).getChunkSource().broadcast(this, new ClientboundSetEquipmentPacket(this.getId(), list));
|
||||
}
|
||||
|
||||
+ // Paper start - prevent oversized data
|
||||
+ public static ItemStack sanitizeItemStack(final ItemStack itemStack, final boolean copyItemStack) {
|
||||
+ if (itemStack.isEmpty() || !itemStack.hasTag()) {
|
||||
+ return itemStack;
|
||||
+ }
|
||||
+
|
||||
+ final ItemStack copy = copyItemStack ? itemStack.copy() : itemStack;
|
||||
+ final CompoundTag tag = copy.getTag();
|
||||
+ if (copy.is(Items.BUNDLE) && tag.get("Items") instanceof ListTag oldItems && !oldItems.isEmpty()) {
|
||||
+ // Bundles change their texture based on their fullness.
|
||||
+ org.bukkit.inventory.meta.BundleMeta bundleMeta = (org.bukkit.inventory.meta.BundleMeta) copy.asBukkitMirror().getItemMeta();
|
||||
+ int sizeUsed = 0;
|
||||
+ for (org.bukkit.inventory.ItemStack item : bundleMeta.getItems()) {
|
||||
+ int scale = 64 / item.getMaxStackSize();
|
||||
+ sizeUsed += scale * item.getAmount();
|
||||
+ }
|
||||
+ // Now we add a single fake item that uses the same amount of slots as all other items.
|
||||
+ ListTag items = new ListTag();
|
||||
+ items.add(new ItemStack(Items.PAPER, sizeUsed).save(new CompoundTag()));
|
||||
+ tag.put("Items", items);
|
||||
+ }
|
||||
+ if (tag.get("BlockEntityTag") instanceof CompoundTag blockEntityTag) {
|
||||
+ blockEntityTag.remove("Items");
|
||||
+ }
|
||||
+ return copy;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
private ItemStack getLastArmorItem(EquipmentSlot slot) {
|
||||
return (ItemStack) this.lastArmorItemStacks.get(slot.getIndex());
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Noah van der Aa <ndvdaa@gmail.com>
|
||||
Date: Tue, 3 Aug 2021 17:28:27 +0200
|
||||
Subject: [PATCH] Hide unnecessary itemmeta from clients
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
index d2fed123ed56eaf550ac2c7a3fcc1678a127bba3..77443525e68fab0211ff48af12020ff0399ba6d0 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
@@ -316,7 +316,7 @@ public class ServerEntity {
|
||||
if (!itemstack.isEmpty()) {
|
||||
// Paper start - prevent oversized data
|
||||
final ItemStack sanitized = LivingEntity.sanitizeItemStack(itemstack.copy(), false);
|
||||
- list.add(Pair.of(enumitemslot, sanitized));
|
||||
+ list.add(Pair.of(enumitemslot, ((LivingEntity) this.entity).stripMeta(sanitized, false))); // Paper - remove unnecessary item meta
|
||||
// Paper end
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index d9a44cc361b1e4a307d91e12a90986a2bbca550d..4246d9a24635024ed82fa103852b8337117a6b32 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -2683,8 +2683,8 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
// Refresh the current entity metadata
|
||||
entity.getEntityData().refresh(player);
|
||||
// SPIGOT-7136 - Allays
|
||||
- if (entity instanceof Allay) {
|
||||
- ServerGamePacketListenerImpl.this.send(new ClientboundSetEquipmentPacket(entity.getId(), Arrays.stream(net.minecraft.world.entity.EquipmentSlot.values()).map((slot) -> Pair.of(slot, ((LivingEntity) entity).getItemBySlot(slot).copy())).collect(Collectors.toList())));
|
||||
+ if (entity instanceof Allay allay) { // Paper
|
||||
+ ServerGamePacketListenerImpl.this.send(new ClientboundSetEquipmentPacket(entity.getId(), Arrays.stream(net.minecraft.world.entity.EquipmentSlot.values()).map((slot) -> Pair.of(slot, allay.stripMeta(allay.getItemBySlot(slot), true))).collect(Collectors.toList()))); // Paper - remove unnecessary item meta
|
||||
player.containerMenu.sendAllDataToRemote();
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
index cd053c211c893f9b9c83204eccf9f40d7910ab7f..c1185c4bd9151b08a9c7707e2eb8517bbeb47919 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -3160,7 +3160,7 @@ public abstract class LivingEntity extends Entity {
|
||||
|
||||
// Paper start - prevent oversized data
|
||||
ItemStack toSend = sanitizeItemStack(itemstack1, true);
|
||||
- list.add(Pair.of(enumitemslot, toSend));
|
||||
+ list.add(Pair.of(enumitemslot, stripMeta(toSend, toSend == itemstack1))); // Paper - hide unnecessary item meta
|
||||
// Paper end
|
||||
switch (enumitemslot.getType()) {
|
||||
case HAND:
|
||||
@@ -3174,6 +3174,70 @@ public abstract class LivingEntity extends Entity {
|
||||
((ServerLevel) this.level).getChunkSource().broadcast(this, new ClientboundSetEquipmentPacket(this.getId(), list));
|
||||
}
|
||||
|
||||
+ // Paper start - hide unnecessary item meta
|
||||
+ public ItemStack stripMeta(final ItemStack itemStack, final boolean copyItemStack) {
|
||||
+ if (itemStack.isEmpty() || (!itemStack.hasTag() && itemStack.getCount() < 2)) {
|
||||
+ return itemStack;
|
||||
+ }
|
||||
+
|
||||
+ final ItemStack copy = copyItemStack ? itemStack.copy() : itemStack;
|
||||
+ if (level.paperConfig().anticheat.obfuscation.items.hideDurability) {
|
||||
+ // Only show damage values for elytra's, since they show a different texture when broken.
|
||||
+ if (!copy.is(Items.ELYTRA) || copy.getDamageValue() < copy.getMaxDamage() - 1) {
|
||||
+ copy.setDamageValue(0);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ final CompoundTag tag = copy.getTag();
|
||||
+ if (level.paperConfig().anticheat.obfuscation.items.hideItemmeta) {
|
||||
+ // Some resource packs show different textures when there is more than one item. Since this shouldn't provide a big advantage,
|
||||
+ // we'll tell the client if there's one or (more than) two items.
|
||||
+ copy.setCount(copy.getCount() > 1 ? 2 : 1);
|
||||
+ // We can't just strip out display, leather helmets still use the display.color tag.
|
||||
+ if (tag != null) {
|
||||
+ if (tag.get("display") instanceof CompoundTag displayTag) {
|
||||
+ displayTag.remove("Lore");
|
||||
+ displayTag.remove("Name");
|
||||
+ }
|
||||
+
|
||||
+ if (tag.get("Enchantments") instanceof ListTag enchantmentsTag && !enchantmentsTag.isEmpty()) {
|
||||
+ // The client still renders items with the enchantment glow if the enchantments tag contains at least one (empty) child.
|
||||
+ ListTag enchantments = new ListTag();
|
||||
+ CompoundTag fakeEnchantment = new CompoundTag();
|
||||
+ // Soul speed boots generate client side particles.
|
||||
+ if (EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SOUL_SPEED, itemStack) > 0) {
|
||||
+ fakeEnchantment.putString("id", org.bukkit.enchantments.Enchantment.SOUL_SPEED.getKey().asString());
|
||||
+ fakeEnchantment.putInt("lvl", 1);
|
||||
+ }
|
||||
+ enchantments.add(fakeEnchantment);
|
||||
+ tag.put("Enchantments", enchantments);
|
||||
+ }
|
||||
+ tag.remove("AttributeModifiers");
|
||||
+
|
||||
+ // Books
|
||||
+ tag.remove("author");
|
||||
+ tag.remove("filtered_title");
|
||||
+ tag.remove("pages");
|
||||
+ tag.remove("filtered_pages");
|
||||
+ tag.remove("title");
|
||||
+ tag.remove("generation");
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (level.paperConfig().anticheat.obfuscation.items.hideItemmetaWithVisualEffects && tag != null) {
|
||||
+ // Lodestone compasses
|
||||
+ tag.remove("LodestonePos");
|
||||
+ if (tag.contains("LodestoneDimension")) {
|
||||
+ // The client shows the glint if either the position or the dimension is present, so we just wipe
|
||||
+ // the position and fake the dimension
|
||||
+ tag.putString("LodestoneDimension", "paper:paper");
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return copy;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
// Paper start - prevent oversized data
|
||||
public static ItemStack sanitizeItemStack(final ItemStack itemStack, final boolean copyItemStack) {
|
||||
if (itemStack.isEmpty() || !itemStack.hasTag()) {
|
|
@ -1,79 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Fri, 3 Dec 2021 17:09:24 -0800
|
||||
Subject: [PATCH] Fix Spigot growth modifiers
|
||||
|
||||
Fixes kelp modifier changing growth for other crops
|
||||
Also add growth modifiers for glow berries
|
||||
Also fix above-mentioned modifiers from having the reverse effect
|
||||
|
||||
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Co-authored-by: Noah van der Aa <ndvdaa@gmail.com>
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java b/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java
|
||||
index fd03fe8d596e7ec05f2a35df0b6587dd2534d5a7..fc76cd43655e0f4b8a8d87f90f0a48a8678ef16c 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java
|
||||
@@ -48,9 +48,17 @@ public class CaveVinesBlock extends GrowingPlantHeadBlock implements Bonemealabl
|
||||
|
||||
@Override
|
||||
protected BlockState getGrowIntoState(BlockState state, RandomSource random) {
|
||||
- return (BlockState) super.getGrowIntoState(state, random).setValue(CaveVinesBlock.BERRIES, random.nextFloat() < 0.11F);
|
||||
+ // Paper start
|
||||
+ return this.getGrowIntoState(state, random, null);
|
||||
}
|
||||
|
||||
+ @Override
|
||||
+ protected BlockState getGrowIntoState(BlockState state, RandomSource random, @javax.annotation.Nullable Level level) {
|
||||
+ final boolean value = random.nextFloat() < (level != null ? (0.11F * (level.spigotConfig.glowBerryModifier / 100.0F)) : 0.11F);
|
||||
+ return (BlockState) super.getGrowIntoState(state, random).setValue(CaveVinesBlock.BERRIES, value);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public ItemStack getCloneItemStack(BlockGetter world, BlockPos pos, BlockState state) {
|
||||
return new ItemStack(Items.GLOW_BERRIES);
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java b/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
|
||||
index 53b91cf8092b46dbf45afea9ccf439d565d1914a..3a1aa4e2405090ccebefb7f5944f36462929e221 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
|
||||
@@ -56,12 +56,18 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements
|
||||
BlockPos blockposition1 = pos.relative(this.growthDirection);
|
||||
|
||||
if (this.canGrowInto(world.getBlockState(blockposition1))) {
|
||||
- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, this.getGrowIntoState(state, world.random)); // CraftBukkit
|
||||
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, this.getGrowIntoState(state, world.random, world)); // CraftBukkit // Paper
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ protected BlockState getGrowIntoState(BlockState state, RandomSource random, @javax.annotation.Nullable Level level) {
|
||||
+ return this.getGrowIntoState(state, random);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
protected BlockState getGrowIntoState(BlockState state, RandomSource random) {
|
||||
return (BlockState) state.cycle(GrowingPlantHeadBlock.AGE);
|
||||
}
|
||||
diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java
|
||||
index 102b038e2566cba4f259a61e502ff0808c47234c..6bcc46795d1f78746192cc107c4a1f61580ec3c5 100644
|
||||
--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
|
||||
+++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
|
||||
@@ -106,6 +106,7 @@ public class SpigotWorldConfig
|
||||
public int twistingVinesModifier;
|
||||
public int weepingVinesModifier;
|
||||
public int caveVinesModifier;
|
||||
+ public int glowBerryModifier; // Paper
|
||||
private int getAndValidateGrowth(String crop)
|
||||
{
|
||||
int modifier = this.getInt( "growth." + crop.toLowerCase(java.util.Locale.ENGLISH) + "-modifier", 100 );
|
||||
@@ -139,6 +140,7 @@ public class SpigotWorldConfig
|
||||
this.twistingVinesModifier = this.getAndValidateGrowth( "TwistingVines" );
|
||||
this.weepingVinesModifier = this.getAndValidateGrowth( "WeepingVines" );
|
||||
this.caveVinesModifier = this.getAndValidateGrowth( "CaveVines" );
|
||||
+ this.glowBerryModifier = this.getAndValidateGrowth("GlowBerry"); // Paper
|
||||
}
|
||||
|
||||
public double itemMerge;
|
|
@ -1,18 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shane Freeder <theboyetronic@gmail.com>
|
||||
Date: Tue, 30 Nov 2021 05:30:35 +0000
|
||||
Subject: [PATCH] Prevent ContainerOpenersCounter openCount from going negative
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java b/src/main/java/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java
|
||||
index ba06184bad7e8ae55cb2d55f6196e8f990d2811d..3e4b3eecc788c564f81b7929bfab7d2fdb6e307d 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java
|
||||
@@ -64,6 +64,7 @@ public abstract class ContainerOpenersCounter {
|
||||
|
||||
public void decrementOpeners(Player player, Level world, BlockPos pos, BlockState state) {
|
||||
int oldPower = Math.max(0, Math.min(15, this.openCount)); // CraftBukkit - Get power before new viewer is added
|
||||
+ if (this.openCount == 0) return; // Paper
|
||||
int i = this.openCount--;
|
||||
|
||||
// CraftBukkit start - Call redstone event
|
|
@ -1,57 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: SamB440 <sam@islandearth.net>
|
||||
Date: Mon, 15 Nov 2021 18:10:10 +0000
|
||||
Subject: [PATCH] Add PlayerItemFrameChangeEvent
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
|
||||
index 880ca7cb3ebccc16949e24a0230b1f83887b653f..db60d29b051bad8d115b333e6c72287860a73123 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
|
||||
@@ -3,6 +3,7 @@ package net.minecraft.world.entity.decoration;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import java.util.OptionalInt;
|
||||
import javax.annotation.Nullable;
|
||||
+import io.papermc.paper.event.player.PlayerItemFrameChangeEvent; // Paper
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@@ -188,6 +189,13 @@ public class ItemFrame extends HangingEntity {
|
||||
return true;
|
||||
}
|
||||
// CraftBukkit end
|
||||
+ // Paper start - call PlayerItemFrameChangeEvent
|
||||
+ if (source.getEntity() instanceof Player player) {
|
||||
+ var event = new PlayerItemFrameChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.ItemFrame) this.getBukkitEntity(), this.getItem().asBukkitCopy(), PlayerItemFrameChangeEvent.ItemFrameChangeAction.REMOVE);
|
||||
+ if (!event.callEvent()) return true; // return true here because you aren't cancelling the damage, just the change
|
||||
+ this.setItem(ItemStack.fromBukkitCopy(event.getItemStack()), false);
|
||||
+ }
|
||||
+ // Paper end
|
||||
this.dropItem(source.getEntity(), false);
|
||||
this.playSound(this.getRemoveItemSound(), 1.0F, 1.0F);
|
||||
}
|
||||
@@ -452,13 +460,22 @@ public class ItemFrame extends HangingEntity {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
}
|
||||
-
|
||||
- this.setItem(itemstack);
|
||||
+ // Paper start - call PlayerItemFrameChangeEvent
|
||||
+ var event = new PlayerItemFrameChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.ItemFrame) this.getBukkitEntity(), itemstack.asBukkitCopy(), PlayerItemFrameChangeEvent.ItemFrameChangeAction.PLACE);
|
||||
+ if (!event.callEvent()) return InteractionResult.FAIL;
|
||||
+ this.setItem(ItemStack.fromBukkitCopy(event.getItemStack()));
|
||||
+ // this.setItem(itemstack);
|
||||
+ // Paper end
|
||||
if (!player.getAbilities().instabuild) {
|
||||
itemstack.shrink(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
+ // Paper start - call PlayerItemFrameChangeEvent
|
||||
+ var event = new PlayerItemFrameChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.ItemFrame) this.getBukkitEntity(), this.getItem().asBukkitCopy(), PlayerItemFrameChangeEvent.ItemFrameChangeAction.ROTATE);
|
||||
+ if (!event.callEvent()) return InteractionResult.FAIL;
|
||||
+ setItem(ItemStack.fromBukkitCopy(event.getItemStack()), false, false);
|
||||
+ // Paper end
|
||||
this.playSound(this.getRotateItemSound(), 1.0F, 1.0F);
|
||||
this.setRotation(this.getRotation() + 1);
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: SamB440 <sam@islandearth.net>
|
||||
Date: Wed, 17 Nov 2021 12:31:42 +0000
|
||||
Subject: [PATCH] Add player health update API
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
index dda1f3e9370110c08ed28fd404c8a246fa468db9..13855196d3c5405116bcd60518e8dd6b4fa0bf3f 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
@@ -2266,9 +2266,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
this.getHandle().maxHealthCache = getMaxHealth();
|
||||
}
|
||||
|
||||
- public void sendHealthUpdate() {
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public void sendHealthUpdate(final double health, final int foodLevel, final float saturationLevel) {
|
||||
// Paper start - cancellable death event
|
||||
- ClientboundSetHealthPacket packet = new ClientboundSetHealthPacket(this.getScaledHealth(), this.getHandle().getFoodData().getFoodLevel(), this.getHandle().getFoodData().getSaturationLevel());
|
||||
+ ClientboundSetHealthPacket packet = new ClientboundSetHealthPacket((float) health, foodLevel, saturationLevel);
|
||||
if (this.getHandle().queueHealthUpdatePacket) {
|
||||
this.getHandle().queuedHealthUpdatePacket = packet;
|
||||
} else {
|
||||
@@ -2276,7 +2278,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
-
|
||||
+
|
||||
+ @Override
|
||||
+ public void sendHealthUpdate() {
|
||||
+ this.sendHealthUpdate(this.getScaledHealth(), this.getHandle().getFoodData().getFoodLevel(), this.getHandle().getFoodData().getSaturationLevel());
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public void injectScaledMaxHealth(Collection<AttributeInstance> collection, boolean force) {
|
||||
if (!this.scaledHealth && !force) {
|
||||
return;
|
|
@ -1,57 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: stonar96 <minecraft.stonar96@gmail.com>
|
||||
Date: Sun, 17 Jan 2021 01:11:36 +0100
|
||||
Subject: [PATCH] Optimize HashMapPalette
|
||||
|
||||
HashMapPalette uses an instance of CrudeIncrementalIntIdentityHashBiMap
|
||||
internally. A Palette has a preset maximum size = 1 << bits.
|
||||
CrudeIncrementalIntIdentityHashBiMap has an initial size but is
|
||||
automatically resized. The CrudeIncrementalIntIdentityHashBiMap is created
|
||||
with the maximum size in the constructor of HashMapPalette, with the aim
|
||||
that it doesn't need to be resized anymore. However, there are two things
|
||||
that I think Mojang hasn't considered here:
|
||||
1) The CrudeIncrementalIntIdentityHashBiMap is resized, when its initial
|
||||
size is reached and not the next time, when a further object is added.
|
||||
2) HashMapPalette adds objects (unnecessarily) before checking if the
|
||||
initial size of CrudeIncrementalIntIdentityHashBiMap is reached.
|
||||
This means to actually avoid resize operations in
|
||||
CrudeIncrementalIntIdentityHashBiMap, one has to add 2 to the initial size
|
||||
or add 1 and check the size before adding objects. This commit implements
|
||||
the second approach. Note that this isn't only an optimization but also
|
||||
makes async reads of Palettes fail-safe. An async read while the
|
||||
CrudeIncrementalIntIdentityHashBiMap is resized is fatal and can even lead
|
||||
to corrupted data. This is also something that Anti-Xray is currently
|
||||
relying on.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java b/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java
|
||||
index a6d0008636f72ff6614eda4b1fa856798e6b46fd..50580313a1e224a9569e343ccf4aa0c00b906a30 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java
|
||||
@@ -19,7 +19,7 @@ public class HashMapPalette<T> implements Palette<T> {
|
||||
}
|
||||
|
||||
public HashMapPalette(IdMap<T> idList, int indexBits, PaletteResize<T> listener) {
|
||||
- this(idList, indexBits, listener, CrudeIncrementalIntIdentityHashBiMap.create(1 << indexBits));
|
||||
+ this(idList, indexBits, listener, CrudeIncrementalIntIdentityHashBiMap.create((1 << indexBits) + 1)); // Paper - Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap
|
||||
}
|
||||
|
||||
private HashMapPalette(IdMap<T> idList, int indexBits, PaletteResize<T> listener, CrudeIncrementalIntIdentityHashBiMap<T> map) {
|
||||
@@ -37,10 +37,16 @@ public class HashMapPalette<T> implements Palette<T> {
|
||||
public int idFor(T object) {
|
||||
int i = this.values.getId(object);
|
||||
if (i == -1) {
|
||||
- i = this.values.add(object);
|
||||
- if (i >= 1 << this.bits) {
|
||||
+ // Paper start - Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap and optimize
|
||||
+ // We use size() instead of the result from add(K)
|
||||
+ // This avoids adding another object unnecessarily
|
||||
+ // Without this change, + 2 would be required in the constructor
|
||||
+ if (this.values.size() >= 1 << this.bits) {
|
||||
i = this.resizeHandler.onResize(this.bits + 1, object);
|
||||
+ } else {
|
||||
+ i = this.values.add(object);
|
||||
}
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
return i;
|
|
@ -1,129 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MiniDigger <admin@benndorf.dev>
|
||||
Date: Wed, 29 Apr 2020 02:10:32 +0200
|
||||
Subject: [PATCH] Allow delegation to vanilla chunk gen
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index a6e5333c6e339a3a1853a6023a2e68c046811a4d..c4d5f3482881376bde77e826009a383804a2203d 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -2339,6 +2339,90 @@ public final class CraftServer implements Server {
|
||||
return new OldCraftChunkData(world.getMinHeight(), world.getMaxHeight(), handle.registryAccess().registryOrThrow(Registries.BIOME), world); // Paper - Anti-Xray - Add parameters
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ private static final List<net.minecraft.world.level.chunk.ChunkStatus> VANILLA_GEN_STATUSES = List.of(
|
||||
+ net.minecraft.world.level.chunk.ChunkStatus.EMPTY,
|
||||
+ net.minecraft.world.level.chunk.ChunkStatus.STRUCTURE_STARTS,
|
||||
+ net.minecraft.world.level.chunk.ChunkStatus.STRUCTURE_REFERENCES,
|
||||
+ net.minecraft.world.level.chunk.ChunkStatus.BIOMES,
|
||||
+ net.minecraft.world.level.chunk.ChunkStatus.NOISE,
|
||||
+ net.minecraft.world.level.chunk.ChunkStatus.SURFACE,
|
||||
+ net.minecraft.world.level.chunk.ChunkStatus.CARVERS,
|
||||
+ net.minecraft.world.level.chunk.ChunkStatus.LIQUID_CARVERS,
|
||||
+ net.minecraft.world.level.chunk.ChunkStatus.FEATURES,
|
||||
+ net.minecraft.world.level.chunk.ChunkStatus.LIGHT
|
||||
+ );
|
||||
+
|
||||
+ @Override
|
||||
+ @Deprecated(forRemoval = true)
|
||||
+ public ChunkGenerator.ChunkData createVanillaChunkData(World world, int x, int z) {
|
||||
+ // do bunch of vanilla shit
|
||||
+ final net.minecraft.server.level.ServerLevel serverLevel = ((CraftWorld) world).getHandle();
|
||||
+ final net.minecraft.core.Registry<net.minecraft.world.level.biome.Biome> biomeRegistry = serverLevel.getServer().registryAccess().registryOrThrow(net.minecraft.core.registries.Registries.BIOME);
|
||||
+ final net.minecraft.world.level.chunk.ProtoChunk protoChunk = new net.minecraft.world.level.chunk.ProtoChunk(
|
||||
+ new net.minecraft.world.level.ChunkPos(x, z),
|
||||
+ net.minecraft.world.level.chunk.UpgradeData.EMPTY,
|
||||
+ serverLevel,
|
||||
+ biomeRegistry,
|
||||
+ null
|
||||
+ );
|
||||
+
|
||||
+ final net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator;
|
||||
+ if (serverLevel.chunkSource.getGenerator() instanceof org.bukkit.craftbukkit.generator.CustomChunkGenerator bukkit) {
|
||||
+ chunkGenerator = bukkit.getDelegate();
|
||||
+ } else {
|
||||
+ chunkGenerator = serverLevel.chunkSource.getGenerator();
|
||||
+ }
|
||||
+
|
||||
+ final net.minecraft.world.level.ChunkPos chunkPos = new net.minecraft.world.level.ChunkPos(x, z);
|
||||
+ final net.minecraft.util.thread.ProcessorMailbox<Runnable> mailbox = net.minecraft.util.thread.ProcessorMailbox.create(
|
||||
+ net.minecraft.Util.backgroundExecutor(),
|
||||
+ "CraftServer#createVanillaChunkData(worldName='" + world.getName() + "', x='" + x + "', z='" + z + "')"
|
||||
+ );
|
||||
+ for (final net.minecraft.world.level.chunk.ChunkStatus chunkStatus : VANILLA_GEN_STATUSES) {
|
||||
+ final List<net.minecraft.world.level.chunk.ChunkAccess> chunks = Lists.newArrayList();
|
||||
+ final int statusRange = Math.max(1, chunkStatus.getRange());
|
||||
+
|
||||
+ for (int zz = chunkPos.z - statusRange; zz <= chunkPos.z + statusRange; ++zz) {
|
||||
+ for (int xx = chunkPos.x - statusRange; xx <= chunkPos.x + statusRange; ++xx) {
|
||||
+ if (xx == chunkPos.x && zz == chunkPos.z) {
|
||||
+ chunks.add(protoChunk);
|
||||
+ } else {
|
||||
+ final net.minecraft.core.Holder<net.minecraft.world.level.biome.Biome> biomeHolder = serverLevel.registryAccess().registryOrThrow(net.minecraft.core.registries.Registries.BIOME).getHolderOrThrow(net.minecraft.world.level.biome.Biomes.PLAINS);
|
||||
+ final net.minecraft.world.level.chunk.ChunkAccess chunk = new net.minecraft.world.level.chunk.EmptyLevelChunk(serverLevel, new net.minecraft.world.level.ChunkPos(xx, zz), biomeHolder);
|
||||
+ chunks.add(chunk);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ chunkStatus.generate(
|
||||
+ mailbox::tell,
|
||||
+ serverLevel,
|
||||
+ chunkGenerator,
|
||||
+ serverLevel.getStructureManager(),
|
||||
+ serverLevel.chunkSource.getLightEngine(),
|
||||
+ chunk -> {
|
||||
+ throw new UnsupportedOperationException("Not creating full chunks here");
|
||||
+ },
|
||||
+ chunks,
|
||||
+ true
|
||||
+ ).thenAccept(either -> {
|
||||
+ if (chunkStatus == net.minecraft.world.level.chunk.ChunkStatus.NOISE) {
|
||||
+ either.left().ifPresent(chunk -> net.minecraft.world.level.levelgen.Heightmap.primeHeightmaps(chunk, net.minecraft.world.level.chunk.ChunkStatus.POST_FEATURES));
|
||||
+ }
|
||||
+ }).join();
|
||||
+ }
|
||||
+
|
||||
+ // get empty object
|
||||
+ OldCraftChunkData data = (OldCraftChunkData) this.createChunkData(world);
|
||||
+ // copy over generated sections
|
||||
+ data.getLights().addAll(protoChunk.getLights().toList());
|
||||
+ data.setRawChunkData(protoChunk.getSections());
|
||||
+ // hooray!
|
||||
+ return data;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public BossBar createBossBar(String title, BarColor color, BarStyle style, BarFlag... flags) {
|
||||
return new CraftBossBar(title, color, style, flags);
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
|
||||
index 4a23d03757e1735b9ebb8c003adcc0374a7d672d..ce006e1d6c38e5b0bdb336c480fb9d291292f75c 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
|
||||
@@ -23,7 +23,7 @@ import org.bukkit.material.MaterialData;
|
||||
public final class OldCraftChunkData implements ChunkGenerator.ChunkData {
|
||||
private final int minHeight;
|
||||
private final int maxHeight;
|
||||
- private final LevelChunkSection[] sections;
|
||||
+ private LevelChunkSection[] sections; // Paper
|
||||
private final Registry<net.minecraft.world.level.biome.Biome> biomes;
|
||||
private Set<BlockPos> tiles;
|
||||
private final Set<BlockPos> lights = new HashSet<>();
|
||||
@@ -194,7 +194,13 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData {
|
||||
return this.tiles;
|
||||
}
|
||||
|
||||
- Set<BlockPos> getLights() {
|
||||
+ public Set<BlockPos> getLights() { // Paper
|
||||
return this.lights;
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ public void setRawChunkData(LevelChunkSection[] sections) {
|
||||
+ this.sections = sections;
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,170 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Thu, 2 Jul 2020 12:02:43 -0700
|
||||
Subject: [PATCH] Optimise collision checking in player move packet handling
|
||||
|
||||
Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 4246d9a24635024ed82fa103852b8337117a6b32..a036621f28f42151871c78337290e1158f1ca83f 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -643,7 +643,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
return;
|
||||
}
|
||||
|
||||
- boolean flag = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D));
|
||||
+ AABB oldBox = entity.getBoundingBox(); // Paper - copy from player movement packet
|
||||
|
||||
d6 = d3 - this.vehicleLastGoodX; // Paper - diff on change, used for checking large move vectors above
|
||||
d7 = d4 - this.vehicleLastGoodY - 1.0E-6D; // Paper - diff on change, used for checking large move vectors above
|
||||
@@ -651,6 +651,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
boolean flag1 = entity.verticalCollisionBelow;
|
||||
|
||||
entity.move(MoverType.PLAYER, new Vec3(d6, d7, d8));
|
||||
+ boolean didCollide = toX != entity.getX() || toY != entity.getY() || toZ != entity.getZ(); // Paper - needed here as the difference in Y can be reset - also note: this is only a guess at whether collisions took place, floating point errors can make this true when it shouldn't be...
|
||||
double d11 = d7;
|
||||
|
||||
d6 = d3 - entity.getX();
|
||||
@@ -664,16 +665,24 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
boolean flag2 = false;
|
||||
|
||||
if (d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
|
||||
- flag2 = true;
|
||||
+ flag2 = true; // Paper - diff on change, this should be moved wrongly
|
||||
ServerGamePacketListenerImpl.LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", new Object[]{entity.getName().getString(), this.player.getName().getString(), Math.sqrt(d10)});
|
||||
}
|
||||
Location curPos = this.getCraftPlayer().getLocation(); // Spigot
|
||||
|
||||
entity.absMoveTo(d3, d4, d5, f, f1);
|
||||
this.player.absMoveTo(d3, d4, d5, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
|
||||
- boolean flag3 = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D));
|
||||
|
||||
- if (flag && (flag2 || !flag3)) {
|
||||
+ // Paper start - optimise out extra getCubes
|
||||
+ boolean teleportBack = flag2; // violating this is always a fail
|
||||
+ if (!teleportBack) {
|
||||
+ // note: only call after setLocation, or else getBoundingBox is wrong
|
||||
+ AABB newBox = entity.getBoundingBox();
|
||||
+ if (didCollide || !oldBox.equals(newBox)) {
|
||||
+ teleportBack = this.hasNewCollision(worldserver, entity, oldBox, newBox);
|
||||
+ } // else: no collision at all detected, why do we care?
|
||||
+ }
|
||||
+ if (teleportBack) { // Paper end - optimise out extra getCubes
|
||||
entity.absMoveTo(d0, d1, d2, f, f1);
|
||||
this.player.absMoveTo(d0, d1, d2, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
|
||||
this.connection.send(new ClientboundMoveVehiclePacket(entity));
|
||||
@@ -759,7 +768,32 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
}
|
||||
|
||||
private boolean noBlocksAround(Entity entity) {
|
||||
- return entity.level.getBlockStates(entity.getBoundingBox().inflate(0.0625D).expandTowards(0.0D, -0.55D, 0.0D)).allMatch(BlockBehaviour.BlockStateBase::isAir);
|
||||
+ // Paper start - stop using streams, this is already a known fixed problem in Entity#move
|
||||
+ AABB box = entity.getBoundingBox().inflate(0.0625D).expandTowards(0.0D, -0.55D, 0.0D);
|
||||
+ int minX = Mth.floor(box.minX);
|
||||
+ int minY = Mth.floor(box.minY);
|
||||
+ int minZ = Mth.floor(box.minZ);
|
||||
+ int maxX = Mth.floor(box.maxX);
|
||||
+ int maxY = Mth.floor(box.maxY);
|
||||
+ int maxZ = Mth.floor(box.maxZ);
|
||||
+
|
||||
+ Level world = entity.level;
|
||||
+ BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
||||
+
|
||||
+ for (int y = minY; y <= maxY; ++y) {
|
||||
+ for (int z = minZ; z <= maxZ; ++z) {
|
||||
+ for (int x = minX; x <= maxX; ++x) {
|
||||
+ pos.set(x, y, z);
|
||||
+ BlockState type = world.getBlockStateIfLoaded(pos);
|
||||
+ if (type != null && !type.isAir()) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+ // Paper end - stop using streams, this is already a known fixed problem in Entity#move
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1339,7 +1373,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
}
|
||||
|
||||
if (this.awaitingPositionFromClient != null) {
|
||||
- if (this.tickCount - this.awaitingTeleportTime > 20) {
|
||||
+ if (false && this.tickCount - this.awaitingTeleportTime > 20) { // Paper - this will greatly screw with clients with > 1000ms RTT
|
||||
this.awaitingTeleportTime = this.tickCount;
|
||||
this.teleport(this.awaitingPositionFromClient.x, this.awaitingPositionFromClient.y, this.awaitingPositionFromClient.z, this.player.getYRot(), this.player.getXRot());
|
||||
}
|
||||
@@ -1433,7 +1467,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
}
|
||||
}
|
||||
|
||||
- AABB axisalignedbb = this.player.getBoundingBox();
|
||||
+ AABB axisalignedbb = this.player.getBoundingBox(); // Paper - diff on change, should be old AABB
|
||||
|
||||
d7 = d0 - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above
|
||||
d8 = d1 - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above
|
||||
@@ -1474,6 +1508,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
boolean flag1 = this.player.verticalCollisionBelow;
|
||||
|
||||
this.player.move(MoverType.PLAYER, new Vec3(d7, d8, d9));
|
||||
+ boolean didCollide = toX != this.player.getX() || toY != this.player.getY() || toZ != this.player.getZ(); // Paper - needed here as the difference in Y can be reset - also note: this is only a guess at whether collisions took place, floating point errors can make this true when it shouldn't be...
|
||||
this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
|
||||
// Paper start - prevent position desync
|
||||
if (this.awaitingPositionFromClient != null) {
|
||||
@@ -1493,12 +1528,23 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
boolean flag2 = false;
|
||||
|
||||
if (!this.player.isChangingDimension() && d11 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.isSleeping() && !this.player.gameMode.isCreative() && this.player.gameMode.getGameModeForPlayer() != GameType.SPECTATOR) { // Spigot
|
||||
- flag2 = true;
|
||||
+ flag2 = true; // Paper - diff on change, this should be moved wrongly
|
||||
ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!", this.player.getName().getString());
|
||||
}
|
||||
|
||||
this.player.absMoveTo(d0, d1, d2, f, f1);
|
||||
- if (!this.player.noPhysics && !this.player.isSleeping() && (flag2 && worldserver.noCollision(this.player, axisalignedbb) || this.isPlayerCollidingWithAnythingNew(worldserver, axisalignedbb))) {
|
||||
+ // Paper start - optimise out extra getCubes
|
||||
+ // Original for reference:
|
||||
+ // boolean teleportBack = flag2 && worldserver.getCubes(this.player, axisalignedbb) || (didCollide && this.a((IWorldReader) worldserver, axisalignedbb));
|
||||
+ boolean teleportBack = flag2; // violating this is always a fail
|
||||
+ if (!this.player.noPhysics && !this.player.isSleeping() && !teleportBack) {
|
||||
+ AABB newBox = this.player.getBoundingBox();
|
||||
+ if (didCollide || !axisalignedbb.equals(newBox)) {
|
||||
+ // note: only call after setLocation, or else getBoundingBox is wrong
|
||||
+ teleportBack = this.hasNewCollision(worldserver, this.player, axisalignedbb, newBox);
|
||||
+ } // else: no collision at all detected, why do we care?
|
||||
+ }
|
||||
+ if (!this.player.noPhysics && !this.player.isSleeping() && teleportBack) { // Paper end - optimise out extra getCubes
|
||||
this.internalTeleport(d3, d4, d5, f, f1, Collections.emptySet(), false); // CraftBukkit - SPIGOT-1807: Don't call teleport event, when the client thinks the player is falling, because the chunks are not loaded on the client yet.
|
||||
this.player.doCheckFallDamage(this.player.getY() - d6, packet.isOnGround());
|
||||
} else {
|
||||
@@ -1585,6 +1631,27 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - optimise out extra getCubes
|
||||
+ private boolean hasNewCollision(final ServerLevel world, final Entity entity, final AABB oldBox, final AABB newBox) {
|
||||
+ final List<AABB> collisions = io.papermc.paper.util.CachedLists.getTempCollisionList();
|
||||
+ try {
|
||||
+ io.papermc.paper.util.CollisionUtil.getCollisions(world, entity, newBox, collisions, false, true,
|
||||
+ true, false, null, null);
|
||||
+
|
||||
+ for (int i = 0, len = collisions.size(); i < len; ++i) {
|
||||
+ final AABB box = collisions.get(i);
|
||||
+ if (!io.papermc.paper.util.CollisionUtil.voxelShapeIntersect(box, oldBox)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+ } finally {
|
||||
+ io.papermc.paper.util.CachedLists.returnTempCollisionList(collisions);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - optimise out extra getCubes
|
||||
+
|
||||
private boolean isPlayerCollidingWithAnythingNew(LevelReader world, AABB box) {
|
||||
Iterable<VoxelShape> iterable = world.getCollisions(this.player, this.player.getBoundingBox().deflate(9.999999747378752E-6D));
|
||||
VoxelShape voxelshape = Shapes.create(box.deflate(9.999999747378752E-6D));
|
|
@ -1,43 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Thu, 9 Dec 2021 00:08:11 -0800
|
||||
Subject: [PATCH] Fix ChunkSnapshot#isSectionEmpty(int) and optimize
|
||||
PalettedContainer copying by not using codecs
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
|
||||
index 0fadc763fb482cf9f3b51ed44427029bec9f7f63..edb726d7db0c2e1c276458c997bbfed9b76b69d4 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
|
||||
@@ -298,13 +298,17 @@ public class CraftChunk implements Chunk {
|
||||
PalettedContainerRO<Holder<net.minecraft.world.level.biome.Biome>>[] biome = (includeBiome || includeBiomeTempRain) ? new PalettedContainer[cs.length] : null;
|
||||
|
||||
Registry<net.minecraft.world.level.biome.Biome> iregistry = this.worldServer.registryAccess().registryOrThrow(Registries.BIOME);
|
||||
- Codec<PalettedContainerRO<Holder<net.minecraft.world.level.biome.Biome>>> biomeCodec = PalettedContainer.codecRO(iregistry.asHolderIdMap(), iregistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getHolderOrThrow(Biomes.PLAINS));
|
||||
|
||||
for (int i = 0; i < cs.length; i++) {
|
||||
- CompoundTag data = new CompoundTag();
|
||||
|
||||
- data.put("block_states", ChunkSerializer.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, cs[i].getStates()).get().left().get());
|
||||
- sectionBlockIDs[i] = ChunkSerializer.BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, data.getCompound("block_states")).get().left().get();
|
||||
+ // Paper start
|
||||
+ sectionEmpty[i] = cs[i].hasOnlyAir(); // Paper - fix sectionEmpty array not being filled
|
||||
+ if (!sectionEmpty[i]) {
|
||||
+ sectionBlockIDs[i] = cs[i].getStates().copy(); // Paper - use copy instead of round tripping with codecs
|
||||
+ } else {
|
||||
+ sectionBlockIDs[i] = CraftChunk.emptyBlockIDs; // Paper - use cached instance for empty block sections
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
LevelLightEngine lightengine = chunk.level.getLightEngine();
|
||||
DataLayer skyLightArray = lightengine.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(x, i, z));
|
||||
@@ -323,8 +327,7 @@ public class CraftChunk implements Chunk {
|
||||
}
|
||||
|
||||
if (biome != null) {
|
||||
- data.put("biomes", biomeCodec.encodeStart(NbtOps.INSTANCE, cs[i].getBiomes()).get().left().get());
|
||||
- biome[i] = biomeCodec.parse(NbtOps.INSTANCE, data.getCompound("biomes")).get().left().get();
|
||||
+ biome[i] = ((PalettedContainer<Holder<net.minecraft.world.level.biome.Biome>>) cs[i].getBiomes()).copy(); // Paper - use copy instead of round tripping with codecs
|
||||
}
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: LemonCaramel <admin@caramel.moe>
|
||||
Date: Fri, 16 Jul 2021 00:39:03 +0900
|
||||
Subject: [PATCH] Add more Campfire API
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/entity/CampfireBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/CampfireBlockEntity.java
|
||||
index 0f7e765263a09a3f8b021bbac8d062c72ab3bdc0..01d8f1c985223eec83beb4a84a710d1bf8d70849 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/entity/CampfireBlockEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/entity/CampfireBlockEntity.java
|
||||
@@ -41,6 +41,7 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable {
|
||||
public final int[] cookingProgress;
|
||||
public final int[] cookingTime;
|
||||
private final RecipeManager.CachedCheck<Container, CampfireCookingRecipe> quickCheck;
|
||||
+ public final boolean[] stopCooking; // Paper
|
||||
|
||||
public CampfireBlockEntity(BlockPos pos, BlockState state) {
|
||||
super(BlockEntityType.CAMPFIRE, pos, state);
|
||||
@@ -48,6 +49,7 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable {
|
||||
this.cookingProgress = new int[4];
|
||||
this.cookingTime = new int[4];
|
||||
this.quickCheck = RecipeManager.createCheck(RecipeType.CAMPFIRE_COOKING);
|
||||
+ this.stopCooking = new boolean[4]; // Paper
|
||||
}
|
||||
|
||||
public static void cookTick(Level world, BlockPos pos, BlockState state, CampfireBlockEntity campfire) {
|
||||
@@ -58,7 +60,9 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable {
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
flag = true;
|
||||
+ if (!campfire.stopCooking[i]) { // Paper
|
||||
int j = campfire.cookingProgress[i]++;
|
||||
+ } // Paper
|
||||
|
||||
if (campfire.cookingProgress[i] >= campfire.cookingTime[i]) {
|
||||
SimpleContainer inventorysubcontainer = new SimpleContainer(new ItemStack[]{itemstack});
|
||||
@@ -167,6 +171,16 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable {
|
||||
System.arraycopy(aint, 0, this.cookingTime, 0, Math.min(this.cookingTime.length, aint.length));
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ if (nbt.contains("Paper.StopCooking", org.bukkit.craftbukkit.util.CraftMagicNumbers.NBT.TAG_BYTE_ARRAY)) {
|
||||
+ byte[] abyte = nbt.getByteArray("Paper.StopCooking");
|
||||
+ boolean[] cookingState = new boolean[4];
|
||||
+ for (int index = 0; index < abyte.length; index++) {
|
||||
+ cookingState[index] = abyte[index] == 1;
|
||||
+ }
|
||||
+ System.arraycopy(cookingState, 0, this.stopCooking, 0, Math.min(this.stopCooking.length, abyte.length));
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -175,6 +189,13 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable {
|
||||
ContainerHelper.saveAllItems(nbt, this.items, true);
|
||||
nbt.putIntArray("CookingTimes", this.cookingProgress);
|
||||
nbt.putIntArray("CookingTotalTimes", this.cookingTime);
|
||||
+ // Paper start
|
||||
+ byte[] cookingState = new byte[4];
|
||||
+ for (int index = 0; index < cookingState.length; index++) {
|
||||
+ cookingState[index] = (byte) (this.stopCooking[index] ? 1 : 0);
|
||||
+ }
|
||||
+ nbt.putByteArray("Paper.StopCooking", cookingState);
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftCampfire.java b/src/main/java/org/bukkit/craftbukkit/block/CraftCampfire.java
|
||||
index 391ff41951f51a5f9225bf4a9e28c0a4d064d8c9..eafba0532920a3162575dbe656e07734605590f5 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftCampfire.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftCampfire.java
|
||||
@@ -47,4 +47,40 @@ public class CraftCampfire extends CraftBlockEntityState<CampfireBlockEntity> im
|
||||
public void setCookTimeTotal(int index, int cookTimeTotal) {
|
||||
getSnapshot().cookingTime[index] = cookTimeTotal;
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public void stopCooking() {
|
||||
+ for (int i = 0; i < getSnapshot().stopCooking.length; ++i)
|
||||
+ this.stopCooking(i);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void startCooking() {
|
||||
+ for (int i = 0; i < getSnapshot().stopCooking.length; ++i)
|
||||
+ this.startCooking(i);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean stopCooking(int index) {
|
||||
+ org.apache.commons.lang.Validate.isTrue(-1 < index && index < 4, "Slot index must be between 0 (incl) to 3 (incl)");
|
||||
+ boolean previous = this.isCookingDisabled(index);
|
||||
+ getSnapshot().stopCooking[index] = true;
|
||||
+ return previous;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean startCooking(int index) {
|
||||
+ org.apache.commons.lang.Validate.isTrue(-1 < index && index < 4, "Slot index must be between 0 (incl) to 3 (incl)");
|
||||
+ boolean previous = this.isCookingDisabled(index);
|
||||
+ getSnapshot().stopCooking[index] = false;
|
||||
+ return previous;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isCookingDisabled(int index) {
|
||||
+ org.apache.commons.lang.Validate.isTrue(-1 < index && index < 4, "Slot index must be between 0 (incl) to 3 (incl)");
|
||||
+ return getSnapshot().stopCooking[index];
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sun, 19 Dec 2021 09:13:41 -0800
|
||||
Subject: [PATCH] Only write chunk data to disk if it serializes without
|
||||
throwing
|
||||
|
||||
This ensures at least a valid version of the chunk exists
|
||||
on disk, even if outdated
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
index 9bae47f99336c377beb72c4b50b7f01cb4db15da..dcfe090c269d4cbcc2eb1b6f85392848bb34656c 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
@@ -1004,6 +1004,9 @@ public class RegionFile implements AutoCloseable {
|
||||
}
|
||||
|
||||
}
|
||||
+
|
||||
+ public static final int MAX_CHUNK_SIZE = 500 * 1024 * 1024; // Paper - don't write garbage data to disk if writing serialization fails
|
||||
+
|
||||
// Paper end
|
||||
private class ChunkBuffer extends ByteArrayOutputStream {
|
||||
|
||||
@@ -1019,6 +1022,24 @@ public class RegionFile implements AutoCloseable {
|
||||
this.pos = chunkcoordintpair;
|
||||
}
|
||||
|
||||
+ // Paper start - don't write garbage data to disk if writing serialization fails
|
||||
+ @Override
|
||||
+ public void write(final int b) {
|
||||
+ if (this.count > MAX_CHUNK_SIZE) {
|
||||
+ throw new RegionFileStorage.RegionFileSizeException("Region file too large: " + this.count);
|
||||
+ }
|
||||
+ super.write(b);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void write(final byte[] b, final int off, final int len) {
|
||||
+ if (this.count + len > MAX_CHUNK_SIZE) {
|
||||
+ throw new RegionFileStorage.RegionFileSizeException("Region file too large: " + (this.count + len));
|
||||
+ }
|
||||
+ super.write(b, off, len);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public void close() throws IOException {
|
||||
ByteBuffer bytebuffer = ByteBuffer.wrap(this.buf, 0, this.count);
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
index d5b501bcb33a785ef0301bf6bb8d396b9d4fbb6a..bd502ca721de0cab438d995efa00ad0554c0d2fe 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
@@ -302,10 +302,17 @@ public class RegionFileStorage implements AutoCloseable {
|
||||
NbtIo.write(nbt, (DataOutput) dataoutputstream);
|
||||
regionfile.setStatus(pos.x, pos.z, ChunkSerializer.getStatus(nbt)); // Paper - cache status on disk
|
||||
regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
|
||||
+ dataoutputstream.close(); // Paper - only write if successful
|
||||
+ // Paper start - don't write garbage data to disk if writing serialization fails
|
||||
+ } catch (RegionFileSizeException e) {
|
||||
+ attempts = 5; // Don't retry
|
||||
+ regionfile.clear(pos);
|
||||
+ throw e;
|
||||
+ // Paper end - don't write garbage data to disk if writing serialization fails
|
||||
} catch (Throwable throwable) {
|
||||
if (dataoutputstream != null) {
|
||||
try {
|
||||
- dataoutputstream.close();
|
||||
+ //dataoutputstream.close(); // Paper - don't write garbage data to disk if writing serialization fails
|
||||
} catch (Throwable throwable1) {
|
||||
throwable.addSuppressed(throwable1);
|
||||
}
|
||||
@@ -313,10 +320,7 @@ public class RegionFileStorage implements AutoCloseable {
|
||||
|
||||
throw throwable;
|
||||
}
|
||||
-
|
||||
- if (dataoutputstream != null) {
|
||||
- dataoutputstream.close();
|
||||
- }
|
||||
+ // Paper - move into try block to only write if successfully serialized
|
||||
}
|
||||
// Paper start
|
||||
return;
|
||||
@@ -362,4 +366,13 @@ public class RegionFileStorage implements AutoCloseable {
|
||||
}
|
||||
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ public static final class RegionFileSizeException extends RuntimeException {
|
||||
+
|
||||
+ public RegionFileSizeException(String message) {
|
||||
+ super(message);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Nassim Jahnke <nassim@njahnke.dev>
|
||||
Date: Sun, 19 Dec 2021 21:11:20 +0100
|
||||
Subject: [PATCH] Fix tripwire state inconsistency
|
||||
|
||||
This patch prevents updating and re-setting the tripwire when being removed in certain conditions
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/TripWireBlock.java b/src/main/java/net/minecraft/world/level/block/TripWireBlock.java
|
||||
index 4e2fb4ee8e46b3c363992ff23e26f5a648c5f003..7f60175bf671d282c11e9084670d2bb900968255 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/TripWireBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/TripWireBlock.java
|
||||
@@ -74,7 +74,7 @@ public class TripWireBlock extends Block {
|
||||
@Override
|
||||
public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
if (!moved && !state.is(newState.getBlock())) {
|
||||
- this.updateSource(world, pos, (BlockState) state.setValue(TripWireBlock.POWERED, true));
|
||||
+ this.updateSource(world, pos, (BlockState) state.setValue(TripWireBlock.POWERED, true), true); // Paper - fix state inconsistency
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,12 @@ public class TripWireBlock extends Block {
|
||||
}
|
||||
|
||||
private void updateSource(Level world, BlockPos pos, BlockState state) {
|
||||
+ // Paper start - fix state inconsistency
|
||||
+ this.updateSource(world, pos, state, false);
|
||||
+ }
|
||||
+
|
||||
+ private void updateSource(Level world, BlockPos pos, BlockState state, boolean beingRemoved) {
|
||||
+ // Paper end
|
||||
Direction[] aenumdirection = new Direction[]{Direction.SOUTH, Direction.WEST};
|
||||
int i = aenumdirection.length;
|
||||
int j = 0;
|
||||
@@ -104,7 +110,7 @@ public class TripWireBlock extends Block {
|
||||
|
||||
if (iblockdata1.is((Block) this.hook)) {
|
||||
if (iblockdata1.getValue(TripWireHookBlock.FACING) == enumdirection.getOpposite()) {
|
||||
- this.hook.calculateState(world, blockposition1, iblockdata1, false, true, k, state);
|
||||
+ this.hook.calculateState(world, blockposition1, iblockdata1, false, true, k, state, beingRemoved); // Paper - fix state inconsistency
|
||||
}
|
||||
} else if (iblockdata1.is((Block) this)) {
|
||||
++k;
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java b/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java
|
||||
index 4a516828e5c6abd63511ee7c93fcff11203cf8d0..004dce26ff073f1de52a84cd425c4f60fdab5e50 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java
|
||||
@@ -108,6 +108,12 @@ public class TripWireHookBlock extends Block {
|
||||
}
|
||||
|
||||
public void calculateState(Level world, BlockPos pos, BlockState state, boolean beingRemoved, boolean flag1, int i, @Nullable BlockState iblockdata1) {
|
||||
+ // Paper start - fix tripwire inconsistency
|
||||
+ this.calculateState(world, pos, state, beingRemoved, flag1, i, iblockdata1, false);
|
||||
+ }
|
||||
+
|
||||
+ public void calculateState(Level world, BlockPos pos, BlockState state, boolean beingRemoved, boolean flag1, int i, @Nullable BlockState iblockdata1, boolean tripWireBeingRemoved) {
|
||||
+ // Paper end
|
||||
Direction enumdirection = (Direction) state.getValue(TripWireHookBlock.FACING);
|
||||
boolean flag2 = (Boolean) state.getValue(TripWireHookBlock.ATTACHED);
|
||||
boolean flag3 = (Boolean) state.getValue(TripWireHookBlock.POWERED);
|
||||
@@ -141,6 +147,7 @@ public class TripWireHookBlock extends Block {
|
||||
boolean flag7 = (Boolean) iblockdata2.getValue(TripWireBlock.POWERED);
|
||||
|
||||
flag5 |= flag6 && flag7;
|
||||
+ if (k != i || !tripWireBeingRemoved || !flag6) // Paper - don't update the tripwire again if being removed and not disarmed
|
||||
aiblockdata[k] = iblockdata2;
|
||||
if (k == i) {
|
||||
world.scheduleTick(pos, (Block) this, 10);
|
|
@ -1,39 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Sat, 4 Dec 2021 17:04:47 -0800
|
||||
Subject: [PATCH] Forward CraftEntity in teleport command
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index 48606a6d6ee4e1ba9d6cdb1600bbe8e62a16e400..9bbc8e1f15b5dfc62bcb065d6ebb87fa58afb91e 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -3311,6 +3311,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
}
|
||||
|
||||
public void restoreFrom(Entity original) {
|
||||
+ // Paper start
|
||||
+ CraftEntity bukkitEntity = original.bukkitEntity;
|
||||
+ if (bukkitEntity != null) {
|
||||
+ bukkitEntity.setHandle(this);
|
||||
+ this.bukkitEntity = bukkitEntity;
|
||||
+ }
|
||||
+ // Paper end
|
||||
CompoundTag nbttagcompound = original.saveWithoutId(new CompoundTag());
|
||||
|
||||
nbttagcompound.remove("Dimension");
|
||||
@@ -3392,10 +3399,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
if (worldserver.getTypeKey() == LevelStem.END) { // CraftBukkit
|
||||
ServerLevel.makeObsidianPlatform(worldserver, this); // CraftBukkit
|
||||
}
|
||||
- // CraftBukkit start - Forward the CraftEntity to the new entity
|
||||
- this.getBukkitEntity().setHandle(entity);
|
||||
- entity.bukkitEntity = this.getBukkitEntity();
|
||||
- // CraftBukkit end
|
||||
+ // // CraftBukkit start - Forward the CraftEntity to the new entity // Paper - moved to Entity#restoreFrom
|
||||
+ // this.getBukkitEntity().setHandle(entity);
|
||||
+ // entity.bukkitEntity = this.getBukkitEntity();
|
||||
+ // // CraftBukkit end
|
||||
}
|
||||
|
||||
this.removeAfterChangingDimensions();
|
|
@ -1,84 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Thu, 4 Nov 2021 12:31:24 -0700
|
||||
Subject: [PATCH] Improve scoreboard entries
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java
|
||||
index b7f0277b50a0f45c32b818bf9fe1218874aa8533..20b29f78fe56909e02061021b82a84cb7728d8a8 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java
|
||||
@@ -146,6 +146,14 @@ final class CraftObjective extends CraftScoreboardComponent implements Objective
|
||||
return new CraftScore(this, entry);
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public Score getScoreFor(org.bukkit.entity.Entity entity) throws IllegalArgumentException, IllegalStateException {
|
||||
+ Validate.notNull(entity, "Entity cannot be null");
|
||||
+ return getScore(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName());
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public void unregister() throws IllegalStateException {
|
||||
CraftScoreboard scoreboard = this.checkState();
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java
|
||||
index a038ede38ebb507d6a0182a4a34f2b0722ef024e..fe57437155ff9471738d3b85e787350601b79584 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java
|
||||
@@ -247,4 +247,23 @@ public final class CraftScoreboard implements org.bukkit.scoreboard.Scoreboard {
|
||||
public Scoreboard getHandle() {
|
||||
return this.board;
|
||||
}
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public ImmutableSet<Score> getScoresFor(org.bukkit.entity.Entity entity) throws IllegalArgumentException {
|
||||
+ Validate.notNull(entity, "Entity cannot be null");
|
||||
+ return this.getScores(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void resetScoresFor(org.bukkit.entity.Entity entity) throws IllegalArgumentException {
|
||||
+ Validate.notNull(entity, "Entity cannot be null");
|
||||
+ this.resetScores(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Team getEntityTeam(org.bukkit.entity.Entity entity) throws IllegalArgumentException {
|
||||
+ Validate.notNull(entity, "Entity cannot be null");
|
||||
+ return this.getEntryTeam(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName());
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java
|
||||
index 60b1cffdccde4715832546d6edbf206fbab4e82f..4b64d6c5c987e127d1ed5edad0a78f7172370b9f 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java
|
||||
@@ -310,6 +310,26 @@ final class CraftTeam extends CraftScoreboardComponent implements Team {
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public void addEntity(org.bukkit.entity.Entity entity) throws IllegalStateException, IllegalArgumentException {
|
||||
+ Validate.notNull(entity, "Entity cannot be null");
|
||||
+ this.addEntry(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean removeEntity(org.bukkit.entity.Entity entity) throws IllegalStateException, IllegalArgumentException {
|
||||
+ Validate.notNull(entity, "Entity cannot be null");
|
||||
+ return this.removeEntry(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean hasEntity(org.bukkit.entity.Entity entity) throws IllegalStateException, IllegalArgumentException {
|
||||
+ Validate.notNull(entity, "Entity cannot be null");
|
||||
+ return this.hasEntry(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName());
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public static Visibility bukkitToNotch(NameTagVisibility visibility) {
|
||||
switch (visibility) {
|
||||
case ALWAYS:
|
|
@ -1,39 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Sun, 24 Oct 2021 20:58:43 -0700
|
||||
Subject: [PATCH] Entity powdered snow API
|
||||
|
||||
== AT ==
|
||||
public net.minecraft.world.entity.monster.Skeleton inPowderSnowTime
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
index 726a4ae7ab928eda3ae1c1e98f3421575af2e065..03b20cec361a2eff27c1187b2a4c5a65d2b4f9b4 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
@@ -1342,5 +1342,10 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||
entity.setRot(location.getYaw(), location.getPitch());
|
||||
return !entity.valid && entity.level.addFreshEntity(entity, reason);
|
||||
}
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isInPowderedSnow() {
|
||||
+ return getHandle().isInPowderSnow || getHandle().wasInPowderSnow; // depending on the location in the entity "tick" either could be needed.
|
||||
+ }
|
||||
// Paper end
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java
|
||||
index 90f34d75f99f31f5c98c499df209979a03e23191..1737857424c5da885c46f39502cafd2a670d3be7 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java
|
||||
@@ -51,4 +51,11 @@ public class CraftSkeleton extends CraftAbstractSkeleton implements Skeleton {
|
||||
public SkeletonType getSkeletonType() {
|
||||
return SkeletonType.NORMAL;
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public int inPowderedSnowTime() {
|
||||
+ return getHandle().inPowderSnowTime;
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Sat, 28 Aug 2021 09:00:45 -0700
|
||||
Subject: [PATCH] Add API for item entity health
|
||||
|
||||
== AT ==
|
||||
public net.minecraft.world.entity.item.ItemEntity health
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
|
||||
index 1d90219c3a0e86786a9497d4c078c2d4077ab6cd..fea44ba6a6584b4a510af6a58cab07eecec6b68b 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
|
||||
@@ -102,6 +102,21 @@ public class CraftItem extends CraftEntity implements Item {
|
||||
public void setWillAge(boolean willAge) {
|
||||
item.age = willAge ? 0 : NO_AGE_TIME;
|
||||
}
|
||||
+
|
||||
+ @Override
|
||||
+ public int getHealth() {
|
||||
+ return item.health;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setHealth(int health) {
|
||||
+ if (health <= 0) {
|
||||
+ item.getItem().onDestroyed(item);
|
||||
+ item.discard();
|
||||
+ } else {
|
||||
+ item.health = health;
|
||||
+ }
|
||||
+ }
|
||||
// Paper End
|
||||
|
||||
@Override
|
|
@ -1,135 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Sun, 22 Aug 2021 15:21:57 -0700
|
||||
Subject: [PATCH] Fix entity type tags suggestions in selectors
|
||||
|
||||
This would really be better as a client fix because just to fix it
|
||||
all EntityArguments have been told to ask the server for completions
|
||||
when if this was fixed on the client, that wouldn't be needed.
|
||||
|
||||
Mojira Issue: https://bugs.mojang.com/browse/MC-235045
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java
|
||||
index 24172c3b7b1d2faa13b18bedf89ad051267feb6c..7b6b51392b123d34382233adcf4c3d4867bdaa32 100644
|
||||
--- a/src/main/java/net/minecraft/commands/CommandSourceStack.java
|
||||
+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java
|
||||
@@ -421,4 +421,20 @@ public class CommandSourceStack implements SharedSuggestionProvider, com.destroy
|
||||
return this.source.getBukkitSender(this);
|
||||
}
|
||||
// CraftBukkit end
|
||||
+ // Paper start - override getSelectedEntities
|
||||
+ @Override
|
||||
+ public Collection<String> getSelectedEntities() {
|
||||
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && this.source instanceof ServerPlayer player) {
|
||||
+ double pickDistance = player.gameMode.getGameModeForPlayer().isCreative() ? 5.0F : 4.5F;
|
||||
+ Vec3 min = player.getEyePosition(1.0F);
|
||||
+ Vec3 viewVector = player.getViewVector(1.0F);
|
||||
+ Vec3 max = min.add(viewVector.x * pickDistance, viewVector.y * pickDistance, viewVector.z * pickDistance);
|
||||
+ net.minecraft.world.phys.AABB aabb = player.getBoundingBox().expandTowards(viewVector.scale(pickDistance)).inflate(1.0D, 1.0D, 1.0D);
|
||||
+ pickDistance = player.gameMode.getGameModeForPlayer().isCreative() ? 6.0F : pickDistance;
|
||||
+ net.minecraft.world.phys.EntityHitResult hitResult = net.minecraft.world.entity.projectile.ProjectileUtil.getEntityHitResult(player, min, max, aabb, (e) -> !e.isSpectator() && e.isPickable(), pickDistance);
|
||||
+ return hitResult != null ? java.util.Collections.singletonList(hitResult.getEntity().getStringUUID()) : SharedSuggestionProvider.super.getSelectedEntities();
|
||||
+ }
|
||||
+ return SharedSuggestionProvider.super.getSelectedEntities();
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
|
||||
index f2b312367895f121bcf4135c12dfdb354f32dddb..401a87de8fc48b9e69423d547f79e9e356cc20f1 100644
|
||||
--- a/src/main/java/net/minecraft/commands/Commands.java
|
||||
+++ b/src/main/java/net/minecraft/commands/Commands.java
|
||||
@@ -464,6 +464,7 @@ public class Commands {
|
||||
private void fillUsableCommands(CommandNode<CommandSourceStack> tree, CommandNode<SharedSuggestionProvider> result, CommandSourceStack source, Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> resultNodes) {
|
||||
Iterator iterator = tree.getChildren().iterator();
|
||||
|
||||
+ boolean registeredAskServerSuggestionsForTree = false; // Paper - tell clients to ask server for suggestions for EntityArguments
|
||||
while (iterator.hasNext()) {
|
||||
CommandNode<CommandSourceStack> commandnode2 = (CommandNode) iterator.next();
|
||||
// Paper start
|
||||
@@ -490,6 +491,12 @@ public class Commands {
|
||||
|
||||
if (requiredargumentbuilder.getSuggestionsProvider() != null) {
|
||||
requiredargumentbuilder.suggests(SuggestionProviders.safelySwap(requiredargumentbuilder.getSuggestionsProvider()));
|
||||
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
|
||||
+ registeredAskServerSuggestionsForTree = requiredargumentbuilder.getSuggestionsProvider() == net.minecraft.commands.synchronization.SuggestionProviders.ASK_SERVER;
|
||||
+ } else if (io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && !registeredAskServerSuggestionsForTree && requiredargumentbuilder.getType() instanceof net.minecraft.commands.arguments.EntityArgument) {
|
||||
+ requiredargumentbuilder.suggests(requiredargumentbuilder.getType()::listSuggestions);
|
||||
+ registeredAskServerSuggestionsForTree = true; // You can only
|
||||
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/commands/arguments/EntityArgument.java b/src/main/java/net/minecraft/commands/arguments/EntityArgument.java
|
||||
index 850db283bf12345e9e7d7e8e590dbe8135c6dce1..a2ea64b7ec5f47224312a1e08dd64347be6f7c43 100644
|
||||
--- a/src/main/java/net/minecraft/commands/arguments/EntityArgument.java
|
||||
+++ b/src/main/java/net/minecraft/commands/arguments/EntityArgument.java
|
||||
@@ -128,7 +128,7 @@ public class EntityArgument implements ArgumentType<EntitySelector> {
|
||||
StringReader stringreader = new StringReader(suggestionsbuilder.getInput());
|
||||
|
||||
stringreader.setCursor(suggestionsbuilder.getStart());
|
||||
- EntitySelectorParser argumentparserselector = new EntitySelectorParser(stringreader, icompletionprovider.hasPermission(2));
|
||||
+ EntitySelectorParser argumentparserselector = new EntitySelectorParser(stringreader, icompletionprovider.hasPermission(2), true); // Paper
|
||||
|
||||
try {
|
||||
argumentparserselector.parse();
|
||||
diff --git a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
|
||||
index cdc1319a7cb45a7c5ea30885958b8c3c54f0a241..4cc2ee22369aef22c9bf9d2b244c7278d6db39bb 100644
|
||||
--- a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
|
||||
+++ b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
|
||||
@@ -113,12 +113,19 @@ public class EntitySelectorParser {
|
||||
private boolean hasScores;
|
||||
private boolean hasAdvancements;
|
||||
private boolean usesSelectors;
|
||||
+ public boolean parsingEntityArgumentSuggestions; // Paper - track when parsing EntityArgument suggestions
|
||||
|
||||
public EntitySelectorParser(StringReader reader) {
|
||||
this(reader, true);
|
||||
}
|
||||
|
||||
public EntitySelectorParser(StringReader reader, boolean atAllowed) {
|
||||
+ // Paper start
|
||||
+ this(reader, atAllowed, false);
|
||||
+ }
|
||||
+ public EntitySelectorParser(StringReader reader, boolean atAllowed, boolean parsingEntityArgumentSuggestions) {
|
||||
+ this.parsingEntityArgumentSuggestions = parsingEntityArgumentSuggestions;
|
||||
+ // Paper end
|
||||
this.distance = MinMaxBounds.Doubles.ANY;
|
||||
this.level = MinMaxBounds.Ints.ANY;
|
||||
this.rotX = WrappedMinMaxBounds.ANY;
|
||||
diff --git a/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java b/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
|
||||
index d34d70029ef30ea8c4f1598f676ad65557c066e1..b6abbf5775b8aa73d32494e6ac0e9afbeb534ae2 100644
|
||||
--- a/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
|
||||
+++ b/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
|
||||
@@ -66,6 +66,19 @@ public class EntitySelectorOptions {
|
||||
public static final DynamicCommandExceptionType ERROR_ENTITY_TYPE_INVALID = new DynamicCommandExceptionType((entity) -> {
|
||||
return Component.translatable("argument.entity.options.type.invalid", entity);
|
||||
});
|
||||
+ // Paper start
|
||||
+ public static final DynamicCommandExceptionType ERROR_ENTITY_TAG_INVALID = new DynamicCommandExceptionType((object) -> {
|
||||
+ return io.papermc.paper.adventure.PaperAdventure
|
||||
+ .asVanilla(net.kyori.adventure.text.Component
|
||||
+ .text("Invalid or unknown entity type tag '" + object + "'")
|
||||
+ .hoverEvent(net.kyori.adventure.text.event.HoverEvent
|
||||
+ .showText(net.kyori.adventure.text.Component
|
||||
+ .text("You can disable this error in 'paper.yml'")
|
||||
+ )
|
||||
+ )
|
||||
+ );
|
||||
+ });
|
||||
+ // Paper end
|
||||
|
||||
private static void register(String id, EntitySelectorOptions.Modifier handler, Predicate<EntitySelectorParser> condition, Component description) {
|
||||
OPTIONS.put(id, new EntitySelectorOptions.Option(handler, condition, description));
|
||||
@@ -313,6 +326,12 @@ public class EntitySelectorOptions {
|
||||
|
||||
if (reader.isTag()) {
|
||||
TagKey<EntityType<?>> tagKey = TagKey.create(Registries.ENTITY_TYPE, ResourceLocation.read(reader.getReader()));
|
||||
+ // Paper start - throw error if invalid entity tag (only on suggestions to keep cmd success behavior)
|
||||
+ if (reader.parsingEntityArgumentSuggestions && io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.getTag(tagKey).isEmpty()) {
|
||||
+ reader.getReader().setCursor(i);
|
||||
+ throw ERROR_ENTITY_TAG_INVALID.createWithContext(reader.getReader(), tagKey);
|
||||
+ }
|
||||
+ // Paper end
|
||||
reader.addPredicate((entity) -> {
|
||||
return entity.getType().is(tagKey) != bl;
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Nassim Jahnke <nassim@njahnke.dev>
|
||||
Date: Thu, 16 Dec 2021 09:40:39 +0100
|
||||
Subject: [PATCH] Configurable max block light for monster spawning
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/monster/Monster.java b/src/main/java/net/minecraft/world/entity/monster/Monster.java
|
||||
index 6e0bd0eab0b06a4ac3042496bbb91292544e9f3c..55c245d0dfa369dc6de2197ae37335fba4fae4ae 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/monster/Monster.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/monster/Monster.java
|
||||
@@ -93,7 +93,7 @@ public abstract class Monster extends PathfinderMob implements Enemy {
|
||||
return false;
|
||||
} else {
|
||||
DimensionType dimensionType = world.dimensionType();
|
||||
- int i = dimensionType.monsterSpawnBlockLightLimit();
|
||||
+ int i = world.getLevel().paperConfig().entities.spawning.monsterSpawnMaxLightLevel >= 0 ? world.getLevel().paperConfig().entities.spawning.monsterSpawnMaxLightLevel : dimensionType.monsterSpawnBlockLightLimit(); // Paper
|
||||
if (i < 15 && world.getBrightness(LightLayer.BLOCK, pos) > i) {
|
||||
return false;
|
||||
} else {
|
|
@ -1,85 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Wed, 22 Dec 2021 09:51:48 -0800
|
||||
Subject: [PATCH] Fix sticky pistons and BlockPistonRetractEvent
|
||||
|
||||
There is an explicit check in the handling code for empty pistons that
|
||||
prevents sticky pistons from firing the event. However when we look back
|
||||
at the history we see that this check was originally added so that ONLY
|
||||
sticky pistons would fire the retract event. I'm not sure why.
|
||||
https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/commits/1092acbddf07edfa4100bc6824504ac75088e913
|
||||
|
||||
Over the course of several updates, the meaning of that field appears to
|
||||
have changed from "is NOT sticky" to "is sticky". So now its having the
|
||||
opposite effect. Only normal pistons fire the retraction event. And like
|
||||
all things in CB, it's just been carried around since.
|
||||
|
||||
If we are to believe the history, the correct fix for this issue is to
|
||||
flip it so it only fires for sticky pistons, but that puts us in a
|
||||
bind. It's already firing for non-sticky pistons, changing it now would
|
||||
likely result in breakage. Furthermore, there is little documentation as
|
||||
to WHY that was ever intended to be the case.
|
||||
|
||||
Instead we opt to remove the check entirely so that the event fires for
|
||||
all piston types.
|
||||
|
||||
Co-authored-by: Zach Brown <zach@zachbr.io>
|
||||
Co-authored-by: Madeline Miller <mnmiller1@me.com>
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
|
||||
index 24a822ade17849a083161216c184f02c30b5cb1f..f456ad8a74464414f69b616a48ee9a2c1cee4d90 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
|
||||
@@ -146,15 +146,15 @@ public class PistonBaseBlock extends DirectionalBlock {
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
- if (!this.isSticky) {
|
||||
- org.bukkit.block.Block block = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
|
||||
- BlockPistonRetractEvent event = new BlockPistonRetractEvent(block, ImmutableList.<org.bukkit.block.Block>of(), CraftBlock.notchToBlockFace(enumdirection));
|
||||
- world.getCraftServer().getPluginManager().callEvent(event);
|
||||
-
|
||||
- if (event.isCancelled()) {
|
||||
- return;
|
||||
- }
|
||||
- }
|
||||
+ // if (!this.isSticky) { // Paper - Move further down
|
||||
+ // org.bukkit.block.Block block = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
|
||||
+ // BlockPistonRetractEvent event = new BlockPistonRetractEvent(block, ImmutableList.<org.bukkit.block.Block>of(), CraftBlock.notchToBlockFace(enumdirection));
|
||||
+ // world.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ //
|
||||
+ // if (event.isCancelled()) {
|
||||
+ // return;
|
||||
+ // }
|
||||
+ // }
|
||||
// PAIL: checkME - what happened to setTypeAndData?
|
||||
// CraftBukkit end
|
||||
world.blockEvent(pos, this, b0, enumdirection.get3DDataValue());
|
||||
@@ -236,6 +236,13 @@ public class PistonBaseBlock extends DirectionalBlock {
|
||||
|
||||
BlockState iblockdata1 = (BlockState) ((BlockState) Blocks.MOVING_PISTON.defaultBlockState().setValue(MovingPistonBlock.FACING, enumdirection)).setValue(MovingPistonBlock.TYPE, this.isSticky ? PistonType.STICKY : PistonType.DEFAULT);
|
||||
|
||||
+ // Paper start - Move empty piston retract call to fix multiple event fires
|
||||
+ if (!this.isSticky) {
|
||||
+ if (!new BlockPistonRetractEvent(CraftBlock.at(world, pos), java.util.Collections.emptyList(), CraftBlock.notchToBlockFace(enumdirection)).callEvent()) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
world.setBlock(pos, iblockdata1, 20);
|
||||
world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata1, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true)); // Paper - diff on change
|
||||
world.blockUpdated(pos, iblockdata1.getBlock());
|
||||
@@ -262,6 +269,13 @@ public class PistonBaseBlock extends DirectionalBlock {
|
||||
if (type == 1 && !iblockdata2.isAir() && PistonBaseBlock.isPushable(iblockdata2, world, blockposition1, enumdirection.getOpposite(), false, enumdirection) && (iblockdata2.getPistonPushReaction() == PushReaction.NORMAL || iblockdata2.is(Blocks.PISTON) || iblockdata2.is(Blocks.STICKY_PISTON))) {
|
||||
this.moveBlocks(world, pos, enumdirection, false);
|
||||
} else {
|
||||
+ // Paper start - fire BlockPistonRetractEvent for sticky pistons retracting nothing (air)
|
||||
+ if (type == TRIGGER_CONTRACT && iblockdata2.isAir()) {
|
||||
+ if (!new BlockPistonRetractEvent(CraftBlock.at(world, pos), java.util.Collections.emptyList(), CraftBlock.notchToBlockFace(enumdirection)).callEvent()) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
world.removeBlock(pos.relative(enumdirection), false);
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Tue, 21 Dec 2021 22:13:26 -0800
|
||||
Subject: [PATCH] Load effect amplifiers greater than 127 correctly
|
||||
|
||||
MOJIRA: MC-118857
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/effect/MobEffectInstance.java b/src/main/java/net/minecraft/world/effect/MobEffectInstance.java
|
||||
index 5ac1aad619a71684a48a2cbd73a8cefc66c587b7..038ba61e4845a4a71bb78ba388ed249d19529b78 100644
|
||||
--- a/src/main/java/net/minecraft/world/effect/MobEffectInstance.java
|
||||
+++ b/src/main/java/net/minecraft/world/effect/MobEffectInstance.java
|
||||
@@ -264,7 +264,7 @@ public class MobEffectInstance implements Comparable<MobEffectInstance> {
|
||||
}
|
||||
|
||||
private static MobEffectInstance loadSpecifiedEffect(MobEffect type, CompoundTag nbt) {
|
||||
- int i = nbt.getByte("Amplifier");
|
||||
+ int i = Byte.toUnsignedInt(nbt.getByte("Amplifier")); // Paper - correctly load amplifiers > 127
|
||||
int j = nbt.getInt("Duration");
|
||||
boolean bl = nbt.getBoolean("Ambient");
|
||||
boolean bl2 = true;
|
|
@ -1,31 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: BillyGalbreath <blake.galbreath@gmail.com>
|
||||
Date: Thu, 23 Dec 2021 15:32:50 -0600
|
||||
Subject: [PATCH] Expose isFuel and canSmelt methods to FurnaceInventory
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java
|
||||
index 05c29a788c96282fc18066ae253cf0b5be37e95c..e8e53d3c7d8b1bba7d77dc0c76d242eb177ad851 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java
|
||||
@@ -40,6 +40,20 @@ public class CraftInventoryFurnace extends CraftInventory implements FurnaceInve
|
||||
setItem(0, stack);
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public boolean isFuel(ItemStack stack) {
|
||||
+ return stack != null && !stack.getType().isEmpty() && AbstractFurnaceBlockEntity.isFuel(CraftItemStack.asNMSCopy(stack));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean canSmelt(ItemStack stack) {
|
||||
+ // data packs are always loaded in the main world
|
||||
+ net.minecraft.server.level.ServerLevel world = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorlds().get(0)).getHandle();
|
||||
+ return stack != null && !stack.getType().isEmpty() && world.getRecipeManager().getRecipeFor(((AbstractFurnaceBlockEntity) this.inventory).recipeType, new net.minecraft.world.SimpleContainer(CraftItemStack.asNMSCopy(stack)), world).isPresent();
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public Furnace getHolder() {
|
||||
return (Furnace) inventory.getOwner();
|
|
@ -1,49 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Sat, 21 Aug 2021 21:54:16 -0700
|
||||
Subject: [PATCH] Fix bees aging inside hives
|
||||
|
||||
Fixes bees incorrectly being aged up due to upstream's
|
||||
resetting the ticks inside hive on a failed release
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java
|
||||
index 82ad97800cb115cc4830337a59cc4608c1d4a7a0..41c9f074203915c31c1ae7a160ce509c13383f84 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java
|
||||
@@ -328,7 +328,7 @@ public class BeehiveBlockEntity extends BlockEntity {
|
||||
|
||||
for (Iterator iterator = bees.iterator(); iterator.hasNext(); ++tileentitybeehive_hivebee.ticksInHive) {
|
||||
tileentitybeehive_hivebee = (BeehiveBlockEntity.BeeData) iterator.next();
|
||||
- if (tileentitybeehive_hivebee.ticksInHive > tileentitybeehive_hivebee.minOccupationTicks) {
|
||||
+ if (tileentitybeehive_hivebee.exitTickCounter > tileentitybeehive_hivebee.minOccupationTicks) { // Paper - use exitTickCounter
|
||||
BeehiveBlockEntity.BeeReleaseStatus tileentitybeehive_releasestatus = tileentitybeehive_hivebee.entityData.getBoolean("HasNectar") ? BeehiveBlockEntity.BeeReleaseStatus.HONEY_DELIVERED : BeehiveBlockEntity.BeeReleaseStatus.BEE_RELEASED;
|
||||
|
||||
if (BeehiveBlockEntity.releaseOccupant(world, pos, state, tileentitybeehive_hivebee, (List) null, tileentitybeehive_releasestatus, flowerPos)) {
|
||||
@@ -336,10 +336,11 @@ public class BeehiveBlockEntity extends BlockEntity {
|
||||
iterator.remove();
|
||||
// CraftBukkit start
|
||||
} else {
|
||||
- tileentitybeehive_hivebee.ticksInHive = tileentitybeehive_hivebee.minOccupationTicks / 2; // Not strictly Vanilla behaviour in cases where bees cannot spawn but still reasonable
|
||||
+ tileentitybeehive_hivebee.exitTickCounter = tileentitybeehive_hivebee.minOccupationTicks / 2; // Not strictly Vanilla behaviour in cases where bees cannot spawn but still reasonable // Paper - use exitTickCounter to keep actual bee life
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
+ tileentitybeehive_hivebee.exitTickCounter++; // Paper
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
@@ -428,12 +429,14 @@ public class BeehiveBlockEntity extends BlockEntity {
|
||||
|
||||
final CompoundTag entityData;
|
||||
int ticksInHive;
|
||||
+ int exitTickCounter; // Paper - separate counter for checking if bee should exit to reduce exit attempts
|
||||
final int minOccupationTicks;
|
||||
|
||||
BeeData(CompoundTag entityData, int ticksInHive, int minOccupationTicks) {
|
||||
BeehiveBlockEntity.removeIgnoredBeeTags(entityData);
|
||||
this.entityData = entityData;
|
||||
this.ticksInHive = ticksInHive;
|
||||
+ this.exitTickCounter = ticksInHive; // Paper
|
||||
this.minOccupationTicks = minOccupationTicks;
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
|
||||
Date: Sun, 26 Dec 2021 14:03:17 -0500
|
||||
Subject: [PATCH] Bucketable API
|
||||
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/entity/PaperBucketable.java b/src/main/java/io/papermc/paper/entity/PaperBucketable.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..c4eee682f5b8cd82f71f92f0549f39b76e735cd8
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/entity/PaperBucketable.java
|
||||
@@ -0,0 +1,31 @@
|
||||
+package io.papermc.paper.entity;
|
||||
+
|
||||
+import org.bukkit.Sound;
|
||||
+import org.bukkit.craftbukkit.CraftSound;
|
||||
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
+import org.bukkit.inventory.ItemStack;
|
||||
+
|
||||
+public interface PaperBucketable extends Bucketable {
|
||||
+
|
||||
+ net.minecraft.world.entity.animal.Bucketable getHandle();
|
||||
+
|
||||
+ @Override
|
||||
+ default boolean isFromBucket() {
|
||||
+ return this.getHandle().fromBucket();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ default void setFromBucket(boolean fromBucket) {
|
||||
+ this.getHandle().setFromBucket(fromBucket);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ default ItemStack getBaseBucketItem() {
|
||||
+ return CraftItemStack.asBukkitCopy(this.getHandle().getBucketItemStack());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ default Sound getPickupSound() {
|
||||
+ return CraftSound.getBukkit(this.getHandle().getPickupSound());
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftAxolotl.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAxolotl.java
|
||||
index 2947298fe69aa8330d4fe399bf2441c367e8bd80..a82acbbe62bf6aa497e627587e2f3b9be2fbf487 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftAxolotl.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAxolotl.java
|
||||
@@ -5,7 +5,7 @@ import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.entity.Axolotl;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
-public class CraftAxolotl extends CraftAnimals implements Axolotl {
|
||||
+public class CraftAxolotl extends CraftAnimals implements Axolotl, io.papermc.paper.entity.PaperBucketable { // Paper - Bucketable API
|
||||
|
||||
public CraftAxolotl(CraftServer server, net.minecraft.world.entity.animal.axolotl.Axolotl entity) {
|
||||
super(server, entity);
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java
|
||||
index 69d103a8ec74b17ee3116bb4d448494bd66f50b6..3c64461119391ec2e987fc936104e21ef0a95ce4 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java
|
||||
@@ -4,7 +4,7 @@ import net.minecraft.world.entity.animal.AbstractFish;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.entity.Fish;
|
||||
|
||||
-public class CraftFish extends CraftWaterMob implements Fish {
|
||||
+public class CraftFish extends CraftWaterMob implements Fish, io.papermc.paper.entity.PaperBucketable { // Paper - Bucketable API
|
||||
|
||||
public CraftFish(CraftServer server, AbstractFish entity) {
|
||||
super(server, entity);
|
|
@ -1,70 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sat, 1 Jan 2022 05:19:37 -0800
|
||||
Subject: [PATCH] Validate usernames
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
index b6dc42aef6458b0c09007bb0e13048b99531f8c0..7e8401a05905775f7209abb1269e24aad409af50 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
@@ -60,6 +60,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
||||
private final String serverId;
|
||||
@Nullable
|
||||
private ServerPlayer delayedAcceptPlayer;
|
||||
+ public boolean iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation = false; // Paper - username validation overriding
|
||||
|
||||
public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection) {
|
||||
this.state = ServerLoginPacketListenerImpl.State.HELLO;
|
||||
@@ -212,10 +213,38 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
||||
// Paper end
|
||||
}
|
||||
|
||||
+ // Paper start - validate usernames
|
||||
+ public static boolean validateUsername(String in) {
|
||||
+ if (in == null || in.isEmpty() || in.length() > 16) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ for (int i = 0, len = in.length(); i < len; ++i) {
|
||||
+ char c = in.charAt(i);
|
||||
+
|
||||
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_' || c == '.')) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Paper end - validate usernames
|
||||
+
|
||||
@Override
|
||||
public void handleHello(ServerboundHelloPacket packet) {
|
||||
Validate.validState(this.state == ServerLoginPacketListenerImpl.State.HELLO, "Unexpected hello packet", new Object[0]);
|
||||
Validate.validState(ServerLoginPacketListenerImpl.isValidUsername(packet.name()), "Invalid characters in username", new Object[0]);
|
||||
+ // Paper start - validate usernames
|
||||
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.performUsernameValidation) {
|
||||
+ if (!this.iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation && !validateUsername(packet.name())) {
|
||||
+ ServerLoginPacketListenerImpl.this.disconnect("Failed to verify username!");
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - validate usernames
|
||||
GameProfile gameprofile = this.server.getSingleplayerProfile();
|
||||
|
||||
if (gameprofile != null && packet.name().equalsIgnoreCase(gameprofile.getName())) {
|
||||
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
index d396e2cb91aa99d60c898f86e0f33d3ce3f885cd..147d955d8be63255ff74457c2c3b92cd22577342 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
@@ -652,7 +652,7 @@ public abstract class PlayerList {
|
||||
|
||||
for (int i = 0; i < this.players.size(); ++i) {
|
||||
entityplayer = (ServerPlayer) this.players.get(i);
|
||||
- if (entityplayer.getUUID().equals(uuid)) {
|
||||
+ if (entityplayer.getUUID().equals(uuid) || (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() && entityplayer.getGameProfile().getName().equalsIgnoreCase(gameprofile.getName()))) { // Paper - validate usernames
|
||||
list.add(entityplayer);
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Brokkonaut <hannos17@gmx.de>
|
||||
Date: Sat, 18 Dec 2021 08:26:55 +0100
|
||||
Subject: [PATCH] Make water animal spawn height configurable
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java b/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java
|
||||
index 522abd880c1e7f7c9026f0ab6457bc649f11802c..18389f46902bb9879ac6d734723e9a720724dc48 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java
|
||||
@@ -79,6 +79,10 @@ public abstract class WaterAnimal extends PathfinderMob {
|
||||
public static boolean checkSurfaceWaterAnimalSpawnRules(EntityType<? extends WaterAnimal> type, LevelAccessor world, MobSpawnType reason, BlockPos pos, RandomSource random) {
|
||||
int i = world.getSeaLevel();
|
||||
int j = i - 13;
|
||||
+ // Paper start
|
||||
+ i = world.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.maximum.or(i);
|
||||
+ j = world.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.minimum.or(j);
|
||||
+ // Paper end
|
||||
return pos.getY() >= j && pos.getY() <= i && world.getFluidState(pos.below()).is(FluidTags.WATER) && world.getBlockState(pos.above()).is(Blocks.WATER);
|
||||
}
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Thu, 6 Jan 2022 15:59:06 -0800
|
||||
Subject: [PATCH] Expose vanilla BiomeProvider from WorldInfo
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index 4fb05a2be3ff06ceddae37e1592db548c727a056..1b1b7560b7ae59c5da7ad0e3dfae32cbcaeedcbd 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -561,7 +561,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
List<CustomSpawner> list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(iworlddataserver));
|
||||
LevelStem worlddimension = (LevelStem) dimensions.get(dimensionKey);
|
||||
|
||||
- org.bukkit.generator.WorldInfo worldInfo = new org.bukkit.craftbukkit.generator.CraftWorldInfo(iworlddataserver, worldSession, org.bukkit.World.Environment.getEnvironment(dimension), worlddimension.type().value());
|
||||
+ org.bukkit.generator.WorldInfo worldInfo = new org.bukkit.craftbukkit.generator.CraftWorldInfo(iworlddataserver, worldSession, org.bukkit.World.Environment.getEnvironment(dimension), worlddimension.type().value(), worlddimension.generator(), this.registryAccess()); // Paper
|
||||
if (biomeProvider == null && gen != null) {
|
||||
biomeProvider = gen.getDefaultBiomeProvider(worldInfo);
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index c4d5f3482881376bde77e826009a383804a2203d..759d091593b75419e275e79eaa3afbfbef6aee10 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -1222,7 +1222,7 @@ public final class CraftServer implements Server {
|
||||
List<CustomSpawner> list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(worlddata));
|
||||
LevelStem worlddimension = iregistry.get(actualDimension);
|
||||
|
||||
- WorldInfo worldInfo = new CraftWorldInfo(worlddata, worldSession, creator.environment(), worlddimension.type().value());
|
||||
+ WorldInfo worldInfo = new CraftWorldInfo(worlddata, worldSession, creator.environment(), worlddimension.type().value(), worlddimension.generator(), this.getHandle().getServer().registryAccess()); // Paper
|
||||
if (biomeProvider == null && generator != null) {
|
||||
biomeProvider = generator.getDefaultBiomeProvider(worldInfo);
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
index 778d7503af4ab00e217c12b314c8ca4ccceae411..08fc562b69f93d6be21124ec4309580a26ad5c85 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
@@ -199,6 +199,30 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
public int getPlayerCount() {
|
||||
return world.players().size();
|
||||
}
|
||||
+
|
||||
+ @Override
|
||||
+ public BiomeProvider vanillaBiomeProvider() {
|
||||
+ net.minecraft.server.level.ServerChunkCache serverCache = this.getHandle().chunkSource;
|
||||
+
|
||||
+ final net.minecraft.world.level.biome.BiomeSource biomeSource = serverCache.getGenerator().getBiomeSource();
|
||||
+ final net.minecraft.world.level.biome.Climate.Sampler sampler = serverCache.randomState().sampler();
|
||||
+ final net.minecraft.core.Registry<net.minecraft.world.level.biome.Biome> biomeRegistry = this.getHandle().registryAccess().registryOrThrow(net.minecraft.core.registries.Registries.BIOME);
|
||||
+
|
||||
+ final List<Biome> possibleBiomes = biomeSource.possibleBiomes().stream()
|
||||
+ .map(biome -> CraftBlock.biomeBaseToBiome(biomeRegistry, biome))
|
||||
+ .toList();
|
||||
+ return new BiomeProvider() {
|
||||
+ @Override
|
||||
+ public Biome getBiome(final org.bukkit.generator.WorldInfo worldInfo, final int x, final int y, final int z) {
|
||||
+ return CraftBlock.biomeBaseToBiome(biomeRegistry, biomeSource.getNoiseBiome(x >> 2, y >> 2, z >> 2, sampler));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public List<Biome> getBiomes(final org.bukkit.generator.WorldInfo worldInfo) {
|
||||
+ return possibleBiomes;
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
// Paper end
|
||||
|
||||
private static final Random rand = new Random();
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java
|
||||
index 5d655d6cd3e23e0287069f8bdf77601487e862fd..5dbd1a6d314ebbb072cb61360ca5c1043f47a38d 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java
|
||||
@@ -17,8 +17,14 @@ public class CraftWorldInfo implements WorldInfo {
|
||||
private final long seed;
|
||||
private final int minHeight;
|
||||
private final int maxHeight;
|
||||
+ // Paper start
|
||||
+ private final net.minecraft.world.level.chunk.ChunkGenerator vanillaChunkGenerator;
|
||||
+ private final net.minecraft.core.RegistryAccess.Frozen registryAccess;
|
||||
|
||||
- public CraftWorldInfo(ServerLevelData worldDataServer, LevelStorageSource.LevelStorageAccess session, World.Environment environment, DimensionType dimensionManager) {
|
||||
+ public CraftWorldInfo(ServerLevelData worldDataServer, LevelStorageSource.LevelStorageAccess session, World.Environment environment, DimensionType dimensionManager, net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator, net.minecraft.core.RegistryAccess.Frozen registryAccess) {
|
||||
+ this.registryAccess = registryAccess;
|
||||
+ this.vanillaChunkGenerator = chunkGenerator;
|
||||
+ // Paper end
|
||||
this.name = worldDataServer.getLevelName();
|
||||
this.uuid = WorldUUID.getUUID(session.levelDirectory.path().toFile());
|
||||
this.environment = environment;
|
||||
@@ -27,15 +33,6 @@ public class CraftWorldInfo implements WorldInfo {
|
||||
this.maxHeight = dimensionManager.minY() + dimensionManager.height();
|
||||
}
|
||||
|
||||
- public CraftWorldInfo(String name, UUID uuid, World.Environment environment, long seed, int minHeight, int maxHeight) {
|
||||
- this.name = name;
|
||||
- this.uuid = uuid;
|
||||
- this.environment = environment;
|
||||
- this.seed = seed;
|
||||
- this.minHeight = minHeight;
|
||||
- this.maxHeight = maxHeight;
|
||||
- }
|
||||
-
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
@@ -65,4 +62,35 @@ public class CraftWorldInfo implements WorldInfo {
|
||||
public int getMaxHeight() {
|
||||
return this.maxHeight;
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public org.bukkit.generator.BiomeProvider vanillaBiomeProvider() {
|
||||
+ final net.minecraft.world.level.levelgen.RandomState randomState;
|
||||
+ if (vanillaChunkGenerator instanceof net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
|
||||
+ randomState = net.minecraft.world.level.levelgen.RandomState.create(noiseBasedChunkGenerator.generatorSettings().value(),
|
||||
+ registryAccess.lookupOrThrow(net.minecraft.core.registries.Registries.NOISE), getSeed());
|
||||
+ } else {
|
||||
+ randomState = net.minecraft.world.level.levelgen.RandomState.create(net.minecraft.world.level.levelgen.NoiseGeneratorSettings.dummy(),
|
||||
+ registryAccess.lookupOrThrow(net.minecraft.core.registries.Registries.NOISE), getSeed());
|
||||
+ }
|
||||
+
|
||||
+ final net.minecraft.core.Registry<net.minecraft.world.level.biome.Biome> biomeRegistry = CraftWorldInfo.this.registryAccess.registryOrThrow(net.minecraft.core.registries.Registries.BIOME);
|
||||
+ final java.util.List<org.bukkit.block.Biome> possibleBiomes = CraftWorldInfo.this.vanillaChunkGenerator.getBiomeSource().possibleBiomes().stream()
|
||||
+ .map(biome -> org.bukkit.craftbukkit.block.CraftBlock.biomeBaseToBiome(biomeRegistry, biome))
|
||||
+ .toList();
|
||||
+ return new org.bukkit.generator.BiomeProvider() {
|
||||
+ @Override
|
||||
+ public org.bukkit.block.Biome getBiome(final WorldInfo worldInfo, final int x, final int y, final int z) {
|
||||
+ return org.bukkit.craftbukkit.block.CraftBlock.biomeBaseToBiome(biomeRegistry,
|
||||
+ CraftWorldInfo.this.vanillaChunkGenerator.getBiomeSource().getNoiseBiome(x >> 2, y >> 2, z >> 2, randomState.sampler()));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public java.util.List<org.bukkit.block.Biome> getBiomes(final org.bukkit.generator.WorldInfo worldInfo) {
|
||||
+ return possibleBiomes;
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Sun, 2 Jan 2022 22:34:51 -0800
|
||||
Subject: [PATCH] Add config option for worlds affected by time cmd
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/commands/TimeCommand.java b/src/main/java/net/minecraft/server/commands/TimeCommand.java
|
||||
index e1908c2fcad3d1505bdcd65ba7ceb3dfa42c5c39..f0a7a8df3caa2ea765bb0a87cfede71d0995d276 100644
|
||||
--- a/src/main/java/net/minecraft/server/commands/TimeCommand.java
|
||||
+++ b/src/main/java/net/minecraft/server/commands/TimeCommand.java
|
||||
@@ -51,7 +51,7 @@ public class TimeCommand {
|
||||
}
|
||||
|
||||
public static int setTime(CommandSourceStack source, int time) {
|
||||
- Iterator iterator = com.google.common.collect.Iterators.singletonIterator(source.getLevel()); // CraftBukkit - SPIGOT-6496: Only set the time for the world the command originates in
|
||||
+ Iterator iterator = io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevels().iterator() : com.google.common.collect.Iterators.singletonIterator(source.getLevel()); // CraftBukkit - SPIGOT-6496: Only set the time for the world the command originates in // Paper - add config option for spigot's change
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
ServerLevel worldserver = (ServerLevel) iterator.next();
|
||||
@@ -70,7 +70,7 @@ public class TimeCommand {
|
||||
}
|
||||
|
||||
public static int addTime(CommandSourceStack source, int time) {
|
||||
- Iterator iterator = com.google.common.collect.Iterators.singletonIterator(source.getLevel()); // CraftBukkit - SPIGOT-6496: Only set the time for the world the command originates in
|
||||
+ Iterator iterator = io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevels().iterator() : com.google.common.collect.Iterators.singletonIterator(source.getLevel()); // CraftBukkit - SPIGOT-6496: Only set the time for the world the command originates in // Paper - add config option for spigot's change
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
ServerLevel worldserver = (ServerLevel) iterator.next();
|
|
@ -1,24 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: u9g <winworkswow@gmail.com>
|
||||
Date: Mon, 3 Jan 2022 23:32:42 -0500
|
||||
Subject: [PATCH] Add new overload to PersistentDataContainer#has
|
||||
|
||||
Adds the new overload: PersistentDataContainer#has(NamespacedKey key)
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
|
||||
index 2c59f09a9261a1690951161fd856a5848d9885b7..f0588bec9be09cb8273c310fb3de8bfe72dee9e5 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
|
||||
@@ -161,5 +161,12 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
|
||||
public void clear() {
|
||||
this.customDataTags.clear();
|
||||
}
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean has(NamespacedKey key) {
|
||||
+ Validate.notNull(key, "The provided key for the custom value was null");
|
||||
+
|
||||
+ return this.customDataTags.containsKey(key.toString());
|
||||
+ }
|
||||
// Paper end
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Cryptite <cryptite@gmail.com>
|
||||
Date: Tue, 21 Sep 2021 18:17:33 -0500
|
||||
Subject: [PATCH] Multiple Entries with Scoreboards
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java
|
||||
index ee37ec0de1ca969144824427ae42b0c81434a1b4..4ebe22ac20f1a98694cc3bec570ef5bbf06f00aa 100644
|
||||
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java
|
||||
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java
|
||||
@@ -42,6 +42,12 @@ public class ClientboundSetPlayerTeamPacket implements Packet<ClientGamePacketLi
|
||||
return new ClientboundSetPlayerTeamPacket(team.getName(), operation == ClientboundSetPlayerTeamPacket.Action.ADD ? 3 : 4, Optional.empty(), ImmutableList.of(playerName));
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public static ClientboundSetPlayerTeamPacket createMultiplePlayerPacket(PlayerTeam team, Collection<String> players, ClientboundSetPlayerTeamPacket.Action operation) {
|
||||
+ return new ClientboundSetPlayerTeamPacket(team.getName(), operation == ClientboundSetPlayerTeamPacket.Action.ADD ? 3 : 4, Optional.empty(), players);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public ClientboundSetPlayerTeamPacket(FriendlyByteBuf buf) {
|
||||
this.name = buf.readUtf();
|
||||
this.method = buf.readByte();
|
||||
diff --git a/src/main/java/net/minecraft/server/ServerScoreboard.java b/src/main/java/net/minecraft/server/ServerScoreboard.java
|
||||
index 610d312b9c8f6c8d1f102e8ba2fe9fc2cc3e98c5..3a4a0727ad44322e3ba85512cd077808dab080b7 100644
|
||||
--- a/src/main/java/net/minecraft/server/ServerScoreboard.java
|
||||
+++ b/src/main/java/net/minecraft/server/ServerScoreboard.java
|
||||
@@ -92,6 +92,25 @@ public class ServerScoreboard extends Scoreboard {
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public boolean addPlayersToTeam(java.util.Collection<String> players, PlayerTeam team) {
|
||||
+ boolean anyAdded = false;
|
||||
+ for (String playerName : players) {
|
||||
+ if (super.addPlayerToTeam(playerName, team)) {
|
||||
+ anyAdded = true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (anyAdded) {
|
||||
+ this.broadcastAll(ClientboundSetPlayerTeamPacket.createMultiplePlayerPacket(team, players, ClientboundSetPlayerTeamPacket.Action.ADD));
|
||||
+ this.setDirty();
|
||||
+ return true;
|
||||
+ } else {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public void removePlayerFromTeam(String playerName, PlayerTeam team) {
|
||||
super.removePlayerFromTeam(playerName, team);
|
||||
@@ -99,6 +118,17 @@ public class ServerScoreboard extends Scoreboard {
|
||||
this.setDirty();
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public void removePlayersFromTeam(java.util.Collection<String> players, PlayerTeam team) {
|
||||
+ for (String playerName : players) {
|
||||
+ super.removePlayerFromTeam(playerName, team);
|
||||
+ }
|
||||
+
|
||||
+ this.broadcastAll(ClientboundSetPlayerTeamPacket.createMultiplePlayerPacket(team, players, ClientboundSetPlayerTeamPacket.Action.REMOVE));
|
||||
+ this.setDirty();
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public void onObjectiveAdded(Objective objective) {
|
||||
super.onObjectiveAdded(objective);
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java
|
||||
index 4b64d6c5c987e127d1ed5edad0a78f7172370b9f..67efb0d38ae369ff5254f7b1ec85d32d4eee8291 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java
|
||||
@@ -234,6 +234,21 @@ final class CraftTeam extends CraftScoreboardComponent implements Team {
|
||||
scoreboard.board.addPlayerToTeam(entry, team);
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public void addEntities(java.util.Collection<org.bukkit.entity.Entity> entities) throws IllegalStateException, IllegalArgumentException {
|
||||
+ this.addEntries(entities.stream().map(entity -> ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName()).toList());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void addEntries(java.util.Collection<String> entries) throws IllegalStateException, IllegalArgumentException {
|
||||
+ Validate.notNull(entries, "Entries cannot be null");
|
||||
+ CraftScoreboard scoreboard = this.checkState();
|
||||
+
|
||||
+ ((net.minecraft.server.ServerScoreboard) scoreboard.board).addPlayersToTeam(entries, this.team);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public boolean removePlayer(OfflinePlayer player) throws IllegalStateException, IllegalArgumentException {
|
||||
Validate.notNull(player, "OfflinePlayer cannot be null");
|
||||
@@ -253,6 +268,28 @@ final class CraftTeam extends CraftScoreboardComponent implements Team {
|
||||
return true;
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public boolean removeEntities(java.util.Collection<org.bukkit.entity.Entity> entities) throws IllegalStateException, IllegalArgumentException {
|
||||
+ return this.removeEntries(entities.stream().map(entity -> ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName()).toList());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean removeEntries(java.util.Collection<String> entries) throws IllegalStateException, IllegalArgumentException {
|
||||
+ Validate.notNull(entries, "Entry cannot be null");
|
||||
+ CraftScoreboard scoreboard = this.checkState();
|
||||
+
|
||||
+ for (String entry : entries) {
|
||||
+ if (this.team.getPlayers().contains(entry)) {
|
||||
+ ((net.minecraft.server.ServerScoreboard) scoreboard.board).removePlayersFromTeam(entries, this.team);
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public boolean hasPlayer(OfflinePlayer player) throws IllegalArgumentException, IllegalStateException {
|
||||
Validate.notNull(player, "OfflinePlayer cannot be null");
|
|
@ -1,38 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Nassim Jahnke <nassim@njahnke.dev>
|
||||
Date: Fri, 7 Jan 2022 11:45:15 +0100
|
||||
Subject: [PATCH] Reset placed block on exception
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/item/BlockItem.java b/src/main/java/net/minecraft/world/item/BlockItem.java
|
||||
index e68b7a1b03783e4f9de24c5ae8773fc359bd2d37..e581dc10f3c805f7f8b6e4c842092609e7e1a0f8 100644
|
||||
--- a/src/main/java/net/minecraft/world/item/BlockItem.java
|
||||
+++ b/src/main/java/net/minecraft/world/item/BlockItem.java
|
||||
@@ -80,6 +80,7 @@ public class BlockItem extends Item {
|
||||
if (this instanceof PlaceOnWaterBlockItem || this instanceof SolidBucketItem) {
|
||||
blockstate = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(blockactioncontext1.getLevel(), blockactioncontext1.getClickedPos());
|
||||
}
|
||||
+ final org.bukkit.block.BlockState oldBlockstate = blockstate != null ? blockstate : org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(blockactioncontext1.getLevel(), blockactioncontext1.getClickedPos()); // Paper
|
||||
// CraftBukkit end
|
||||
|
||||
if (iblockdata == null) {
|
||||
@@ -95,7 +96,19 @@ public class BlockItem extends Item {
|
||||
|
||||
if (iblockdata1.is(iblockdata.getBlock())) {
|
||||
iblockdata1 = this.updateBlockStateFromTag(blockposition, world, itemstack, iblockdata1);
|
||||
+ // Paper start - reset block on exception
|
||||
+ try {
|
||||
this.updateCustomBlockEntityTag(blockposition, world, entityhuman, itemstack, iblockdata1);
|
||||
+ } catch (Exception e) {
|
||||
+ oldBlockstate.update(true, false);
|
||||
+ if (entityhuman instanceof ServerPlayer player) {
|
||||
+ org.apache.logging.log4j.LogManager.getLogger().error("Player {} tried placing invalid block", player.getScoreboardName(), e);
|
||||
+ player.getBukkitEntity().kickPlayer("Packet processing error");
|
||||
+ return InteractionResult.FAIL;
|
||||
+ }
|
||||
+ throw e; // Rethrow exception if not placed by a player
|
||||
+ }
|
||||
+ // Paper end
|
||||
iblockdata1.getBlock().setPlacedBy(world, blockposition, iblockdata1, entityhuman, itemstack);
|
||||
// CraftBukkit start
|
||||
if (blockstate != null) {
|
|
@ -1,35 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Doc <nachito94@msn.com>
|
||||
Date: Mon, 2 Aug 2021 11:24:39 -0400
|
||||
Subject: [PATCH] Add configurable height for slime spawn
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/monster/Slime.java b/src/main/java/net/minecraft/world/entity/monster/Slime.java
|
||||
index 7b05640465a47ea8680b4a0b6648a77ea7a1b404..3da612821708f10f7f53b1c9e70adc649689529e 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/monster/Slime.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java
|
||||
@@ -322,7 +322,11 @@ public class Slime extends Mob implements Enemy {
|
||||
|
||||
public static boolean checkSlimeSpawnRules(EntityType<Slime> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
||||
if (world.getDifficulty() != Difficulty.PEACEFUL) {
|
||||
- if (world.getBiome(pos).is(BiomeTags.ALLOWS_SURFACE_SLIME_SPAWNS) && pos.getY() > 50 && pos.getY() < 70 && random.nextFloat() < 0.5F && random.nextFloat() < world.getMoonBrightness() && world.getMaxLocalRawBrightness(pos) <= random.nextInt(8)) {
|
||||
+ // Paper start - Replace rules for Height in Swamp Biome
|
||||
+ final double maxHeightSwamp = world.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.surfaceBiome.maximum;
|
||||
+ final double minHeightSwamp = world.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.surfaceBiome.minimum;
|
||||
+ if (world.getBiome(pos).is(BiomeTags.ALLOWS_SURFACE_SLIME_SPAWNS) && pos.getY() > minHeightSwamp && pos.getY() < maxHeightSwamp && random.nextFloat() < 0.5F && random.nextFloat() < world.getMoonBrightness() && world.getMaxLocalRawBrightness(pos) <= random.nextInt(8)) {
|
||||
+ // Paper end
|
||||
return checkMobSpawnRules(type, world, spawnReason, pos, random);
|
||||
}
|
||||
|
||||
@@ -333,7 +337,10 @@ public class Slime extends Mob implements Enemy {
|
||||
ChunkPos chunkcoordintpair = new ChunkPos(pos);
|
||||
boolean flag = world.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(chunkcoordintpair.x, chunkcoordintpair.z, ((WorldGenLevel) world).getSeed(), world.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Spigot // Paper
|
||||
|
||||
- if (random.nextInt(10) == 0 && flag && pos.getY() < 40) {
|
||||
+ // Paper start - Replace rules for Height in Slime Chunks
|
||||
+ final double maxHeightSlimeChunk = world.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.slimeChunk.maximum;
|
||||
+ if (random.nextInt(10) == 0 && flag && pos.getY() < maxHeightSlimeChunk) {
|
||||
+ // Paper end
|
||||
return checkMobSpawnRules(type, world, spawnReason, pos, random);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MCMDEV <john-m.1@gmx.de>
|
||||
Date: Fri, 24 Sep 2021 17:59:21 +0200
|
||||
Subject: [PATCH] Added getHostname to AsyncPlayerPreLoginEvent
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
index 7e8401a05905775f7209abb1269e24aad409af50..0bedd9eadbfe1ce290f22d6c648571e25e3ae0e9 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
@@ -371,7 +371,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
||||
|
||||
// Paper start
|
||||
com.destroystokyo.paper.profile.PlayerProfile profile = com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(ServerLoginPacketListenerImpl.this.gameProfile);
|
||||
- AsyncPlayerPreLoginEvent asyncEvent = new AsyncPlayerPreLoginEvent(playerName, address, rawAddress, uniqueId, profile); // Paper - add rawAddress
|
||||
+ AsyncPlayerPreLoginEvent asyncEvent = new AsyncPlayerPreLoginEvent(playerName, address, rawAddress, uniqueId, profile, ServerLoginPacketListenerImpl.this.connection.hostname); // Paper - add rawAddress & hostname
|
||||
server.getPluginManager().callEvent(asyncEvent);
|
||||
profile = asyncEvent.getPlayerProfile();
|
||||
profile.complete(true); // Paper - setPlayerProfileAPI
|
|
@ -1,32 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Sun, 16 Jan 2022 10:34:02 -0800
|
||||
Subject: [PATCH] Fix xp reward for baby zombies
|
||||
|
||||
The field that tracks the xpReward was not
|
||||
getting reset if the death was cancelled
|
||||
so this resets it after each call to
|
||||
Zombie#getExperienceReward
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
|
||||
index 185f0c0d709f374cf6df2654dd9708fca90b1349..9976205537cfe228735687f1e9c52c74ac025690 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
|
||||
@@ -174,11 +174,16 @@ public class Zombie extends Monster {
|
||||
|
||||
@Override
|
||||
public int getExperienceReward() {
|
||||
+ final int previousReward = this.xpReward; // Paper - store previous value to reset after calculating XP reward
|
||||
if (this.isBaby()) {
|
||||
this.xpReward = (int) ((double) this.xpReward * 2.5D);
|
||||
}
|
||||
|
||||
- return super.getExperienceReward();
|
||||
+ // Paper start - only change the XP reward for the calculations in the super method
|
||||
+ int reward = super.getExperienceReward();
|
||||
+ this.xpReward = previousReward;
|
||||
+ return reward;
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
@Override
|
|
@ -1,42 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Nassim Jahnke <nassim@njahnke.dev>
|
||||
Date: Mon, 17 Jan 2022 19:47:19 +0100
|
||||
Subject: [PATCH] Kick on main for illegal chat
|
||||
|
||||
Makes the PlayerKickEvent fire on the main thread for
|
||||
illegal characters or chat out-of-order errors.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index a036621f28f42151871c78337290e1158f1ca83f..7a274daf2dcc96ce529708293cf0708456067188 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -2168,7 +2168,9 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
}
|
||||
// CraftBukkit end
|
||||
if (ServerGamePacketListenerImpl.isChatMessageIllegal(packet.message())) {
|
||||
+ this.server.scheduleOnMain(() -> { // Paper - push to main for event firing
|
||||
this.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper - add cause
|
||||
+ }); // Paper - push to main for event firing
|
||||
} else {
|
||||
Optional<LastSeenMessages> optional = this.tryHandleChat(packet.message(), packet.timeStamp(), packet.lastSeenMessages());
|
||||
|
||||
@@ -2202,7 +2204,9 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
@Override
|
||||
public void handleChatCommand(ServerboundChatCommandPacket packet) {
|
||||
if (ServerGamePacketListenerImpl.isChatMessageIllegal(packet.command())) {
|
||||
+ this.server.scheduleOnMain(() -> { // Paper - push to main for event firing
|
||||
this.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper
|
||||
+ }); // Paper - push to main for event firing
|
||||
} else {
|
||||
Optional<LastSeenMessages> optional = this.tryHandleChat(packet.command(), packet.timeStamp(), packet.lastSeenMessages());
|
||||
|
||||
@@ -2282,7 +2286,9 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
private Optional<LastSeenMessages> tryHandleChat(String message, Instant timestamp, LastSeenMessages.Update acknowledgment) {
|
||||
if (!this.updateChatOrder(timestamp)) {
|
||||
ServerGamePacketListenerImpl.LOGGER.warn("{} sent out-of-order chat: '{}'", this.player.getName().getString(), message);
|
||||
+ this.server.scheduleOnMain(() -> { // Paper - push to main
|
||||
this.disconnect(Component.translatable("multiplayer.disconnect.out_of_order_chat"), org.bukkit.event.player.PlayerKickEvent.Cause.OUT_OF_ORDER_CHAT); // Paper - kick event ca
|
||||
+ }); // Paper - push to main
|
||||
return Optional.empty();
|
||||
} else if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales
|
||||
this.send(new ClientboundSystemChatPacket(PaperAdventure.asAdventure(Component.translatable("chat.disabled.options").withStyle(ChatFormatting.RED)), false)); // Paper - Adventure
|
|
@ -1,66 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Brody Beckwith <brody@beckwith.dev>
|
||||
Date: Fri, 14 Jan 2022 00:41:11 -0500
|
||||
Subject: [PATCH] Multi Block Change API Implementation
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
|
||||
index 0d851e197e65ec79386ad1f981f6d1d38b65124a..24c677e80af652952263253409c050641e72e3b5 100644
|
||||
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
|
||||
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
|
||||
@@ -63,6 +63,15 @@ public class ClientboundSectionBlocksUpdatePacket implements Packet<ClientGamePa
|
||||
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public ClientboundSectionBlocksUpdatePacket(SectionPos sectionPos, it.unimi.dsi.fastutil.shorts.Short2ObjectMap<BlockState> blockChanges, boolean suppressLightUpdates) {
|
||||
+ this.sectionPos = sectionPos;
|
||||
+ this.positions = blockChanges.keySet().toShortArray();
|
||||
+ this.states = blockChanges.values().toArray(new BlockState[0]);
|
||||
+ this.suppressLightUpdates = suppressLightUpdates;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeLong(this.sectionPos.asLong());
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
index 13855196d3c5405116bcd60518e8dd6b4fa0bf3f..7034ca7b50c12b000abf33b14fbe216fe8b14a8c 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
@@ -943,6 +943,35 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
this.getHandle().connection.send(packet);
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public void sendMultiBlockChange(Map<Location, BlockData> blockChanges, boolean suppressLightUpdates) {
|
||||
+ if (this.getHandle().connection == null) return;
|
||||
+
|
||||
+ Map<SectionPos, it.unimi.dsi.fastutil.shorts.Short2ObjectMap<net.minecraft.world.level.block.state.BlockState>> sectionMap = new HashMap<>();
|
||||
+
|
||||
+ for (Map.Entry<Location, BlockData> entry : blockChanges.entrySet()) {
|
||||
+ Location location = entry.getKey();
|
||||
+ if (!location.getWorld().equals(this.getWorld())) continue;
|
||||
+
|
||||
+ BlockData blockData = entry.getValue();
|
||||
+ BlockPos blockPos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
+ SectionPos sectionPos = SectionPos.of(blockPos);
|
||||
+
|
||||
+ it.unimi.dsi.fastutil.shorts.Short2ObjectMap<net.minecraft.world.level.block.state.BlockState> sectionData = sectionMap.computeIfAbsent(sectionPos, key -> new it.unimi.dsi.fastutil.shorts.Short2ObjectArrayMap<>());
|
||||
+ sectionData.put(SectionPos.sectionRelativePos(blockPos), ((CraftBlockData) blockData).getState());
|
||||
+ }
|
||||
+
|
||||
+ for (Map.Entry<SectionPos, it.unimi.dsi.fastutil.shorts.Short2ObjectMap<net.minecraft.world.level.block.state.BlockState>> entry : sectionMap.entrySet()) {
|
||||
+ SectionPos sectionPos = entry.getKey();
|
||||
+ it.unimi.dsi.fastutil.shorts.Short2ObjectMap<net.minecraft.world.level.block.state.BlockState> blockData = entry.getValue();
|
||||
+
|
||||
+ net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket packet = new net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket(sectionPos, blockData, suppressLightUpdates);
|
||||
+ this.getHandle().connection.send(packet);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public void sendBlockChanges(Collection<BlockState> blocks, boolean suppressLightUpdates) {
|
||||
Preconditions.checkArgument(blocks != null, "blocks must not be null");
|
|
@ -1,55 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Kieran Wallbanks <kieran.wallbanks@gmail.com>
|
||||
Date: Mon, 21 Jun 2021 14:23:50 +0100
|
||||
Subject: [PATCH] Fix NotePlayEvent
|
||||
|
||||
== AT ==
|
||||
public org.bukkit.craftbukkit.block.data.CraftBlockData toNMS(Ljava/lang/Enum;Ljava/lang/Class;)Ljava/lang/Enum;
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/NoteBlock.java b/src/main/java/net/minecraft/world/level/block/NoteBlock.java
|
||||
index 52a174884f416c9e61591e18aba3b4cd0073ea02..df7965c86b9c9e89b07b76c75b638d391ea6cc34 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/NoteBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/NoteBlock.java
|
||||
@@ -88,11 +88,12 @@ public class NoteBlock extends Block {
|
||||
private void playNote(@Nullable Entity entity, BlockState state, Level world, BlockPos pos) {
|
||||
if (!((NoteBlockInstrument) state.getValue(NoteBlock.INSTRUMENT)).requiresAirAbove() || world.getBlockState(pos.above()).isAir()) {
|
||||
// CraftBukkit start
|
||||
- org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, pos, state.getValue(NoteBlock.INSTRUMENT), state.getValue(NoteBlock.NOTE));
|
||||
- if (event.isCancelled()) {
|
||||
- return;
|
||||
- }
|
||||
+ // org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, pos, state.getValue(NoteBlock.INSTRUMENT), state.getValue(NoteBlock.NOTE));
|
||||
+ // if (event.isCancelled()) {
|
||||
+ // return;
|
||||
+ // }
|
||||
// CraftBukkit end
|
||||
+ // Paper - TODO any way to cancel the game event?
|
||||
world.blockEvent(pos, this, 0, 0);
|
||||
world.gameEvent(entity, GameEvent.NOTE_BLOCK_PLAY, pos);
|
||||
}
|
||||
@@ -122,11 +123,15 @@ public class NoteBlock extends Block {
|
||||
|
||||
@Override
|
||||
public boolean triggerEvent(BlockState state, Level world, BlockPos pos, int type, int data) {
|
||||
+ // Paper start - move NotePlayEvent call to fix instrument/note changes
|
||||
+ org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, pos, state.getValue(INSTRUMENT), state.getValue(NOTE));
|
||||
+ if (event.isCancelled()) return false;
|
||||
+ // Paper end
|
||||
NoteBlockInstrument blockpropertyinstrument = (NoteBlockInstrument) state.getValue(NoteBlock.INSTRUMENT);
|
||||
float f;
|
||||
|
||||
if (blockpropertyinstrument.isTunable()) {
|
||||
- int k = (Integer) state.getValue(NoteBlock.NOTE);
|
||||
+ int k = event.getNote().getId(); // Paper
|
||||
|
||||
f = (float) Math.pow(2.0D, (double) (k - 12) / 12.0D);
|
||||
world.addParticle(ParticleTypes.NOTE, (double) pos.getX() + 0.5D, (double) pos.getY() + 1.2D, (double) pos.getZ() + 0.5D, (double) k / 24.0D, 0.0D, 0.0D);
|
||||
@@ -148,7 +153,7 @@ public class NoteBlock extends Block {
|
||||
holder = blockpropertyinstrument.getSoundEvent();
|
||||
}
|
||||
|
||||
- world.playSeededSound((Player) null, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, holder, SoundSource.RECORDS, 3.0F, f, world.random.nextLong());
|
||||
+ world.playSeededSound((Player) null, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, org.bukkit.craftbukkit.block.data.CraftBlockData.toNMS(event.getInstrument(), NoteBlockInstrument.class).getSoundEvent(), SoundSource.RECORDS, 3.0F, f, world.random.nextLong()); // Paper
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
|
||||
Date: Sun, 26 Dec 2021 20:27:43 -0500
|
||||
Subject: [PATCH] Freeze Tick Lock API
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index 9bbc8e1f15b5dfc62bcb065d6ebb87fa58afb91e..ac9a612488bf8a3efdfbfbfe9fb1de818dac742b 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -396,6 +396,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
private org.bukkit.util.Vector origin;
|
||||
@javax.annotation.Nullable
|
||||
private UUID originWorld;
|
||||
+ public boolean freezeLocked = false; // Paper - Freeze Tick Lock API
|
||||
|
||||
public void setOrigin(@javax.annotation.Nonnull Location location) {
|
||||
this.origin = location.toVector();
|
||||
@@ -825,7 +826,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
this.setRemainingFireTicks(this.remainingFireTicks - 1);
|
||||
}
|
||||
|
||||
- if (this.getTicksFrozen() > 0) {
|
||||
+ if (this.getTicksFrozen() > 0 && !freezeLocked) { // Paper - Freeze Tick Lock API
|
||||
this.setTicksFrozen(0);
|
||||
this.level.levelEvent((Player) null, 1009, this.blockPosition, 1);
|
||||
}
|
||||
@@ -2285,6 +2286,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
if (fromNetherPortal) {
|
||||
nbt.putBoolean("Paper.FromNetherPortal", true);
|
||||
}
|
||||
+ if (freezeLocked) {
|
||||
+ nbt.putBoolean("Paper.FreezeLock", true);
|
||||
+ }
|
||||
// Paper end
|
||||
return nbt;
|
||||
} catch (Throwable throwable) {
|
||||
@@ -2449,6 +2453,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
if (spawnReason == null) {
|
||||
spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT;
|
||||
}
|
||||
+ if (nbt.contains("Paper.FreezeLock")) {
|
||||
+ freezeLocked = nbt.getBoolean("Paper.FreezeLock");
|
||||
+ }
|
||||
// Paper end
|
||||
|
||||
} catch (Throwable throwable) {
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
index c1185c4bd9151b08a9c7707e2eb8517bbeb47919..13ff58b76d7c1c02921ee3266a67f7c5befef30e 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -3411,7 +3411,7 @@ public abstract class LivingEntity extends Entity {
|
||||
boolean flag1 = this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES);
|
||||
int i;
|
||||
|
||||
- if (!this.level.isClientSide && !this.isDeadOrDying()) {
|
||||
+ if (!this.level.isClientSide && !this.isDeadOrDying() && !freezeLocked) { // Paper - Freeze Tick Lock API
|
||||
i = this.getTicksFrozen();
|
||||
if (this.isInPowderSnow && this.canFreeze()) {
|
||||
this.setTicksFrozen(Math.min(this.getTicksRequiredToFreeze(), i + 1));
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
index 03b20cec361a2eff27c1187b2a4c5a65d2b4f9b4..32b9816283c8c1de929d5664733553277cf6bf3c 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
@@ -652,6 +652,17 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||
return this.getHandle().isFullyFrozen();
|
||||
}
|
||||
|
||||
+ // Paper Start - Freeze Tick Lock API
|
||||
+ @Override
|
||||
+ public boolean isFreezeTickingLocked() {
|
||||
+ return this.entity.freezeLocked;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void lockFreezeTicks(boolean locked) {
|
||||
+ this.entity.freezeLocked = locked;
|
||||
+ }
|
||||
+ // Paper end - Freeze Tick Lock API
|
||||
@Override
|
||||
public void remove() {
|
||||
this.entity.discard();
|
|
@ -1,45 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
|
||||
Date: Tue, 7 Dec 2021 19:34:23 -0500
|
||||
Subject: [PATCH] Dolphin API
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java
|
||||
index 938e141f161acf5de5d3361382b514caea02c6fb..c1db88ceb65eb81c542171fc5465224ef613ce3b 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java
|
||||
@@ -24,4 +24,34 @@ public class CraftDolphin extends CraftWaterMob implements Dolphin {
|
||||
public EntityType getType() {
|
||||
return EntityType.DOLPHIN;
|
||||
}
|
||||
+
|
||||
+ @Override
|
||||
+ public int getMoistness() {
|
||||
+ return this.getHandle().getMoistnessLevel();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setMoistness(int moistness) {
|
||||
+ this.getHandle().setMoisntessLevel(moistness);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setHasFish(boolean hasFish) {
|
||||
+ this.getHandle().setGotFish(hasFish);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean hasFish() {
|
||||
+ return this.getHandle().gotFish();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public org.bukkit.Location getTreasureLocation() {
|
||||
+ return io.papermc.paper.util.MCUtil.toLocation(this.getHandle().level, this.getHandle().getTreasurePos());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setTreasureLocation(org.bukkit.Location location) {
|
||||
+ this.getHandle().setTreasurePos(io.papermc.paper.util.MCUtil.toBlockPosition(location));
|
||||
+ }
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Thu, 27 May 2021 21:58:24 -0700
|
||||
Subject: [PATCH] More PotionEffectType API
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java
|
||||
index 5a8c29cc117ce9478cc205f6fdbe63d227268775..3bda325cfd5d0d8dcc878cf547c9ae9e82fde240 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java
|
||||
@@ -105,4 +105,46 @@ public class CraftPotionEffectType extends PotionEffectType {
|
||||
public Color getColor() {
|
||||
return Color.fromRGB(this.handle.getColor());
|
||||
}
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public org.bukkit.NamespacedKey getKey() {
|
||||
+ return org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(net.minecraft.core.registries.BuiltInRegistries.MOB_EFFECT.getKey(this.handle));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public java.util.Map<org.bukkit.attribute.Attribute, org.bukkit.attribute.AttributeModifier> getEffectAttributes() {
|
||||
+ // re-create map each time because a nms MobEffect can have its attributes modified
|
||||
+ final java.util.Map<org.bukkit.attribute.Attribute, org.bukkit.attribute.AttributeModifier> attributeMap = new java.util.HashMap<>();
|
||||
+ this.handle.getAttributeModifiers().forEach((attribute, attributeModifier) -> {
|
||||
+ attributeMap.put(org.bukkit.craftbukkit.attribute.CraftAttributeMap.fromMinecraft(attribute.toString()), org.bukkit.craftbukkit.attribute.CraftAttributeInstance.convert(attributeModifier));
|
||||
+ });
|
||||
+ return java.util.Map.copyOf(attributeMap);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public double getAttributeModifierAmount(org.bukkit.attribute.Attribute attribute, int effectAmplifier) {
|
||||
+ com.google.common.base.Preconditions.checkArgument(effectAmplifier >= 0, "effectAmplifier must be greater than or equal to 0");
|
||||
+ net.minecraft.world.entity.ai.attributes.Attribute nmsAttribute = org.bukkit.craftbukkit.attribute.CraftAttributeMap.toMinecraft(attribute);
|
||||
+ com.google.common.base.Preconditions.checkArgument(this.handle.getAttributeModifiers().containsKey(nmsAttribute), attribute + " is not present on " + this.getKey());
|
||||
+ return this.handle.getAttributeModifierValue(effectAmplifier, this.handle.getAttributeModifiers().get(nmsAttribute));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public PotionEffectType.Category getEffectCategory() {
|
||||
+ return fromNMS(handle.getCategory());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String translationKey() {
|
||||
+ return this.handle.getDescriptionId();
|
||||
+ }
|
||||
+
|
||||
+ public static PotionEffectType.Category fromNMS(net.minecraft.world.effect.MobEffectCategory mobEffectInfo) {
|
||||
+ return switch (mobEffectInfo) {
|
||||
+ case BENEFICIAL -> PotionEffectType.Category.BENEFICIAL;
|
||||
+ case HARMFUL -> PotionEffectType.Category.HARMFUL;
|
||||
+ case NEUTRAL -> PotionEffectType.Category.NEUTRAL;
|
||||
+ };
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
diff --git a/src/test/java/io/papermc/paper/effects/EffectCategoryTest.java b/src/test/java/io/papermc/paper/effects/EffectCategoryTest.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..a5012bc0469ba03cde66749a11f4e7d93206bfd7
|
||||
--- /dev/null
|
||||
+++ b/src/test/java/io/papermc/paper/effects/EffectCategoryTest.java
|
||||
@@ -0,0 +1,28 @@
|
||||
+package io.papermc.paper.effects;
|
||||
+
|
||||
+import io.papermc.paper.adventure.PaperAdventure;
|
||||
+import net.minecraft.world.effect.MobEffectCategory;
|
||||
+import org.bukkit.craftbukkit.potion.CraftPotionEffectType;
|
||||
+import org.bukkit.potion.PotionEffectType;
|
||||
+import org.junit.Test;
|
||||
+
|
||||
+import static org.junit.Assert.assertEquals;
|
||||
+import static org.junit.Assert.assertNotNull;
|
||||
+
|
||||
+public class EffectCategoryTest {
|
||||
+
|
||||
+ @Test
|
||||
+ public void testEffectCategoriesExist() {
|
||||
+ for (MobEffectCategory mobEffectInfo : MobEffectCategory.values()) {
|
||||
+ assertNotNull(mobEffectInfo + " is missing a bukkit equivalent", CraftPotionEffectType.fromNMS(mobEffectInfo));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Test
|
||||
+ public void testCategoryHasEquivalentColors() {
|
||||
+ for (MobEffectCategory mobEffectInfo : MobEffectCategory.values()) {
|
||||
+ PotionEffectType.Category bukkitEffectCategory = CraftPotionEffectType.fromNMS(mobEffectInfo);
|
||||
+ assertEquals(mobEffectInfo.getTooltipFormatting().name() + " doesn't equal " + bukkitEffectCategory.getColor(), bukkitEffectCategory.getColor(), PaperAdventure.asAdventure(mobEffectInfo.getTooltipFormatting()));
|
||||
+ }
|
||||
+ }
|
||||
+}
|
|
@ -1,20 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shane Freeder <theboyetronic@gmail.com>
|
||||
Date: Mon, 12 Jul 2021 12:28:29 +0100
|
||||
Subject: [PATCH] Use a CHM for StructureTemplate.Pallete cache
|
||||
|
||||
fixes a CME due to this collection being shared across threads
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java b/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java
|
||||
index 76666318690368bab672c2302c90dd3abb050299..d1bfb95114b733dde9537c4de6bd1bccb696cd18 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java
|
||||
@@ -833,7 +833,7 @@ public class StructureTemplate {
|
||||
public static final class Palette {
|
||||
|
||||
private final List<StructureTemplate.StructureBlockInfo> blocks;
|
||||
- private final Map<Block, List<StructureTemplate.StructureBlockInfo>> cache = Maps.newHashMap();
|
||||
+ private final Map<Block, List<StructureTemplate.StructureBlockInfo>> cache = Maps.newConcurrentMap(); // Paper
|
||||
|
||||
Palette(List<StructureTemplate.StructureBlockInfo> infos) {
|
||||
this.blocks = infos;
|
|
@ -1,178 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Tue, 1 Feb 2022 15:51:55 -0700
|
||||
Subject: [PATCH] API for creating command sender which forwards feedback
|
||||
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/commands/FeedbackForwardingSender.java b/src/main/java/io/papermc/paper/commands/FeedbackForwardingSender.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..e3a5f1ec376319bdfda87fa27ae217bff3914292
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/commands/FeedbackForwardingSender.java
|
||||
@@ -0,0 +1,111 @@
|
||||
+package io.papermc.paper.commands;
|
||||
+
|
||||
+import io.papermc.paper.adventure.PaperAdventure;
|
||||
+import java.util.UUID;
|
||||
+import java.util.function.Consumer;
|
||||
+import net.kyori.adventure.audience.MessageType;
|
||||
+import net.kyori.adventure.identity.Identity;
|
||||
+import net.kyori.adventure.text.Component;
|
||||
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
+import net.minecraft.commands.CommandSource;
|
||||
+import net.minecraft.commands.CommandSourceStack;
|
||||
+import net.minecraft.server.level.ServerLevel;
|
||||
+import net.minecraft.world.phys.Vec2;
|
||||
+import net.minecraft.world.phys.Vec3;
|
||||
+import org.bukkit.command.CommandSender;
|
||||
+import org.bukkit.craftbukkit.CraftServer;
|
||||
+import org.bukkit.craftbukkit.command.ServerCommandSender;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class FeedbackForwardingSender extends ServerCommandSender {
|
||||
+ private final Consumer<? super Component> feedback;
|
||||
+ private final CraftServer server;
|
||||
+
|
||||
+ public FeedbackForwardingSender(final Consumer<? super Component> feedback, final CraftServer server) {
|
||||
+ super(((ServerCommandSender) server.getConsoleSender()).perm);
|
||||
+ this.server = server;
|
||||
+ this.feedback = feedback;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void sendMessage(final String message) {
|
||||
+ this.sendMessage(LegacyComponentSerializer.legacySection().deserialize(message));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void sendMessage(final String... messages) {
|
||||
+ for (final String message : messages) {
|
||||
+ this.sendMessage(message);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void sendMessage(final Identity identity, final Component message, final MessageType type) {
|
||||
+ this.feedback.accept(message);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getName() {
|
||||
+ return "FeedbackForwardingSender";
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Component name() {
|
||||
+ return Component.text(this.getName());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isOp() {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setOp(final boolean value) {
|
||||
+ throw new UnsupportedOperationException("Cannot change operator status of " + this.getClass().getName());
|
||||
+ }
|
||||
+
|
||||
+ public CommandSourceStack asVanilla() {
|
||||
+ final @Nullable ServerLevel overworld = this.server.getServer().overworld();
|
||||
+ return new CommandSourceStack(
|
||||
+ new Source(this),
|
||||
+ overworld == null ? Vec3.ZERO : Vec3.atLowerCornerOf(overworld.getSharedSpawnPos()),
|
||||
+ Vec2.ZERO,
|
||||
+ overworld,
|
||||
+ 4,
|
||||
+ this.getName(),
|
||||
+ net.minecraft.network.chat.Component.literal(this.getName()),
|
||||
+ this.server.getServer(),
|
||||
+ null
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ private record Source(FeedbackForwardingSender sender) implements CommandSource {
|
||||
+ @Override
|
||||
+ public void sendSystemMessage(final net.minecraft.network.chat.Component message) {
|
||||
+ this.sender.sendMessage(Identity.nil(), PaperAdventure.asAdventure(message));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean acceptsSuccess() {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean acceptsFailure() {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean shouldInformAdmins() {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public CommandSender getBukkitSender(final CommandSourceStack stack) {
|
||||
+ return this.sender;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 759d091593b75419e275e79eaa3afbfbef6aee10..11f133dccb7bb837d369b2b41f96b26875dc6967 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -2000,6 +2000,13 @@ public final class CraftServer implements Server {
|
||||
return console.console;
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public CommandSender createCommandSender(final java.util.function.Consumer<? super net.kyori.adventure.text.Component> feedback) {
|
||||
+ return new io.papermc.paper.commands.FeedbackForwardingSender(feedback, this);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public EntityMetadataStore getEntityMetadata() {
|
||||
return this.entityMetadata;
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java
|
||||
index eaff8df6c8c12c64e005a68a02e2e35ed88f757c..1c638a4b1f2c841928d8b2a7ae43e4ebb1f7eac7 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java
|
||||
@@ -13,9 +13,15 @@ import org.bukkit.plugin.Plugin;
|
||||
|
||||
public abstract class ServerCommandSender implements CommandSender {
|
||||
private static PermissibleBase blockPermInst;
|
||||
- private final PermissibleBase perm;
|
||||
+ public final PermissibleBase perm; // Paper
|
||||
private net.kyori.adventure.pointer.Pointers adventure$pointers; // Paper - implement pointers
|
||||
|
||||
+ // Paper start
|
||||
+ public ServerCommandSender(final PermissibleBase permissibleBase) {
|
||||
+ this.perm = permissibleBase;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public ServerCommandSender() {
|
||||
if (this instanceof CraftBlockCommandSender) {
|
||||
if (ServerCommandSender.blockPermInst == null) {
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
|
||||
index 6df44aa60d2b41b95fe79ed4cf92a6d3369400ea..6035af2cf08353b3d3801220d8116d8611a0cd37 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
|
||||
@@ -84,6 +84,11 @@ public final class VanillaCommandWrapper extends BukkitCommand {
|
||||
if (sender instanceof ProxiedCommandSender) {
|
||||
return ((ProxiedNativeCommandSender) sender).getHandle();
|
||||
}
|
||||
+ // Paper start
|
||||
+ if (sender instanceof io.papermc.paper.commands.FeedbackForwardingSender feedback) {
|
||||
+ return feedback.asVanilla();
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
throw new IllegalArgumentException("Cannot make " + sender + " a vanilla command listener");
|
||||
}
|
|
@ -1,264 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Thu, 13 Jan 2022 23:05:53 -0800
|
||||
Subject: [PATCH] Add missing structure set seed configs
|
||||
|
||||
The 4 missing structure set seed configs are strongholds, mineshafts,
|
||||
buried treasure, and ancient cities.
|
||||
|
||||
Strongholds use a ring placement scheme which isn't random so they
|
||||
utilize the world seed by default, this adds a config to override it
|
||||
for just generating the ring positions.
|
||||
|
||||
Mineshafts and Buried Treasure structure sets are special cases
|
||||
where the "salt" that can be defined for them via datapacks has 0
|
||||
effect because the difference between the spacing and separation is 1
|
||||
which is used as the upper bound in the random with salt. So the random
|
||||
always returns the same int (0) so the salt has no effect. This adds
|
||||
seeds/salts to the frequency reducer which has a similar effect.
|
||||
|
||||
Co-authored-by: William Blake Galbreath <blake.galbreath@gmail.com>
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
|
||||
index 9ef0937b7292ec118d2b65e9b098f5538410dbac..130ac7164c63374120ca2cdfbb1f6c3eefb4b7a5 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
|
||||
@@ -568,7 +568,7 @@ public abstract class ChunkGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
- if (structureplacement.isStructureChunk(placementCalculator, chunkcoordintpair.x, chunkcoordintpair.z)) {
|
||||
+ if (structureplacement.isStructureChunk(placementCalculator, chunkcoordintpair.x, chunkcoordintpair.z, structureplacement instanceof net.minecraft.world.level.chunk.ChunkGeneratorStructureState.KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - add missing structure set configs
|
||||
if (list.size() == 1) {
|
||||
this.tryGenerateStructure((StructureSet.StructureSelectionEntry) list.get(0), structureAccessor, registryManager, randomstate, structureTemplateManager, placementCalculator.getLevelSeed(), chunk, chunkcoordintpair, sectionposition);
|
||||
} else {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
|
||||
index a310bfbf0d08187375ea17f4b04b276a0b7d0b9f..51a05900d02dc116ea215730713cd2cf2a4f1c23 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
|
||||
@@ -50,13 +50,14 @@ public class ChunkGeneratorStructureState {
|
||||
private final Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> ringPositions = new Object2ObjectArrayMap();
|
||||
private boolean hasGeneratedPositions;
|
||||
private final List<Holder<StructureSet>> possibleStructureSets;
|
||||
+ public final SpigotWorldConfig conf; // Paper
|
||||
|
||||
public static ChunkGeneratorStructureState createForFlat(RandomState randomstate, long i, BiomeSource worldchunkmanager, Stream<Holder<StructureSet>> stream, SpigotWorldConfig conf) { // Spigot
|
||||
List<Holder<StructureSet>> list = stream.filter((holder) -> {
|
||||
return ChunkGeneratorStructureState.hasBiomesForStructureSet((StructureSet) holder.value(), worldchunkmanager);
|
||||
}).toList();
|
||||
|
||||
- return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, 0L, ChunkGeneratorStructureState.injectSpigot(list, conf)); // Spigot
|
||||
+ return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, 0L, ChunkGeneratorStructureState.injectSpigot(list, conf), conf); // Spigot
|
||||
}
|
||||
|
||||
public static ChunkGeneratorStructureState createForNormal(RandomState randomstate, long i, BiomeSource worldchunkmanager, HolderLookup<StructureSet> holderlookup, SpigotWorldConfig conf) { // Spigot
|
||||
@@ -64,14 +65,24 @@ public class ChunkGeneratorStructureState {
|
||||
return ChunkGeneratorStructureState.hasBiomesForStructureSet((StructureSet) holder_c.value(), worldchunkmanager);
|
||||
}).collect(Collectors.toUnmodifiableList());
|
||||
|
||||
- return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, i, ChunkGeneratorStructureState.injectSpigot(list, conf)); // Spigot
|
||||
+ return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, i, ChunkGeneratorStructureState.injectSpigot(list, conf), conf); // Spigot
|
||||
}
|
||||
+ // Paper start - horrible hack because spigot creates a ton of direct Holders which lose track of the identifying key
|
||||
+ public static final class KeyedRandomSpreadStructurePlacement extends RandomSpreadStructurePlacement {
|
||||
+ public final net.minecraft.resources.ResourceKey<StructureSet> key;
|
||||
+ public KeyedRandomSpreadStructurePlacement(net.minecraft.resources.ResourceKey<StructureSet> key, net.minecraft.core.Vec3i locateOffset, FrequencyReductionMethod frequencyReductionMethod, float frequency, int salt, java.util.Optional<StructurePlacement.ExclusionZone> exclusionZone, int spacing, int separation, net.minecraft.world.level.levelgen.structure.placement.RandomSpreadType spreadType) {
|
||||
+ super(locateOffset, frequencyReductionMethod, frequency, salt, exclusionZone, spacing, separation, spreadType);
|
||||
+ this.key = key;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
// Spigot start
|
||||
private static List<Holder<StructureSet>> injectSpigot(List<Holder<StructureSet>> list, SpigotWorldConfig conf) {
|
||||
return list.stream().map((holder) -> {
|
||||
StructureSet structureset = holder.value();
|
||||
- if (structureset.placement() instanceof RandomSpreadStructurePlacement randomConfig) {
|
||||
+ final Holder<StructureSet> newHolder; // Paper
|
||||
+ if (structureset.placement() instanceof RandomSpreadStructurePlacement randomConfig && holder.unwrapKey().orElseThrow().location().getNamespace().equals(net.minecraft.resources.ResourceLocation.DEFAULT_NAMESPACE)) { // Paper - check namespace cause datapacks could add structure sets with the same path
|
||||
String name = holder.unwrapKey().orElseThrow().location().getPath();
|
||||
int seed = randomConfig.salt;
|
||||
|
||||
@@ -118,11 +129,21 @@ public class ChunkGeneratorStructureState {
|
||||
case "villages":
|
||||
seed = conf.villageSeed;
|
||||
break;
|
||||
+ // Paper start
|
||||
+ case "ancient_cities":
|
||||
+ seed = conf.ancientCitySeed;
|
||||
+ break;
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
- structureset = new StructureSet(structureset.structures(), new RandomSpreadStructurePlacement(randomConfig.locateOffset, randomConfig.frequencyReductionMethod, randomConfig.frequency, seed, randomConfig.exclusionZone, randomConfig.spacing(), randomConfig.separation(), randomConfig.spreadType()));
|
||||
+ // Paper start
|
||||
+ structureset = new StructureSet(structureset.structures(), new KeyedRandomSpreadStructurePlacement(holder.unwrapKey().orElseThrow(), randomConfig.locateOffset, randomConfig.frequencyReductionMethod, randomConfig.frequency, seed, randomConfig.exclusionZone, randomConfig.spacing(), randomConfig.separation(), randomConfig.spreadType()));
|
||||
+ newHolder = Holder.direct(structureset); // I really wish we didn't have to do this here
|
||||
+ } else {
|
||||
+ newHolder = holder;
|
||||
}
|
||||
- return Holder.direct(structureset);
|
||||
+ return newHolder;
|
||||
+ // Paper end
|
||||
}).collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
// Spigot end
|
||||
@@ -139,12 +160,13 @@ public class ChunkGeneratorStructureState {
|
||||
return stream.anyMatch(set::contains);
|
||||
}
|
||||
|
||||
- private ChunkGeneratorStructureState(RandomState noiseConfig, BiomeSource biomeSource, long structureSeed, long concentricRingSeed, List<Holder<StructureSet>> structureSets) {
|
||||
+ private ChunkGeneratorStructureState(RandomState noiseConfig, BiomeSource biomeSource, long structureSeed, long concentricRingSeed, List<Holder<StructureSet>> structureSets, SpigotWorldConfig conf) { // Paper
|
||||
this.randomState = noiseConfig;
|
||||
this.levelSeed = structureSeed;
|
||||
this.biomeSource = biomeSource;
|
||||
this.concentricRingsSeed = concentricRingSeed;
|
||||
this.possibleStructureSets = structureSets;
|
||||
+ this.conf = conf; // Paper
|
||||
}
|
||||
|
||||
public List<Holder<StructureSet>> possibleStructureSets() {
|
||||
@@ -198,7 +220,13 @@ public class ChunkGeneratorStructureState {
|
||||
HolderSet<Biome> holderset = placement.preferredBiomes();
|
||||
RandomSource randomsource = RandomSource.create();
|
||||
|
||||
+ // Paper start
|
||||
+ if (this.conf.strongholdSeed != null && structureSetEntry.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) {
|
||||
+ randomsource.setSeed(this.conf.strongholdSeed);
|
||||
+ } else {
|
||||
+ // Paper end
|
||||
randomsource.setSeed(this.concentricRingsSeed);
|
||||
+ } // Paper
|
||||
double d0 = randomsource.nextDouble() * 3.141592653589793D * 2.0D;
|
||||
int l = 0;
|
||||
int i1 = 0;
|
||||
@@ -275,7 +303,7 @@ public class ChunkGeneratorStructureState {
|
||||
|
||||
for (int l = centerChunkX - chunkCount; l <= centerChunkX + chunkCount; ++l) {
|
||||
for (int i1 = centerChunkZ - chunkCount; i1 <= centerChunkZ + chunkCount; ++i1) {
|
||||
- if (structureplacement.isStructureChunk(this, l, i1)) {
|
||||
+ if (structureplacement.isStructureChunk(this, l, i1, structureplacement instanceof KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - add missing structure set configs
|
||||
return true;
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
|
||||
index 65dcb14241baadb2c9f8f16919d7b562198ad9c3..594a2dd3b1d4c29c969d1992b8e93795da00e682 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
|
||||
@@ -59,10 +59,24 @@ public abstract class StructurePlacement {
|
||||
return this.exclusionZone;
|
||||
}
|
||||
|
||||
+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper
|
||||
public boolean isStructureChunk(ChunkGeneratorStructureState calculator, int chunkX, int chunkZ) {
|
||||
+ // Paper start - add missing structure set configs
|
||||
+ return this.isStructureChunk(calculator, chunkX, chunkZ, null);
|
||||
+ }
|
||||
+ public boolean isStructureChunk(ChunkGeneratorStructureState calculator, int chunkX, int chunkZ, @org.jetbrains.annotations.Nullable net.minecraft.resources.ResourceKey<StructureSet> structureSetKey) {
|
||||
+ Integer saltOverride = null;
|
||||
+ if (structureSetKey != null) {
|
||||
+ if (structureSetKey == net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.MINESHAFTS) {
|
||||
+ saltOverride = calculator.conf.mineshaftSeed;
|
||||
+ } else if (structureSetKey == net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.BURIED_TREASURES) {
|
||||
+ saltOverride = calculator.conf.buriedTreasureSeed;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
if (!this.isPlacementChunk(calculator, chunkX, chunkZ)) {
|
||||
return false;
|
||||
- } else if (this.frequency < 1.0F && !this.frequencyReductionMethod.shouldGenerate(calculator.getLevelSeed(), this.salt, chunkX, chunkZ, this.frequency)) {
|
||||
+ } else if (this.frequency < 1.0F && !this.frequencyReductionMethod.shouldGenerate(calculator.getLevelSeed(), this.salt, chunkX, chunkZ, this.frequency, saltOverride)) { // Paper
|
||||
return false;
|
||||
} else {
|
||||
return !this.exclusionZone.isPresent() || !this.exclusionZone.get().isPlacementForbidden(calculator, chunkX, chunkZ);
|
||||
@@ -77,25 +91,31 @@ public abstract class StructurePlacement {
|
||||
|
||||
public abstract StructurePlacementType<?> type();
|
||||
|
||||
- private static boolean probabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency) {
|
||||
+ private static boolean probabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - ignore here
|
||||
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
|
||||
worldgenRandom.setLargeFeatureWithSalt(seed, salt, chunkX, chunkZ);
|
||||
return worldgenRandom.nextFloat() < frequency;
|
||||
}
|
||||
|
||||
- private static boolean legacyProbabilityReducerWithDouble(long seed, int salt, int chunkX, int chunkZ, float frequency) {
|
||||
+ private static boolean legacyProbabilityReducerWithDouble(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper
|
||||
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
|
||||
+ if (saltOverride == null) { // Paper
|
||||
worldgenRandom.setLargeFeatureSeed(seed, chunkX, chunkZ);
|
||||
+ // Paper start
|
||||
+ } else {
|
||||
+ worldgenRandom.setLargeFeatureWithSalt(seed, chunkX, chunkZ, saltOverride);
|
||||
+ }
|
||||
+ // Paper end
|
||||
return worldgenRandom.nextDouble() < (double)frequency;
|
||||
}
|
||||
|
||||
- private static boolean legacyArbitrarySaltProbabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency) {
|
||||
+ private static boolean legacyArbitrarySaltProbabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper
|
||||
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
|
||||
- worldgenRandom.setLargeFeatureWithSalt(seed, chunkX, chunkZ, 10387320);
|
||||
+ worldgenRandom.setLargeFeatureWithSalt(seed, chunkX, chunkZ, saltOverride != null ? saltOverride : HIGHLY_ARBITRARY_RANDOM_SALT); // Paper
|
||||
return worldgenRandom.nextFloat() < frequency;
|
||||
}
|
||||
|
||||
- private static boolean legacyPillagerOutpostReducer(long seed, int salt, int chunkX, int chunkZ, float frequency) {
|
||||
+ private static boolean legacyPillagerOutpostReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - ignore here
|
||||
int i = chunkX >> 4;
|
||||
int j = chunkZ >> 4;
|
||||
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
|
||||
@@ -118,7 +138,7 @@ public abstract class StructurePlacement {
|
||||
|
||||
@FunctionalInterface
|
||||
public interface FrequencyReducer {
|
||||
- boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance);
|
||||
+ boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance, @org.jetbrains.annotations.Nullable Integer saltOverride); // Paper
|
||||
}
|
||||
|
||||
public static enum FrequencyReductionMethod implements StringRepresentable {
|
||||
@@ -136,8 +156,8 @@ public abstract class StructurePlacement {
|
||||
this.reducer = generationPredicate;
|
||||
}
|
||||
|
||||
- public boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance) {
|
||||
- return this.reducer.shouldGenerate(seed, salt, chunkX, chunkZ, chance);
|
||||
+ public boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper
|
||||
+ return this.reducer.shouldGenerate(seed, salt, chunkX, chunkZ, chance, saltOverride); // Paper
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java
|
||||
index 6bcc46795d1f78746192cc107c4a1f61580ec3c5..5503ad6a93d331771a0e92c0da6adedf2ac81aff 100644
|
||||
--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
|
||||
+++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
|
||||
@@ -362,6 +362,16 @@ public class SpigotWorldConfig
|
||||
public int mansionSeed;
|
||||
public int fossilSeed;
|
||||
public int portalSeed;
|
||||
+ // Paper start - add missing structure set configs
|
||||
+ public int ancientCitySeed;
|
||||
+ public int buriedTreasureSeed;
|
||||
+ public Integer mineshaftSeed;
|
||||
+ public Long strongholdSeed;
|
||||
+ private <N extends Number> N getSeed(String path, java.util.function.Function<String, N> toNumberFunc) {
|
||||
+ final String value = this.getString(path, "default");
|
||||
+ return org.apache.commons.lang3.math.NumberUtils.isParsable(value) ? toNumberFunc.apply(value) : null;
|
||||
+ }
|
||||
+ // Paper end
|
||||
private void initWorldGenSeeds()
|
||||
{
|
||||
this.villageSeed = this.getInt( "seed-village", 10387312 );
|
||||
@@ -379,6 +389,12 @@ public class SpigotWorldConfig
|
||||
this.mansionSeed = this.getInt( "seed-mansion", 10387319 );
|
||||
this.fossilSeed = this.getInt( "seed-fossil", 14357921 );
|
||||
this.portalSeed = this.getInt( "seed-portal", 34222645 );
|
||||
+ // Paper start - add missing structure set configs
|
||||
+ this.ancientCitySeed = this.getInt("seed-ancientcity", 20083232);
|
||||
+ this.buriedTreasureSeed = this.getInt("seed-buriedtreasure", 10387320); // StructurePlacement#HIGHLY_ARBITRARY_RANDOM_SALT
|
||||
+ this.mineshaftSeed = this.getSeed("seed-mineshaft", Integer::parseInt);
|
||||
+ this.strongholdSeed = this.getSeed("seed-stronghold", Long::parseLong);
|
||||
+ // Paper end
|
||||
this.log( "Custom Map Seeds: Village: " + this.villageSeed + " Desert: " + this.desertSeed + " Igloo: " + this.iglooSeed + " Jungle: " + this.jungleSeed + " Swamp: " + this.swampSeed + " Monument: " + this.monumentSeed
|
||||
+ " Ocean: " + this.oceanSeed + " Shipwreck: " + this.shipwreckSeed + " End City: " + this.endCitySeed + " Slime: " + this.slimeSeed + " Nether: " + this.netherSeed + " Mansion: " + this.mansionSeed + " Fossil: " + this.fossilSeed + " Portal: " + this.portalSeed );
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Nassim Jahnke <nassim@njahnke.dev>
|
||||
Date: Mon, 31 Jan 2022 11:21:50 +0100
|
||||
Subject: [PATCH] Implement regenerateChunk
|
||||
|
||||
Co-authored-by: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
index 08fc562b69f93d6be21124ec4309580a26ad5c85..7fc663f4e91fa885a7977638e423b1cce6efce39 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
@@ -138,6 +138,7 @@ import org.bukkit.util.Vector;
|
||||
public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
public static final int CUSTOM_DIMENSION_OFFSET = 10;
|
||||
private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry();
|
||||
+ private static final ChunkStatus[] REGEN_CHUNK_STATUSES = {ChunkStatus.BIOMES, ChunkStatus.NOISE, ChunkStatus.SURFACE, ChunkStatus.CARVERS, ChunkStatus.LIQUID_CARVERS, ChunkStatus.FEATURES}; // Paper - implement regenerate chunk method
|
||||
|
||||
private final ServerLevel world;
|
||||
private WorldBorder worldBorder;
|
||||
@@ -418,27 +419,62 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
@Override
|
||||
public boolean regenerateChunk(int x, int z) {
|
||||
org.spigotmc.AsyncCatcher.catchOp("chunk regenerate"); // Spigot
|
||||
- throw new UnsupportedOperationException("Not supported in this Minecraft version! Unless you can fix it, this is not a bug :)");
|
||||
- /*
|
||||
- if (!unloadChunk0(x, z, false)) {
|
||||
- return false;
|
||||
- }
|
||||
-
|
||||
- final long chunkKey = ChunkCoordIntPair.pair(x, z);
|
||||
- world.getChunkProvider().unloadQueue.remove(chunkKey);
|
||||
+ // Paper start - implement regenerateChunk method
|
||||
+ final ServerLevel serverLevel = this.world;
|
||||
+ final net.minecraft.server.level.ServerChunkCache serverChunkCache = serverLevel.getChunkSource();
|
||||
+ final ChunkPos chunkPos = new ChunkPos(x, z);
|
||||
+ final net.minecraft.world.level.chunk.LevelChunk levelChunk = serverChunkCache.getChunk(chunkPos.x, chunkPos.z, true);
|
||||
+ for (final BlockPos blockPos : BlockPos.betweenClosed(chunkPos.getMinBlockX(), serverLevel.getMinBuildHeight(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), serverLevel.getMaxBuildHeight() - 1, chunkPos.getMaxBlockZ())) {
|
||||
+ levelChunk.removeBlockEntity(blockPos);
|
||||
+ serverLevel.setBlock(blockPos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(), 16);
|
||||
+ }
|
||||
+
|
||||
+ for (final ChunkStatus chunkStatus : REGEN_CHUNK_STATUSES) {
|
||||
+ final List<ChunkAccess> list = new ArrayList<>();
|
||||
+ final int range = Math.max(1, chunkStatus.getRange());
|
||||
+ for (int chunkX = chunkPos.z - range; chunkX <= chunkPos.z + range; chunkX++) {
|
||||
+ for (int chunkZ = chunkPos.x - range; chunkZ <= chunkPos.x + range; chunkZ++) {
|
||||
+ ChunkAccess chunkAccess = serverChunkCache.getChunk(chunkZ, chunkX, chunkStatus.getParent(), true);
|
||||
+ if (chunkAccess instanceof ImposterProtoChunk accessProtoChunk) {
|
||||
+ chunkAccess = new ImposterProtoChunk(accessProtoChunk.getWrapped(), true);
|
||||
+ } else if (chunkAccess instanceof net.minecraft.world.level.chunk.LevelChunk accessLevelChunk) {
|
||||
+ chunkAccess = new ImposterProtoChunk(accessLevelChunk, true);
|
||||
+ }
|
||||
+ list.add(chunkAccess);
|
||||
+ }
|
||||
+ }
|
||||
|
||||
- net.minecraft.server.Chunk chunk = world.getChunkProvider().generateChunk(x, z);
|
||||
- PlayerChunk playerChunk = world.getPlayerChunkMap().getChunk(x, z);
|
||||
- if (playerChunk != null) {
|
||||
- playerChunk.chunk = chunk;
|
||||
+ final java.util.concurrent.CompletableFuture<com.mojang.datafixers.util.Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = chunkStatus.generate(
|
||||
+ Runnable::run,
|
||||
+ serverLevel,
|
||||
+ serverChunkCache.getGenerator(),
|
||||
+ serverLevel.getStructureManager(),
|
||||
+ serverChunkCache.getLightEngine(),
|
||||
+ chunk -> {
|
||||
+ throw new UnsupportedOperationException("Not creating full chunks here");
|
||||
+ },
|
||||
+ list,
|
||||
+ true
|
||||
+ );
|
||||
+ serverChunkCache.mainThreadProcessor.managedBlock(future::isDone);
|
||||
+ if (chunkStatus == ChunkStatus.NOISE) {
|
||||
+ future.join().left().ifPresent(chunk -> net.minecraft.world.level.levelgen.Heightmap.primeHeightmaps(chunk, ChunkStatus.POST_FEATURES));
|
||||
+ }
|
||||
}
|
||||
|
||||
- if (chunk != null) {
|
||||
- refreshChunk(x, z);
|
||||
+ for (final BlockPos blockPos : BlockPos.betweenClosed(chunkPos.getMinBlockX(), serverLevel.getMinBuildHeight(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), serverLevel.getMaxBuildHeight() - 1, chunkPos.getMaxBlockZ())) {
|
||||
+ serverChunkCache.blockChanged(blockPos);
|
||||
}
|
||||
|
||||
- return chunk != null;
|
||||
- */
|
||||
+ final Set<ChunkPos> chunksToRelight = new HashSet<>(9);
|
||||
+ for (int chunkX = chunkPos.x - 1; chunkX <= chunkPos.x + 1 ; chunkX++) {
|
||||
+ for (int chunkZ = chunkPos.z - 1; chunkZ <= chunkPos.z + 1 ; chunkZ++) {
|
||||
+ chunksToRelight.add(new ChunkPos(chunkX, chunkZ));
|
||||
+ }
|
||||
+ }
|
||||
+ serverChunkCache.getLightEngine().relight(chunksToRelight, pos -> {}, relit -> {});
|
||||
+ return true;
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
@Override
|
|
@ -1,43 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Fri, 8 Oct 2021 13:12:58 -0700
|
||||
Subject: [PATCH] Fix cancelled powdered snow bucket placement
|
||||
|
||||
Cancelling the placement of powdered snow from the powdered
|
||||
snow bucket didn't revert grass that became snowy because of the
|
||||
placement.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/item/BlockItem.java b/src/main/java/net/minecraft/world/item/BlockItem.java
|
||||
index e581dc10f3c805f7f8b6e4c842092609e7e1a0f8..b0204af850ee182773ad458208cccd946ad148d5 100644
|
||||
--- a/src/main/java/net/minecraft/world/item/BlockItem.java
|
||||
+++ b/src/main/java/net/minecraft/world/item/BlockItem.java
|
||||
@@ -117,6 +117,7 @@ public class BlockItem extends Item {
|
||||
blockstate.update(true, false);
|
||||
|
||||
if (this instanceof SolidBucketItem) {
|
||||
+ ((ServerPlayer) entityhuman).connection.send(new net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket(world, blockposition.below())); // Paper - update block below
|
||||
((ServerPlayer) entityhuman).getBukkitEntity().updateInventory(); // SPIGOT-4541
|
||||
}
|
||||
return InteractionResult.FAIL;
|
||||
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
|
||||
index 218065e92c2d0d1141359255f77eb6747dabce35..1d52646d68ff05a597a41495f288471ce60b2d29 100644
|
||||
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
|
||||
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
|
||||
@@ -345,7 +345,7 @@ public final class ItemStack {
|
||||
int oldCount = this.getCount();
|
||||
ServerLevel world = (ServerLevel) itemactioncontext.getLevel();
|
||||
|
||||
- if (!(this.getItem() instanceof BucketItem || this.getItem() instanceof SolidBucketItem)) { // if not bucket
|
||||
+ if (!(this.getItem() instanceof BucketItem/* || this.getItem() instanceof SolidBucketItem*/)) { // if not bucket // Paper - capture block states for snow buckets
|
||||
world.captureBlockStates = true;
|
||||
// special case bonemeal
|
||||
if (this.getItem() == Items.BONE_MEAL) {
|
||||
@@ -400,7 +400,7 @@ public final class ItemStack {
|
||||
world.capturedBlockStates.clear();
|
||||
if (blocks.size() > 1) {
|
||||
placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockMultiPlaceEvent(world, entityhuman, enumhand, blocks, blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
- } else if (blocks.size() == 1) {
|
||||
+ } else if (blocks.size() == 1 && item != Items.POWDER_SNOW_BUCKET) { // Paper - don't call event twice for snow buckets
|
||||
placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(world, entityhuman, enumhand, blocks.get(0), blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Sat, 12 Feb 2022 12:40:50 -0700
|
||||
Subject: [PATCH] Add missing Validate calls to CraftServer#getSpawnLimit
|
||||
|
||||
Copies appropriate checks from CraftWorld#getSpawnLimit
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 11f133dccb7bb837d369b2b41f96b26875dc6967..93f6b4ebf1ad238248b201878cfb3cdeee2515f9 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -2174,6 +2174,8 @@ public final class CraftServer implements Server {
|
||||
@Override
|
||||
public int getSpawnLimit(SpawnCategory spawnCategory) {
|
||||
// Paper start
|
||||
+ Validate.notNull(spawnCategory, "SpawnCategory cannot be null");
|
||||
+ Validate.isTrue(CraftSpawnCategory.isValidForLimits(spawnCategory), "SpawnCategory." + spawnCategory + " does not have a spawn limit.");
|
||||
return this.getSpawnLimitUnsafe(spawnCategory);
|
||||
}
|
||||
public int getSpawnLimitUnsafe(final SpawnCategory spawnCategory) {
|
|
@ -1,81 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Sun, 3 Jan 2021 20:03:35 -0800
|
||||
Subject: [PATCH] Add GameEvent tags
|
||||
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/CraftGameEventTag.java b/src/main/java/io/papermc/paper/CraftGameEventTag.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..e7d9fd2702a1ce96596580fff8f5ee4fd3d22b5b
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/CraftGameEventTag.java
|
||||
@@ -0,0 +1,35 @@
|
||||
+package io.papermc.paper;
|
||||
+
|
||||
+import net.minecraft.core.registries.BuiltInRegistries;
|
||||
+import net.minecraft.core.registries.Registries;
|
||||
+import net.minecraft.resources.ResourceKey;
|
||||
+import net.minecraft.tags.TagKey;
|
||||
+import org.bukkit.GameEvent;
|
||||
+import org.bukkit.craftbukkit.tag.CraftTag;
|
||||
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+
|
||||
+import java.util.Collections;
|
||||
+import java.util.IdentityHashMap;
|
||||
+import java.util.Map;
|
||||
+import java.util.Objects;
|
||||
+import java.util.Set;
|
||||
+import java.util.stream.Collectors;
|
||||
+
|
||||
+public class CraftGameEventTag extends CraftTag<net.minecraft.world.level.gameevent.GameEvent, GameEvent> {
|
||||
+
|
||||
+ public CraftGameEventTag(net.minecraft.core.Registry<net.minecraft.world.level.gameevent.GameEvent> registry, TagKey<net.minecraft.world.level.gameevent.GameEvent> tag) {
|
||||
+ super(registry, tag);
|
||||
+ }
|
||||
+
|
||||
+ private static final Map<GameEvent, ResourceKey<net.minecraft.world.level.gameevent.GameEvent>> KEY_CACHE = Collections.synchronizedMap(new IdentityHashMap<>());
|
||||
+ @Override
|
||||
+ public boolean isTagged(@NotNull GameEvent gameEvent) {
|
||||
+ return registry.getHolderOrThrow(KEY_CACHE.computeIfAbsent(gameEvent, event -> ResourceKey.create(Registries.GAME_EVENT, CraftNamespacedKey.toMinecraft(event.getKey())))).is(tag);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @NotNull Set<GameEvent> getValues() {
|
||||
+ return getHandle().stream().map((nms) -> Objects.requireNonNull(GameEvent.getByKey(CraftNamespacedKey.fromMinecraft(BuiltInRegistries.GAME_EVENT.getKey(nms.value()))), nms + " is not a recognized game event")).collect(Collectors.toUnmodifiableSet());
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 93f6b4ebf1ad238248b201878cfb3cdeee2515f9..1e38084012b576fcd26c1d432fd8b275d1054d7f 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -2585,6 +2585,15 @@ public final class CraftServer implements Server {
|
||||
return (org.bukkit.Tag<T>) new CraftEntityTag(BuiltInRegistries.ENTITY_TYPE, entityTagKey);
|
||||
}
|
||||
}
|
||||
+ // Paper start
|
||||
+ case org.bukkit.Tag.REGISTRY_GAME_EVENTS -> {
|
||||
+ Preconditions.checkArgument(clazz == org.bukkit.GameEvent.class, "Game Event namespace must have GameEvent type");
|
||||
+ TagKey<net.minecraft.world.level.gameevent.GameEvent> gameEventTagKey = TagKey.create(net.minecraft.core.registries.Registries.GAME_EVENT, key);
|
||||
+ if (net.minecraft.core.registries.BuiltInRegistries.GAME_EVENT.getTag(gameEventTagKey).isPresent()) {
|
||||
+ return (org.bukkit.Tag<T>) new io.papermc.paper.CraftGameEventTag(net.minecraft.core.registries.BuiltInRegistries.GAME_EVENT, gameEventTagKey);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
default -> throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
@@ -2617,6 +2626,13 @@ public final class CraftServer implements Server {
|
||||
net.minecraft.core.Registry<EntityType<?>> entityTags = BuiltInRegistries.ENTITY_TYPE;
|
||||
return entityTags.getTags().map(pair -> (org.bukkit.Tag<T>) new CraftEntityTag(entityTags, pair.getFirst())).collect(ImmutableList.toImmutableList());
|
||||
}
|
||||
+ // Paper start
|
||||
+ case org.bukkit.Tag.REGISTRY_GAME_EVENTS -> {
|
||||
+ Preconditions.checkArgument(clazz == org.bukkit.GameEvent.class);
|
||||
+ net.minecraft.core.Registry<net.minecraft.world.level.gameevent.GameEvent> gameEvents = net.minecraft.core.registries.BuiltInRegistries.GAME_EVENT;
|
||||
+ return gameEvents.getTags().map(pair -> (org.bukkit.Tag<T>) new io.papermc.paper.CraftGameEventTag(gameEvents, pair.getFirst())).collect(ImmutableList.toImmutableList());
|
||||
+ // Paper end
|
||||
+ }
|
||||
default -> throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Tue, 28 Dec 2021 07:19:01 -0800
|
||||
Subject: [PATCH] Execute chunk tasks fairly for worlds while waiting for next
|
||||
tick
|
||||
|
||||
Currently, only the first world would have had tasks executed.
|
||||
This might result in chunks loading far slower in the nether,
|
||||
for example.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index 1b1b7560b7ae59c5da7ad0e3dfae32cbcaeedcbd..d0eedbf2970bd839422f2c911abf20d2c90c0177 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1308,6 +1308,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
this.executeMidTickTasks(); // Paper - execute chunk tasks mid tick
|
||||
return true;
|
||||
} else {
|
||||
+ boolean ret = false; // Paper - force execution of all worlds, do not just bias the first
|
||||
if (this.haveTime()) {
|
||||
Iterator iterator = this.getAllLevels().iterator();
|
||||
|
||||
@@ -1315,12 +1316,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
ServerLevel worldserver = (ServerLevel) iterator.next();
|
||||
|
||||
if (worldserver.getChunkSource().pollTask()) {
|
||||
- return true;
|
||||
+ ret = true; // Paper - force execution of all worlds, do not just bias the first
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- return false;
|
||||
+ return ret; // Paper - force execution of all worlds, do not just bias the first
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue