diff --git a/patches/server/0007-MC-Utils.patch b/patches/server/0007-MC-Utils.patch index f8b5ec815..0928d4a02 100644 --- a/patches/server/0007-MC-Utils.patch +++ b/patches/server/0007-MC-Utils.patch @@ -2890,23 +2890,25 @@ index 0000000000000000000000000000000000000000..a1bc1d1d0c86217ef18883d281195bc6 +} diff --git a/src/main/java/io/papermc/paper/util/IntervalledCounter.java b/src/main/java/io/papermc/paper/util/IntervalledCounter.java new file mode 100644 -index 0000000000000000000000000000000000000000..059e8c61108cb78a80895cae36f2f8ac644e704c +index 0000000000000000000000000000000000000000..cea9c098ade00ee87b8efc8164ab72f5279758f0 --- /dev/null +++ b/src/main/java/io/papermc/paper/util/IntervalledCounter.java -@@ -0,0 +1,100 @@ +@@ -0,0 +1,115 @@ +package io.papermc.paper.util; + +public final class IntervalledCounter { + + protected long[] times; ++ protected long[] counts; + protected final long interval; + protected long minTime; -+ protected int sum; ++ protected long sum; + protected int head; // inclusive + protected int tail; // exclusive + + public IntervalledCounter(final long interval) { + this.times = new long[8]; ++ this.counts = new long[8]; + this.interval = interval; + } + @@ -2915,7 +2917,7 @@ index 0000000000000000000000000000000000000000..059e8c61108cb78a80895cae36f2f8ac + } + + public void updateCurrentTime(final long currentTime) { -+ int sum = this.sum; ++ long sum = this.sum; + int head = this.head; + final int tail = this.tail; + final long minTime = currentTime - this.interval; @@ -2924,8 +2926,15 @@ index 0000000000000000000000000000000000000000..059e8c61108cb78a80895cae36f2f8ac + + // guard against overflow by using subtraction + while (head != tail && this.times[head] - minTime < 0) { -+ head = (head + 1) % arrayLen; -+ --sum; ++ sum -= this.counts[head]; ++ // there are two ways we can do this: ++ // 1. free the count when adding ++ // 2. free it now ++ // option #2 ++ this.counts[head] = 0; ++ if (++head >= arrayLen) { ++ head = 0; ++ } + } + + this.sum = sum; @@ -2934,6 +2943,10 @@ index 0000000000000000000000000000000000000000..059e8c61108cb78a80895cae36f2f8ac + } + + public void addTime(final long currTime) { ++ this.addTime(currTime, 1L); ++ } ++ ++ public void addTime(final long currTime, final long count) { + // guard against overflow by using subtraction + if (currTime - this.minTime < 0) { + return; @@ -2945,28 +2958,29 @@ index 0000000000000000000000000000000000000000..059e8c61108cb78a80895cae36f2f8ac + } + + this.times[this.tail] = currTime; ++ this.counts[this.tail] += count; ++ this.sum += count; + this.tail = nextTail; + } + + public void updateAndAdd(final int count) { + final long currTime = System.nanoTime(); + this.updateCurrentTime(currTime); -+ for (int i = 0; i < count; ++i) { -+ this.addTime(currTime); -+ } ++ this.addTime(currTime, count); + } + + public void updateAndAdd(final int count, final long currTime) { + this.updateCurrentTime(currTime); -+ for (int i = 0; i < count; ++i) { -+ this.addTime(currTime); -+ } ++ this.addTime(currTime, count); + } + + private void resize() { + final long[] oldElements = this.times; ++ final long[] oldCounts = this.counts; + final long[] newElements = new long[this.times.length * 2]; ++ final long[] newCounts = new long[this.times.length * 2]; + this.times = newElements; ++ this.counts = newCounts; + + final int head = this.head; + final int tail = this.tail; @@ -2976,9 +2990,13 @@ index 0000000000000000000000000000000000000000..059e8c61108cb78a80895cae36f2f8ac + + if (tail >= head) { + System.arraycopy(oldElements, head, newElements, 0, size); ++ System.arraycopy(oldCounts, head, newCounts, 0, size); + } else { + System.arraycopy(oldElements, head, newElements, 0, oldElements.length - head); + System.arraycopy(oldElements, 0, newElements, oldElements.length - head, tail); ++ ++ System.arraycopy(oldCounts, head, newCounts, 0, oldCounts.length - head); ++ System.arraycopy(oldCounts, 0, newCounts, oldCounts.length - head, tail); + } + } + @@ -2987,11 +3005,8 @@ index 0000000000000000000000000000000000000000..059e8c61108cb78a80895cae36f2f8ac + return this.size() / (this.interval * 1.0e-9); + } + -+ public int size() { -+ final int head = this.head; -+ final int tail = this.tail; -+ -+ return tail >= head ? (tail - head) : (tail + (this.times.length - head)); ++ public long size() { ++ return this.sum; + } +} diff --git a/src/main/java/io/papermc/paper/util/WorldUtil.java b/src/main/java/io/papermc/paper/util/WorldUtil.java diff --git a/patches/server/0857-Replace-player-chunk-loader-system.patch b/patches/server/0857-Replace-player-chunk-loader-system.patch index 46d7fd489..9dac047cd 100644 --- a/patches/server/0857-Replace-player-chunk-loader-system.patch +++ b/patches/server/0857-Replace-player-chunk-loader-system.patch @@ -84,10 +84,10 @@ index 309dbf5fce3ce940d5e1b57d267b9d6b2c5ff5b6..5ba64e1083b7cb1eec64d1925095c6ca })); diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 2e68fe31fc788a3ff1fe4ac52140e5e518f660b1..9bff729df7156b071b08913549838024bb17c3c9 100644 +index 2e68fe31fc788a3ff1fe4ac52140e5e518f660b1..b7cb07e36c0056dea64dea220a465337f0d5c843 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -652,4 +652,33 @@ public class PaperConfig { +@@ -652,4 +652,35 @@ public class PaperConfig { private static void timeCommandAffectsAllWorlds() { timeCommandAffectsAllWorlds = getBoolean("settings.time-command-affects-all-worlds", timeCommandAffectsAllWorlds); } @@ -102,6 +102,7 @@ index 2e68fe31fc788a3ff1fe4ac52140e5e518f660b1..9bff729df7156b071b08913549838024 + public static double globalMaxChunkLoadRate; + public static double playerMaxConcurrentChunkLoads; + public static double globalMaxConcurrentChunkLoads; ++ public static double playerMaxChunkLoadRate; + + private static void newPlayerChunkManagement() { + playerMinChunkLoadRadius = getInt("settings.chunk-loading.min-load-radius", 2); @@ -119,14 +120,15 @@ index 2e68fe31fc788a3ff1fe4ac52140e5e518f660b1..9bff729df7156b071b08913549838024 + set("settings.chunk-loading.player-max-concurrent-loads", playerMaxConcurrentChunkLoads = 20.0); + } + globalMaxConcurrentChunkLoads = getDouble("settings.chunk-loading.global-max-concurrent-loads", 500.0); ++ playerMaxChunkLoadRate = getDouble("settings.chunk-loading.player-max-chunk-load-rate", -1.0); + } } diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java new file mode 100644 -index 0000000000000000000000000000000000000000..bdd4cc040111d18b82d3ebeb5dbe2537cf148090 +index 0000000000000000000000000000000000000000..0f62a766a3249d8651a11dce6e9051b162693716 --- /dev/null +++ b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java -@@ -0,0 +1,1108 @@ +@@ -0,0 +1,1128 @@ +package io.papermc.paper.chunk; + +import com.destroystokyo.paper.PaperConfig; @@ -218,10 +220,16 @@ index 0000000000000000000000000000000000000000..bdd4cc040111d18b82d3ebeb5dbe2537 + + final int priorityCompare = Double.compare(holder1 == null ? Double.MAX_VALUE : holder1.priority, holder2 == null ? Double.MAX_VALUE : holder2.priority); + -+ if (priorityCompare != 0) { ++ final int lastLoadTimeCompare = Long.compare(p1.lastChunkLoad, p2.lastChunkLoad); ++ ++ if ((holder1 == null || holder2 == null || lastLoadTimeCompare == 0 || holder1.priority < 0.0 || holder2.priority < 0.0) && priorityCompare != 0) { + return priorityCompare; + } + ++ if (lastLoadTimeCompare != 0) { ++ return lastLoadTimeCompare; ++ } ++ + final int idCompare = Integer.compare(p1.player.getId(), p2.player.getId()); + + if (idCompare != 0) { @@ -744,6 +752,8 @@ index 0000000000000000000000000000000000000000..bdd4cc040111d18b82d3ebeb5dbe2537 + for (;;) { + final PlayerLoaderData data = this.chunkLoadQueue.pollFirst(); + ++ data.lastChunkLoad = time; ++ + final ChunkPriorityHolder queuedLoad = data.loadQueue.peekFirst(); + if (queuedLoad == null) { + if (this.chunkLoadQueue.isEmpty()) { @@ -756,6 +766,8 @@ index 0000000000000000000000000000000000000000..bdd4cc040111d18b82d3ebeb5dbe2537 + updatedCounters = true; + TICKET_ADDITION_COUNTER_SHORT.updateCurrentTime(time); + TICKET_ADDITION_COUNTER_LONG.updateCurrentTime(time); ++ data.ticketAdditionCounterShort.updateCurrentTime(time); ++ data.ticketAdditionCounterLong.updateCurrentTime(time); + } + + if (this.isChunkPlayerLoaded(queuedLoad.chunkX, queuedLoad.chunkZ)) { @@ -791,7 +803,8 @@ index 0000000000000000000000000000000000000000..bdd4cc040111d18b82d3ebeb5dbe2537 + // priority >= 0.0 implies rate limited chunks + + final int currentChunkLoads = this.concurrentChunkLoads; -+ if (currentChunkLoads >= maxLoads || (PaperConfig.globalMaxChunkLoadRate > 0 && (TICKET_ADDITION_COUNTER_SHORT.getRate() >= PaperConfig.globalMaxChunkLoadRate || TICKET_ADDITION_COUNTER_LONG.getRate() >= PaperConfig.globalMaxChunkLoadRate))) { ++ if (currentChunkLoads >= maxLoads || (PaperConfig.globalMaxChunkLoadRate > 0 && (TICKET_ADDITION_COUNTER_SHORT.getRate() >= PaperConfig.globalMaxChunkLoadRate || TICKET_ADDITION_COUNTER_LONG.getRate() >= PaperConfig.globalMaxChunkLoadRate)) ++ || (PaperConfig.playerMaxChunkLoadRate > 0.0 && (data.ticketAdditionCounterShort.getRate() >= PaperConfig.playerMaxChunkLoadRate || data.ticketAdditionCounterLong.getRate() >= PaperConfig.playerMaxChunkLoadRate))) { + // don't poll, we didn't load it + this.chunkLoadQueue.add(data); + break; @@ -821,6 +834,8 @@ index 0000000000000000000000000000000000000000..bdd4cc040111d18b82d3ebeb5dbe2537 + ++this.concurrentChunkLoads; + TICKET_ADDITION_COUNTER_SHORT.addTime(time); + TICKET_ADDITION_COUNTER_LONG.addTime(time); ++ data.ticketAdditionCounterShort.addTime(time); ++ data.ticketAdditionCounterLong.addTime(time); + } + } + } @@ -926,6 +941,13 @@ index 0000000000000000000000000000000000000000..bdd4cc040111d18b82d3ebeb5dbe2537 + + protected long nextChunkSendTarget; + ++ // this interval prevents bursting a lot of chunk loads ++ protected final IntervalledCounter ticketAdditionCounterShort = new IntervalledCounter((long)(1.0e6 * 50.0)); // 50ms ++ // this ensures the rate is kept between ticks correctly ++ protected final IntervalledCounter ticketAdditionCounterLong = new IntervalledCounter((long)(1.0e6 * 1000.0)); // 1000ms ++ ++ public long lastChunkLoad; ++ + public PlayerLoaderData(final ServerPlayer player, final PlayerChunkLoader loader) { + this.player = player; + this.loader = loader; diff --git a/patches/server/0863-Option-to-have-default-CustomSpawners-in-custom-worl.patch b/patches/server/0863-Option-to-have-default-CustomSpawners-in-custom-worl.patch index e4ea20bd1..5711927a4 100644 --- a/patches/server/0863-Option-to-have-default-CustomSpawners-in-custom-worl.patch +++ b/patches/server/0863-Option-to-have-default-CustomSpawners-in-custom-worl.patch @@ -10,12 +10,12 @@ just looking at the LevelStem key, look at the DimensionType key which is one level below that. Defaults to off to keep vanilla behavior. diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 9bff729df7156b071b08913549838024bb17c3c9..88a4dda44e59fbe6215d7ac2e5af0c54527a2fc7 100644 +index b7cb07e36c0056dea64dea220a465337f0d5c843..c9c08e20f729b40b1d90f3ac144f5245f4f35230 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -681,4 +681,9 @@ public class PaperConfig { - } +@@ -683,4 +683,9 @@ public class PaperConfig { globalMaxConcurrentChunkLoads = getDouble("settings.chunk-loading.global-max-concurrent-loads", 500.0); + playerMaxChunkLoadRate = getDouble("settings.chunk-loading.player-max-chunk-load-rate", -1.0); } + + public static boolean useDimensionTypeForCustomSpawners; diff --git a/patches/server/0876-Don-t-allow-vehicle-movement-from-players-while-tele.patch b/patches/server/0876-Don-t-allow-vehicle-movement-from-players-while-tele.patch index bd0b39982..d7c1d836f 100644 --- a/patches/server/0876-Don-t-allow-vehicle-movement-from-players-while-tele.patch +++ b/patches/server/0876-Don-t-allow-vehicle-movement-from-players-while-tele.patch @@ -7,7 +7,7 @@ Bring the vehicle move packet behavior in line with the regular player move packet. diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 952debf2fb4abf675e226224909b14c866528e62..68e1ab0057452228817ecbc6556338a8906c2538 100644 +index 952debf2fb4abf675e226224909b14c866528e62..e4d54fdc28b6161e74626f25299b1081e6605e98 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -517,6 +517,12 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser @@ -15,7 +15,7 @@ index 952debf2fb4abf675e226224909b14c866528e62..68e1ab0057452228817ecbc6556338a8 Entity entity = this.player.getRootVehicle(); + // Paper start -+ if (this.awaitingPositionFromClient != null) { ++ if (this.awaitingPositionFromClient != null || this.player.isImmobile() || entity.isRemoved()) { + return; + } + // Paper end diff --git a/patches/server/0886-Ensure-entity-passenger-world-matches-ridden-entity.patch b/patches/server/0886-Ensure-entity-passenger-world-matches-ridden-entity.patch new file mode 100644 index 000000000..7de0352db --- /dev/null +++ b/patches/server/0886-Ensure-entity-passenger-world-matches-ridden-entity.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 31 Mar 2022 05:11:37 -0700 +Subject: [PATCH] Ensure entity passenger world matches ridden entity + +Bad plugins doing this would cause some obvious problems... + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 8b57a24d4e8469dfbfb4eb2d11ca616e1db98598..26911884384d5e8afd1b43360494b793374f505f 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -2590,6 +2590,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + } + + protected boolean addPassenger(Entity entity) { // CraftBukkit ++ // Paper start ++ if (entity.level != this.level) { ++ throw new IllegalArgumentException("Entity passenger world must match"); ++ } ++ // Paper end + if (entity == this) throw new IllegalArgumentException("Entities cannot become a passenger of themselves"); // Paper - issue 572 + if (entity.getVehicle() != this) { + throw new IllegalStateException("Use x.startRiding(y), not y.addPassenger(x)"); diff --git a/patches/server/0887-Guard-against-invalid-entity-positions.patch b/patches/server/0887-Guard-against-invalid-entity-positions.patch new file mode 100644 index 000000000..a9bce4333 --- /dev/null +++ b/patches/server/0887-Guard-against-invalid-entity-positions.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 31 Mar 2022 05:18:28 -0700 +Subject: [PATCH] Guard against invalid entity positions + +Anything not finite should be blocked and logged + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 26911884384d5e8afd1b43360494b793374f505f..49cf3601df7b145d49b1fe9a71ba0bc60c5394b3 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -4090,11 +4090,33 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + return this.getZ((2.0D * this.random.nextDouble() - 1.0D) * widthScale); + } + ++ // Paper start - block invalid positions ++ public static boolean checkPosition(Entity entity, double newX, double newY, double newZ) { ++ if (Double.isFinite(newX) && Double.isFinite(newY) && Double.isFinite(newZ)) { ++ return true; ++ } ++ ++ String entityInfo = null; ++ try { ++ entityInfo = entity.toString(); ++ } catch (Exception ex) { ++ entityInfo = "[Entity info unavailable] "; ++ } ++ LOGGER.error("New entity position is invalid! Tried to set invalid position (" + newX + "," + newY + "," + newZ + ") for entity " + entity.getClass().getName() + " located at " + entity.position + ", entity info: " + entityInfo, new Throwable()); ++ return false; ++ } ++ // Paper end - block invalid positions ++ + public final void setPosRaw(double x, double y, double z) { + // Paper start + this.setPosRaw(x, y, z, false); + } + public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) { ++ // Paper start - block invalid positions ++ if (!checkPosition(this, x, y, z)) { ++ return; ++ } ++ // Paper end - block invalid positions + // Paper end + // Paper start - fix MC-4 + if (this instanceof ItemEntity) {