More more patches, including hopper optimization patch
This commit is contained in:
parent
c9907c650f
commit
2debcaff9d
11 changed files with 690 additions and 663 deletions
|
@ -0,0 +1,375 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Andrew Steinborn <git@steinborn.me>
|
||||
Date: Mon, 26 Jul 2021 02:15:17 -0400
|
||||
Subject: [PATCH] Use Velocity compression and cipher natives
|
||||
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index 2fb342f9d86e169571070b2b40538633f4b0d7cf..e2e3abe6a4b837fcef96ce7da6679b14afba0519 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -38,6 +38,11 @@ dependencies {
|
||||
implementation("org.spongepowered:configurate-yaml:4.2.0-SNAPSHOT") // Paper - config files
|
||||
implementation("commons-lang:commons-lang:2.6")
|
||||
runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ implementation("com.velocitypowered:velocity-native:3.1.2-SNAPSHOT") {
|
||||
+ isTransitive = false
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
|
||||
runtimeOnly("org.apache.maven:maven-resolver-provider:3.9.6")
|
||||
runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18")
|
||||
diff --git a/src/main/java/net/minecraft/network/CipherDecoder.java b/src/main/java/net/minecraft/network/CipherDecoder.java
|
||||
index 778beb445eac5769b9e4e07b4d1294c50ae2602b..7b895b6a626da6297f07582e96d98ecfb6c8c951 100644
|
||||
--- a/src/main/java/net/minecraft/network/CipherDecoder.java
|
||||
+++ b/src/main/java/net/minecraft/network/CipherDecoder.java
|
||||
@@ -7,13 +7,29 @@ import java.util.List;
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
public class CipherDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
- private final CipherBase cipher;
|
||||
+ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper - Use Velocity cipher
|
||||
|
||||
- public CipherDecoder(Cipher cipher) {
|
||||
- this.cipher = new CipherBase(cipher);
|
||||
+ public CipherDecoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper - Use Velocity cipher
|
||||
+ this.cipher = cipher; // Paper - Use Velocity cipher
|
||||
}
|
||||
|
||||
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
|
||||
- list.add(this.cipher.decipher(channelHandlerContext, byteBuf));
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), cipher, byteBuf);
|
||||
+ try {
|
||||
+ cipher.process(compatible);
|
||||
+ list.add(compatible);
|
||||
+ } catch (Exception e) {
|
||||
+ compatible.release(); // compatible will never be used if we throw an exception
|
||||
+ throw e;
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
+
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ @Override
|
||||
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
||||
+ cipher.close();
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/network/CipherEncoder.java b/src/main/java/net/minecraft/network/CipherEncoder.java
|
||||
index 0f3d502a9680006bcdcd7d272240a2e5c3b46790..ffa1c48585fbbc1d30826d435043527f6183a3ee 100644
|
||||
--- a/src/main/java/net/minecraft/network/CipherEncoder.java
|
||||
+++ b/src/main/java/net/minecraft/network/CipherEncoder.java
|
||||
@@ -4,15 +4,32 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import javax.crypto.Cipher;
|
||||
+import java.util.List;
|
||||
|
||||
-public class CipherEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
- private final CipherBase cipher;
|
||||
+public class CipherEncoder extends io.netty.handler.codec.MessageToMessageEncoder<ByteBuf> { // Paper - Use Velocity cipher; change superclass
|
||||
+ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper - Use Velocity cipher
|
||||
|
||||
- public CipherEncoder(Cipher cipher) {
|
||||
- this.cipher = new CipherBase(cipher);
|
||||
+ public CipherEncoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper - Use Velocity cipher
|
||||
+ this.cipher = cipher; // Paper - Use Velocity cipher
|
||||
}
|
||||
|
||||
- protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception {
|
||||
- this.cipher.encipher(byteBuf, byteBuf2);
|
||||
+ protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), cipher, byteBuf);
|
||||
+ try {
|
||||
+ cipher.process(compatible);
|
||||
+ list.add(compatible);
|
||||
+ } catch (Exception e) {
|
||||
+ compatible.release(); // compatible will never be used if we throw an exception
|
||||
+ throw e;
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
+
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ @Override
|
||||
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
||||
+ cipher.close();
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/network/CompressionDecoder.java b/src/main/java/net/minecraft/network/CompressionDecoder.java
|
||||
index 5927c928b27f64bd3973e95d5867b59f67476d41..ba3619b03ea73e4ea55ae2eb79d3b6cd1f48a191 100644
|
||||
--- a/src/main/java/net/minecraft/network/CompressionDecoder.java
|
||||
+++ b/src/main/java/net/minecraft/network/CompressionDecoder.java
|
||||
@@ -13,13 +13,20 @@ public class CompressionDecoder extends ByteToMessageDecoder {
|
||||
public static final int MAXIMUM_COMPRESSED_LENGTH = 2097152;
|
||||
public static final int MAXIMUM_UNCOMPRESSED_LENGTH = 8388608;
|
||||
private final Inflater inflater;
|
||||
+ private com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper - Use Velocity cipher
|
||||
private int threshold;
|
||||
private boolean validateDecompressed;
|
||||
|
||||
+ // Paper start - Use Velocity cipher
|
||||
public CompressionDecoder(int compressionThreshold, boolean rejectsBadPackets) {
|
||||
+ this(null, compressionThreshold, rejectsBadPackets);
|
||||
+ }
|
||||
+ public CompressionDecoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold, boolean rejectsBadPackets) {
|
||||
this.threshold = compressionThreshold;
|
||||
this.validateDecompressed = rejectsBadPackets;
|
||||
- this.inflater = new Inflater();
|
||||
+ this.inflater = compressor == null ? new Inflater() : null;
|
||||
+ this.compressor = compressor;
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
|
||||
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
|
||||
@@ -38,14 +45,42 @@ public class CompressionDecoder extends ByteToMessageDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
+ if (inflater != null) { // Paper - Use Velocity cipher; fallback to vanilla inflater
|
||||
this.setupInflaterInput(byteBuf);
|
||||
ByteBuf byteBuf2 = this.inflate(channelHandlerContext, i);
|
||||
this.inflater.reset();
|
||||
list.add(byteBuf2);
|
||||
+ return; // Paper - Use Velocity cipher
|
||||
+ } // Paper - use velocity compression
|
||||
+
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ int claimedUncompressedSize = i; // OBFHELPER
|
||||
+ ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), this.compressor, byteBuf);
|
||||
+ ByteBuf uncompressed = com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(channelHandlerContext.alloc(), this.compressor, claimedUncompressedSize);
|
||||
+ try {
|
||||
+ this.compressor.inflate(compatibleIn, uncompressed, claimedUncompressedSize);
|
||||
+ list.add(uncompressed);
|
||||
+ byteBuf.clear();
|
||||
+ } catch (Exception e) {
|
||||
+ uncompressed.release();
|
||||
+ throw e;
|
||||
+ } finally {
|
||||
+ compatibleIn.release();
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ @Override
|
||||
+ public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
|
||||
+ if (this.compressor != null) {
|
||||
+ this.compressor.close();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
+
|
||||
private void setupInflaterInput(ByteBuf buf) {
|
||||
ByteBuffer byteBuffer;
|
||||
if (buf.nioBufferCount() > 0) {
|
||||
@@ -82,7 +117,7 @@ public class CompressionDecoder extends ByteToMessageDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
- public void setThreshold(int compressionThreshold, boolean rejectsBadPackets) {
|
||||
+ public void setThreshold(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold, boolean rejectsBadPackets) { // Paper - Use Velocity cipher
|
||||
this.threshold = compressionThreshold;
|
||||
this.validateDecompressed = rejectsBadPackets;
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/network/CompressionEncoder.java b/src/main/java/net/minecraft/network/CompressionEncoder.java
|
||||
index 34a80d2f34555663ab1b394972957089214cb337..ec30c291188ac3bba7f1c3bc397576d1edb0a57f 100644
|
||||
--- a/src/main/java/net/minecraft/network/CompressionEncoder.java
|
||||
+++ b/src/main/java/net/minecraft/network/CompressionEncoder.java
|
||||
@@ -6,16 +6,31 @@ import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
- private final byte[] encodeBuf = new byte[8192];
|
||||
+ @javax.annotation.Nullable private final byte[] encodeBuf; // Paper - Use Velocity cipher
|
||||
+ @javax.annotation.Nullable // Paper - Use Velocity cipher
|
||||
private final Deflater deflater;
|
||||
+ @javax.annotation.Nullable // Paper - Use Velocity cipher
|
||||
+ private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper - Use Velocity cipher
|
||||
private int threshold;
|
||||
|
||||
+ // Paper start - Use Velocity cipher
|
||||
public CompressionEncoder(int compressionThreshold) {
|
||||
+ this(null, compressionThreshold);
|
||||
+ }
|
||||
+ public CompressionEncoder(@javax.annotation.Nullable com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold) {
|
||||
this.threshold = compressionThreshold;
|
||||
- this.deflater = new Deflater();
|
||||
+ if (compressor == null) {
|
||||
+ this.encodeBuf = new byte[8192];
|
||||
+ this.deflater = new Deflater();
|
||||
+ } else {
|
||||
+ this.encodeBuf = null;
|
||||
+ this.deflater = null;
|
||||
+ }
|
||||
+ this.compressor = compressor;
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
|
||||
- protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) {
|
||||
+ protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception { // Paper - Use Velocity cipher
|
||||
int i = byteBuf.readableBytes();
|
||||
if (i > 8388608) {
|
||||
throw new IllegalArgumentException("Packet too big (is " + i + ", should be less than 8388608)");
|
||||
@@ -24,6 +39,7 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
VarInt.write(byteBuf2, 0);
|
||||
byteBuf2.writeBytes(byteBuf);
|
||||
} else {
|
||||
+ if (this.deflater != null) { // Paper - Use Velocity cipher
|
||||
byte[] bs = new byte[i];
|
||||
byteBuf.readBytes(bs);
|
||||
VarInt.write(byteBuf2, bs.length);
|
||||
@@ -36,10 +52,47 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
}
|
||||
|
||||
this.deflater.reset();
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ VarInt.write(byteBuf2, i);
|
||||
+ final ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), this.compressor, byteBuf);
|
||||
+ try {
|
||||
+ this.compressor.deflate(compatibleIn, byteBuf2);
|
||||
+ } finally {
|
||||
+ compatibleIn.release();
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ @Override
|
||||
+ protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception{
|
||||
+ if (this.compressor != null) {
|
||||
+ // We allocate bytes to be compressed plus 1 byte. This covers two cases:
|
||||
+ //
|
||||
+ // - Compression
|
||||
+ // According to https://github.com/ebiggers/libdeflate/blob/master/libdeflate.h#L103,
|
||||
+ // if the data compresses well (and we do not have some pathological case) then the maximum
|
||||
+ // size the compressed size will ever be is the input size minus one.
|
||||
+ // - Uncompressed
|
||||
+ // This is fairly obvious - we will then have one more than the uncompressed size.
|
||||
+ final int initialBufferSize = msg.readableBytes() + 1;
|
||||
+ return com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(ctx.alloc(), this.compressor, initialBufferSize);
|
||||
+ }
|
||||
+
|
||||
+ return super.allocateBuffer(ctx, msg, preferDirect);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
||||
+ if (this.compressor != null) {
|
||||
+ this.compressor.close();
|
||||
+ // Paper end - Use Velocity cipher
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
public int getThreshold() {
|
||||
return this.threshold;
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
||||
index f94458d4270042062b9ae18931cc8179ef1fb420..63066dfde00569833546da01cd580434f8be6593 100644
|
||||
--- a/src/main/java/net/minecraft/network/Connection.java
|
||||
+++ b/src/main/java/net/minecraft/network/Connection.java
|
||||
@@ -763,11 +763,28 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
return networkmanager;
|
||||
}
|
||||
|
||||
- public void setEncryptionKey(Cipher decryptionCipher, Cipher encryptionCipher) {
|
||||
- this.encrypted = true;
|
||||
- this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher));
|
||||
- this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher));
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+// public void setEncryptionKey(Cipher decryptionCipher, Cipher encryptionCipher) {
|
||||
+// this.encrypted = true;
|
||||
+// this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher));
|
||||
+// this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher));
|
||||
+// }
|
||||
+
|
||||
+ public void setupEncryption(javax.crypto.SecretKey key) throws net.minecraft.util.CryptException {
|
||||
+ if (!this.encrypted) {
|
||||
+ try {
|
||||
+ com.velocitypowered.natives.encryption.VelocityCipher decryption = com.velocitypowered.natives.util.Natives.cipher.get().forDecryption(key);
|
||||
+ com.velocitypowered.natives.encryption.VelocityCipher encryption = com.velocitypowered.natives.util.Natives.cipher.get().forEncryption(key);
|
||||
+
|
||||
+ this.encrypted = true;
|
||||
+ this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryption));
|
||||
+ this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryption));
|
||||
+ } catch (java.security.GeneralSecurityException e) {
|
||||
+ throw new net.minecraft.util.CryptException(e);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
+ // Paper end - Use Velocity cipher
|
||||
|
||||
public boolean isEncrypted() {
|
||||
return this.encrypted;
|
||||
@@ -800,12 +817,13 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
public void setupCompression(int compressionThreshold, boolean rejectsBadPackets) {
|
||||
if (compressionThreshold >= 0) {
|
||||
+ com.velocitypowered.natives.compression.VelocityCompressor compressor = com.velocitypowered.natives.util.Natives.compress.get().create(io.papermc.paper.configuration.GlobalConfiguration.get().misc.compressionLevel.or(-1)); // Paper - Use Velocity cipher
|
||||
ChannelHandler channelhandler = this.channel.pipeline().get("decompress");
|
||||
|
||||
if (channelhandler instanceof CompressionDecoder) {
|
||||
CompressionDecoder packetdecompressor = (CompressionDecoder) channelhandler;
|
||||
|
||||
- packetdecompressor.setThreshold(compressionThreshold, rejectsBadPackets);
|
||||
+ packetdecompressor.setThreshold(compressor, compressionThreshold, rejectsBadPackets); // Paper - Use Velocity cipher
|
||||
} else {
|
||||
this.channel.pipeline().addAfter("splitter", "decompress", new CompressionDecoder(compressionThreshold, rejectsBadPackets));
|
||||
}
|
||||
@@ -816,7 +834,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
packetcompressor.setThreshold(compressionThreshold);
|
||||
} else {
|
||||
- this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressionThreshold));
|
||||
+ this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressor, compressionThreshold)); // Paper - Use Velocity cipher
|
||||
}
|
||||
this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_THRESHOLD_SET); // Paper - Add Channel initialization listeners
|
||||
} else {
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
index a08d9aa6e420f691795df9b627a9cd5b5c0112c5..52f537b7bfbdeaad9d17c0e88a1ed1c8925a833f 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
@@ -106,6 +106,11 @@ public class ServerConnectionListener {
|
||||
ServerConnectionListener.LOGGER.info("Using default channel type");
|
||||
}
|
||||
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ ServerConnectionListener.LOGGER.info("Paper: Using " + com.velocitypowered.natives.util.Natives.compress.getLoadedVariant() + " compression from Velocity.");
|
||||
+ ServerConnectionListener.LOGGER.info("Paper: Using " + com.velocitypowered.natives.util.Natives.cipher.getLoadedVariant() + " cipher from Velocity.");
|
||||
+ // Paper end - Use Velocity cipher
|
||||
+
|
||||
this.channels.add(((ServerBootstrap) ((ServerBootstrap) (new ServerBootstrap()).channel(oclass)).childHandler(new ChannelInitializer<Channel>() {
|
||||
protected void initChannel(Channel channel) {
|
||||
try {
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
index 21d97c2b533a6528dd73c4e514d49273c120e171..cb25d42ba40bd0901bbe0d8399e5d8e87988706f 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
@@ -274,12 +274,14 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
||||
}
|
||||
|
||||
SecretKey secretkey = packet.getSecretKey(privatekey);
|
||||
- Cipher cipher = Crypt.getCipher(2, secretkey);
|
||||
- Cipher cipher1 = Crypt.getCipher(1, secretkey);
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+// Cipher cipher = Crypt.getCipher(2, secretkey);
|
||||
+// Cipher cipher1 = Crypt.getCipher(1, secretkey);
|
||||
+ // Paper end - Use Velocity cipher
|
||||
|
||||
s = (new BigInteger(Crypt.digestData("", this.server.getKeyPair().getPublic(), secretkey))).toString(16);
|
||||
this.state = ServerLoginPacketListenerImpl.State.AUTHENTICATING;
|
||||
- this.connection.setEncryptionKey(cipher, cipher1);
|
||||
+ this.connection.setupEncryption(secretkey); // Paper - Use Velocity cipher
|
||||
} catch (CryptException cryptographyexception) {
|
||||
throw new IllegalStateException("Protocol error", cryptographyexception);
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Thu, 26 Mar 2020 21:59:32 -0700
|
||||
Subject: [PATCH] Detail more information in watchdog dumps
|
||||
|
||||
- Dump position, world, velocity, and uuid for currently ticking entities
|
||||
- Dump player name, player uuid, position, and world for packet handling
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
||||
index 63066dfde00569833546da01cd580434f8be6593..7fb162fa031fd76aa9a94f5fdaa3e32ceb9b9abc 100644
|
||||
--- a/src/main/java/net/minecraft/network/Connection.java
|
||||
+++ b/src/main/java/net/minecraft/network/Connection.java
|
||||
@@ -607,7 +607,13 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
if (!(this.packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginPacketListener)
|
||||
|| loginPacketListener.state != net.minecraft.server.network.ServerLoginPacketListenerImpl.State.VERIFYING
|
||||
|| Connection.joinAttemptsThisTick++ < MAX_PER_TICK) {
|
||||
+ // Paper start - detailed watchdog information
|
||||
+ net.minecraft.network.protocol.PacketUtils.packetProcessing.push(this.packetListener);
|
||||
+ try {
|
||||
tickablepacketlistener.tick();
|
||||
+ } finally {
|
||||
+ net.minecraft.network.protocol.PacketUtils.packetProcessing.pop();
|
||||
+ } // Paper end - detailed watchdog information
|
||||
} // Paper end - Buffer joins to world
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/network/protocol/PacketUtils.java b/src/main/java/net/minecraft/network/protocol/PacketUtils.java
|
||||
index e161ad0f53a21a68e8c78575ba5d3cdbdb11fca0..57e76b53e5e314c3e6b8856010f7a84188121582 100644
|
||||
--- a/src/main/java/net/minecraft/network/protocol/PacketUtils.java
|
||||
+++ b/src/main/java/net/minecraft/network/protocol/PacketUtils.java
|
||||
@@ -19,6 +19,24 @@ public class PacketUtils {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
+ // Paper start - detailed watchdog information
|
||||
+ public static final java.util.concurrent.ConcurrentLinkedDeque<PacketListener> packetProcessing = new java.util.concurrent.ConcurrentLinkedDeque<>();
|
||||
+ static final java.util.concurrent.atomic.AtomicLong totalMainThreadPacketsProcessed = new java.util.concurrent.atomic.AtomicLong();
|
||||
+
|
||||
+ public static long getTotalProcessedPackets() {
|
||||
+ return totalMainThreadPacketsProcessed.get();
|
||||
+ }
|
||||
+
|
||||
+ public static java.util.List<PacketListener> getCurrentPacketProcessors() {
|
||||
+ java.util.List<PacketListener> ret = new java.util.ArrayList<>(4);
|
||||
+ for (PacketListener listener : packetProcessing) {
|
||||
+ ret.add(listener);
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+ // Paper end - detailed watchdog information
|
||||
+
|
||||
public PacketUtils() {}
|
||||
|
||||
public static <T extends PacketListener> void ensureRunningOnSameThread(Packet<T> packet, T listener, ServerLevel world) throws RunningOnDifferentThreadException {
|
||||
@@ -28,6 +46,8 @@ public class PacketUtils {
|
||||
public static <T extends PacketListener> void ensureRunningOnSameThread(Packet<T> packet, T listener, BlockableEventLoop<?> engine) throws RunningOnDifferentThreadException {
|
||||
if (!engine.isSameThread()) {
|
||||
engine.executeIfPossible(() -> {
|
||||
+ packetProcessing.push(listener); // Paper - detailed watchdog information
|
||||
+ try { // Paper - detailed watchdog information
|
||||
if (listener instanceof ServerCommonPacketListenerImpl serverCommonPacketListener && serverCommonPacketListener.processedDisconnect) return; // CraftBukkit - Don't handle sync packets for kicked players
|
||||
if (listener.shouldHandleMessage(packet)) {
|
||||
co.aikar.timings.Timing timing = co.aikar.timings.MinecraftTimings.getPacketTiming(packet); // Paper - timings
|
||||
@@ -47,6 +67,12 @@ public class PacketUtils {
|
||||
} else {
|
||||
PacketUtils.LOGGER.debug("Ignoring packet due to disconnection: {}", packet);
|
||||
}
|
||||
+ // Paper start - detailed watchdog information
|
||||
+ } finally {
|
||||
+ totalMainThreadPacketsProcessed.getAndIncrement();
|
||||
+ packetProcessing.pop();
|
||||
+ }
|
||||
+ // Paper end - detailed watchdog information
|
||||
|
||||
});
|
||||
throw RunningOnDifferentThreadException.RUNNING_ON_DIFFERENT_THREAD;
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index 16d24e70072e3846b3c35d331c3f474238a8def2..98fded81545a96deb6a05df713a5b69e27189cf4 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -1245,7 +1245,26 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
|
||||
}
|
||||
|
||||
+ // Paper start - log detailed entity tick information
|
||||
+ // TODO replace with varhandle
|
||||
+ static final java.util.concurrent.atomic.AtomicReference<Entity> currentlyTickingEntity = new java.util.concurrent.atomic.AtomicReference<>();
|
||||
+
|
||||
+ public static List<Entity> getCurrentlyTickingEntities() {
|
||||
+ Entity ticking = currentlyTickingEntity.get();
|
||||
+ List<Entity> ret = java.util.Arrays.asList(ticking == null ? new Entity[0] : new Entity[] { ticking });
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+ // Paper end - log detailed entity tick information
|
||||
+
|
||||
public void tickNonPassenger(Entity entity) {
|
||||
+ // Paper start - log detailed entity tick information
|
||||
+ io.papermc.paper.util.TickThread.ensureTickThread("Cannot tick an entity off-main");
|
||||
+ try {
|
||||
+ if (currentlyTickingEntity.get() == null) {
|
||||
+ currentlyTickingEntity.lazySet(entity);
|
||||
+ }
|
||||
+ // Paper end - log detailed entity tick information
|
||||
++TimingHistory.entityTicks; // Paper - timings
|
||||
// Spigot start
|
||||
co.aikar.timings.Timing timer; // Paper
|
||||
@@ -1285,7 +1304,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
this.tickPassenger(entity, entity1);
|
||||
}
|
||||
// } finally { timer.stopTiming(); } // Paper - timings - move up
|
||||
-
|
||||
+ // Paper start - log detailed entity tick information
|
||||
+ } finally {
|
||||
+ if (currentlyTickingEntity.get() == entity) {
|
||||
+ currentlyTickingEntity.lazySet(null);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - log detailed entity tick information
|
||||
}
|
||||
|
||||
private void tickPassenger(Entity vehicle, Entity passenger) {
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index 257943b4c984d6faee29eca17c8f951e7f43168b..0fbcf60a994f67bdd81d40e4a8bf38f0cbb8993d 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -1074,8 +1074,43 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
return this.onGround;
|
||||
}
|
||||
|
||||
+ // Paper start - detailed watchdog information
|
||||
+ public final Object posLock = new Object(); // Paper - log detailed entity tick information
|
||||
+
|
||||
+ private Vec3 moveVector;
|
||||
+ private double moveStartX;
|
||||
+ private double moveStartY;
|
||||
+ private double moveStartZ;
|
||||
+
|
||||
+ public final Vec3 getMoveVector() {
|
||||
+ return this.moveVector;
|
||||
+ }
|
||||
+
|
||||
+ public final double getMoveStartX() {
|
||||
+ return this.moveStartX;
|
||||
+ }
|
||||
+
|
||||
+ public final double getMoveStartY() {
|
||||
+ return this.moveStartY;
|
||||
+ }
|
||||
+
|
||||
+ public final double getMoveStartZ() {
|
||||
+ return this.moveStartZ;
|
||||
+ }
|
||||
+ // Paper end - detailed watchdog information
|
||||
+
|
||||
public void move(MoverType movementType, Vec3 movement) {
|
||||
final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity
|
||||
+ // Paper start - detailed watchdog information
|
||||
+ io.papermc.paper.util.TickThread.ensureTickThread("Cannot move an entity off-main");
|
||||
+ synchronized (this.posLock) {
|
||||
+ this.moveStartX = this.getX();
|
||||
+ this.moveStartY = this.getY();
|
||||
+ this.moveStartZ = this.getZ();
|
||||
+ this.moveVector = movement;
|
||||
+ }
|
||||
+ try {
|
||||
+ // Paper end - detailed watchdog information
|
||||
if (this.noPhysics) {
|
||||
this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z);
|
||||
} else {
|
||||
@@ -1245,6 +1280,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
this.level().getProfiler().pop();
|
||||
}
|
||||
}
|
||||
+ // Paper start - detailed watchdog information
|
||||
+ } finally {
|
||||
+ synchronized (this.posLock) { // Paper
|
||||
+ this.moveVector = null;
|
||||
+ } // Paper
|
||||
+ }
|
||||
+ // Paper end - detailed watchdog information
|
||||
}
|
||||
|
||||
private boolean isStateClimbable(BlockState state) {
|
||||
@@ -4405,7 +4447,9 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
|
||||
public void setDeltaMovement(Vec3 velocity) {
|
||||
+ synchronized (this.posLock) { // Paper
|
||||
this.deltaMovement = velocity;
|
||||
+ } // Paper
|
||||
}
|
||||
|
||||
public void addDeltaMovement(Vec3 velocity) {
|
||||
@@ -4508,7 +4552,9 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
// Paper end - Fix MC-4
|
||||
if (this.position.x != x || this.position.y != y || this.position.z != z) {
|
||||
+ synchronized (this.posLock) { // Paper
|
||||
this.position = new Vec3(x, y, z);
|
||||
+ } // Paper
|
||||
int i = Mth.floor(x);
|
||||
int j = Mth.floor(y);
|
||||
int k = Mth.floor(z);
|
||||
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
index 577f29519156f33b49ee319a64a52249630e28d8..6db566e3111ec08a99aa429624979cb83a85e272 100644
|
||||
--- a/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
@@ -22,6 +22,78 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
|
||||
private volatile long lastTick;
|
||||
private volatile boolean stopping;
|
||||
|
||||
+ // Paper start - log detailed tick information
|
||||
+ private void dumpEntity(net.minecraft.world.entity.Entity entity) {
|
||||
+ Logger log = Bukkit.getServer().getLogger();
|
||||
+ double posX, posY, posZ;
|
||||
+ net.minecraft.world.phys.Vec3 mot;
|
||||
+ double moveStartX, moveStartY, moveStartZ;
|
||||
+ net.minecraft.world.phys.Vec3 moveVec;
|
||||
+ synchronized (entity.posLock) {
|
||||
+ posX = entity.getX();
|
||||
+ posY = entity.getY();
|
||||
+ posZ = entity.getZ();
|
||||
+ mot = entity.getDeltaMovement();
|
||||
+ moveStartX = entity.getMoveStartX();
|
||||
+ moveStartY = entity.getMoveStartY();
|
||||
+ moveStartZ = entity.getMoveStartZ();
|
||||
+ moveVec = entity.getMoveVector();
|
||||
+ }
|
||||
+
|
||||
+ String entityType = net.minecraft.world.entity.EntityType.getKey(entity.getType()).toString();
|
||||
+ java.util.UUID entityUUID = entity.getUUID();
|
||||
+ net.minecraft.world.level.Level world = entity.level();
|
||||
+
|
||||
+ log.log(Level.SEVERE, "Ticking entity: " + entityType + ", entity class: " + entity.getClass().getName());
|
||||
+ log.log(Level.SEVERE, "Entity status: removed: " + entity.isRemoved() + ", valid: " + entity.valid + ", alive: " + entity.isAlive() + ", is passenger: " + entity.isPassenger());
|
||||
+ log.log(Level.SEVERE, "Entity UUID: " + entityUUID);
|
||||
+ log.log(Level.SEVERE, "Position: world: '" + (world == null ? "unknown world?" : world.getWorld().getName()) + "' at location (" + posX + ", " + posY + ", " + posZ + ")");
|
||||
+ log.log(Level.SEVERE, "Velocity: " + (mot == null ? "unknown velocity" : mot.toString()) + " (in blocks per tick)");
|
||||
+ log.log(Level.SEVERE, "Entity AABB: " + entity.getBoundingBox());
|
||||
+ if (moveVec != null) {
|
||||
+ log.log(Level.SEVERE, "Move call information: ");
|
||||
+ log.log(Level.SEVERE, "Start position: (" + moveStartX + ", " + moveStartY + ", " + moveStartZ + ")");
|
||||
+ log.log(Level.SEVERE, "Move vector: " + moveVec.toString());
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void dumpTickingInfo() {
|
||||
+ Logger log = Bukkit.getServer().getLogger();
|
||||
+
|
||||
+ // ticking entities
|
||||
+ for (net.minecraft.world.entity.Entity entity : net.minecraft.server.level.ServerLevel.getCurrentlyTickingEntities()) {
|
||||
+ this.dumpEntity(entity);
|
||||
+ net.minecraft.world.entity.Entity vehicle = entity.getVehicle();
|
||||
+ if (vehicle != null) {
|
||||
+ log.log(Level.SEVERE, "Detailing vehicle for above entity:");
|
||||
+ this.dumpEntity(vehicle);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // packet processors
|
||||
+ for (net.minecraft.network.PacketListener packetListener : net.minecraft.network.protocol.PacketUtils.getCurrentPacketProcessors()) {
|
||||
+ if (packetListener instanceof net.minecraft.server.network.ServerGamePacketListenerImpl) {
|
||||
+ net.minecraft.server.level.ServerPlayer player = ((net.minecraft.server.network.ServerGamePacketListenerImpl)packetListener).player;
|
||||
+ long totalPackets = net.minecraft.network.protocol.PacketUtils.getTotalProcessedPackets();
|
||||
+ if (player == null) {
|
||||
+ log.log(Level.SEVERE, "Handling packet for player connection or ticking player connection (null player): " + packetListener);
|
||||
+ log.log(Level.SEVERE, "Total packets processed on the main thread for all players: " + totalPackets);
|
||||
+ } else {
|
||||
+ this.dumpEntity(player);
|
||||
+ net.minecraft.world.entity.Entity vehicle = player.getVehicle();
|
||||
+ if (vehicle != null) {
|
||||
+ log.log(Level.SEVERE, "Detailing vehicle for above entity:");
|
||||
+ this.dumpEntity(vehicle);
|
||||
+ }
|
||||
+ log.log(Level.SEVERE, "Total packets processed on the main thread for all players: " + totalPackets);
|
||||
+ }
|
||||
+ } else {
|
||||
+ log.log(Level.SEVERE, "Handling packet for connection: " + packetListener);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - log detailed tick information
|
||||
+
|
||||
private WatchdogThread(long timeoutTime, boolean restart)
|
||||
{
|
||||
super( "Paper Watchdog Thread" );
|
||||
@@ -119,6 +191,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
|
||||
log.log( Level.SEVERE, "------------------------------" );
|
||||
log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
|
||||
io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(isLongTimeout); // Paper - rewrite chunk system
|
||||
+ this.dumpTickingInfo(); // Paper - log detailed tick information
|
||||
WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
|
||||
log.log( Level.SEVERE, "------------------------------" );
|
||||
//
|
4726
patches/server/1019-Collision-optimisations.patch
Normal file
4726
patches/server/1019-Collision-optimisations.patch
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,170 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Thu, 2 Jul 2020 12:02:43 -0700
|
||||
Subject: [PATCH] Optimise collision checking in player move packet handling
|
||||
|
||||
Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision
|
||||
|
||||
CHECK ME
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 6061f7104dd2094e316739a1b0e541475aed40b0..88fc56fa521c36accd807ca1704136f29733e52f 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -578,7 +578,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
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
|
||||
@@ -594,6 +594,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -607,15 +608,23 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
boolean flag2 = false;
|
||||
|
||||
if (d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
|
||||
- flag2 = true;
|
||||
+ flag2 = true; // Paper - diff on change, this should be moved wrongly
|
||||
ServerGamePacketListenerImpl.LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", new Object[]{entity.getName().getString(), this.player.getName().getString(), Math.sqrt(d10)});
|
||||
}
|
||||
|
||||
entity.absMoveTo(d3, d4, d5, f, f1);
|
||||
this.player.absMoveTo(d3, d4, d5, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
|
||||
- boolean flag3 = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D));
|
||||
|
||||
- if (flag && (flag2 || !flag3)) {
|
||||
+ // Paper start - optimise out extra getCubes
|
||||
+ boolean teleportBack = flag2; // violating this is always a fail
|
||||
+ if (!teleportBack) {
|
||||
+ // note: only call after setLocation, or else getBoundingBox is wrong
|
||||
+ AABB newBox = entity.getBoundingBox();
|
||||
+ if (didCollide || !oldBox.equals(newBox)) {
|
||||
+ teleportBack = this.hasNewCollision(worldserver, entity, oldBox, newBox);
|
||||
+ } // else: no collision at all detected, why do we care?
|
||||
+ }
|
||||
+ if (teleportBack) { // Paper end - optimise out extra getCubes
|
||||
entity.absMoveTo(d0, d1, d2, f, f1);
|
||||
this.player.absMoveTo(d0, d1, d2, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
|
||||
this.send(new ClientboundMoveVehiclePacket(entity));
|
||||
@@ -694,7 +703,32 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
}
|
||||
|
||||
private boolean noBlocksAround(Entity entity) {
|
||||
- return entity.level().getBlockStates(entity.getBoundingBox().inflate(0.0625D).expandTowards(0.0D, -0.55D, 0.0D)).allMatch(BlockBehaviour.BlockStateBase::isAir);
|
||||
+ // Paper start - stop using streams, this is already a known fixed problem in Entity#move
|
||||
+ AABB box = entity.getBoundingBox().inflate(0.0625D).expandTowards(0.0D, -0.55D, 0.0D);
|
||||
+ int minX = Mth.floor(box.minX);
|
||||
+ int minY = Mth.floor(box.minY);
|
||||
+ int minZ = Mth.floor(box.minZ);
|
||||
+ int maxX = Mth.floor(box.maxX);
|
||||
+ int maxY = Mth.floor(box.maxY);
|
||||
+ int maxZ = Mth.floor(box.maxZ);
|
||||
+
|
||||
+ Level world = entity.level();
|
||||
+ BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
||||
+
|
||||
+ for (int y = minY; y <= maxY; ++y) {
|
||||
+ for (int z = minZ; z <= maxZ; ++z) {
|
||||
+ for (int x = minX; x <= maxX; ++x) {
|
||||
+ pos.set(x, y, z);
|
||||
+ BlockState type = world.getBlockStateIfLoaded(pos);
|
||||
+ if (type != null && !type.isAir()) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+ // Paper end - stop using streams, this is already a known fixed problem in Entity#move
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1289,7 +1323,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
@@ -1398,7 +1432,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
}
|
||||
}
|
||||
|
||||
- AABB axisalignedbb = this.player.getBoundingBox();
|
||||
+ AABB axisalignedbb = this.player.getBoundingBox(); // Paper - diff on change, should be old AABB
|
||||
|
||||
d6 = d0 - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above
|
||||
d7 = d1 - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above
|
||||
@@ -1440,6 +1474,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
|
||||
this.player.move(MoverType.PLAYER, new Vec3(d6, d7, d8));
|
||||
this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
|
||||
+ 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...
|
||||
// Paper start - prevent position desync
|
||||
if (this.awaitingPositionFromClient != null) {
|
||||
return; // ... thanks Mojang for letting move calls teleport across dimensions.
|
||||
@@ -1470,7 +1505,17 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
}
|
||||
|
||||
// Paper start - Add fail move event
|
||||
- boolean teleportBack = !this.player.noPhysics && !this.player.isSleeping() && (movedWrongly && worldserver.noCollision(this.player, axisalignedbb) || this.isPlayerCollidingWithAnythingNew(worldserver, axisalignedbb, d0, d1, d2));
|
||||
+ // Paper start - optimise out extra getCubes
|
||||
+ boolean teleportBack = !this.player.noPhysics && !this.player.isSleeping() && movedWrongly;
|
||||
+ this.player.absMoveTo(d0, d1, d2, f, f1); // prevent desync by tping to the set position, dropped for unknown reasons by mojang
|
||||
+ 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?
|
||||
+ }
|
||||
+ // Paper end - optimise out extra getCubes
|
||||
if (teleportBack) {
|
||||
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK,
|
||||
toX, toY, toZ, toYaw, toPitch, false);
|
||||
@@ -1576,6 +1621,33 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - optimise out extra getCubes
|
||||
+ private boolean hasNewCollision(final ServerLevel world, final Entity entity, final AABB oldBox, final AABB newBox) {
|
||||
+ final List<AABB> collisionsBB = new java.util.ArrayList<>();
|
||||
+ final List<VoxelShape> collisionsVoxel = new java.util.ArrayList<>();
|
||||
+ io.papermc.paper.util.CollisionUtil.getCollisions(
|
||||
+ world, entity, newBox, collisionsVoxel, collisionsBB,
|
||||
+ io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_COLLIDE_WITH_UNLOADED_CHUNKS | io.papermc.paper.util.CollisionUtil.COLLISION_FLAG_CHECK_BORDER,
|
||||
+ null, null
|
||||
+ );
|
||||
+
|
||||
+ for (int i = 0, len = collisionsBB.size(); i < len; ++i) {
|
||||
+ final AABB box = collisionsBB.get(i);
|
||||
+ if (!io.papermc.paper.util.CollisionUtil.voxelShapeIntersect(box, oldBox)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for (int i = 0, len = collisionsVoxel.size(); i < len; ++i) {
|
||||
+ final VoxelShape voxel = collisionsVoxel.get(i);
|
||||
+ if (!io.papermc.paper.util.CollisionUtil.voxelShapeIntersectNoEmpty(voxel, oldBox)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end - optimise out extra getCubes
|
||||
private boolean isPlayerCollidingWithAnythingNew(LevelReader world, AABB box, double newX, double newY, double newZ) {
|
||||
AABB axisalignedbb1 = this.player.getBoundingBox().move(newX - this.player.getX(), newY - this.player.getY(), newZ - this.player.getZ());
|
||||
Iterable<VoxelShape> iterable = world.getCollisions(this.player, axisalignedbb1.deflate(9.999999747378752E-6D));
|
|
@ -0,0 +1,22 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: DungeonDev <dungeondevtn@gmail.com>
|
||||
Date: Sun, 2 Jul 2023 02:34:54 +0100
|
||||
Subject: [PATCH] Fix tripwire disarming not working as intended
|
||||
|
||||
Fixes MC-129055
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java b/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java
|
||||
index 8614fad5b3df7a6030384b108b1689bf6b9f1209..76aca266d3f3222502ff4c196228f08fcd88c5f8 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java
|
||||
@@ -202,9 +202,8 @@ public class TripWireHookBlock extends Block {
|
||||
BlockState iblockdata4 = aiblockdata[l];
|
||||
|
||||
if (iblockdata4 != null) {
|
||||
+ if (world.getBlockState(blockposition2).is(Blocks.TRIPWIRE) || io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowTripwireDisarmingExploits) { // Paper - Fix tripwire disarming not working as intended
|
||||
world.setBlock(blockposition2, (BlockState) iblockdata4.trySetValue(TripWireHookBlock.ATTACHED, flag4), 3);
|
||||
- if (!world.getBlockState(blockposition2).isAir()) {
|
||||
- ;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Sun, 22 Aug 2021 15:21:57 -0700
|
||||
Subject: [PATCH] Fix entity type tags suggestions in selectors
|
||||
|
||||
This would really be better as a client fix because just to fix it
|
||||
all EntityArguments have been told to ask the server for completions
|
||||
when if this was fixed on the client, that wouldn't be needed.
|
||||
|
||||
Mojira Issue: https://bugs.mojang.com/browse/MC-235045
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java
|
||||
index 3c0d2332207ba638faaaa4280bce18c334a01271..e6c7f62ed379a78645933670299e4fcda8540ed1 100644
|
||||
--- a/src/main/java/net/minecraft/commands/CommandSourceStack.java
|
||||
+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java
|
||||
@@ -462,4 +462,20 @@ public class CommandSourceStack implements ExecutionCommandSource<CommandSourceS
|
||||
return this.source.getBukkitSender(this);
|
||||
}
|
||||
// CraftBukkit end
|
||||
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
|
||||
+ @Override
|
||||
+ public Collection<String> getSelectedEntities() {
|
||||
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && this.source instanceof ServerPlayer player) {
|
||||
+ final Entity cameraEntity = player.getCamera();
|
||||
+ final double pickDistance = player.gameMode.getGameModeForPlayer().isCreative() ? 6.0F : 4.5F;
|
||||
+ final Vec3 min = cameraEntity.getEyePosition(1.0F);
|
||||
+ final Vec3 viewVector = cameraEntity.getViewVector(1.0F);
|
||||
+ final Vec3 max = min.add(viewVector.x * pickDistance, viewVector.y * pickDistance, viewVector.z * pickDistance);
|
||||
+ final net.minecraft.world.phys.AABB aabb = cameraEntity.getBoundingBox().expandTowards(viewVector.scale(pickDistance)).inflate(1.0D, 1.0D, 1.0D);
|
||||
+ final net.minecraft.world.phys.EntityHitResult hitResult = net.minecraft.world.entity.projectile.ProjectileUtil.getEntityHitResult(cameraEntity, min, max, aabb, (e) -> !e.isSpectator() && e.isPickable(), pickDistance * pickDistance);
|
||||
+ return hitResult != null ? java.util.Collections.singletonList(hitResult.getEntity().getStringUUID()) : SharedSuggestionProvider.super.getSelectedEntities();
|
||||
+ }
|
||||
+ return SharedSuggestionProvider.super.getSelectedEntities();
|
||||
+ }
|
||||
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
|
||||
index 60406cb01c5a409ba6fe98677a5cc332effaebea..a0702dccbb07c79febe2e65a2ff82c4436c09f12 100644
|
||||
--- a/src/main/java/net/minecraft/commands/Commands.java
|
||||
+++ b/src/main/java/net/minecraft/commands/Commands.java
|
||||
@@ -525,6 +525,7 @@ public class Commands {
|
||||
private void fillUsableCommands(CommandNode<CommandSourceStack> tree, CommandNode<SharedSuggestionProvider> result, CommandSourceStack source, Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> resultNodes) {
|
||||
Iterator iterator = tree.getChildren().iterator();
|
||||
|
||||
+ boolean registeredAskServerSuggestionsForTree = false; // Paper - tell clients to ask server for suggestions for EntityArguments
|
||||
while (iterator.hasNext()) {
|
||||
CommandNode<CommandSourceStack> commandnode2 = (CommandNode) iterator.next();
|
||||
// Paper start - Brigadier API
|
||||
@@ -551,6 +552,12 @@ public class Commands {
|
||||
|
||||
if (requiredargumentbuilder.getSuggestionsProvider() != null) {
|
||||
requiredargumentbuilder.suggests(SuggestionProviders.safelySwap(requiredargumentbuilder.getSuggestionsProvider()));
|
||||
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
|
||||
+ registeredAskServerSuggestionsForTree = requiredargumentbuilder.getSuggestionsProvider() == net.minecraft.commands.synchronization.SuggestionProviders.ASK_SERVER;
|
||||
+ } else if (io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && !registeredAskServerSuggestionsForTree && requiredargumentbuilder.getType() instanceof net.minecraft.commands.arguments.EntityArgument) {
|
||||
+ requiredargumentbuilder.suggests(requiredargumentbuilder.getType()::listSuggestions);
|
||||
+ registeredAskServerSuggestionsForTree = true; // You can only
|
||||
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/commands/arguments/EntityArgument.java b/src/main/java/net/minecraft/commands/arguments/EntityArgument.java
|
||||
index 2043001c16b3530c2d3f52efda10bcad424881c0..7976885b902a6ce7d80f31e49448c99452eb9765 100644
|
||||
--- a/src/main/java/net/minecraft/commands/arguments/EntityArgument.java
|
||||
+++ b/src/main/java/net/minecraft/commands/arguments/EntityArgument.java
|
||||
@@ -131,7 +131,7 @@ public class EntityArgument implements ArgumentType<EntitySelector> {
|
||||
final boolean permission = object instanceof CommandSourceStack stack
|
||||
? stack.bypassSelectorPermissions || stack.hasPermission(2, "minecraft.command.selector")
|
||||
: icompletionprovider.hasPermission(2);
|
||||
- EntitySelectorParser argumentparserselector = new EntitySelectorParser(stringreader, permission);
|
||||
+ EntitySelectorParser argumentparserselector = new EntitySelectorParser(stringreader, permission, true); // Paper - tell clients to ask server for suggestions for EntityArguments
|
||||
// Paper end - Fix EntityArgument suggestion permissions
|
||||
|
||||
try {
|
||||
@@ -141,7 +141,19 @@ public class EntityArgument implements ArgumentType<EntitySelector> {
|
||||
}
|
||||
|
||||
return argumentparserselector.fillSuggestions(suggestionsbuilder, (suggestionsbuilder1) -> {
|
||||
- Collection<String> collection = icompletionprovider.getOnlinePlayerNames();
|
||||
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
|
||||
+ final Collection<String> collection;
|
||||
+ if (icompletionprovider instanceof CommandSourceStack commandSourceStack && commandSourceStack.getEntity() instanceof ServerPlayer sourcePlayer) {
|
||||
+ collection = new java.util.ArrayList<>();
|
||||
+ for (final ServerPlayer player : commandSourceStack.getServer().getPlayerList().getPlayers()) {
|
||||
+ if (sourcePlayer.getBukkitEntity().canSee(player.getBukkitEntity())) {
|
||||
+ collection.add(player.getGameProfile().getName());
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ collection = icompletionprovider.getOnlinePlayerNames();
|
||||
+ }
|
||||
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
|
||||
Iterable<String> iterable = this.playersOnly ? collection : Iterables.concat(collection, icompletionprovider.getSelectedEntities());
|
||||
|
||||
SharedSuggestionProvider.suggest((Iterable) iterable, suggestionsbuilder1);
|
||||
diff --git a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
|
||||
index 3d897ec6920eff6176ddac9f0442a997b9ef14fd..abaf1ea340c69c8bee80e64567b44b5ce66d5fa3 100644
|
||||
--- a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
|
||||
+++ b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
|
||||
@@ -113,12 +113,19 @@ public class EntitySelectorParser {
|
||||
private boolean hasScores;
|
||||
private boolean hasAdvancements;
|
||||
private boolean usesSelectors;
|
||||
+ public boolean parsingEntityArgumentSuggestions; // Paper - tell clients to ask server for suggestions for EntityArguments
|
||||
|
||||
public EntitySelectorParser(StringReader reader) {
|
||||
this(reader, true);
|
||||
}
|
||||
|
||||
public EntitySelectorParser(StringReader reader, boolean atAllowed) {
|
||||
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
|
||||
+ this(reader, atAllowed, false);
|
||||
+ }
|
||||
+ public EntitySelectorParser(StringReader reader, boolean atAllowed, boolean parsingEntityArgumentSuggestions) {
|
||||
+ this.parsingEntityArgumentSuggestions = parsingEntityArgumentSuggestions;
|
||||
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
|
||||
this.distance = MinMaxBounds.Doubles.ANY;
|
||||
this.level = MinMaxBounds.Ints.ANY;
|
||||
this.rotX = WrappedMinMaxBounds.ANY;
|
||||
diff --git a/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java b/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
|
||||
index 44982ff2cdd6e03f0b9ce8c3cc87561ef183ef06..626fe7a45c2edba68eb201974e9f8f5eebf75cc0 100644
|
||||
--- a/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
|
||||
+++ b/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
|
||||
@@ -76,6 +76,19 @@ public class EntitySelectorOptions {
|
||||
public static final DynamicCommandExceptionType ERROR_ENTITY_TYPE_INVALID = new DynamicCommandExceptionType(
|
||||
entity -> Component.translatableEscape("argument.entity.options.type.invalid", entity)
|
||||
);
|
||||
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
|
||||
+ public static final DynamicCommandExceptionType ERROR_ENTITY_TAG_INVALID = new DynamicCommandExceptionType((object) -> {
|
||||
+ return io.papermc.paper.adventure.PaperAdventure
|
||||
+ .asVanilla(net.kyori.adventure.text.Component
|
||||
+ .text("Invalid or unknown entity type tag '" + object + "'")
|
||||
+ .hoverEvent(net.kyori.adventure.text.event.HoverEvent
|
||||
+ .showText(net.kyori.adventure.text.Component
|
||||
+ .text("You can disable this error in 'paper.yml'")
|
||||
+ )
|
||||
+ )
|
||||
+ );
|
||||
+ });
|
||||
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
|
||||
|
||||
private static void register(String id, EntitySelectorOptions.Modifier handler, Predicate<EntitySelectorParser> condition, Component description) {
|
||||
OPTIONS.put(id, new EntitySelectorOptions.Option(handler, condition, description));
|
||||
@@ -297,6 +310,12 @@ public class EntitySelectorOptions {
|
||||
|
||||
if (reader.isTag()) {
|
||||
TagKey<EntityType<?>> tagKey = TagKey.create(Registries.ENTITY_TYPE, ResourceLocation.read(reader.getReader()));
|
||||
+ // Paper start - tell clients to ask server for suggestions for EntityArguments; throw error if invalid entity tag (only on suggestions to keep cmd success behavior)
|
||||
+ if (reader.parsingEntityArgumentSuggestions && io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.getTag(tagKey).isEmpty()) {
|
||||
+ reader.getReader().setCursor(i);
|
||||
+ throw ERROR_ENTITY_TAG_INVALID.createWithContext(reader.getReader(), tagKey);
|
||||
+ }
|
||||
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
|
||||
reader.addPredicate(entity -> entity.getType().is(tagKey) != bl);
|
||||
} else {
|
||||
ResourceLocation resourceLocation = ResourceLocation.read(reader.getReader());
|
File diff suppressed because it is too large
Load diff
78
patches/server/1024-optimize-dirt-and-snow-spreading.patch
Normal file
78
patches/server/1024-optimize-dirt-and-snow-spreading.patch
Normal file
|
@ -0,0 +1,78 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: lukas81298 <lukas81298@gommehd.net>
|
||||
Date: Fri, 22 Jan 2021 21:50:18 +0100
|
||||
Subject: [PATCH] optimize dirt and snow spreading
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java b/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
|
||||
index 5a39e8d359dc13383711e49ffb2d1294dad26192..b7165ec19bef1a07f6618fc0429d86cda1b08da4 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
|
||||
@@ -18,8 +18,13 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
|
||||
}
|
||||
|
||||
private static boolean canBeGrass(BlockState state, LevelReader world, BlockPos pos) {
|
||||
+ // Paper start - Perf: optimize dirt and snow spreading
|
||||
+ return canBeGrass(world.getChunk(pos), state, world, pos);
|
||||
+ }
|
||||
+ private static boolean canBeGrass(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader world, BlockPos pos) {
|
||||
+ // Paper end - Perf: optimize dirt and snow spreading
|
||||
BlockPos blockposition1 = pos.above();
|
||||
- BlockState iblockdata1 = world.getBlockState(blockposition1);
|
||||
+ BlockState iblockdata1 = chunk.getBlockState(blockposition1); // Paper - Perf: optimize dirt and snow spreading
|
||||
|
||||
if (iblockdata1.is(Blocks.SNOW) && (Integer) iblockdata1.getValue(SnowLayerBlock.LAYERS) == 1) {
|
||||
return true;
|
||||
@@ -36,15 +41,27 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
|
||||
protected abstract MapCodec<? extends SpreadingSnowyDirtBlock> codec();
|
||||
|
||||
private static boolean canPropagate(BlockState state, LevelReader world, BlockPos pos) {
|
||||
+ // Paper start - Perf: optimize dirt and snow spreading
|
||||
+ return canPropagate(world.getChunk(pos), state, world, pos);
|
||||
+ }
|
||||
+
|
||||
+ private static boolean canPropagate(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader world, BlockPos pos) {
|
||||
+ // Paper end - Perf: optimize dirt and snow spreading
|
||||
BlockPos blockposition1 = pos.above();
|
||||
|
||||
- return SpreadingSnowyDirtBlock.canBeGrass(state, world, pos) && !world.getFluidState(blockposition1).is(FluidTags.WATER);
|
||||
+ return SpreadingSnowyDirtBlock.canBeGrass(chunk, state, world, pos) && !chunk.getFluidState(blockposition1).is(FluidTags.WATER); // Paper - Perf: optimize dirt and snow spreading
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
|
||||
if (this instanceof GrassBlock && world.paperConfig().tickRates.grassSpread != 1 && (world.paperConfig().tickRates.grassSpread < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % world.paperConfig().tickRates.grassSpread != 0)) { return; } // Paper - Configurable random tick rates for blocks
|
||||
- if (!SpreadingSnowyDirtBlock.canBeGrass(state, world, pos)) {
|
||||
+ // Paper start - Perf: optimize dirt and snow spreading
|
||||
+ final net.minecraft.world.level.chunk.ChunkAccess cachedBlockChunk = world.getChunkIfLoaded(pos);
|
||||
+ if (cachedBlockChunk == null) { // Is this needed?
|
||||
+ return;
|
||||
+ }
|
||||
+ if (!SpreadingSnowyDirtBlock.canBeGrass(cachedBlockChunk, state, world, pos)) {
|
||||
+ // Paper end - Perf: optimize dirt and snow spreading
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, pos, Blocks.DIRT.defaultBlockState()).isCancelled()) {
|
||||
return;
|
||||
@@ -57,9 +74,19 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
BlockPos blockposition1 = pos.offset(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1);
|
||||
-
|
||||
- if (world.getBlockState(blockposition1).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(iblockdata1, world, blockposition1)) {
|
||||
- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, (BlockState) iblockdata1.setValue(SpreadingSnowyDirtBlock.SNOWY, world.getBlockState(blockposition1.above()).is(Blocks.SNOW))); // CraftBukkit
|
||||
+ // Paper start - Perf: optimize dirt and snow spreading
|
||||
+ if (pos.getX() == blockposition1.getX() && pos.getY() == blockposition1.getY() && pos.getZ() == blockposition1.getZ()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ net.minecraft.world.level.chunk.ChunkAccess access;
|
||||
+ if (cachedBlockChunk.locX == blockposition1.getX() >> 4 && cachedBlockChunk.locZ == blockposition1.getZ() >> 4) {
|
||||
+ access = cachedBlockChunk;
|
||||
+ } else {
|
||||
+ access = world.getChunkAt(blockposition1);
|
||||
+ }
|
||||
+ if (access.getBlockState(blockposition1).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(access, iblockdata1, world, blockposition1)) {
|
||||
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, (BlockState) iblockdata1.setValue(SpreadingSnowyDirtBlock.SNOWY, access.getBlockState(blockposition1.above()).is(Blocks.SNOW))); // CraftBukkit
|
||||
+ // Paper end - Perf: optimize dirt and snow spreading
|
||||
}
|
||||
}
|
||||
}
|
337
patches/server/1025-Properly-resend-entities.patch
Normal file
337
patches/server/1025-Properly-resend-entities.patch
Normal file
|
@ -0,0 +1,337 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
|
||||
Date: Wed, 7 Dec 2022 17:25:19 -0500
|
||||
Subject: [PATCH] Properly resend entities
|
||||
|
||||
This resolves some issues which caused entities to not be resent correctly.
|
||||
Entities that are interacted with need to be resent to the client, so we resend all the entity
|
||||
data to the player whilst making sure not to clear dirty entries from the tracker. This makes
|
||||
sure that values will be correctly updated to other players.
|
||||
|
||||
This also adds utilities to aid in further preventing entity desyncs.
|
||||
|
||||
This also also fixes the bug causing cancelling PlayerInteractEvent to cause items to continue
|
||||
to be used despite being cancelled on the server.
|
||||
|
||||
For example, items being consumed but never finishing, shields being put up, etc.
|
||||
The underlying issue of this is that the client modifies their synced data values,
|
||||
and so we have to (forcibly) resend them in order for the client to reset their using item state.
|
||||
|
||||
See: https://github.com/PaperMC/Paper/pull/1896
|
||||
|
||||
== AT ==
|
||||
public net.minecraft.server.level.ChunkMap$TrackedEntity serverEntity
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
|
||||
index 02bf2705ca1c99023a83a22d92e1962181102297..0f99733660f91280e4c6262cf75b3c9cae86f65a 100644
|
||||
--- a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
|
||||
+++ b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
|
||||
@@ -50,7 +50,7 @@ public class SynchedEntityData {
|
||||
}
|
||||
}
|
||||
|
||||
- private <T> SynchedEntityData.DataItem<T> getItem(EntityDataAccessor<T> key) {
|
||||
+ public <T> SynchedEntityData.DataItem<T> getItem(EntityDataAccessor<T> key) { // Paper - public
|
||||
return (SynchedEntityData.DataItem<T>) this.itemsById[key.id()]; // CraftBukkit - decompile error
|
||||
}
|
||||
|
||||
@@ -151,6 +151,20 @@ public class SynchedEntityData {
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ // We need to pack all as we cannot rely on "non default values" or "dirty" ones.
|
||||
+ // Because these values can possibly be desynced on the client.
|
||||
+ @Nullable
|
||||
+ public List<SynchedEntityData.DataValue<?>> packAll() {
|
||||
+ final List<SynchedEntityData.DataValue<?>> list = new ArrayList<>();
|
||||
+ for (final DataItem<?> dataItem : this.itemsById) {
|
||||
+ list.add(dataItem.value());
|
||||
+ }
|
||||
+
|
||||
+ return list;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public static class DataItem<T> {
|
||||
|
||||
final EntityDataAccessor<T> accessor;
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
||||
index abb9a86cd42a34cf722a312068134e820ac21956..3b6ebe8f9575783a1607eb6667554ca66de94271 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
||||
@@ -556,6 +556,7 @@ public class ServerPlayerGameMode {
|
||||
}
|
||||
// Paper end - extend Player Interact cancellation
|
||||
player.getBukkitEntity().updateInventory(); // SPIGOT-2867
|
||||
+ this.player.resyncUsingItem(this.player); // Paper - Properly cancel usable items
|
||||
return (event.useItemInHand() != Event.Result.ALLOW) ? InteractionResult.SUCCESS : InteractionResult.PASS;
|
||||
} else if (this.gameModeForPlayer == GameType.SPECTATOR) {
|
||||
MenuProvider itileinventory = iblockdata.getMenuProvider(world, blockposition);
|
||||
@@ -609,6 +610,11 @@ public class ServerPlayerGameMode {
|
||||
} else {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
+ // Paper start - Properly cancel usable items; Cancel only if cancelled + if the interact result is different from default response
|
||||
+ else if (this.interactResult && this.interactResult != cancelledItem) {
|
||||
+ this.player.resyncUsingItem(this.player);
|
||||
+ }
|
||||
+ // Paper end - Properly cancel usable items
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 88fc56fa521c36accd807ca1704136f29733e52f..32076a765d48d59b339d600f69afa85edbcf833c 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -45,7 +45,6 @@ import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.ConnectionProtocol;
|
||||
import net.minecraft.network.TickablePacketListener;
|
||||
-import net.minecraft.network.chat.ChatDecorator;
|
||||
import net.minecraft.network.chat.ChatType;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.LastSeenMessages;
|
||||
@@ -63,7 +62,6 @@ import net.minecraft.network.protocol.PacketUtils;
|
||||
import net.minecraft.network.protocol.common.ServerboundClientInformationPacket;
|
||||
import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.configuration.ConfigurationProtocols;
|
||||
-import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockChangedAckPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket;
|
||||
@@ -185,7 +183,6 @@ import net.minecraft.world.level.block.entity.CrafterBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.JigsawBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.SignBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.StructureBlockEntity;
|
||||
-import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
@@ -216,7 +213,6 @@ import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemType;
|
||||
-import org.bukkit.craftbukkit.util.CraftChatMessage;
|
||||
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
||||
import org.bukkit.craftbukkit.util.LazyPlayerSet;
|
||||
import org.bukkit.craftbukkit.util.Waitable;
|
||||
@@ -231,8 +227,6 @@ import org.bukkit.event.inventory.InventoryCreativeEvent;
|
||||
import org.bukkit.event.inventory.InventoryType.SlotType;
|
||||
import org.bukkit.event.inventory.SmithItemEvent;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
-import org.bukkit.event.player.PlayerAnimationEvent;
|
||||
-import org.bukkit.event.player.PlayerAnimationType;
|
||||
import org.bukkit.event.player.PlayerChatEvent;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
|
||||
@@ -2010,6 +2004,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
}
|
||||
|
||||
if (cancelled) {
|
||||
+ this.player.resyncUsingItem(this.player); // Paper - Properly cancel usable items
|
||||
this.player.getBukkitEntity().updateInventory(); // SPIGOT-2524
|
||||
return;
|
||||
}
|
||||
@@ -2794,7 +2789,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
|
||||
// Entity in bucket - SPIGOT-4048 and SPIGOT-6859a
|
||||
if ((entity instanceof Bucketable && entity instanceof LivingEntity && origItem != null && origItem.asItem() == Items.WATER_BUCKET) && (event.isCancelled() || ServerGamePacketListenerImpl.this.player.getInventory().getSelected() == null || ServerGamePacketListenerImpl.this.player.getInventory().getSelected().getItem() != origItem)) {
|
||||
- ServerGamePacketListenerImpl.this.send(new ClientboundAddEntityPacket(entity));
|
||||
+ entity.getEntityData().resendPossiblyDesyncedEntityData(player); // Paper - The entire mob gets deleted, so resend it.
|
||||
ServerGamePacketListenerImpl.this.player.containerMenu.sendAllDataToRemote();
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
index c83ec24c0bf54f8699caa2d37bfe82d273f7b16c..dd628c8190704505fde543a8853d5b4dbc8252d5 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
@@ -393,7 +393,7 @@ public abstract class PlayerList {
|
||||
((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - Fire PlayerJoinEvent when Player is actually ready; track entity now
|
||||
// CraftBukkit end
|
||||
|
||||
- player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn
|
||||
+ //player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn // Paper - THIS IS NOT NEEDED ANYMORE
|
||||
|
||||
this.sendLevelInfo(player, worldserver1);
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index 8ad2d17615ff489b2fbbb13480dd0b217a42d805..130ad5dc8ef5cfe1cfc0a166eba2cf67d424ff69 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -6,6 +6,7 @@ import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.UnmodifiableIterator;
|
||||
import com.mojang.logging.LogUtils;
|
||||
+import io.papermc.paper.util.MCUtil;
|
||||
import it.unimi.dsi.fastutil.objects.Object2DoubleArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
|
||||
import java.util.Arrays;
|
||||
@@ -59,7 +60,6 @@ import net.minecraft.network.syncher.SyncedDataHolder;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
-import io.papermc.paper.util.MCUtil;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -129,28 +129,22 @@ import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import net.minecraft.world.scores.PlayerTeam;
|
||||
import net.minecraft.world.scores.ScoreHolder;
|
||||
import net.minecraft.world.scores.Team;
|
||||
-import org.slf4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
-import org.bukkit.Server;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.command.CommandSender;
|
||||
-import org.bukkit.craftbukkit.event.CraftPortalEvent;
|
||||
-import org.bukkit.entity.Hanging;
|
||||
-import org.bukkit.entity.LivingEntity;
|
||||
-import org.bukkit.entity.Vehicle;
|
||||
-import org.bukkit.event.entity.EntityCombustByEntityEvent;
|
||||
-import org.bukkit.event.hanging.HangingBreakByEntityEvent;
|
||||
-import org.bukkit.event.vehicle.VehicleBlockCollisionEvent;
|
||||
-import org.bukkit.event.vehicle.VehicleEnterEvent;
|
||||
-import org.bukkit.event.vehicle.VehicleExitEvent;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.craftbukkit.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory;
|
||||
+import org.bukkit.craftbukkit.event.CraftPortalEvent;
|
||||
import org.bukkit.craftbukkit.util.CraftLocation;
|
||||
+import org.bukkit.entity.Hanging;
|
||||
+import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Pose;
|
||||
+import org.bukkit.entity.Vehicle;
|
||||
import org.bukkit.event.entity.EntityAirChangeEvent;
|
||||
+import org.bukkit.event.entity.EntityCombustByEntityEvent;
|
||||
import org.bukkit.event.entity.EntityCombustEvent;
|
||||
import org.bukkit.event.entity.EntityDismountEvent;
|
||||
import org.bukkit.event.entity.EntityDropItemEvent;
|
||||
@@ -158,8 +152,13 @@ import org.bukkit.event.entity.EntityMountEvent;
|
||||
import org.bukkit.event.entity.EntityPortalEvent;
|
||||
import org.bukkit.event.entity.EntityPoseChangeEvent;
|
||||
import org.bukkit.event.entity.EntityRemoveEvent;
|
||||
+import org.bukkit.event.hanging.HangingBreakByEntityEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
+import org.bukkit.event.vehicle.VehicleBlockCollisionEvent;
|
||||
+import org.bukkit.event.vehicle.VehicleEnterEvent;
|
||||
+import org.bukkit.event.vehicle.VehicleExitEvent;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
+import org.slf4j.Logger;
|
||||
// CraftBukkit end
|
||||
|
||||
public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess, CommandSource, ScoreHolder {
|
||||
@@ -704,13 +703,44 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
|
||||
// CraftBukkit start
|
||||
public void refreshEntityData(ServerPlayer to) {
|
||||
- List<SynchedEntityData.DataValue<?>> list = this.getEntityData().getNonDefaultValues();
|
||||
+ List<SynchedEntityData.DataValue<?>> list = this.entityData.packAll(); // Paper - Update EVERYTHING not just not default
|
||||
|
||||
- if (list != null) {
|
||||
+ if (list != null && to.getBukkitEntity().canSee(this.getBukkitEntity())) { // Paper
|
||||
to.connection.send(new ClientboundSetEntityDataPacket(this.getId(), list));
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
+ // Paper start
|
||||
+ // This method should only be used if the data of an entity could have become desynced
|
||||
+ // due to interactions on the client.
|
||||
+ public void resendPossiblyDesyncedEntityData(net.minecraft.server.level.ServerPlayer player) {
|
||||
+ if (this.tracker == null) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (player.getBukkitEntity().canSee(this.getBukkitEntity())) {
|
||||
+ final net.minecraft.server.level.ServerEntity serverEntity = this.tracker.serverEntity;
|
||||
+ final List<net.minecraft.network.protocol.Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> list = new java.util.ArrayList<>();
|
||||
+ serverEntity.sendPairingData(player, list::add);
|
||||
+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundBundlePacket(list));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // This method allows you to specifically resend certain data accessor keys to the client
|
||||
+ public void resendPossiblyDesyncedDataValues(List<EntityDataAccessor<?>> keys, ServerPlayer to) {
|
||||
+ if (!to.getBukkitEntity().canSee(this.getBukkitEntity())) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ final List<SynchedEntityData.DataValue<?>> values = new java.util.ArrayList<>(keys.size());
|
||||
+ for (final EntityDataAccessor<?> key : keys) {
|
||||
+ final SynchedEntityData.DataItem<?> synchedValue = this.entityData.getItem(key);
|
||||
+ values.add(synchedValue.value());
|
||||
+ }
|
||||
+
|
||||
+ to.connection.send(new ClientboundSetEntityDataPacket(this.id, values));
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
public boolean equals(Object object) {
|
||||
return object instanceof Entity ? ((Entity) object).id == this.id : false;
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
index 75e01c1e01e0782fc8af48777bbe4716d37aeafa..9ff43ff4076c658b8561c5a7abd067423830f964 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -3828,6 +3828,11 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||
return ((Byte) this.entityData.get(LivingEntity.DATA_LIVING_ENTITY_FLAGS) & 2) > 0 ? InteractionHand.OFF_HAND : InteractionHand.MAIN_HAND;
|
||||
}
|
||||
|
||||
+ // Paper start - Properly cancel usable items
|
||||
+ public void resyncUsingItem(ServerPlayer serverPlayer) {
|
||||
+ this.getEntityData().resendPossiblyDesyncedDataValues(java.util.List.of(DATA_LIVING_ENTITY_FLAGS), serverPlayer);
|
||||
+ }
|
||||
+ // Paper end - Properly cancel usable items
|
||||
private void updatingUsingItem() {
|
||||
if (this.isUsingItem()) {
|
||||
if (ItemStack.isSameItem(this.getItemInHand(this.getUsedItemHand()), this.useItem)) {
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/animal/Bucketable.java b/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
|
||||
index cb4a6439e9774bbec07e69b13df8dddd395b9ece..cfe37b4f5e33795ee717d824d86e3a0919129cf5 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
|
||||
@@ -109,8 +109,7 @@ public interface Bucketable {
|
||||
itemstack1 = CraftItemStack.asNMSCopy(playerBucketFishEvent.getEntityBucket());
|
||||
if (playerBucketFishEvent.isCancelled()) {
|
||||
((ServerPlayer) player).containerMenu.sendAllDataToRemote(); // We need to update inventory to resync client's bucket
|
||||
- ((ServerPlayer) player).connection.send(new ClientboundAddEntityPacket(entity)); // We need to play out these packets as the client assumes the fish is gone
|
||||
- entity.refreshEntityData((ServerPlayer) player); // Need to send data such as the display name to client
|
||||
+ entity.resendPossiblyDesyncedEntityData((ServerPlayer) player); // Paper
|
||||
return Optional.of(InteractionResult.FAIL);
|
||||
}
|
||||
entity.playSound(((Bucketable) entity).getPickupSound(), 1.0F, 1.0F);
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
index 3ab04c4bdbe26ff7f6f54eb9cdd58376c592fa05..f949514cf68d0ae2db754576e46f5bfc14ffdff4 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
@@ -1010,7 +1010,11 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||
return;
|
||||
}
|
||||
|
||||
- entityTracker.broadcast(this.getHandle().getAddEntityPacket());
|
||||
+ // Paper start, resend possibly desynced entity instead of add entity packet
|
||||
+ for (ServerPlayerConnection playerConnection : entityTracker.seenBy) {
|
||||
+ this.getHandle().getEntityData().resendPossiblyDesyncedEntityData(playerConnection.getPlayer());
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
private static PermissibleBase getPermissibleBase() {
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
|
||||
index 0801bcdee8fcff0d388d302387e4f1d587e0a283..2fcd9b836d42e3549a3b6b921c57a4c103146dff 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
|
||||
@@ -39,9 +39,11 @@ public class CraftItemFrame extends CraftHanging implements ItemFrame {
|
||||
protected void update() {
|
||||
super.update();
|
||||
|
||||
+ // Paper start, don't mark as dirty as this is handled in super.update()
|
||||
// mark dirty, so that the client gets updated with item and rotation
|
||||
- this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ITEM);
|
||||
- this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ROTATION);
|
||||
+ //this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ITEM);
|
||||
+ //this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ROTATION);
|
||||
+ // Paper end
|
||||
|
||||
// update redstone
|
||||
if (!this.getHandle().generation) {
|
637
patches/server/1026-Optimize-Hoppers.patch
Normal file
637
patches/server/1026-Optimize-Hoppers.patch
Normal file
|
@ -0,0 +1,637 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Wed, 27 Apr 2016 22:09:52 -0400
|
||||
Subject: [PATCH] Optimize Hoppers
|
||||
|
||||
* Removes unnecessary extra calls to .update() that are very expensive
|
||||
* Lots of itemstack cloning removed. Only clone if the item is actually moved
|
||||
* Return true when a plugin cancels inventory move item event instead of false, as false causes pulls to cycle through all items.
|
||||
However, pushes do not exhibit the same behavior, so this is not something plugins could of been relying on.
|
||||
* Add option (Default on) to cooldown hoppers when they fail to move an item due to full inventory
|
||||
* Skip subsequent InventoryMoveItemEvents if a plugin does not use the item after first event fire for an iteration by tracking changes to the event via an internal event implementation.
|
||||
* Don't check for Entities with Inventories if the block above us is also occluding (not just Inventoried)
|
||||
* Remove Streams from Item Suck In and restore restore 1.12 AABB checks which is simpler and no voxel allocations (was doing TWO Item Suck ins)
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java b/src/main/java/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..5c42823726e70ce6c9d0121d074315488e8b3f60
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java
|
||||
@@ -0,0 +1,31 @@
|
||||
+package io.papermc.paper.event.inventory;
|
||||
+
|
||||
+import org.bukkit.event.inventory.InventoryMoveItemEvent;
|
||||
+import org.bukkit.inventory.Inventory;
|
||||
+import org.bukkit.inventory.ItemStack;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public class PaperInventoryMoveItemEvent extends InventoryMoveItemEvent {
|
||||
+
|
||||
+ public boolean calledSetItem;
|
||||
+ public boolean calledGetItem;
|
||||
+
|
||||
+ public PaperInventoryMoveItemEvent(final @NotNull Inventory sourceInventory, final @NotNull ItemStack itemStack, final @NotNull Inventory destinationInventory, final boolean didSourceInitiate) {
|
||||
+ super(sourceInventory, itemStack, destinationInventory, didSourceInitiate);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public ItemStack getItem() {
|
||||
+ this.calledGetItem = true;
|
||||
+ return super.getItem();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setItem(final ItemStack itemStack) {
|
||||
+ super.setItem(itemStack);
|
||||
+ this.calledSetItem = true;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index 88549de2768149885c4eabbaaa71a38341453aed..1adcbfb16ea1fe3378cde7c53e8868840eade963 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1756,6 +1756,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
ServerLevel worldserver = (ServerLevel) iterator.next();
|
||||
worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
|
||||
worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
|
||||
+ net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
|
||||
|
||||
this.profiler.push(() -> {
|
||||
String s = String.valueOf(worldserver);
|
||||
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
|
||||
index 9510558d03faf5b704a763456c5f2d4f28bca6e8..8e5d8e146ae045797e07260e4108ba5f1e4d976d 100644
|
||||
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
|
||||
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
|
||||
@@ -795,7 +795,13 @@ public final class ItemStack implements DataComponentHolder {
|
||||
}
|
||||
|
||||
public ItemStack copy() {
|
||||
- if (this.isEmpty()) {
|
||||
+ // Paper start - Perf: Optimize Hoppers
|
||||
+ return this.copy(false);
|
||||
+ }
|
||||
+
|
||||
+ public ItemStack copy(boolean originalItem) {
|
||||
+ if (!originalItem && this.isEmpty()) {
|
||||
+ // Paper end - Perf: Optimize Hoppers
|
||||
return ItemStack.EMPTY;
|
||||
} else {
|
||||
ItemStack itemstack = new ItemStack(this.getItem(), this.count, this.components.copy());
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
||||
index a7dfad2696e7145af1355a5db132af14d09a6f30..b0421823684ff6b9474b81675742d2ee3b17edf7 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
||||
@@ -37,6 +37,7 @@ import co.aikar.timings.MinecraftTimings; // Paper
|
||||
import co.aikar.timings.Timing; // Paper
|
||||
|
||||
public abstract class BlockEntity {
|
||||
+ static boolean ignoreTileUpdates; // Paper - Perf: Optimize Hoppers
|
||||
|
||||
public Timing tickTimer = MinecraftTimings.getTileEntityTimings(this); // Paper
|
||||
// CraftBukkit start - data containers
|
||||
@@ -210,6 +211,7 @@ public abstract class BlockEntity {
|
||||
|
||||
public void setChanged() {
|
||||
if (this.level != null) {
|
||||
+ if (ignoreTileUpdates) return; // Paper - Perf: Optimize Hoppers
|
||||
BlockEntity.setChanged(this.level, this.worldPosition, this.blockState);
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
||||
index 43ee9017fbbe0a5485a281712f76c23b059a849b..57dc38e39a11fa51f0d27830b709fe5b7ae9dc8b 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
||||
@@ -156,6 +156,43 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
|
||||
}
|
||||
|
||||
+ // Paper start - Perf: Optimize Hoppers
|
||||
+ private static final int HOPPER_EMPTY = 0;
|
||||
+ private static final int HOPPER_HAS_ITEMS = 1;
|
||||
+ private static final int HOPPER_IS_FULL = 2;
|
||||
+
|
||||
+ private static int getFullState(final HopperBlockEntity tileEntity) {
|
||||
+ tileEntity.unpackLootTable(null);
|
||||
+
|
||||
+ final List<ItemStack> hopperItems = tileEntity.getItems();
|
||||
+
|
||||
+ boolean empty = true;
|
||||
+ boolean full = true;
|
||||
+
|
||||
+ for (int i = 0, len = hopperItems.size(); i < len; ++i) {
|
||||
+ final ItemStack stack = hopperItems.get(i);
|
||||
+ if (stack.isEmpty()) {
|
||||
+ full = false;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (!full) {
|
||||
+ // can't be full
|
||||
+ return HOPPER_HAS_ITEMS;
|
||||
+ }
|
||||
+
|
||||
+ empty = false;
|
||||
+
|
||||
+ if (stack.getCount() != stack.getMaxStackSize()) {
|
||||
+ // can't be full or empty
|
||||
+ return HOPPER_HAS_ITEMS;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return empty ? HOPPER_EMPTY : (full ? HOPPER_IS_FULL : HOPPER_HAS_ITEMS);
|
||||
+ }
|
||||
+ // Paper end - Perf: Optimize Hoppers
|
||||
+
|
||||
private static boolean tryMoveItems(Level world, BlockPos pos, BlockState state, HopperBlockEntity blockEntity, BooleanSupplier booleansupplier) {
|
||||
if (world.isClientSide) {
|
||||
return false;
|
||||
@@ -163,11 +200,12 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
if (!blockEntity.isOnCooldown() && (Boolean) state.getValue(HopperBlock.ENABLED)) {
|
||||
boolean flag = false;
|
||||
|
||||
- if (!blockEntity.isEmpty()) {
|
||||
+ final int fullState = getFullState(blockEntity); // Paper - Perf: Optimize Hoppers
|
||||
+ if (fullState != HOPPER_EMPTY) { // Paper - Perf: Optimize Hoppers
|
||||
flag = HopperBlockEntity.ejectItems(world, pos, blockEntity);
|
||||
}
|
||||
|
||||
- if (!blockEntity.inventoryFull()) {
|
||||
+ if (fullState != HOPPER_IS_FULL || flag) { // Paper - Perf: Optimize Hoppers
|
||||
flag |= booleansupplier.getAsBoolean();
|
||||
}
|
||||
|
||||
@@ -198,6 +236,202 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
return false;
|
||||
}
|
||||
|
||||
+ // Paper start - Perf: Optimize Hoppers
|
||||
+ private static boolean skipPullModeEventFire;
|
||||
+ private static boolean skipPushModeEventFire;
|
||||
+ public static boolean skipHopperEvents;
|
||||
+
|
||||
+ private static boolean hopperPush(final Level level, final Container destination, final Direction direction, final HopperBlockEntity hopper) {
|
||||
+ skipPushModeEventFire = skipHopperEvents;
|
||||
+ boolean foundItem = false;
|
||||
+ for (int i = 0; i < hopper.getContainerSize(); ++i) {
|
||||
+ final ItemStack item = hopper.getItem(i);
|
||||
+ if (!item.isEmpty()) {
|
||||
+ foundItem = true;
|
||||
+ ItemStack origItemStack = item;
|
||||
+ ItemStack movedItem = origItemStack;
|
||||
+
|
||||
+ final int originalItemCount = origItemStack.getCount();
|
||||
+ final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount);
|
||||
+ origItemStack.setCount(movedItemCount);
|
||||
+
|
||||
+ // We only need to fire the event once to give protection plugins a chance to cancel this event
|
||||
+ // Because nothing uses getItem, every event call should end up the same result.
|
||||
+ if (!skipPushModeEventFire) {
|
||||
+ movedItem = callPushMoveEvent(destination, movedItem, hopper);
|
||||
+ if (movedItem == null) { // cancelled
|
||||
+ origItemStack.setCount(originalItemCount);
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ final ItemStack remainingItem = addItem(hopper, destination, movedItem, direction);
|
||||
+ final int remainingItemCount = remainingItem.getCount();
|
||||
+ if (remainingItemCount != movedItemCount) {
|
||||
+ origItemStack = origItemStack.copy(true);
|
||||
+ origItemStack.setCount(originalItemCount);
|
||||
+ if (!origItemStack.isEmpty()) {
|
||||
+ origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount);
|
||||
+ }
|
||||
+ hopper.setItem(i, origItemStack);
|
||||
+ destination.setChanged();
|
||||
+ return true;
|
||||
+ }
|
||||
+ origItemStack.setCount(originalItemCount);
|
||||
+ }
|
||||
+ }
|
||||
+ if (foundItem && level.paperConfig().hopper.cooldownWhenFull) { // Inventory was full - cooldown
|
||||
+ hopper.setCooldown(level.spigotConfig.hopperTransfer);
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ private static boolean hopperPull(final Level level, final Hopper hopper, final Container container, ItemStack origItemStack, final int i) {
|
||||
+ ItemStack movedItem = origItemStack;
|
||||
+ final int originalItemCount = origItemStack.getCount();
|
||||
+ final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount);
|
||||
+ container.setChanged(); // original logic always marks source inv as changed even if no move happens.
|
||||
+ movedItem.setCount(movedItemCount);
|
||||
+
|
||||
+ if (!skipPullModeEventFire) {
|
||||
+ movedItem = callPullMoveEvent(hopper, container, movedItem);
|
||||
+ if (movedItem == null) { // cancelled
|
||||
+ origItemStack.setCount(originalItemCount);
|
||||
+ // Drastically improve performance by returning true.
|
||||
+ // No plugin could of relied on the behavior of false as the other call
|
||||
+ // site for IMIE did not exhibit the same behavior
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ final ItemStack remainingItem = addItem(container, hopper, movedItem, null);
|
||||
+ final int remainingItemCount = remainingItem.getCount();
|
||||
+ if (remainingItemCount != movedItemCount) {
|
||||
+ origItemStack = origItemStack.copy(true);
|
||||
+ origItemStack.setCount(originalItemCount);
|
||||
+ if (!origItemStack.isEmpty()) {
|
||||
+ origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount);
|
||||
+ }
|
||||
+
|
||||
+ ignoreTileUpdates = true;
|
||||
+ container.setItem(i, origItemStack);
|
||||
+ ignoreTileUpdates = false;
|
||||
+ container.setChanged();
|
||||
+ return true;
|
||||
+ }
|
||||
+ origItemStack.setCount(originalItemCount);
|
||||
+
|
||||
+ if (level.paperConfig().hopper.cooldownWhenFull) {
|
||||
+ cooldownHopper(hopper);
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ @Nullable
|
||||
+ private static ItemStack callPushMoveEvent(Container iinventory, ItemStack itemstack, HopperBlockEntity hopper) {
|
||||
+ final Inventory destinationInventory = getInventory(iinventory);
|
||||
+ final io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent event = new io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent(hopper.getOwner(false).getInventory(),
|
||||
+ CraftItemStack.asCraftMirror(itemstack), destinationInventory, true);
|
||||
+ final boolean result = event.callEvent();
|
||||
+ if (!event.calledGetItem && !event.calledSetItem) {
|
||||
+ skipPushModeEventFire = true;
|
||||
+ }
|
||||
+ if (!result) {
|
||||
+ cooldownHopper(hopper);
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ if (event.calledSetItem) {
|
||||
+ return CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ } else {
|
||||
+ return itemstack;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Nullable
|
||||
+ private static ItemStack callPullMoveEvent(final Hopper hopper, final Container container, final ItemStack itemstack) {
|
||||
+ final Inventory sourceInventory = getInventory(container);
|
||||
+ final Inventory destination = getInventory(hopper);
|
||||
+
|
||||
+ // Mirror is safe as no plugins ever use this item
|
||||
+ final io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent event = new io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent(sourceInventory, CraftItemStack.asCraftMirror(itemstack), destination, false);
|
||||
+ final boolean result = event.callEvent();
|
||||
+ if (!event.calledGetItem && !event.calledSetItem) {
|
||||
+ skipPullModeEventFire = true;
|
||||
+ }
|
||||
+ if (!result) {
|
||||
+ cooldownHopper(hopper);
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ if (event.calledSetItem) {
|
||||
+ return CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ } else {
|
||||
+ return itemstack;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static Inventory getInventory(final Container container) {
|
||||
+ final Inventory sourceInventory;
|
||||
+ if (container instanceof CompoundContainer compoundContainer) {
|
||||
+ // Have to special-case large chests as they work oddly
|
||||
+ sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
|
||||
+ } else if (container instanceof BlockEntity blockEntity) {
|
||||
+ sourceInventory = blockEntity.getOwner(false).getInventory();
|
||||
+ } else if (container.getOwner() != null) {
|
||||
+ sourceInventory = container.getOwner().getInventory();
|
||||
+ } else {
|
||||
+ sourceInventory = new CraftInventory(container);
|
||||
+ }
|
||||
+ return sourceInventory;
|
||||
+ }
|
||||
+
|
||||
+ private static void cooldownHopper(final Hopper hopper) {
|
||||
+ if (hopper instanceof HopperBlockEntity blockEntity && blockEntity.getLevel() != null) {
|
||||
+ blockEntity.setCooldown(blockEntity.getLevel().spigotConfig.hopperTransfer);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static boolean allMatch(Container iinventory, Direction enumdirection, java.util.function.BiPredicate<ItemStack, Integer> test) {
|
||||
+ if (iinventory instanceof WorldlyContainer) {
|
||||
+ for (int i : ((WorldlyContainer) iinventory).getSlotsForFace(enumdirection)) {
|
||||
+ if (!test.test(iinventory.getItem(i), i)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ int size = iinventory.getContainerSize();
|
||||
+ for (int i = 0; i < size; i++) {
|
||||
+ if (!test.test(iinventory.getItem(i), i)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ private static boolean anyMatch(Container iinventory, Direction enumdirection, java.util.function.BiPredicate<ItemStack, Integer> test) {
|
||||
+ if (iinventory instanceof WorldlyContainer) {
|
||||
+ for (int i : ((WorldlyContainer) iinventory).getSlotsForFace(enumdirection)) {
|
||||
+ if (test.test(iinventory.getItem(i), i)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ int size = iinventory.getContainerSize();
|
||||
+ for (int i = 0; i < size; i++) {
|
||||
+ if (test.test(iinventory.getItem(i), i)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+ private static final java.util.function.BiPredicate<ItemStack, Integer> STACK_SIZE_TEST = (itemstack, i) -> itemstack.getCount() >= itemstack.getMaxStackSize();
|
||||
+ private static final java.util.function.BiPredicate<ItemStack, Integer> IS_EMPTY_TEST = (itemstack, i) -> itemstack.isEmpty();
|
||||
+ // Paper end - Perf: Optimize Hoppers
|
||||
+
|
||||
private static boolean ejectItems(Level world, BlockPos pos, HopperBlockEntity blockEntity) {
|
||||
Container iinventory = HopperBlockEntity.getAttachedContainer(world, pos, blockEntity);
|
||||
|
||||
@@ -209,47 +443,50 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
if (HopperBlockEntity.isFullContainer(iinventory, enumdirection)) {
|
||||
return false;
|
||||
} else {
|
||||
- for (int i = 0; i < blockEntity.getContainerSize(); ++i) {
|
||||
- ItemStack itemstack = blockEntity.getItem(i);
|
||||
-
|
||||
- if (!itemstack.isEmpty()) {
|
||||
- int j = itemstack.getCount();
|
||||
- // CraftBukkit start - Call event when pushing items into other inventories
|
||||
- CraftItemStack oitemstack = CraftItemStack.asCraftMirror(blockEntity.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
|
||||
-
|
||||
- Inventory destinationInventory;
|
||||
- // Have to special case large chests as they work oddly
|
||||
- if (iinventory instanceof CompoundContainer) {
|
||||
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
|
||||
- } else if (iinventory.getOwner() != null) {
|
||||
- destinationInventory = iinventory.getOwner().getInventory();
|
||||
- } else {
|
||||
- destinationInventory = new CraftInventory(iinventory);
|
||||
- }
|
||||
-
|
||||
- InventoryMoveItemEvent event = new InventoryMoveItemEvent(iinventory.getOwner().getInventory(), oitemstack, destinationInventory, true);
|
||||
- world.getCraftServer().getPluginManager().callEvent(event);
|
||||
- if (event.isCancelled()) {
|
||||
- blockEntity.setItem(i, itemstack);
|
||||
- blockEntity.setCooldown(world.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot
|
||||
- return false;
|
||||
- }
|
||||
- ItemStack itemstack1 = HopperBlockEntity.addItem(blockEntity, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection);
|
||||
- // CraftBukkit end
|
||||
-
|
||||
- if (itemstack1.isEmpty()) {
|
||||
- iinventory.setChanged();
|
||||
- return true;
|
||||
- }
|
||||
-
|
||||
- itemstack.setCount(j);
|
||||
- if (j == 1) {
|
||||
- blockEntity.setItem(i, itemstack);
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- return false;
|
||||
+ // Paper start - Perf: Optimize Hoppers
|
||||
+ return hopperPush(world, iinventory, enumdirection, blockEntity);
|
||||
+ //for (int i = 0; i < blockEntity.getContainerSize(); ++i) {
|
||||
+ // ItemStack itemstack = blockEntity.getItem(i);
|
||||
+
|
||||
+ // if (!itemstack.isEmpty()) {
|
||||
+ // int j = itemstack.getCount();
|
||||
+ // // CraftBukkit start - Call event when pushing items into other inventories
|
||||
+ // CraftItemStack oitemstack = CraftItemStack.asCraftMirror(blockEntity.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
|
||||
+
|
||||
+ // Inventory destinationInventory;
|
||||
+ // // Have to special case large chests as they work oddly
|
||||
+ // if (iinventory instanceof CompoundContainer) {
|
||||
+ // destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
|
||||
+ // } else if (iinventory.getOwner() != null) {
|
||||
+ // destinationInventory = iinventory.getOwner().getInventory();
|
||||
+ // } else {
|
||||
+ // destinationInventory = new CraftInventory(iinventory);
|
||||
+ // }
|
||||
+
|
||||
+ // InventoryMoveItemEvent event = new InventoryMoveItemEvent(iinventory.getOwner().getInventory(), oitemstack, destinationInventory, true);
|
||||
+ // world.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ // if (event.isCancelled()) {
|
||||
+ // blockEntity.setItem(i, itemstack);
|
||||
+ // blockEntity.setCooldown(world.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot
|
||||
+ // return false;
|
||||
+ // }
|
||||
+ // ItemStack itemstack1 = HopperBlockEntity.addItem(blockEntity, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection);
|
||||
+ // // CraftBukkit end
|
||||
+
|
||||
+ // if (itemstack1.isEmpty()) {
|
||||
+ // iinventory.setChanged();
|
||||
+ // return true;
|
||||
+ // }
|
||||
+
|
||||
+ // itemstack.setCount(j);
|
||||
+ // if (j == 1) {
|
||||
+ // blockEntity.setItem(i, itemstack);
|
||||
+ // }
|
||||
+ // }
|
||||
+ //}
|
||||
+
|
||||
+ // return false;
|
||||
+ // Paper end - Perf: Optimize Hoppers
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,7 +537,6 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
return false;
|
||||
}
|
||||
}
|
||||
-
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -342,48 +578,52 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
}
|
||||
}
|
||||
|
||||
+ @io.papermc.paper.annotation.DoNotUse // Paper - Optimize hoppers
|
||||
private static boolean tryTakeInItemFromSlot(Hopper ihopper, Container iinventory, int i, Direction enumdirection, Level world) { // Spigot
|
||||
ItemStack itemstack = iinventory.getItem(i);
|
||||
|
||||
if (!itemstack.isEmpty() && HopperBlockEntity.canTakeItemFromContainer(ihopper, iinventory, itemstack, i, enumdirection)) {
|
||||
- int j = itemstack.getCount();
|
||||
- // CraftBukkit start - Call event on collection of items from inventories into the hopper
|
||||
- CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
|
||||
-
|
||||
- Inventory sourceInventory;
|
||||
- // Have to special case large chests as they work oddly
|
||||
- if (iinventory instanceof CompoundContainer) {
|
||||
- sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
|
||||
- } else if (iinventory.getOwner() != null) {
|
||||
- sourceInventory = iinventory.getOwner().getInventory();
|
||||
- } else {
|
||||
- sourceInventory = new CraftInventory(iinventory);
|
||||
- }
|
||||
-
|
||||
- InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack, ihopper.getOwner().getInventory(), false);
|
||||
-
|
||||
- Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
- if (event.isCancelled()) {
|
||||
- iinventory.setItem(i, itemstack);
|
||||
-
|
||||
- if (ihopper instanceof HopperBlockEntity) {
|
||||
- ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot
|
||||
- }
|
||||
-
|
||||
- return false;
|
||||
- }
|
||||
- ItemStack itemstack1 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
|
||||
- // CraftBukkit end
|
||||
-
|
||||
- if (itemstack1.isEmpty()) {
|
||||
- iinventory.setChanged();
|
||||
- return true;
|
||||
- }
|
||||
-
|
||||
- itemstack.setCount(j);
|
||||
- if (j == 1) {
|
||||
- iinventory.setItem(i, itemstack);
|
||||
- }
|
||||
+ // Paper start - Perf: Optimize Hoppers
|
||||
+ return hopperPull(world, ihopper, iinventory, itemstack, i);
|
||||
+ // int j = itemstack.getCount();
|
||||
+ // // CraftBukkit start - Call event on collection of items from inventories into the hopper
|
||||
+ // CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
|
||||
+
|
||||
+ // Inventory sourceInventory;
|
||||
+ // // Have to special case large chests as they work oddly
|
||||
+ // if (iinventory instanceof CompoundContainer) {
|
||||
+ // sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
|
||||
+ // } else if (iinventory.getOwner() != null) {
|
||||
+ // sourceInventory = iinventory.getOwner().getInventory();
|
||||
+ // } else {
|
||||
+ // sourceInventory = new CraftInventory(iinventory);
|
||||
+ // }
|
||||
+
|
||||
+ // InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack, ihopper.getOwner().getInventory(), false);
|
||||
+
|
||||
+ // Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
+ // if (event.isCancelled()) {
|
||||
+ // iinventory.setItem(i, itemstack);
|
||||
+
|
||||
+ // if (ihopper instanceof HopperBlockEntity) {
|
||||
+ // ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot
|
||||
+ // }
|
||||
+
|
||||
+ // return false;
|
||||
+ // }
|
||||
+ // ItemStack itemstack1 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
|
||||
+ // // CraftBukkit end
|
||||
+
|
||||
+ // if (itemstack1.isEmpty()) {
|
||||
+ // iinventory.setChanged();
|
||||
+ // return true;
|
||||
+ // }
|
||||
+
|
||||
+ // itemstack.setCount(j);
|
||||
+ // if (j == 1) {
|
||||
+ // iinventory.setItem(i, itemstack);
|
||||
+ // }
|
||||
+ // Paper end - Perf: Optimize Hoppers
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -392,12 +632,14 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
public static boolean addItem(Container inventory, ItemEntity itemEntity) {
|
||||
boolean flag = false;
|
||||
// CraftBukkit start
|
||||
- InventoryPickupItemEvent event = new InventoryPickupItemEvent(inventory.getOwner().getInventory(), (org.bukkit.entity.Item) itemEntity.getBukkitEntity());
|
||||
+ if (InventoryPickupItemEvent.getHandlerList().getRegisteredListeners().length > 0) { // Paper - optimize hoppers
|
||||
+ InventoryPickupItemEvent event = new InventoryPickupItemEvent(getInventory(inventory), (org.bukkit.entity.Item) itemEntity.getBukkitEntity()); // Paper - Perf: Optimize Hoppers; use getInventory() to avoid snapshot creation
|
||||
itemEntity.level().getCraftServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
// CraftBukkit end
|
||||
+ } // Paper - Perf: Optimize Hoppers
|
||||
ItemStack itemstack = itemEntity.getItem().copy();
|
||||
ItemStack itemstack1 = HopperBlockEntity.addItem((Container) null, inventory, itemstack, (Direction) null);
|
||||
|
||||
@@ -491,7 +733,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
stack = stack.split(to.getMaxStackSize());
|
||||
}
|
||||
// Spigot end
|
||||
+ ignoreTileUpdates = true; // Paper - Perf: Optimize Hoppers
|
||||
to.setItem(slot, stack);
|
||||
+ ignoreTileUpdates = false; // Paper - Perf: Optimize Hoppers
|
||||
stack = leftover; // Paper - Make hoppers respect inventory max stack size
|
||||
flag = true;
|
||||
} else if (HopperBlockEntity.canMergeItems(itemstack1, stack)) {
|
||||
@@ -571,14 +815,20 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
|
||||
@Nullable
|
||||
public static Container getContainerAt(Level world, BlockPos pos) {
|
||||
- return HopperBlockEntity.getContainerAt(world, pos, world.getBlockState(pos), (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D);
|
||||
+ return HopperBlockEntity.getContainerAt(world, pos, world.getBlockState(pos), (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Container getContainerAt(Level world, BlockPos pos, BlockState state, double x, double y, double z) {
|
||||
+ // Paper start - Perf: Optimize Hoppers
|
||||
+ return HopperBlockEntity.getContainerAt(world, pos, state, x, y, z, false);
|
||||
+ }
|
||||
+ @Nullable
|
||||
+ private static Container getContainerAt(Level world, BlockPos pos, BlockState state, double x, double y, double z, boolean optimizeEntities) {
|
||||
+ // Paper end - Perf: Optimize Hoppers
|
||||
Container iinventory = HopperBlockEntity.getBlockContainer(world, pos, state);
|
||||
|
||||
- if (iinventory == null) {
|
||||
+ if (iinventory == null && (!optimizeEntities || !world.paperConfig().hopper.ignoreOccludingBlocks || !state.getBukkitMaterial().isOccluding())) { // Paper - Perf: Optimize Hoppers
|
||||
iinventory = HopperBlockEntity.getEntityContainer(world, x, y, z);
|
||||
}
|
||||
|
||||
@@ -613,13 +863,13 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
|
||||
@Nullable
|
||||
private static Container getEntityContainer(Level world, double x, double y, double z) {
|
||||
- List<Entity> list = world.getEntities((Entity) null, new AABB(x - 0.5D, y - 0.5D, z - 0.5D, x + 0.5D, y + 0.5D, z + 0.5D), EntitySelector.CONTAINER_ENTITY_SELECTOR);
|
||||
+ List<Entity> list = world.getEntitiesOfClass((Class) Container.class, new AABB(x - 0.5D, y - 0.5D, z - 0.5D, x + 0.5D, y + 0.5D, z + 0.5D), EntitySelector.CONTAINER_ENTITY_SELECTOR); // Paper - Perf: Optimize hoppers
|
||||
|
||||
return !list.isEmpty() ? (Container) list.get(world.random.nextInt(list.size())) : null;
|
||||
}
|
||||
|
||||
private static boolean canMergeItems(ItemStack first, ItemStack second) {
|
||||
- return first.getCount() <= first.getMaxStackSize() && ItemStack.isSameItemSameComponents(first, second);
|
||||
+ return first.getCount() < first.getMaxStackSize() && ItemStack.isSameItemSameComponents(first, second); // Paper - Perf: Optimize Hoppers; used to return true for full itemstacks?!
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
||||
index 6a6473ebf03e5b1e502db7e66203783b9980d072..674c3c2b3cc2c00a8830900baf797bc20f154fe7 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
||||
@@ -105,7 +105,7 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int slot) {
|
||||
- this.unpackLootTable(null);
|
||||
+ if (slot == 0) this.unpackLootTable(null); // Paper - Perf: Optimize Hoppers
|
||||
return super.getItem(slot);
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue