Paper/patches/server/0516-Add-Channel-initialization-listeners.patch
Spottedleaf 8c5b837e05 Rework async chunk api implementation
Firstly, the old methods all routed to the CompletableFuture method.
However, the CF method could not guarantee that if the caller
was off-main that the future would be "completed" on-main. Since
the callback methods used the CF one, this meant that the callback
methods did not guarantee that the callbacks were to be called on
the main thread.

Now, all methods route to getChunkAtAsync(x, z, gen, urgent, cb)
so that the methods with the callback are guaranteed to invoke
the callback on the main thread. The CF behavior remains unchanged;
it may still appear to complete on main if invoked off-main.

Secondly, remove the scheduleOnMain invocation in the async
chunk completion. This unnecessarily delays the callback
by 1 tick.

Thirdly, add getChunksAtAsync(minX, minZ, maxX, maxZ, ...) which
will load chunks within an area. This method is provided as a helper
as keeping all chunks loaded within an area can be complicated to
implement for plugins (due to the lacking ticket API), and is
already implemented internally anyways.

Fourthly, remove the ticket addition that occured with getChunkAt
and getChunkAtAsync. The ticket addition may delay the unloading
of the chunk unnecessarily. It also fixes a very rare timing bug
where the future/callback would be completed after the chunk
unloads.
2024-11-18 23:00:59 -08:00

156 lines
6.8 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
Date: Thu, 29 Apr 2021 21:19:33 +0200
Subject: [PATCH] Add Channel initialization listeners
diff --git a/src/main/java/io/papermc/paper/network/ChannelInitializeListener.java b/src/main/java/io/papermc/paper/network/ChannelInitializeListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..88099df34c2d74daba9645aadf65b446ca795a91
--- /dev/null
+++ b/src/main/java/io/papermc/paper/network/ChannelInitializeListener.java
@@ -0,0 +1,15 @@
+package io.papermc.paper.network;
+
+import io.netty.channel.Channel;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+/**
+ * Internal API to register channel initialization listeners.
+ * <p>
+ * This is not officially supported API and we make no guarantees to the existence or state of this interface.
+ */
+@FunctionalInterface
+public interface ChannelInitializeListener {
+
+ void afterInitChannel(@NonNull Channel channel);
+}
diff --git a/src/main/java/io/papermc/paper/network/ChannelInitializeListenerHolder.java b/src/main/java/io/papermc/paper/network/ChannelInitializeListenerHolder.java
new file mode 100644
index 0000000000000000000000000000000000000000..30e62719e0a83525daa33cf41cb61df360c0e046
--- /dev/null
+++ b/src/main/java/io/papermc/paper/network/ChannelInitializeListenerHolder.java
@@ -0,0 +1,74 @@
+package io.papermc.paper.network;
+
+import io.netty.channel.Channel;
+import net.kyori.adventure.key.Key;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Internal API to register channel initialization listeners.
+ * <p>
+ * This is not officially supported API and we make no guarantees to the existence or state of this class.
+ */
+public final class ChannelInitializeListenerHolder {
+
+ private static final Map<Key, ChannelInitializeListener> LISTENERS = new HashMap<>();
+ private static final Map<Key, ChannelInitializeListener> IMMUTABLE_VIEW = Collections.unmodifiableMap(LISTENERS);
+
+ private ChannelInitializeListenerHolder() {
+ }
+
+ /**
+ * Registers whether an initialization listener is registered under the given key.
+ *
+ * @param key key
+ * @return whether an initialization listener is registered under the given key
+ */
+ public static boolean hasListener(@NonNull Key key) {
+ return LISTENERS.containsKey(key);
+ }
+
+ /**
+ * Registers a channel initialization listener called after ServerConnection is initialized.
+ *
+ * @param key key
+ * @param listener initialization listeners
+ */
+ public static void addListener(@NonNull Key key, @NonNull ChannelInitializeListener listener) {
+ LISTENERS.put(key, listener);
+ }
+
+ /**
+ * Removes and returns an initialization listener registered by the given key if present.
+ *
+ * @param key key
+ * @return removed initialization listener if present
+ */
+ public static @Nullable ChannelInitializeListener removeListener(@NonNull Key key) {
+ return LISTENERS.remove(key);
+ }
+
+ /**
+ * Returns an immutable map of registered initialization listeners.
+ *
+ * @return immutable map of registered initialization listeners
+ */
+ public static @NonNull Map<Key, ChannelInitializeListener> getListeners() {
+ return IMMUTABLE_VIEW;
+ }
+
+ /**
+ * Calls the registered listeners with the given channel.
+ *
+ * @param channel channel
+ */
+ public static void callListeners(@NonNull Channel channel) {
+ for (ChannelInitializeListener listener : LISTENERS.values()) {
+ listener.afterInitChannel(channel);
+ }
+ }
+}
diff --git a/src/main/java/io/papermc/paper/network/ConnectionEvent.java b/src/main/java/io/papermc/paper/network/ConnectionEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..0d7e7db9e37ef0183c32b217bd944fb4f41ab83a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/network/ConnectionEvent.java
@@ -0,0 +1,10 @@
+package io.papermc.paper.network;
+
+/**
+ * Internal connection pipeline events.
+ */
+public enum ConnectionEvent {
+
+ COMPRESSION_THRESHOLD_SET,
+ COMPRESSION_DISABLED
+}
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
index ba41646a5edb57c4d9766df08bbc57016e2de189..2b86415e4ea197c5c44c23072c9a1cda595544a8 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -680,6 +680,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
} else {
this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressionThreshold));
}
+ this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_THRESHOLD_SET); // Paper - Add Channel initialization listeners
} else {
if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder) {
this.channel.pipeline().remove("decompress");
@@ -688,6 +689,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
if (this.channel.pipeline().get("compress") instanceof CompressionEncoder) {
this.channel.pipeline().remove("compress");
}
+ this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_DISABLED); // Paper - Add Channel initialization listeners
}
}
diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
index 64119f2188f8958b8a5913fec82ac5bba1b74b2a..d6d7f1c446ba5507f67038ff27775ba75156f4a7 100644
--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
@@ -115,6 +115,7 @@ public class ServerConnectionListener {
pending.add(object); // Paper - prevent blocking on adding a new connection while the server is ticking
((Connection) object).configurePacketHandler(channelpipeline);
((Connection) object).setListenerForServerboundHandshake(new ServerHandshakePacketListenerImpl(ServerConnectionListener.this.server, (Connection) object));
+ io.papermc.paper.network.ChannelInitializeListenerHolder.callListeners(channel); // Paper - Add Channel initialization listeners
}
}).group(eventloopgroup).localAddress(address, port)).option(ChannelOption.AUTO_READ, false).bind().syncUninterruptibly()); // CraftBukkit
}