mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-28 03:48:01 +01:00
even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even more patches
This commit is contained in:
parent
437205bba8
commit
de5dbadea0
@ -28,8 +28,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
@@ -0,0 +0,0 @@ public class PaperConfig {
|
||||
playerMaxConcurrentChunkLoads = getDouble("settings.chunk-loading.player-max-concurrent-loads", 4.0);
|
||||
globalMaxConcurrentChunkLoads = getDouble("settings.chunk-loading.global-max-concurrent-loads", 500.0);
|
||||
itemValidationBookAuthorLength = getInt("settings.item-validation.book.author", itemValidationBookAuthorLength);
|
||||
itemValidationBookPageLength = getInt("settings.item-validation.book.page", itemValidationBookPageLength);
|
||||
}
|
||||
+
|
||||
+ public static final class PacketLimit {
|
@ -26,27 +26,27 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
||||
this.level.getProfiler().pop();
|
||||
});
|
||||
gameprofilerfiller.pop();
|
||||
gameprofilerfiller.pop();
|
||||
+ // 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
|
||||
}
|
||||
|
||||
+ // 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
|
||||
}
|
||||
|
||||
private void getFullChunk(long pos, Consumer<LevelChunk> chunkConsumer) {
|
@ -126,7 +126,7 @@ diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/jav
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
||||
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
|
||||
return this.onGround;
|
||||
}
|
||||
|
||||
@ -169,7 +169,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
if (this.noPhysics) {
|
||||
this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z);
|
||||
} else {
|
||||
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
||||
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
|
||||
this.level.getProfiler().pop();
|
||||
}
|
||||
}
|
||||
@ -182,8 +182,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
+ // Paper end - detailed watchdog information
|
||||
}
|
||||
|
||||
protected void tryCheckInsideBlocks() {
|
||||
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
||||
protected boolean isHorizontalCollisionMinor(Vec3 adjustedMovement) {
|
||||
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
|
||||
}
|
||||
|
||||
public void setDeltaMovement(Vec3 velocity) {
|
||||
@ -193,7 +193,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
}
|
||||
|
||||
public void setDeltaMovement(double x, double y, double z) {
|
||||
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
||||
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
|
||||
}
|
||||
// Paper end - fix MC-4
|
||||
if (this.position.x != x || this.position.y != y || this.position.z != z) {
|
@ -23,8 +23,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
||||
|
||||
public boolean runDistanceManagerUpdates() {
|
||||
if (distanceManager.delayDistanceManagerTick) return false; // Paper - Chunk priority
|
||||
if (this.chunkMap.unloadingPlayerChunk) { net.minecraft.server.MinecraftServer.LOGGER.fatal("Cannot tick distance manager while unloading playerchunks", new Throwable()); throw new IllegalStateException("Cannot tick distance manager while unloading playerchunks"); } // Paper
|
||||
+ co.aikar.timings.MinecraftTimings.distanceManagerTick.startTiming(); try { // Paper - add timings for distance manager
|
||||
boolean flag = this.distanceManager.runAllUpdates(this.chunkMap);
|
@ -0,0 +1,21 @@
|
||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
|
||||
@@ -0,0 +0,0 @@ public class ChunkStorage implements AutoCloseable {
|
||||
|
||||
// CraftBukkit start
|
||||
private boolean check(ServerChunkCache cps, int x, int z) throws IOException {
|
||||
+ 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
|
@ -59,5 +59,5 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
+ protected int y; // Paper - protected
|
||||
+ protected int z; // Paper - protected
|
||||
|
||||
// Paper start
|
||||
public boolean isValidLocation(net.minecraft.world.level.LevelHeightAccessor levelHeightAccessor) {
|
||||
private static Function<Vec3i, DataResult<Vec3i>> checkOffsetAxes(int maxAbsValue) {
|
||||
return (vec) -> {
|
@ -12,13 +12,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -0,0 +0,0 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import org.bukkit.entity.Player; // CraftBukkit
|
||||
import org.bukkit.entity.Player;
|
||||
// CraftBukkit end
|
||||
+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper
|
||||
|
||||
public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider {
|
||||
|
||||
@@ -0,0 +0,0 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
|
||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
final Entity entity;
|
||||
private final int range;
|
||||
SectionPos lastSectionPos;
|
@ -114,7 +114,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
+ // Paper end
|
||||
Biome biomebase = this.getBiome(blockposition);
|
||||
|
||||
- if (biomebase.shouldFreeze((LevelReader) this, blockposition1)) {
|
||||
- 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);
|
||||
@ -135,14 +135,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
+ blockposition.setY(normalY); // Paper
|
||||
Biome.Precipitation biomebase_precipitation = this.getBiome(blockposition).getPrecipitation();
|
||||
|
||||
- if (biomebase_precipitation == Biome.Precipitation.RAIN && biomebase.isColdEnoughToSnow(blockposition1)) {
|
||||
- if (biomebase_precipitation == Biome.Precipitation.RAIN && biomebase.coldEnoughToSnow(blockposition1)) {
|
||||
+ blockposition.setY(downY); // Paper
|
||||
+ if (biomebase_precipitation == Biome.Precipitation.RAIN && biomebase.isColdEnoughToSnow(blockposition)) { // Paper
|
||||
+ if (biomebase_precipitation == Biome.Precipitation.RAIN && biomebase.coldEnoughToSnow(blockposition)) { // Paper
|
||||
biomebase_precipitation = Biome.Precipitation.SNOW;
|
||||
}
|
||||
|
||||
- iblockdata.getBlock().handlePrecipitation(iblockdata, (Level) this, blockposition1, biomebase_precipitation);
|
||||
+ iblockdata.getBlock().handlePrecipitation(iblockdata, (Level) this, blockposition, biomebase_precipitation); // Paper
|
||||
- iblockdata.getBlock().handlePrecipitation(iblockdata, this, blockposition1, biomebase_precipitation);
|
||||
+ iblockdata.getBlock().handlePrecipitation(iblockdata, this, blockposition, biomebase_precipitation); // Paper
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,7 +157,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
- for (int i1 = 0; i1 < l; ++i1) {
|
||||
- LevelChunkSection chunksection = achunksection[i1];
|
||||
-
|
||||
- if (chunksection != LevelChunk.EMPTY_SECTION && chunksection.isRandomlyTicking()) {
|
||||
- if (chunksection.isRandomlyTicking()) {
|
||||
- int j1 = chunksection.bottomBlockY();
|
||||
-
|
||||
- for (int k1 = 0; k1 < randomTickSpeed; ++k1) {
|
||||
@ -214,12 +214,32 @@ diff --git a/src/main/java/net/minecraft/util/BitStorage.java b/src/main/java/ne
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/util/BitStorage.java
|
||||
+++ b/src/main/java/net/minecraft/util/BitStorage.java
|
||||
@@ -0,0 +0,0 @@ public class BitStorage {
|
||||
}
|
||||
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/util/SimpleBitStorage.java
|
||||
+++ b/src/main/java/net/minecraft/util/SimpleBitStorage.java
|
||||
@@ -0,0 +0,0 @@ public class SimpleBitStorage implements BitStorage {
|
||||
return this.bits;
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public final void forEach(DataBitConsumer consumer) {
|
||||
+ int i = 0;
|
||||
+ long[] along = this.data;
|
||||
@ -238,15 +258,31 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
+ @FunctionalInterface
|
||||
+ public static interface DataBitConsumer {
|
||||
+
|
||||
+ void accept(int location, int data);
|
||||
+
|
||||
@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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/util/ZeroBitStorage.java
|
||||
+++ b/src/main/java/net/minecraft/util/ZeroBitStorage.java
|
||||
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java
|
||||
@ -289,16 +325,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||
@@ -0,0 +0,0 @@ public class LevelChunkSection {
|
||||
private short tickingBlockCount;
|
||||
private short tickingFluidCount;
|
||||
public final PalettedContainer<BlockState> states; // Paper - package-private // Paper - public
|
||||
public final PalettedContainer<BlockState> states;
|
||||
private final PalettedContainer<Biome> biomes;
|
||||
+ public final com.destroystokyo.paper.util.maplist.IBlockDataList tickingList = new com.destroystokyo.paper.util.maplist.IBlockDataList(); // Paper
|
||||
|
||||
// Paper start - Anti-Xray - Add parameters
|
||||
@Deprecated public LevelChunkSection(int yOffset) { this(yOffset, null, null, true); } // Notice for updates: Please make sure this constructor isn't used anywhere
|
||||
public LevelChunkSection(int chunkPos, PalettedContainer<BlockState> blockStateContainer, PalettedContainer<Biome> biomeContainer) {
|
||||
this.bottomBlockY = LevelChunkSection.getBottomBlockY(chunkPos);
|
||||
@@ -0,0 +0,0 @@ public class LevelChunkSection {
|
||||
--this.nonEmptyBlockCount;
|
||||
if (blockState.isRandomlyTicking()) {
|
||||
if (iblockdata1.isRandomlyTicking()) {
|
||||
--this.tickingBlockCount;
|
||||
+ // Paper start
|
||||
+ this.tickingList.remove(x, y, z);
|
||||
@ -326,27 +362,28 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
this.nonEmptyBlockCount = 0;
|
||||
this.tickingBlockCount = 0;
|
||||
this.tickingFluidCount = 0;
|
||||
- this.states.count((state, count) -> {
|
||||
+ this.states.forEachLocation((state, location) -> { // Paper
|
||||
FluidState fluidState = state.getFluidState();
|
||||
if (!state.isAir()) {
|
||||
- this.nonEmptyBlockCount = (short)(this.nonEmptyBlockCount + count);
|
||||
+ this.nonEmptyBlockCount = (short)(this.nonEmptyBlockCount + 1); // Paper
|
||||
if (state.isRandomlyTicking()) {
|
||||
- this.tickingBlockCount = (short)(this.tickingBlockCount + count);
|
||||
- this.states.count((iblockdata, i) -> {
|
||||
+ this.states.forEachLocation((iblockdata, i) -> { // Paper
|
||||
FluidState fluid = iblockdata.getFluidState();
|
||||
|
||||
if (!iblockdata.isAir()) {
|
||||
- this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + i);
|
||||
+ this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + 1); // Paper
|
||||
if (iblockdata.isRandomlyTicking()) {
|
||||
- this.tickingBlockCount = (short) (this.tickingBlockCount + i);
|
||||
+ // Paper start
|
||||
+ this.tickingBlockCount = (short)(this.tickingBlockCount + 1);
|
||||
+ this.tickingList.add(location, state);
|
||||
+ this.tickingList.add(i, iblockdata);
|
||||
+ // Paper end
|
||||
}
|
||||
}
|
||||
|
||||
if (!fluidState.isEmpty()) {
|
||||
- this.nonEmptyBlockCount = (short)(this.nonEmptyBlockCount + count);
|
||||
+ this.nonEmptyBlockCount = (short)(this.nonEmptyBlockCount + 1); // Paper
|
||||
if (fluidState.isRandomlyTicking()) {
|
||||
- this.tickingFluidCount = (short)(this.tickingFluidCount + count);
|
||||
+ this.tickingFluidCount = (short)(this.tickingFluidCount + 1); // Paper
|
||||
if (!fluid.isEmpty()) {
|
||||
- this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + i);
|
||||
+ this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + 1); // Paper
|
||||
if (fluid.isRandomlyTicking()) {
|
||||
- this.tickingFluidCount = (short) (this.tickingFluidCount + i);
|
||||
+ this.tickingFluidCount = (short) (this.tickingFluidCount + 1); // Paper
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,15 +392,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
||||
@@ -0,0 +0,0 @@ public class PalettedContainer<T> implements PaletteResize<T> {
|
||||
}
|
||||
}
|
||||
|
||||
+ // 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);
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ public void forEachLocation(PalettedContainer.CountConsumer<T> datapaletteblock_a) {
|
||||
+ this.storage.forEach((int location, int data) -> {
|
||||
+ datapaletteblock_a.accept(this.palette.valueFor(data), location);
|
||||
+ });
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Mariell Hoversholm <proximyst@proximyst.com>
|
||||
Date: Sun, 24 Oct 2021 16:20:31 -0400
|
||||
Subject: [PATCH] Add Raw Byte Entity Serialization
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - Entity serialization api
|
||||
+ public boolean serializeEntity(CompoundTag compound) {
|
||||
+ List<Entity> pass = new java.util.ArrayList<>(this.getPassengers());
|
||||
+ this.passengers = ImmutableList.of();
|
||||
+ boolean result = save(compound);
|
||||
+ this.passengers = ImmutableList.copyOf(pass);
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end
|
||||
public boolean save(CompoundTag nbt) {
|
||||
return this.isPassenger() ? false : this.saveAsPassenger(nbt);
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
@@ -0,0 +0,0 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||
}
|
||||
return set;
|
||||
}
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean spawnAt(Location location, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
|
||||
+ Preconditions.checkNotNull(location, "location cannot be null");
|
||||
+ Preconditions.checkNotNull(reason, "reason cannot be null");
|
||||
+ entity.level = ((CraftWorld) location.getWorld()).getHandle();
|
||||
+ entity.setPos(location.getX(), location.getY(), location.getZ());
|
||||
+ entity.setRot(location.getYaw(), location.getPitch());
|
||||
+ return !entity.valid && entity.level.addEntity(entity, reason);
|
||||
+ }
|
||||
// Paper end
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
|
||||
@@ -0,0 +0,0 @@ public final class CraftMagicNumbers implements UnsafeValues {
|
||||
return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.of((CompoundTag) converted.getValue()));
|
||||
}
|
||||
|
||||
+ @Override
|
||||
+ public byte[] serializeEntity(org.bukkit.entity.Entity entity) {
|
||||
+ Preconditions.checkNotNull(entity, "null cannot be serialized");
|
||||
+ Preconditions.checkArgument(entity instanceof org.bukkit.craftbukkit.entity.CraftEntity, "only CraftEntities can be serialized");
|
||||
+
|
||||
+ CompoundTag compound = new CompoundTag();
|
||||
+ ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().serializeEntity(compound);
|
||||
+ return serializeNbtToBytes(compound);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public org.bukkit.entity.Entity deserializeEntity(byte[] data, org.bukkit.World world, boolean preserveUUID) {
|
||||
+ Preconditions.checkNotNull(data, "null cannot be deserialized");
|
||||
+ Preconditions.checkArgument(data.length > 0, "cannot deserialize nothing");
|
||||
+
|
||||
+ CompoundTag compound = deserializeNbtFromBytes(data);
|
||||
+ int dataVersion = compound.getInt("DataVersion");
|
||||
+ compound = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.ENTITY, compound, dataVersion, getDataVersion());
|
||||
+ if (!preserveUUID) compound.remove("UUID"); // Generate a new UUID so we don't have to worry about deserializing the same entity twice
|
||||
+ return net.minecraft.world.entity.EntityType.create(compound, ((org.bukkit.craftbukkit.CraftWorld) world).getHandle())
|
||||
+ .orElseThrow(() -> new IllegalArgumentException("An ID was not found for the data. Did you downgrade?")).getBukkitEntity();
|
||||
+ }
|
||||
+
|
||||
private byte[] serializeNbtToBytes(CompoundTag compound) {
|
||||
compound.putInt("DataVersion", getDataVersion());
|
||||
java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();
|
@ -1,373 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Mon, 16 Aug 2021 01:31:54 -0500
|
||||
Subject: [PATCH] Add '/paper mobcaps' and '/paper playermobcaps'
|
||||
|
||||
Add commands to get the mobcaps for a world, as well as the mobcaps for
|
||||
each player when per-player mob spawning is enabled.
|
||||
|
||||
Also has a hover text on each mob category listing what entity types are
|
||||
in said category
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@ package com.destroystokyo.paper;
|
||||
import com.destroystokyo.paper.io.SyncLoadFinder;
|
||||
import com.google.common.base.Functions;
|
||||
import com.google.common.base.Joiner;
|
||||
+import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
@@ -0,0 +0,0 @@ import com.google.common.collect.Maps;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
+import net.kyori.adventure.text.Component;
|
||||
+import net.kyori.adventure.text.ComponentLike;
|
||||
+import net.kyori.adventure.text.TextComponent;
|
||||
+import net.kyori.adventure.text.format.NamedTextColor;
|
||||
+import net.kyori.adventure.text.format.TextColor;
|
||||
+import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MCUtil;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
@@ -0,0 +0,0 @@ import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
+import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MCUtil;
|
||||
+import net.minecraft.world.level.NaturalSpawner;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -0,0 +0,0 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
+import java.util.function.ToIntFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PaperCommand extends Command {
|
||||
private static final String BASE_PERM = "bukkit.command.paper.";
|
||||
- private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "fixlight", "syncloadinfo", "dumpitem").build();
|
||||
+ private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "fixlight", "syncloadinfo", "dumpitem", "mobcaps", "playermobcaps").build();
|
||||
|
||||
public PaperCommand(String name) {
|
||||
super(name);
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
return getListMatchingLast(sender, args, "help", "chunks");
|
||||
}
|
||||
break;
|
||||
+ case "mobcaps":
|
||||
+ return getListMatchingLast(sender, args, this.suggestMobcaps(sender, args));
|
||||
+ case "playermobcaps":
|
||||
+ return getListMatchingLast(sender, args, this.suggestPlayerMobcaps(sender, args));
|
||||
case "chunkinfo":
|
||||
List<String> worldNames = new ArrayList<>();
|
||||
worldNames.add("*");
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
case "syncloadinfo":
|
||||
this.doSyncLoadInfo(sender, args);
|
||||
break;
|
||||
+ case "mobcaps":
|
||||
+ this.printMobcaps(sender, args);
|
||||
+ break;
|
||||
+ case "playermobcaps":
|
||||
+ this.printPlayerMobcaps(sender, args);
|
||||
+ break;
|
||||
case "ver":
|
||||
if (!testPermission(sender, "version")) break; // "ver" needs a special check because it's an alias. All other commands are checked up before the switch statement (because they are present in the SUBCOMMANDS set)
|
||||
case "version":
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
}
|
||||
}
|
||||
|
||||
+ public static final Map<MobCategory, TextColor> MOB_CATEGORY_COLORS = ImmutableMap.<MobCategory, TextColor>builder()
|
||||
+ .put(MobCategory.MONSTER, NamedTextColor.RED)
|
||||
+ .put(MobCategory.CREATURE, NamedTextColor.GREEN)
|
||||
+ .put(MobCategory.AMBIENT, NamedTextColor.GRAY)
|
||||
+ .put(MobCategory.UNDERGROUND_WATER_CREATURE, TextColor.color(0x3541E6))
|
||||
+ .put(MobCategory.WATER_CREATURE, TextColor.color(0x006EFF))
|
||||
+ .put(MobCategory.WATER_AMBIENT, TextColor.color(0x00B3FF))
|
||||
+ .put(MobCategory.MISC, TextColor.color(0x636363))
|
||||
+ .build();
|
||||
+
|
||||
+ private List<String> suggestMobcaps(CommandSender sender, String[] args) {
|
||||
+ if (args.length == 2) {
|
||||
+ final List<String> worlds = new ArrayList<>(Bukkit.getWorlds().stream().map(World::getName).toList());
|
||||
+ worlds.add("*");
|
||||
+ return worlds;
|
||||
+ }
|
||||
+
|
||||
+ return Collections.emptyList();
|
||||
+ }
|
||||
+
|
||||
+ private List<String> suggestPlayerMobcaps(CommandSender sender, String[] args) {
|
||||
+ if (args.length == 2) {
|
||||
+ final List<String> list = new ArrayList<>();
|
||||
+ for (final Player player : Bukkit.getOnlinePlayers()) {
|
||||
+ if (!(sender instanceof Player senderPlayer) || senderPlayer.canSee(player)) {
|
||||
+ list.add(player.getName());
|
||||
+ }
|
||||
+ }
|
||||
+ return list;
|
||||
+ }
|
||||
+
|
||||
+ return Collections.emptyList();
|
||||
+ }
|
||||
+
|
||||
+ private void printMobcaps(CommandSender sender, String[] args) {
|
||||
+ final List<World> worlds;
|
||||
+ if (args.length == 1) {
|
||||
+ if (sender instanceof Player player) {
|
||||
+ worlds = List.of(player.getWorld());
|
||||
+ } else {
|
||||
+ sender.sendMessage(Component.text("Must specify a world! ex: '/paper mobcaps world'", NamedTextColor.RED));
|
||||
+ return;
|
||||
+ }
|
||||
+ } else if (args.length == 2) {
|
||||
+ final String input = args[1];
|
||||
+ if (input.equals("*")) {
|
||||
+ worlds = Bukkit.getWorlds();
|
||||
+ } else {
|
||||
+ final World world = Bukkit.getWorld(input);
|
||||
+ if (world == null) {
|
||||
+ sender.sendMessage(Component.text("'" + input + "' is not a valid world!", NamedTextColor.RED));
|
||||
+ return;
|
||||
+ } else {
|
||||
+ worlds = List.of(world);
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ sender.sendMessage(Component.text("Too many arguments!", NamedTextColor.RED));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ for (final World world : worlds) {
|
||||
+ final ServerLevel level = ((CraftWorld) world).getHandle();
|
||||
+ final NaturalSpawner.SpawnState state = level.getChunkSource().getLastSpawnState();
|
||||
+
|
||||
+ final int chunks;
|
||||
+ if (state == null) {
|
||||
+ chunks = 0;
|
||||
+ } else {
|
||||
+ chunks = state.getSpawnableChunkCount();
|
||||
+ }
|
||||
+ sender.sendMessage(TextComponent.ofChildren(
|
||||
+ Component.text("Mobcaps for world: "),
|
||||
+ Component.text(world.getName(), NamedTextColor.AQUA),
|
||||
+ Component.text(" (" + chunks + " spawnable chunks)")
|
||||
+ ));
|
||||
+
|
||||
+ sender.sendMessage(this.buildMobcapsComponent(
|
||||
+ category -> {
|
||||
+ if (state == null) {
|
||||
+ return 0;
|
||||
+ } else {
|
||||
+ return state.getMobCategoryCounts().getOrDefault(category, 0);
|
||||
+ }
|
||||
+ },
|
||||
+ category -> NaturalSpawner.globalLimitForCategory(level, category, chunks)
|
||||
+ ));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void printPlayerMobcaps(CommandSender sender, String[] args) {
|
||||
+ final Player player;
|
||||
+ if (args.length == 1) {
|
||||
+ if (sender instanceof Player pl) {
|
||||
+ player = pl;
|
||||
+ } else {
|
||||
+ sender.sendMessage(Component.text("Must specify a player! ex: '/paper playermobcount playerName'", NamedTextColor.RED));
|
||||
+ return;
|
||||
+ }
|
||||
+ } else if (args.length == 2) {
|
||||
+ final String input = args[1];
|
||||
+ player = Bukkit.getPlayerExact(input);
|
||||
+ if (player == null) {
|
||||
+ sender.sendMessage(Component.text("Could not find player named '" + input + "'", NamedTextColor.RED));
|
||||
+ return;
|
||||
+ }
|
||||
+ } else {
|
||||
+ sender.sendMessage(Component.text("Too many arguments!", NamedTextColor.RED));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
|
||||
+ final ServerLevel level = serverPlayer.getLevel();
|
||||
+
|
||||
+ if (!level.paperConfig.perPlayerMobSpawns) {
|
||||
+ sender.sendMessage(Component.text("Use '/paper mobcaps' for worlds where per-player mob spawning is disabled.", NamedTextColor.RED));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ sender.sendMessage(TextComponent.ofChildren(Component.text("Mobcaps for player: "), Component.text(player.getName(), NamedTextColor.GREEN)));
|
||||
+ sender.sendMessage(this.buildMobcapsComponent(
|
||||
+ category -> level.chunkSource.chunkMap.getMobCountNear(serverPlayer, category),
|
||||
+ category -> NaturalSpawner.limitForCategory(level, category)
|
||||
+ ));
|
||||
+ }
|
||||
+
|
||||
+ private Component buildMobcapsComponent(final ToIntFunction<MobCategory> countGetter, final ToIntFunction<MobCategory> limitGetter) {
|
||||
+ return MOB_CATEGORY_COLORS.entrySet().stream()
|
||||
+ .map(entry -> {
|
||||
+ final MobCategory category = entry.getKey();
|
||||
+ final TextColor color = entry.getValue();
|
||||
+
|
||||
+ final Component categoryHover = TextComponent.ofChildren(
|
||||
+ Component.text("Entity types in category ", TextColor.color(0xE0E0E0)),
|
||||
+ Component.text(category.getName(), color),
|
||||
+ Component.text(':', NamedTextColor.GRAY),
|
||||
+ Component.newline(),
|
||||
+ Component.newline(),
|
||||
+ Registry.ENTITY_TYPE.entrySet().stream()
|
||||
+ .filter(it -> it.getValue().getCategory() == category)
|
||||
+ .map(it -> Component.translatable(it.getValue().getDescriptionId()))
|
||||
+ .collect(Component.toComponent(Component.text(", ", NamedTextColor.GRAY)))
|
||||
+ );
|
||||
+
|
||||
+ final Component categoryComponent = Component.text()
|
||||
+ .content(" " + category.getName())
|
||||
+ .color(color)
|
||||
+ .hoverEvent(categoryHover)
|
||||
+ .build();
|
||||
+
|
||||
+ final TextComponent.Builder builder = Component.text()
|
||||
+ .append(
|
||||
+ categoryComponent,
|
||||
+ Component.text(": ", NamedTextColor.GRAY)
|
||||
+ );
|
||||
+ final int limit = limitGetter.applyAsInt(category);
|
||||
+ if (limit != -1) {
|
||||
+ builder.append(
|
||||
+ Component.text(countGetter.applyAsInt(category)),
|
||||
+ Component.text("/", NamedTextColor.GRAY),
|
||||
+ Component.text(limit)
|
||||
+ );
|
||||
+ } else {
|
||||
+ builder.append(Component.text()
|
||||
+ .append(
|
||||
+ Component.text('n'),
|
||||
+ Component.text("/", NamedTextColor.GRAY),
|
||||
+ Component.text('a')
|
||||
+ )
|
||||
+ .hoverEvent(Component.text("This category does not naturally spawn.")));
|
||||
+ }
|
||||
+ return builder;
|
||||
+ })
|
||||
+ .map(ComponentLike::asComponent)
|
||||
+ .collect(Component.toComponent(Component.newline()));
|
||||
+ }
|
||||
+
|
||||
private void doChunkInfo(CommandSender sender, String[] args) {
|
||||
List<org.bukkit.World> worlds;
|
||||
if (args.length < 2 || args[1].equals("*")) {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
|
||||
MobCategory enumcreaturetype = aenumcreaturetype[j];
|
||||
// CraftBukkit start - Use per-world spawn limits
|
||||
boolean spawnThisTick = true;
|
||||
- int limit = enumcreaturetype.getMaxInstancesPerChunk();
|
||||
+ final int limit = limitForCategory(world, enumcreaturetype); // Paper
|
||||
switch (enumcreaturetype) {
|
||||
- case MONSTER:
|
||||
- spawnThisTick = spawnMonsterThisTick;
|
||||
- limit = world.getWorld().getMonsterSpawnLimit();
|
||||
- break;
|
||||
- case CREATURE:
|
||||
- spawnThisTick = spawnAnimalThisTick;
|
||||
- limit = world.getWorld().getAnimalSpawnLimit();
|
||||
- break;
|
||||
- case WATER_CREATURE:
|
||||
- spawnThisTick = spawnWaterThisTick;
|
||||
- limit = world.getWorld().getWaterAnimalSpawnLimit();
|
||||
- break;
|
||||
- case UNDERGROUND_WATER_CREATURE:
|
||||
- spawnThisTick = spawnWaterUndergroundCreatureThisTick;
|
||||
- limit = world.getWorld().getWaterUndergroundCreatureSpawnLimit();
|
||||
- break;
|
||||
- case AMBIENT:
|
||||
- spawnThisTick = spawnAmbientThisTick;
|
||||
- limit = world.getWorld().getAmbientSpawnLimit();
|
||||
- break;
|
||||
- case WATER_AMBIENT:
|
||||
- spawnThisTick = spawnWaterAmbientThisTick;
|
||||
- limit = world.getWorld().getWaterAmbientSpawnLimit();
|
||||
- break;
|
||||
+ // Paper start - not mindiff so we get conflict on change
|
||||
+ case MONSTER -> spawnThisTick = spawnMonsterThisTick;
|
||||
+ case CREATURE -> spawnThisTick = spawnAnimalThisTick;
|
||||
+ case WATER_CREATURE -> spawnThisTick = spawnWaterThisTick;
|
||||
+ case UNDERGROUND_WATER_CREATURE -> spawnThisTick = spawnWaterUndergroundCreatureThisTick;
|
||||
+ case AMBIENT -> spawnThisTick = spawnAmbientThisTick;
|
||||
+ case WATER_AMBIENT -> spawnThisTick = spawnWaterAmbientThisTick;
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
if (!spawnThisTick || limit == 0) {
|
||||
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
|
||||
world.getProfiler().pop();
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public static int limitForCategory(final ServerLevel world, final MobCategory enumcreaturetype) {
|
||||
+ return switch (enumcreaturetype) {
|
||||
+ case MONSTER -> world.getWorld().getMonsterSpawnLimit();
|
||||
+ case CREATURE -> world.getWorld().getAnimalSpawnLimit();
|
||||
+ case WATER_CREATURE -> world.getWorld().getWaterAnimalSpawnLimit();
|
||||
+ case UNDERGROUND_WATER_CREATURE -> world.getWorld().getWaterUndergroundCreatureSpawnLimit();
|
||||
+ case AMBIENT -> world.getWorld().getAmbientSpawnLimit();
|
||||
+ case WATER_AMBIENT -> world.getWorld().getWaterAmbientSpawnLimit();
|
||||
+ default -> enumcreaturetype.getMaxInstancesPerChunk();
|
||||
+ };
|
||||
+ }
|
||||
+
|
||||
+ public static int globalLimitForCategory(final ServerLevel level, final MobCategory category, final int spawnableChunkCount) {
|
||||
+ final int categoryLimit = limitForCategory(level, category);
|
||||
+ if (categoryLimit < 1) {
|
||||
+ return categoryLimit;
|
||||
+ }
|
||||
+ return categoryLimit * spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
// Paper start - add parameters and int ret type
|
||||
public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
|
||||
spawnCategoryForChunk(group, world, chunk, checker, runner);
|
||||
diff --git a/src/test/java/io/papermc/paper/PaperCommandTest.java b/src/test/java/io/papermc/paper/PaperCommandTest.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/test/java/io/papermc/paper/PaperCommandTest.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+package io.papermc.paper;
|
||||
+
|
||||
+import com.destroystokyo.paper.PaperCommand;
|
||||
+import java.util.HashSet;
|
||||
+import java.util.Set;
|
||||
+import net.minecraft.world.entity.MobCategory;
|
||||
+import org.junit.Assert;
|
||||
+import org.junit.Test;
|
||||
+
|
||||
+public class PaperCommandTest {
|
||||
+ @Test
|
||||
+ public void testMobCategoryColors() {
|
||||
+ final Set<String> missing = new HashSet<>();
|
||||
+ for (final MobCategory value : MobCategory.values()) {
|
||||
+ if (!PaperCommand.MOB_CATEGORY_COLORS.containsKey(value)) {
|
||||
+ missing.add(value.getName());
|
||||
+ }
|
||||
+ }
|
||||
+ Assert.assertTrue("PaperCommand.MOB_CATEGORY_COLORS map missing TextColors for [" + String.join(", ", missing + "]"), missing.isEmpty());
|
||||
+ }
|
||||
+}
|
File diff suppressed because it is too large
Load Diff
@ -1,94 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <spottedleaf@spottedleaf.dev>
|
||||
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/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
||||
|
||||
this.lastSpawnState = spawnercreature_d;
|
||||
this.level.getProfiler().pop();
|
||||
- List<ChunkHolder> list = Lists.newArrayList(this.chunkMap.getChunks());
|
||||
-
|
||||
- Collections.shuffle(list);
|
||||
+ // Paper - moved down, enabled if per-player = false
|
||||
// Paper - moved natural spawn event up
|
||||
this.level.timings.chunkTicks.startTiming(); // Paper
|
||||
- list.forEach((playerchunk) -> {
|
||||
- Optional<LevelChunk> optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left();
|
||||
-
|
||||
- if (optional.isPresent()) {
|
||||
- LevelChunk chunk = (LevelChunk) optional.get();
|
||||
+ // Paper start
|
||||
+ java.util.Iterator<LevelChunk> iterator;
|
||||
+ if (this.level.paperConfig.perPlayerMobSpawns) {
|
||||
+ iterator = this.entityTickingChunks.iterator();
|
||||
+ } else {
|
||||
+ iterator = this.entityTickingChunks.unsafeIterator();
|
||||
+ List<LevelChunk> shuffled = new java.util.ArrayList<>(this.entityTickingChunks.size());
|
||||
+ while (iterator.hasNext()) {
|
||||
+ shuffled.add(iterator.next());
|
||||
+ }
|
||||
+ Collections.shuffle(shuffled);
|
||||
+ iterator = shuffled.iterator();
|
||||
+ }
|
||||
+ try { while (iterator.hasNext()) {
|
||||
+ LevelChunk chunk = iterator.next();
|
||||
+ ChunkHolder playerchunk = chunk.playerChunk;
|
||||
+ if (playerchunk != null) {
|
||||
+ this.level.getProfiler().push("broadcast");
|
||||
+ this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timings
|
||||
+ playerchunk.broadcastChanges(chunk);
|
||||
+ this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timings
|
||||
+ this.level.getProfiler().pop();
|
||||
+ // Paper end
|
||||
ChunkPos chunkcoordintpair = chunk.getPos();
|
||||
|
||||
- if (this.level.isPositionEntityTicking(chunkcoordintpair) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, false)) { // Paper - optimise isOutsideOfRange
|
||||
+ if ((true || this.level.isPositionEntityTicking(chunkcoordintpair)) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, false)) { // Paper - optimise isOutsideOfRange // Paper - we only iterate entity ticking chunks
|
||||
chunk.setInhabitedTime(chunk.getInhabitedTime() + j);
|
||||
if (flag1 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunk.getPos()) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, true)) { // Spigot // Paper - optimise isOutsideOfRange
|
||||
NaturalSpawner.spawnForChunk(this.level, chunk, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag2);
|
||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
||||
// this.level.timings.doTickTiles.stopTiming(); // Spigot // Paper
|
||||
}
|
||||
}
|
||||
- });
|
||||
+ } // Paper start - optimise chunk tick iteration
|
||||
+ } finally {
|
||||
+ if (iterator instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator) {
|
||||
+ ((io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator<LevelChunk>)iterator).finishedIterating();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - optimise chunk tick iteration
|
||||
this.level.timings.chunkTicks.stopTiming(); // Paper
|
||||
this.level.getProfiler().push("customSpawners");
|
||||
if (flag1) {
|
||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
||||
} // Paper - timings
|
||||
}
|
||||
|
||||
- this.level.getProfiler().popPush("broadcast");
|
||||
- this.chunkMap.getChunks().forEach((playerchunk) -> { // Paper - no... just no...
|
||||
- Optional<LevelChunk> optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); // CraftBukkit - decompile error
|
||||
-
|
||||
- Objects.requireNonNull(playerchunk);
|
||||
-
|
||||
- // Paper start - timings
|
||||
- optional.ifPresent(chunk -> {
|
||||
- this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timings
|
||||
- playerchunk.broadcastChanges(chunk);
|
||||
- this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timings
|
||||
- });
|
||||
- // Paper end
|
||||
- });
|
||||
- this.level.getProfiler().pop();
|
||||
+ // Paper - no, iterating just ONCE is expensive enough! Don't do it TWICE! Code moved up
|
||||
this.level.getProfiler().pop();
|
||||
}
|
||||
|
@ -1,168 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <spottedleaf@spottedleaf.dev>
|
||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
|
||||
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
|
||||
d8 = d5 - this.vehicleLastGoodZ; // Paper - diff on change, used for checking large move vectors above
|
||||
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();
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
|
||||
boolean flag1 = false;
|
||||
|
||||
if (d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
|
||||
- flag1 = true;
|
||||
+ flag1 = true; // Paper - diff on change, this should be moved wrongly
|
||||
ServerGamePacketListenerImpl.LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", 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 flag2 = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D));
|
||||
-
|
||||
- if (flag && (flag1 || !flag2)) {
|
||||
+ // Paper start - optimise out extra getCubes
|
||||
+ boolean teleportBack = flag1; // 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));
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
|
||||
}
|
||||
|
||||
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.getTypeIfLoaded(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
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
|
||||
}
|
||||
}
|
||||
|
||||
- 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
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
|
||||
}
|
||||
|
||||
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.setOnGround(packet.isOnGround()); // CraftBukkit - SPIGOT-5810, SPIGOT-5835: reset by this.player.move
|
||||
// Paper start - prevent position desync
|
||||
if (this.awaitingPositionFromClient != null) {
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
|
||||
boolean flag1 = false;
|
||||
|
||||
if (!this.player.isChangingDimension() && d11 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.isSleeping() && !this.player.gameMode.isCreative() && this.player.gameMode.getGameModeForPlayer() != GameType.SPECTATOR) { // Spigot
|
||||
- flag1 = true;
|
||||
+ flag1 = 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() && (flag1 && worldserver.noCollision(this.player, axisalignedbb) || this.isPlayerCollidingWithAnythingNew((LevelReader) worldserver, axisalignedbb))) {
|
||||
+ // Paper start - optimise out extra getCubes
|
||||
+ // Original for reference:
|
||||
+ // boolean teleportBack = flag1 && worldserver.getCubes(this.player, axisalignedbb) || (didCollide && this.a((IWorldReader) worldserver, axisalignedbb));
|
||||
+ boolean teleportBack = flag1; // 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.teleport(d3, d4, d5, f, f1);
|
||||
} else {
|
||||
// CraftBukkit start - fire PlayerMoveEvent
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
|
||||
}
|
||||
}
|
||||
|
||||
+ // 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) {
|
||||
Stream<VoxelShape> stream = world.getCollisions(this.player, this.player.getBoundingBox().deflate(9.999999747378752E-6D), (entity) -> {
|
||||
return true;
|
@ -1,67 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <spottedleaf@spottedleaf.dev>
|
||||
Date: Thu, 18 Jun 2020 18:23:20 -0700
|
||||
Subject: [PATCH] Prevent unload() calls removing tickets for sync loads
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
||||
return completablefuture;
|
||||
}
|
||||
|
||||
+ private long syncLoadCounter; // Paper - prevent plugin unloads from removing our ticket
|
||||
+
|
||||
private CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkFutureMainThread(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create) {
|
||||
// Paper start - add isUrgent - old sig left in place for dirty nms plugins
|
||||
return getChunkFutureMainThread(chunkX, chunkZ, leastStatus, create, false);
|
||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
||||
ChunkHolder.FullChunkStatus currentChunkState = ChunkHolder.getFullChunkStatus(playerchunk.getTicketLevel());
|
||||
currentlyUnloading = (oldChunkState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && !currentChunkState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER));
|
||||
}
|
||||
+ final Long identifier; // Paper - prevent plugin unloads from removing our ticket
|
||||
if (create && !currentlyUnloading) {
|
||||
// CraftBukkit end
|
||||
this.distanceManager.addTicket(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair);
|
||||
+ identifier = Long.valueOf(this.syncLoadCounter++); // Paper - prevent plugin unloads from removing our ticket
|
||||
+ this.distanceManager.addTicketAtLevel(TicketType.REQUIRED_LOAD, chunkcoordintpair, l, identifier); // Paper - prevent plugin unloads from removing our ticket
|
||||
if (isUrgent) this.distanceManager.markUrgent(chunkcoordintpair); // Paper - Chunk priority
|
||||
if (this.chunkAbsent(playerchunk, l)) {
|
||||
ProfilerFiller gameprofilerfiller = this.level.getProfiler();
|
||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
||||
playerchunk = this.getVisibleChunkIfPresent(k);
|
||||
gameprofilerfiller.pop();
|
||||
if (this.chunkAbsent(playerchunk, l)) {
|
||||
+ this.distanceManager.removeTicketAtLevel(TicketType.REQUIRED_LOAD, chunkcoordintpair, l, identifier); // Paper
|
||||
throw (IllegalStateException) Util.pauseInIde((Throwable) (new IllegalStateException("No chunk holder after ticket has been added")));
|
||||
}
|
||||
}
|
||||
- }
|
||||
|
||||
+ } else { identifier = null; } // Paper - prevent plugin unloads from removing our ticket
|
||||
// Paper start - Chunk priority
|
||||
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = this.chunkAbsent(playerchunk, l) ? ChunkHolder.UNLOADED_CHUNK_FUTURE : playerchunk.getOrScheduleFuture(leastStatus, this.chunkMap);
|
||||
+ // Paper start - prevent plugin unloads from removing our ticket
|
||||
+ if (create && !currentlyUnloading) {
|
||||
+ future.thenAcceptAsync((either) -> {
|
||||
+ ServerChunkCache.this.distanceManager.removeTicketAtLevel(TicketType.REQUIRED_LOAD, chunkcoordintpair, l, identifier);
|
||||
+ }, ServerChunkCache.this.mainThreadProcessor);
|
||||
+ }
|
||||
+ // Paper end - prevent plugin unloads from removing our ticket
|
||||
if (isUrgent) {
|
||||
future.thenAccept(either -> this.distanceManager.clearUrgent(chunkcoordintpair));
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/TicketType.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/TicketType.java
|
||||
@@ -0,0 +0,0 @@ public class TicketType<T> {
|
||||
public static final TicketType<Unit> PLUGIN = TicketType.create("plugin", (a, b) -> 0); // CraftBukkit
|
||||
public static final TicketType<org.bukkit.plugin.Plugin> PLUGIN_TICKET = TicketType.create("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit
|
||||
public static final TicketType<Long> DELAY_UNLOAD = create("delay_unload", Long::compareTo, 300); // Paper
|
||||
+ public static final TicketType<Long> REQUIRED_LOAD = create("required_load", Long::compareTo); // Paper - make sure getChunkAt does not fail
|
||||
|
||||
public static <T> TicketType<T> create(String name, Comparator<T> argumentComparator) {
|
||||
return new TicketType<>(name, argumentComparator, 0L);
|
File diff suppressed because it is too large
Load Diff
@ -1,248 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sun, 21 Mar 2021 16:25:42 -0700
|
||||
Subject: [PATCH] Replace ticket level propagator
|
||||
|
||||
Mojang's propagator is slow, and this isn't surprising
|
||||
given it's built on the same utilities the vanilla light engine
|
||||
is built on. The simple propagator I wrote is approximately 4x
|
||||
faster when simulating player movement. For a long time timing
|
||||
reports have shown this function take up significant tick, (
|
||||
approx 10% or more), and async sampling data shows the level
|
||||
propagation alone takes up a significant amount. So this
|
||||
should help with that. A big side effect is that mid-tick
|
||||
will be more effective, since more time will be allocated
|
||||
to actually processing chunk tasks vs the ticket level updates.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
@@ -0,0 +0,0 @@ import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
+import it.unimi.dsi.fastutil.longs.Long2IntLinkedOpenHashMap; // Paper
|
||||
public abstract class DistanceManager {
|
||||
|
||||
static final Logger LOGGER = LogManager.getLogger();
|
||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
|
||||
private static final int INITIAL_TICKET_LIST_CAPACITY = 4;
|
||||
final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap();
|
||||
public final Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> tickets = new Long2ObjectOpenHashMap();
|
||||
- private final DistanceManager.ChunkTicketTracker ticketTracker = new DistanceManager.ChunkTicketTracker();
|
||||
+ //private final DistanceManager.ChunkTicketTracker ticketTracker = new DistanceManager.ChunkTicketTracker(); // Paper - replace ticket level propagator
|
||||
public static final int MOB_SPAWN_RANGE = 8; // private final ChunkMapDistance.b f = new ChunkMapDistance.b(8); // Paper - no longer used
|
||||
//private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(33); // Paper - no longer used
|
||||
// Paper start use a queue, but still keep unique requirement
|
||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
|
||||
this.mainThreadExecutor = mainThreadExecutor;
|
||||
}
|
||||
|
||||
+ // Paper start - replace ticket level propagator
|
||||
+ protected final Long2IntLinkedOpenHashMap ticketLevelUpdates = new Long2IntLinkedOpenHashMap() {
|
||||
+ @Override
|
||||
+ protected void rehash(int newN) {
|
||||
+ // no downsizing allowed
|
||||
+ if (newN < this.n) {
|
||||
+ return;
|
||||
+ }
|
||||
+ super.rehash(newN);
|
||||
+ }
|
||||
+ };
|
||||
+ protected final io.papermc.paper.util.misc.Delayed8WayDistancePropagator2D ticketLevelPropagator = new io.papermc.paper.util.misc.Delayed8WayDistancePropagator2D(
|
||||
+ (long coordinate, byte oldLevel, byte newLevel) -> {
|
||||
+ DistanceManager.this.ticketLevelUpdates.putAndMoveToLast(coordinate, convertBetweenTicketLevels(newLevel));
|
||||
+ }
|
||||
+ );
|
||||
+ // function for converting between ticket levels and propagator levels and vice versa
|
||||
+ // the problem is the ticket level propagator will propagate from a set source down to zero, whereas mojang expects
|
||||
+ // levels to propagate from a set value up to a maximum value. so we need to convert the levels we put into the propagator
|
||||
+ // and the levels we get out of the propagator
|
||||
+
|
||||
+ // this maps so that GOLDEN_TICKET + 1 will be 0 in the propagator, GOLDEN_TICKET will be 1, and so on
|
||||
+ // we need GOLDEN_TICKET+1 as 0 because anything >= GOLDEN_TICKET+1 should be unloaded
|
||||
+ public static int convertBetweenTicketLevels(final int level) {
|
||||
+ return ChunkMap.MAX_CHUNK_DISTANCE - level + 1;
|
||||
+ }
|
||||
+
|
||||
+ protected final int getPropagatedTicketLevel(final long coordinate) {
|
||||
+ return convertBetweenTicketLevels(this.ticketLevelPropagator.getLevel(coordinate));
|
||||
+ }
|
||||
+
|
||||
+ protected final void updateTicketLevel(final long coordinate, final int ticketLevel) {
|
||||
+ if (ticketLevel > ChunkMap.MAX_CHUNK_DISTANCE) {
|
||||
+ this.ticketLevelPropagator.removeSource(coordinate);
|
||||
+ } else {
|
||||
+ this.ticketLevelPropagator.setSource(coordinate, convertBetweenTicketLevels(ticketLevel));
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - replace ticket level propagator
|
||||
+
|
||||
protected void purgeStaleTickets() {
|
||||
++this.ticketTickCounter;
|
||||
ObjectIterator objectiterator = this.tickets.long2ObjectEntrySet().fastIterator();
|
||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
|
||||
if ((entry.getValue()).removeIf((ticket) -> { // CraftBukkit - decompile error
|
||||
return ticket.timedOut(this.ticketTickCounter);
|
||||
})) {
|
||||
- this.ticketTracker.update(entry.getLongKey(), DistanceManager.getTicketLevelAt((SortedArraySet) entry.getValue()), false);
|
||||
+ this.updateTicketLevel(entry.getLongKey(), getTicketLevelAt(entry.getValue())); // Paper - replace ticket level propagator
|
||||
}
|
||||
|
||||
if (((SortedArraySet) entry.getValue()).isEmpty()) {
|
||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
|
||||
@Nullable
|
||||
protected abstract ChunkHolder updateChunkScheduling(long pos, int level, @Nullable ChunkHolder holder, int k);
|
||||
|
||||
+ protected long ticketLevelUpdateCount; // Paper - replace ticket level propagator
|
||||
public boolean runAllUpdates(ChunkMap playerchunkmap) {
|
||||
//this.f.a(); // Paper - no longer used
|
||||
org.spigotmc.AsyncCatcher.catchOp("DistanceManagerTick"); // Paper
|
||||
//this.playerTicketManager.runAllUpdates(); // Paper - no longer used
|
||||
- int i = Integer.MAX_VALUE - this.ticketTracker.runDistanceUpdates(Integer.MAX_VALUE);
|
||||
- boolean flag = i != 0;
|
||||
+ boolean flag = this.ticketLevelPropagator.propagateUpdates(); // Paper - replace ticket level propagator
|
||||
|
||||
if (flag) {
|
||||
;
|
||||
}
|
||||
|
||||
- // Paper start
|
||||
- if (!this.pendingChunkUpdates.isEmpty()) {
|
||||
- this.pollingPendingChunkUpdates = true; try { // Paper - Chunk priority
|
||||
- while(!this.pendingChunkUpdates.isEmpty()) {
|
||||
- ChunkHolder remove = this.pendingChunkUpdates.remove();
|
||||
- remove.isUpdateQueued = false;
|
||||
- remove.updateFutures(playerchunkmap, this.mainThreadExecutor);
|
||||
- }
|
||||
- } finally { this.pollingPendingChunkUpdates = false; } // Paper - Chunk priority
|
||||
- // Paper end
|
||||
- return true;
|
||||
- } else {
|
||||
- if (!this.ticketsToRelease.isEmpty()) {
|
||||
- LongIterator longiterator = this.ticketsToRelease.iterator();
|
||||
+ // Paper start - replace level propagator
|
||||
+ ticket_update_loop:
|
||||
+ while (!this.ticketLevelUpdates.isEmpty()) {
|
||||
+ flag = true;
|
||||
|
||||
- while (longiterator.hasNext()) {
|
||||
- long j = longiterator.nextLong();
|
||||
+ boolean oldPolling = this.pollingPendingChunkUpdates;
|
||||
+ this.pollingPendingChunkUpdates = true;
|
||||
+ try {
|
||||
+ for (java.util.Iterator<Long2IntMap.Entry> iterator = this.ticketLevelUpdates.long2IntEntrySet().fastIterator(); iterator.hasNext();) {
|
||||
+ Long2IntMap.Entry entry = iterator.next();
|
||||
+ long key = entry.getLongKey();
|
||||
+ int newLevel = entry.getIntValue();
|
||||
+ ChunkHolder chunk = this.getChunk(key);
|
||||
+
|
||||
+ if (chunk == null && newLevel > ChunkMap.MAX_CHUNK_DISTANCE) {
|
||||
+ // not loaded and it shouldn't be loaded!
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ int currentLevel = chunk == null ? ChunkMap.MAX_CHUNK_DISTANCE + 1 : chunk.getTicketLevel();
|
||||
+
|
||||
+ if (currentLevel == newLevel) {
|
||||
+ // nothing to do
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
- if (this.getTickets(j).stream().anyMatch((ticket) -> {
|
||||
- return ticket.getType() == TicketType.PLAYER;
|
||||
- })) {
|
||||
- ChunkHolder playerchunk = playerchunkmap.getUpdatingChunkIfPresent(j);
|
||||
+ this.updateChunkScheduling(key, newLevel, chunk, currentLevel);
|
||||
+ }
|
||||
|
||||
- if (playerchunk == null) {
|
||||
- throw new IllegalStateException();
|
||||
+ long recursiveCheck = ++this.ticketLevelUpdateCount;
|
||||
+ while (!this.ticketLevelUpdates.isEmpty()) {
|
||||
+ long key = this.ticketLevelUpdates.firstLongKey();
|
||||
+ int newLevel = this.ticketLevelUpdates.removeFirstInt();
|
||||
+ ChunkHolder chunk = this.getChunk(key);
|
||||
+
|
||||
+ if (chunk == null) {
|
||||
+ if (newLevel <= ChunkMap.MAX_CHUNK_DISTANCE) {
|
||||
+ throw new IllegalStateException("Expected chunk holder to be created");
|
||||
}
|
||||
+ // not loaded and it shouldn't be loaded!
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
- CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completablefuture = playerchunk.getEntityTickingChunkFuture();
|
||||
+ int currentLevel = chunk.oldTicketLevel;
|
||||
|
||||
- completablefuture.thenAccept((either) -> {
|
||||
- this.mainThreadExecutor.execute(() -> {
|
||||
- this.ticketThrottlerReleaser.tell(ChunkTaskPriorityQueueSorter.release(() -> {
|
||||
- }, j, false));
|
||||
- });
|
||||
- });
|
||||
+ if (currentLevel == newLevel) {
|
||||
+ // nothing to do
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ chunk.updateFutures(playerchunkmap, this.mainThreadExecutor);
|
||||
+ if (recursiveCheck != this.ticketLevelUpdateCount) {
|
||||
+ // back to the start, we must create player chunks and update the ticket level fields before
|
||||
+ // processing the actual level updates
|
||||
+ continue ticket_update_loop;
|
||||
}
|
||||
}
|
||||
|
||||
- this.ticketsToRelease.clear();
|
||||
- }
|
||||
+ for (;;) {
|
||||
+ if (recursiveCheck != this.ticketLevelUpdateCount) {
|
||||
+ continue ticket_update_loop;
|
||||
+ }
|
||||
+ ChunkHolder pendingUpdate = this.pendingChunkUpdates.poll();
|
||||
+ if (pendingUpdate == null) {
|
||||
+ break;
|
||||
+ }
|
||||
|
||||
- return flag;
|
||||
+ pendingUpdate.updateFutures(playerchunkmap, this.mainThreadExecutor);
|
||||
+ }
|
||||
+ } finally {
|
||||
+ this.pollingPendingChunkUpdates = oldPolling;
|
||||
+ }
|
||||
}
|
||||
+
|
||||
+ return flag;
|
||||
+ // Paper end - replace level propagator
|
||||
}
|
||||
boolean pollingPendingChunkUpdates = false; // Paper - Chunk priority
|
||||
|
||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
|
||||
|
||||
ticket1.setCreatedTick(this.ticketTickCounter);
|
||||
if (ticket.getTicketLevel() < j) {
|
||||
- this.ticketTracker.update(i, ticket.getTicketLevel(), true);
|
||||
+ this.updateTicketLevel(i, ticket.getTicketLevel()); // Paper - replace ticket level propagator
|
||||
}
|
||||
|
||||
return ticket == ticket1; // CraftBukkit
|
||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
|
||||
// Paper start - Chunk priority
|
||||
int newLevel = getTicketLevelAt(arraysetsorted);
|
||||
if (newLevel > oldLevel) {
|
||||
- this.ticketTracker.update(i, newLevel, false);
|
||||
+ this.updateTicketLevel(i, newLevel); // Paper // Paper - replace ticket level propagator
|
||||
}
|
||||
// Paper end
|
||||
return removed; // CraftBukkit
|
||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
|
||||
SortedArraySet<Ticket<?>> tickets = entry.getValue();
|
||||
if (tickets.remove(target)) {
|
||||
// copied from removeTicket
|
||||
- this.ticketTracker.update(entry.getLongKey(), DistanceManager.getTicketLevelAt(tickets), false);
|
||||
+ this.updateTicketLevel(entry.getLongKey(), getTicketLevelAt(tickets)); // Paper - replace ticket level propagator
|
||||
|
||||
// can't use entry after it's removed
|
||||
if (tickets.isEmpty()) {
|
File diff suppressed because it is too large
Load Diff
@ -1,30 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Thu, 11 Mar 2021 21:17:02 -0800
|
||||
Subject: [PATCH] Use hash table for maintaing changed block set
|
||||
|
||||
When a lot of block changes occur the iteration for checking can
|
||||
add up a bit and cause a small performance impact.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
@@ -0,0 +0,0 @@ import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
// CraftBukkit end
|
||||
|
||||
+import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; // Paper
|
||||
+
|
||||
public class ChunkHolder {
|
||||
|
||||
public static final Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> UNLOADED_CHUNK = Either.right(ChunkHolder.ChunkLoadingFailure.UNLOADED);
|
||||
@@ -0,0 +0,0 @@ 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.changedBlocksPerSection[i] = new ShortArraySet();
|
||||
+ this.changedBlocksPerSection[i] = new ShortOpenHashSet(); // Paper - use a set to make setting constant-time
|
||||
}
|
||||
|
||||
this.changedBlocksPerSection[i].add(SectionPos.sectionRelativePos(pos));
|
Loading…
Reference in New Issue
Block a user