mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2026-01-04 15:41:40 +00:00
Async playerPacket sending (#245)
* async player packet sending * small cleanup * eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee * holy shit this is fast * some cleanup * change .size to O(1) * rewrite starts (i need to do this OMEGA SAFE) * rebuilt * rebase * Rewritten AsyncPacketSending
This commit is contained in:
@@ -84,7 +84,7 @@ index 571db5f9bf94745a8afe2cd313e593fb15db5e37..1487b7d8be435b3fbad2aabd05796965
|
||||
valueInMap = new ServerChunkTasks(
|
||||
keyInMap, ServerLightQueue.this.lightInterface, ServerLightQueue.this, priority
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 268c463d379528b8242f1628e97e67ea638b7ced..20ded514a74652685b2f785c7fe5fda19e36b2a5 100644
|
||||
index 5423d8228c1da56135ae32b958f432d5b94707ed..95bed1e67758543a7aec12eee1229ee2c4057c88 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -508,7 +508,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
|
||||
@@ -5,7 +5,7 @@ Subject: [PATCH] Cache supporting block check
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 80ad278eac81aac72d6ec7737287ad018eff7c54..9bc978ca290ca772b0367e89b69fe16b502b0cd2 100644
|
||||
index 4544dd876d3cbcdb9b774b4a1f0c4737f3124bc5..6ca446fd9ab38329ba505526a56f8e4f64a9a639 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -1083,12 +1083,36 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taiyou06 <kaandindar21@gmail.com>
|
||||
Date: Fri, 21 Feb 2025 15:52:42 +0100
|
||||
Subject: [PATCH] Rewrite queue on Connection.flushQueue
|
||||
|
||||
|
||||
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
|
||||
index 00a82873d226f113278632a53c0faca420dd67d4..9aeadf792eb7c6c39044df00fa7ff80017ba72d0 100644
|
||||
--- a/net/minecraft/network/Connection.java
|
||||
+++ b/net/minecraft/network/Connection.java
|
||||
@@ -85,7 +85,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
private static final ProtocolInfo<ServerHandshakePacketListener> INITIAL_PROTOCOL = HandshakeProtocols.SERVERBOUND;
|
||||
private final PacketFlow receiving;
|
||||
private volatile boolean sendLoginDisconnect = true;
|
||||
- private final Queue<WrappedConsumer> pendingActions = Queues.newConcurrentLinkedQueue(); // Paper - Optimize network
|
||||
+ private final Queue<WrappedConsumer> pendingActions = org.dreeam.leaf.config.modules.network.ConnectionFlushQueueRewrite.enabled ? new java.util.ArrayDeque<>() : Queues.newConcurrentLinkedQueue(); // Paper - Optimize network // Leaf - Rewrite queue on Connection.flushQueue
|
||||
public Channel channel;
|
||||
public SocketAddress address;
|
||||
// Spigot start
|
||||
@@ -541,9 +541,17 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
if (io.papermc.paper.util.MCUtil.isMainThread()) {
|
||||
return this.processQueue();
|
||||
} else if (this.isPending) {
|
||||
- // Should only happen during login/status stages
|
||||
- synchronized (this.pendingActions) {
|
||||
- return this.processQueue();
|
||||
+ // Leaf start - Rewrite queue on Connection.flushQueue
|
||||
+ if (org.dreeam.leaf.config.modules.network.ConnectionFlushQueueRewrite.enabled) {
|
||||
+ // Submit to the event loop to ensure thread confinement
|
||||
+ this.channel.eventLoop().execute(this::processQueue);
|
||||
+ return false;
|
||||
+ } else {
|
||||
+ // Original Paper behavior
|
||||
+ synchronized (this.pendingActions) {
|
||||
+ return this.processQueue();
|
||||
+ // Leaf end - Rewrite queue on Connection.flushQueue
|
||||
+ }
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -554,6 +562,24 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
return true;
|
||||
}
|
||||
|
||||
+ // Leaf start - Rewrite queue on Connection.flushQueue
|
||||
+ if (org.dreeam.leaf.config.modules.network.ConnectionFlushQueueRewrite.enabled) {
|
||||
+ WrappedConsumer queued;
|
||||
+ while ((queued = this.pendingActions.poll()) != null) {
|
||||
+ if (queued instanceof PacketSendAction packetSendAction) {
|
||||
+ final Packet<?> packet = packetSendAction.packet;
|
||||
+ if (!packet.isReady()) {
|
||||
+ // Re-add to the front and exit
|
||||
+ this.pendingActions.add(queued);
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (queued.tryMarkConsumed()) {
|
||||
+ queued.accept(this);
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
// If we are on main, we are safe here in that nothing else should be processing queue off main anymore
|
||||
// But if we are not on main due to login/status, the parent is synchronized on packetQueue
|
||||
final java.util.Iterator<WrappedConsumer> iterator = this.pendingActions.iterator();
|
||||
@@ -581,6 +607,8 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
queued.accept(this);
|
||||
}
|
||||
}
|
||||
+ }
|
||||
+ // Leaf end - Rewrite queue on Connection.flushQueue
|
||||
return true;
|
||||
}
|
||||
// Paper end - Optimize network
|
||||
@@ -913,6 +941,31 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
// Paper start - Optimize network
|
||||
public void clearPacketQueue() {
|
||||
final net.minecraft.server.level.ServerPlayer player = getPlayer();
|
||||
+
|
||||
+ // Leaf start - Rewrite queue on Connection.flushQueue
|
||||
+ if (org.dreeam.leaf.config.modules.network.ConnectionFlushQueueRewrite.enabled) {
|
||||
+ // When using Leaf's queue rewrite, ensure thread safety via event loop
|
||||
+ if (this.channel != null && !this.channel.eventLoop().inEventLoop()) {
|
||||
+ this.channel.eventLoop().execute(() -> this.clearPacketQueue());
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ // Take a snapshot to avoid ConcurrentModificationException
|
||||
+ java.util.List<Consumer<Connection>> queueSnapshot = new java.util.ArrayList<>(this.pendingActions);
|
||||
+
|
||||
+ for (Consumer<Connection> queuedAction : queueSnapshot) {
|
||||
+ if (queuedAction instanceof PacketSendAction packetSendAction) {
|
||||
+ final Packet<?> packet = packetSendAction.packet;
|
||||
+ if (packet.hasFinishListener()) {
|
||||
+ packet.onPacketDispatchFinish(player, null);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ this.pendingActions.clear();
|
||||
+ } else {
|
||||
+ // Original Paper behavior - use synchronization
|
||||
+ synchronized (this.pendingActions) {
|
||||
for (final Consumer<Connection> queuedAction : this.pendingActions) {
|
||||
if (queuedAction instanceof PacketSendAction packetSendAction) {
|
||||
final Packet<?> packet = packetSendAction.packet;
|
||||
@@ -922,6 +975,9 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
}
|
||||
}
|
||||
this.pendingActions.clear();
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaf end - Rewrite queue on Connection.flushQueue
|
||||
}
|
||||
|
||||
private static class InnerUtil { // Attempt to hide these methods from ProtocolLib, so it doesn't accidently pick them up.
|
||||
@@ -6,7 +6,7 @@ Subject: [PATCH] ShreddedPaper: Don't block main thread in
|
||||
|
||||
|
||||
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
|
||||
index 9aeadf792eb7c6c39044df00fa7ff80017ba72d0..2aba58b4c1afb843b61d9a5113ed058b06278348 100644
|
||||
index 00a82873d226f113278632a53c0faca420dd67d4..5b46036868b6c9d082e35591e58735e16adaae62 100644
|
||||
--- a/net/minecraft/network/Connection.java
|
||||
+++ b/net/minecraft/network/Connection.java
|
||||
@@ -325,6 +325,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
@@ -0,0 +1,23 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
|
||||
Date: Wed, 19 Mar 2025 13:32:39 -0400
|
||||
Subject: [PATCH] Validate recipe display index before retrieving it
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/item/crafting/RecipeManager.java b/net/minecraft/world/item/crafting/RecipeManager.java
|
||||
index 4bd1b514f91c0a2c9261b41211a4a341f784a995..5b69c4927174611d62f0f4698215ab97c827c7f5 100644
|
||||
--- a/net/minecraft/world/item/crafting/RecipeManager.java
|
||||
+++ b/net/minecraft/world/item/crafting/RecipeManager.java
|
||||
@@ -206,7 +206,11 @@ public class RecipeManager extends SimplePreparableReloadListener<RecipeMap> imp
|
||||
|
||||
@Nullable
|
||||
public RecipeManager.ServerDisplayInfo getRecipeFromDisplay(RecipeDisplayId display) {
|
||||
- return this.allDisplays.get(display.index());
|
||||
+ // Leaf start - Validate recipe display index before retrieving it
|
||||
+ final int index = display.index();
|
||||
+
|
||||
+ return this.allDisplays.size() > index ? this.allDisplays.get(index) : null;
|
||||
+ // Leaf end - Validate recipe display index before retrieving it
|
||||
}
|
||||
|
||||
public void listDisplaysForRecipe(ResourceKey<Recipe<?>> recipe, Consumer<RecipeDisplayEntry> output) {
|
||||
@@ -0,0 +1,281 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taiyou06 <kaandindar21@gmail.com>
|
||||
Date: Tue, 25 Mar 2025 00:00:36 +0100
|
||||
Subject: [PATCH] AsyncPacketSending
|
||||
|
||||
|
||||
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
|
||||
index 5b46036868b6c9d082e35591e58735e16adaae62..b352523e08e212ca4086904042b921af32cd3172 100644
|
||||
--- a/net/minecraft/network/Connection.java
|
||||
+++ b/net/minecraft/network/Connection.java
|
||||
@@ -66,6 +66,16 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.Marker;
|
||||
import org.slf4j.MarkerFactory;
|
||||
|
||||
+
|
||||
+import java.util.concurrent.ExecutorService;
|
||||
+import java.util.concurrent.LinkedBlockingQueue;
|
||||
+import java.util.concurrent.ThreadPoolExecutor;
|
||||
+import java.util.concurrent.TimeUnit;
|
||||
+import java.util.concurrent.ThreadFactory;
|
||||
+import java.util.concurrent.atomic.AtomicInteger;
|
||||
+import java.util.concurrent.atomic.AtomicBoolean;
|
||||
+import org.dreeam.leaf.config.modules.async.AsyncPacketSending;
|
||||
+
|
||||
public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
private static final float AVERAGE_PACKETS_SMOOTHING = 0.75F;
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
@@ -128,6 +138,35 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
return null;
|
||||
}
|
||||
// Paper end - add utility methods
|
||||
+
|
||||
+ private static final ExecutorService PACKET_EXECUTOR;
|
||||
+ private static final AtomicInteger PACKET_THREAD_ID = new AtomicInteger(0);
|
||||
+ private final AtomicBoolean processingScheduled = new AtomicBoolean(false);
|
||||
+
|
||||
+ static {
|
||||
+ // Initialize the packet executor only if async sending is enabled
|
||||
+ if (AsyncPacketSending.enabled) {
|
||||
+ ThreadFactory threadFactory = r -> {
|
||||
+ Thread thread = new Thread(r, "Leaf - Async-Packet-Sender-" + PACKET_THREAD_ID.incrementAndGet());
|
||||
+ thread.setDaemon(true);
|
||||
+ thread.setPriority(Thread.NORM_PRIORITY - 1); // Slightly lower priority
|
||||
+ return thread;
|
||||
+ };
|
||||
+
|
||||
+ // Use bounded queue to prevent memory issues
|
||||
+ PACKET_EXECUTOR = new ThreadPoolExecutor(
|
||||
+ Math.max(1, AsyncPacketSending.threadPoolSize),
|
||||
+ Math.max(1, AsyncPacketSending.threadPoolSize),
|
||||
+ 60L, TimeUnit.SECONDS,
|
||||
+ new LinkedBlockingQueue<>(AsyncPacketSending.queueCapacity),
|
||||
+ threadFactory,
|
||||
+ new ThreadPoolExecutor.CallerRunsPolicy() // Fallback to caller thread if queue is full
|
||||
+ );
|
||||
+ } else {
|
||||
+ PACKET_EXECUTOR = null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
// Paper start - packet limiter
|
||||
protected final Object PACKET_LIMIT_LOCK = new Object();
|
||||
protected final @Nullable io.papermc.paper.util.IntervalledCounter allPacketCounts = io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.allPackets.isEnabled() ? new io.papermc.paper.util.IntervalledCounter(
|
||||
@@ -474,11 +513,82 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
private void sendPacket(Packet<?> packet, @Nullable PacketSendListener sendListener, boolean flush) {
|
||||
this.sentPackets++;
|
||||
+
|
||||
+ // Fast path: if we're already in the event loop, execute directly
|
||||
if (this.channel.eventLoop().inEventLoop()) {
|
||||
this.doSendPacket(packet, sendListener, flush);
|
||||
- } else {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ // Early return if async sending is disabled or executor not initialized
|
||||
+ if (!AsyncPacketSending.enabled || PACKET_EXECUTOR == null) {
|
||||
+ this.channel.eventLoop().execute(() -> this.doSendPacket(packet, sendListener, flush));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ // Handle high priority packets directly on Netty event loop
|
||||
+ if (isHighPriorityPacket(packet)) {
|
||||
this.channel.eventLoop().execute(() -> this.doSendPacket(packet, sendListener, flush));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ // For regular packets, use our async executor
|
||||
+ PACKET_EXECUTOR.execute(() -> {
|
||||
+ try {
|
||||
+ // Wait for packet to be ready if needed
|
||||
+ if (AsyncPacketSending.spinWaitForReadyPackets && !packet.isReady()) {
|
||||
+ long startTime = System.nanoTime();
|
||||
+ while (!packet.isReady() &&
|
||||
+ System.nanoTime() - startTime < AsyncPacketSending.spinTimeNanos) {
|
||||
+ Thread.onSpinWait();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // We still need to execute on the event loop for the actual channel operations
|
||||
+ // as Netty requires channel operations to be on its event loop
|
||||
+ this.channel.eventLoop().execute(() -> this.doSendPacket(packet, sendListener, flush));
|
||||
+ } catch (RejectedExecutionException e) {
|
||||
+ // If the event loop is shutting down
|
||||
+ LOGGER.debug("Failed to schedule packet send, event loop may be shutting down", e);
|
||||
+ if (packet.hasFinishListener()) {
|
||||
+ packet.onPacketDispatchFinish(this.getPlayer(), null);
|
||||
+ }
|
||||
+ } catch (Exception e) {
|
||||
+ LOGGER.error("Error in async packet sending", e);
|
||||
+ if (packet.hasFinishListener()) {
|
||||
+ packet.onPacketDispatchFinish(this.getPlayer(), null);
|
||||
+ }
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ // Helper method to determine if a packet should have high priority
|
||||
+ private boolean isHighPriorityPacket(Packet<?> packet) {
|
||||
+ // Critical packets that should never be delayed
|
||||
+ if (packet instanceof net.minecraft.network.protocol.common.ClientboundKeepAlivePacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.common.ClientboundDisconnectPacket) {
|
||||
+ return true;
|
||||
}
|
||||
+
|
||||
+ // Movement packets if prioritization is enabled
|
||||
+ if (AsyncPacketSending.prioritizeMovementPackets && (
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerLookAtPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket
|
||||
+ )) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ // Chat packets if prioritization is enabled
|
||||
+ if (AsyncPacketSending.prioritizeChatPackets && (
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSystemChatPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket
|
||||
+ )) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
}
|
||||
|
||||
private void doSendPacket(Packet<?> packet, @Nullable PacketSendListener sendListener, boolean flush) {
|
||||
@@ -539,15 +649,106 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
if (!this.isConnected()) {
|
||||
return true;
|
||||
}
|
||||
- if (io.papermc.paper.util.MCUtil.isMainThread()) {
|
||||
- return this.processQueue();
|
||||
- } else if (this.isPending) {
|
||||
- // Should only happen during login/status stages
|
||||
+
|
||||
+ // If async sending is disabled or we're in a special case, use original logic
|
||||
+ if (!AsyncPacketSending.enabled || PACKET_EXECUTOR == null || this.pendingActions.isEmpty()) {
|
||||
+ if (io.papermc.paper.util.MCUtil.isMainThread()) {
|
||||
+ return this.processQueue();
|
||||
+ } else if (this.isPending) {
|
||||
+ // Should only happen during login/status stages
|
||||
+ synchronized (this.pendingActions) {
|
||||
+ return this.processQueue();
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // For login/status stages, stick with synchronous processing
|
||||
+ if (this.isPending) {
|
||||
synchronized (this.pendingActions) {
|
||||
return this.processQueue();
|
||||
}
|
||||
}
|
||||
- return false;
|
||||
+
|
||||
+ // Schedule async processing if not already scheduled
|
||||
+ if (processingScheduled.compareAndSet(false, true)) {
|
||||
+ PACKET_EXECUTOR.execute(this::processQueueAsync);
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ private void processQueueAsync() {
|
||||
+ try {
|
||||
+ if (AsyncPacketSending.batchProcessing) {
|
||||
+ processQueueBatched();
|
||||
+ } else {
|
||||
+ processQueue();
|
||||
+ }
|
||||
+ } finally {
|
||||
+ // Allow scheduling again
|
||||
+ processingScheduled.set(false);
|
||||
+
|
||||
+ // If there are still items in the queue, schedule another processing round
|
||||
+ if (!this.pendingActions.isEmpty()) {
|
||||
+ if (processingScheduled.compareAndSet(false, true)) {
|
||||
+ PACKET_EXECUTOR.execute(this::processQueueAsync);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Process queue in batches for better efficiency
|
||||
+ private void processQueueBatched() {
|
||||
+ int maxToProcess = AsyncPacketSending.batchSize;
|
||||
+ int processed = 0;
|
||||
+
|
||||
+ while (processed < maxToProcess && !this.pendingActions.isEmpty()) {
|
||||
+ WrappedConsumer action = this.pendingActions.poll();
|
||||
+ if (action == null) {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ processed++;
|
||||
+
|
||||
+ if (action.isConsumed()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ // Check if packet is ready for sending
|
||||
+ if (action instanceof PacketSendAction packetSendAction) {
|
||||
+ Packet<?> packet = packetSendAction.packet;
|
||||
+ if (!packet.isReady()) {
|
||||
+ // Not ready, put back in queue
|
||||
+ this.pendingActions.add(action);
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Execute the action
|
||||
+ if (action.tryMarkConsumed()) {
|
||||
+ try {
|
||||
+ action.accept(this);
|
||||
+ } catch (Exception e) {
|
||||
+ LOGGER.error("Error processing packet action", e);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Add a shutdown method for clean shutdown
|
||||
+ public static void shutdownAsyncPacketSender() {
|
||||
+ if (PACKET_EXECUTOR != null) {
|
||||
+ PACKET_EXECUTOR.shutdown();
|
||||
+ try {
|
||||
+ if (!PACKET_EXECUTOR.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
+ PACKET_EXECUTOR.shutdownNow();
|
||||
+ }
|
||||
+ } catch (InterruptedException e) {
|
||||
+ PACKET_EXECUTOR.shutdownNow();
|
||||
+ Thread.currentThread().interrupt();
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
private boolean processQueue() {
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 7739b4955dcb489c6bba9c9db65ba87025f7c669..e8b4be3d9f4838d8bad8bed5270c725141b7368f 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -70,6 +70,7 @@ import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.data.worldgen.features.MiscOverworldFeatures;
|
||||
import net.minecraft.gametest.framework.GameTestTicker;
|
||||
+import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.chat.ChatDecorator;
|
||||
import net.minecraft.network.chat.ChatType;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -1018,6 +1019,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
// CraftBukkit end
|
||||
if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.shutdown(); // Paper - Plugin remapping
|
||||
this.getConnection().stop();
|
||||
+
|
||||
+ Connection.shutdownAsyncPacketSender();
|
||||
+
|
||||
this.isSaving = true;
|
||||
if (this.playerList != null) {
|
||||
LOGGER.info("Saving players");
|
||||
@@ -177,4 +177,19 @@ public class LeafGlobalConfig {
|
||||
public String pickStringRegionBased(String en, String cn) {
|
||||
return isCN ? cn : en;
|
||||
}
|
||||
|
||||
public long getLong(String path, long def, String comment) {
|
||||
configFile.addDefault(path, def, comment);
|
||||
return configFile.getLong(path, def);
|
||||
}
|
||||
|
||||
public long getLong(String path, long def) {
|
||||
configFile.addDefault(path, def);
|
||||
return configFile.getLong(path, def);
|
||||
}
|
||||
public Long getLong(String path) {
|
||||
String value = configFile.getString(path, null);
|
||||
return value == null ? null : Long.parseLong(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.dreeam.leaf.config.modules.async;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
import org.dreeam.leaf.config.annotations.Experimental;
|
||||
|
||||
public class AsyncPacketSending extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-packet-sending";
|
||||
}
|
||||
|
||||
@Experimental
|
||||
public static boolean enabled = false;
|
||||
|
||||
public static int threadPoolSize = 4;
|
||||
public static int queueCapacity = 4096;
|
||||
public static boolean prioritizeMovementPackets = true;
|
||||
public static boolean prioritizeChatPackets = true;
|
||||
public static boolean spinWaitForReadyPackets = true;
|
||||
public static long spinTimeNanos = 1000; // 1 microsecond
|
||||
public static boolean batchProcessing = true;
|
||||
public static int batchSize = 128;
|
||||
|
||||
private static boolean asyncPacketSendingInitialized;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
config.addCommentRegionBased(getBasePath(), """
|
||||
**Experimental feature**
|
||||
This moves packet sending operations to background threads, reducing main thread load.
|
||||
Can significantly improve performance on high-player-count servers.""",
|
||||
"""
|
||||
这将数据包发送操作移至后台线程,减少主线程负载。
|
||||
在高玩家数量的服务器上可以显著提高性能。""");
|
||||
|
||||
if (!asyncPacketSendingInitialized) {
|
||||
asyncPacketSendingInitialized = true;
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
threadPoolSize = config.getInt(getBasePath() + ".thread-pool-size", threadPoolSize);
|
||||
queueCapacity = config.getInt(getBasePath() + ".queue-capacity", queueCapacity);
|
||||
prioritizeMovementPackets = config.getBoolean(getBasePath() + ".prioritize-movement-packets", prioritizeMovementPackets);
|
||||
prioritizeChatPackets = config.getBoolean(getBasePath() + ".prioritize-chat-packets", prioritizeChatPackets);
|
||||
spinWaitForReadyPackets = config.getBoolean(getBasePath() + ".spin-wait-for-ready-packets", spinWaitForReadyPackets);
|
||||
spinTimeNanos = config.getLong(getBasePath() + ".spin-time-nanos", spinTimeNanos);
|
||||
batchProcessing = config.getBoolean(getBasePath() + ".batch-processing", batchProcessing);
|
||||
batchSize = config.getInt(getBasePath() + ".batch-size", batchSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user