diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinPlayerConnection.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinPlayerConnection.java new file mode 100644 index 000000000..6ec221159 --- /dev/null +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinPlayerConnection.java @@ -0,0 +1,14 @@ +package io.akarin.server.mixin.core; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import net.minecraft.server.PlayerConnection; + +@Mixin(value = PlayerConnection.class, remap = false) +public class MixinPlayerConnection { + @Overwrite + private long d() { + return System.currentTimeMillis(); + } +} diff --git a/sources/src/main/java/io/akarin/server/mixin/core/OptimisticNetworkManager.java b/sources/src/main/java/io/akarin/server/mixin/core/OptimisticNetworkManager.java new file mode 100644 index 000000000..129c99db4 --- /dev/null +++ b/sources/src/main/java/io/akarin/server/mixin/core/OptimisticNetworkManager.java @@ -0,0 +1,55 @@ +package io.akarin.server.mixin.core; + +import java.util.Queue; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import io.akarin.api.CheckedConcurrentLinkedQueue; +import io.netty.channel.Channel; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; +import net.minecraft.server.NetworkManager; +import net.minecraft.server.Packet; + +@Mixin(value = NetworkManager.class, remap = false) +public class OptimisticNetworkManager { + @Shadow public Channel channel; + @Shadow @Final private Queue i; + @Shadow @Final private ReentrantReadWriteLock j; + + @Shadow private Queue getPacketQueue() { return null; } + @Shadow private void dispatchPacket(Packet packet, GenericFutureListener>[] genericFutureListeners) {} + + @Overwrite + private boolean m() { + if (this.channel != null && this.channel.isOpen()) { + if (this.i.isEmpty()) { // return if the packet queue is empty so that the write lock by Anti-Xray doesn't affect the vanilla performance at all + return true; + } + + this.j.readLock().lock(); + try { + while (!this.i.isEmpty()) { + NetworkManager.QueuedPacket packet = ((CheckedConcurrentLinkedQueue) getPacketQueue()).checkedPoll(); + + if (packet != null) { // Fix NPE (Spigot bug caused by handleDisconnection()) + if (packet == CheckedConcurrentLinkedQueue.emptyPacket) { // Check if the peeked packet is a chunk packet which is not ready + return false; // Return false if the peeked packet is a chunk packet which is not ready + } else { + dispatchPacket(packet.getPacket(), packet.getGenericFutureListeners()); // dispatch the packet + } + } + } + } finally { + this.j.readLock().unlock(); + } + + } + return true; // Return true if all packets were dispatched + } + +} diff --git a/sources/src/main/java/net/minecraft/server/NetworkManager.java b/sources/src/main/java/net/minecraft/server/NetworkManager.java index 9a2bca0bd..7844b8890 100644 --- a/sources/src/main/java/net/minecraft/server/NetworkManager.java +++ b/sources/src/main/java/net/minecraft/server/NetworkManager.java @@ -240,23 +240,23 @@ public class NetworkManager extends SimpleChannelInboundHandler> { return true; } - this.j.readLock().lock(); // readLock -> writeLock (because of race condition between peek and poll) // Akarin - writeLock -> readLock + this.j.writeLock().lock(); // readLock -> writeLock (because of race condition between peek and poll) try { while (!this.i.isEmpty()) { - NetworkManager.QueuedPacket networkmanager_queuedpacket = ((CheckedConcurrentLinkedQueue) this.getPacketQueue()).checkedPoll(); // poll -> peek // Akarin + NetworkManager.QueuedPacket networkmanager_queuedpacket = this.getPacketQueue().peek(); // poll -> peek if (networkmanager_queuedpacket != null) { // Fix NPE (Spigot bug caused by handleDisconnection()) - if (networkmanager_queuedpacket == CheckedConcurrentLinkedQueue.emptyPacket) { // Check if the peeked packet is a chunk packet which is not ready // Akarin + if (networkmanager_queuedpacket.getPacket() instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) networkmanager_queuedpacket.getPacket()).isReady()) { // Check if the peeked packet is a chunk packet which is not ready return false; // Return false if the peeked packet is a chunk packet which is not ready } else { - // this.getPacketQueue().poll(); // poll here // Akarin - polled + this.getPacketQueue().poll(); // poll here this.dispatchPacket(networkmanager_queuedpacket.getPacket(), networkmanager_queuedpacket.getGenericFutureListeners()); // dispatch the packet } } } } finally { - this.j.readLock().unlock(); // readLock -> writeLock (because of race condition between peek and poll) // Akarin - writeLock -> readLock + this.j.writeLock().unlock(); // readLock -> writeLock (because of race condition between peek and poll) } } @@ -372,7 +372,7 @@ public class NetworkManager extends SimpleChannelInboundHandler> { public static class QueuedPacket { // Akarin - default -> public private final Packet a; public final Packet getPacket() { return this.a; } // Paper - Anti-Xray - OBFHELPER // Akarin - private -> public - private final GenericFutureListener>[] b; private final GenericFutureListener>[] getGenericFutureListeners() { return this.b; } // Paper - Anti-Xray - OBFHELPER + private final GenericFutureListener>[] b; public final GenericFutureListener>[] getGenericFutureListeners() { return this.b; } // Paper - Anti-Xray - OBFHELPER // Akarin - private -> public public QueuedPacket(Packet packet, GenericFutureListener>... agenericfuturelistener) { this.a = packet; diff --git a/sources/src/main/java/net/minecraft/server/PlayerConnection.java b/sources/src/main/java/net/minecraft/server/PlayerConnection.java index 1297801de..ea8a45b4c 100644 --- a/sources/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/sources/src/main/java/net/minecraft/server/PlayerConnection.java @@ -66,7 +66,6 @@ import co.aikar.timings.MinecraftTimings; // Paper * 1) Add volatile to fields
* 2) Expose private members
* 3) Migrated keep alive packet handling to service thread
- * 4) Use currentMillis() instead of nanoTime() / 1000000 * @author cakoyo */ public class PlayerConnection implements PacketListenerPlayIn, ITickable { @@ -2299,7 +2298,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable { private long getCurrentMillis() { return d(); } // Paper - OBFHELPER private long d() { - return System.currentTimeMillis(); // Akarin + return System.nanoTime() / 1000000L; } @Override diff --git a/sources/src/main/resources/mixins.akarin.core.json b/sources/src/main/resources/mixins.akarin.core.json index df8dcbd90..2e8a101ef 100644 --- a/sources/src/main/resources/mixins.akarin.core.json +++ b/sources/src/main/resources/mixins.akarin.core.json @@ -11,6 +11,7 @@ "DesyncCatcher", "ParallelRegistry", "MetricsBootstrap", + "OptimisticNetworkManager", "NonblockingServerConnection", "MixinMCUtil", @@ -19,6 +20,7 @@ "MixinVersionCommand", "MixinMinecraftServer", "MixinChunkIOExecutor", + "MixinPlayerConnection", "MixinTileEntityEnchantTable" ] } \ No newline at end of file