Skip to content

Commit

Permalink
feat: pass player, nullable during handshake, & improve comments
Browse files Browse the repository at this point in the history
  • Loading branch information
Boy0000 committed Jun 8, 2024
1 parent 2181827 commit a65b7c1
Showing 1 changed file with 42 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,80 +7,98 @@ import io.papermc.paper.network.ChannelInitializeListenerHolder
import net.kyori.adventure.key.Key
import net.minecraft.network.Connection
import net.minecraft.network.protocol.Packet
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket
import org.bukkit.Bukkit
import net.minecraft.network.protocol.game.ClientGamePacketListener
import net.minecraft.server.network.ServerGamePacketListenerImpl
import org.bukkit.entity.Player
import org.bukkit.plugin.java.JavaPlugin
import java.util.concurrent.ConcurrentHashMap

/**
* Intercept a Clientbound packet to either alter it or prevent it from being sent to the player
* @param key Optional key to register the packet-listener with
* @param intercept A lambda function that takes two parameters:
* * `packet` (Packet<*>): The intercepted packet.
* * `player` (Player?): The player associated with the intercepted packet, or `null` if the player is not available.
* * The lambda should return the modified packet or `null` to prevent it from being sent.
* @return The modified packet or null to prevent it from sending
*/
fun JavaPlugin.interceptClientbound(key: String = "write_packet_interceptor", intercept: (Packet<*>) -> Packet<*>?) {
PacketListener.interceptClientbound(Key.key(this.name, key.substringAfter(":")), intercept)
fun JavaPlugin.interceptClientbound(key: String = "write_packet_interceptor", intercept: (Packet<*>, Player?) -> Packet<*>?) {
PacketListener.interceptClientbound(Key.key(this.name.lowercase(), key.substringAfter(":")), intercept)
}

/**
* Intercept a Clientbound packet to either alter it or prevent it from being sent to the player
* @param key Optional key to register the packet-listener with
* @param intercept A lambda function that takes two parameters:
* * `packet` (Packet<*>): The intercepted packet.
* * `player` (Player?): The player associated with the intercepted packet, or `null` if the player is not available.
* * The lambda should return the modified packet or `null` to prevent it from being sent.
* @return The modified packet or null to prevent it from sending
*/
fun JavaPlugin.interceptServerbound(key: String = "read_packet_interceptor", intercept: (Packet<*>) -> Packet<*>?) {
PacketListener.interceptServerbound(Key.key(this.name, key.substringAfter(":")), intercept)
fun JavaPlugin.interceptServerbound(key: String = "read_packet_interceptor", intercept: (Packet<*>, Player?) -> Packet<*>?) {
PacketListener.interceptServerbound(Key.key(this.name.lowercase(), key.substringAfter(":")), intercept)
}

object PacketListener {

/**
* Intercept a Clientbound packet to either alter it or prevent it from being sent to the player
* @param key Optional key to register the packet-listener with
* @param intercept A lambda function that takes two parameters:
* * `packet` (Packet<*>): The intercepted packet.
* * `player` (Player?): The player associated with the intercepted packet, or `null` if the player is not available.
* * The lambda should return the modified packet or `null` to prevent it from being sent.
* @return The modified packet or null to prevent it from sending
*/
fun interceptClientbound(plugin: JavaPlugin, key: String = "write_packet_interceptor", intercept: (Packet<*>) -> Packet<*>?) {
interceptClientbound(Key.key(plugin.name, key.substringAfter(":")), intercept)
fun interceptClientbound(plugin: JavaPlugin, key: String = "write_packet_interceptor", intercept: (Packet<*>, Player?) -> Packet<*>?) {
interceptClientbound(Key.key(plugin.name.lowercase(), key.substringAfter(":")), intercept)
}

/**
* Intercept a Clientbound packet to either alter it or prevent it from being sent to the player
* @param key Optional key to register the packet-listener with
* @param intercept A lambda function that takes two parameters:
* * `packet` (Packet<*>): The intercepted packet.
* * `player` (Player?): The player associated with the intercepted packet, or `null` if the player is not available.
* * The lambda should return the modified packet or `null` to prevent it from being sent.
* @return The modified packet or null to prevent it from sending
*/
fun interceptServerbound(plugin: JavaPlugin, key: String = "read_packet_interceptor", intercept: (Packet<*>) -> Packet<*>?) {
interceptServerbound(Key.key(plugin.name, key.substringAfter(":")), intercept)
fun interceptServerbound(plugin: JavaPlugin, key: String = "read_packet_interceptor", intercept: (Packet<*>, Player?) -> Packet<*>?) {
interceptServerbound(Key.key(plugin.name.lowercase(), key.substringAfter(":")), intercept)
}

fun unregisterListener(plugin: JavaPlugin) {
ChannelInitializeListenerHolder.getListeners().keys.filter { it.namespace() == plugin.name }.forEach(ChannelInitializeListenerHolder::removeListener)
ChannelInitializeListenerHolder.getListeners().keys.filter { it.namespace() == plugin.name.lowercase() }.forEach(ChannelInitializeListenerHolder::removeListener)
}

fun unregisterListener(key: Key) {
ChannelInitializeListenerHolder.removeListener(key)
}

/**
* Intercept a Clientbound packet to either alter it or prevent it from being sent to the player
* @return The modified packet or null to prevent it from sending
*/
internal fun interceptClientbound(key: Key? = null, intercept: (Packet<*>) -> Packet<*>?) {
internal fun interceptClientbound(key: Key? = null, intercept: (Packet<*>, Player?) -> Packet<*>?) {
val key = key ?: Key.key("write_packet_interceptor${ChannelInitializeListenerHolder.getListeners().keys.indexOfLast { it.value().startsWith("write_packet_interceptor") }.plus(1)}")

ChannelInitializeListenerHolder.addListener(key) { channel ->
channel.pipeline().addBefore("packet_handler", key.asString(), object : ChannelDuplexHandler() {
val connection = channel.pipeline()["packet_handler"] as Connection
override fun write(ctx: ChannelHandlerContext, packet: Any, promise: ChannelPromise) {
(packet as? Packet<*>)?.let { intercept(it)?.let { ctx.write(it, promise) } }
val player = if (connection.packetListener is ClientGamePacketListener) connection.player.bukkitEntity else null
if (packet is Packet<*>) intercept(packet, player)?.let { ctx.write(it, promise) }
else ctx.write(packet, promise)
}
})
}
}

/**
* Intercepts a Serverbound packet to either alter it or prevent it from being sent to the server
* @return The modified packet or null to prevent it from sending
*/
internal fun interceptServerbound(key: Key? = null, intercept: (Packet<*>) -> Packet<*>?) {
internal fun interceptServerbound(key: Key? = null, intercept: (Packet<*>, Player?) -> Packet<*>?) {
val key = key ?: Key.key("write_packet_interceptor${ChannelInitializeListenerHolder.getListeners().keys.indexOfLast { it.value().startsWith("read_packet_interceptor") }.plus(1)}")

ChannelInitializeListenerHolder.addListener(key) { channel ->
channel.pipeline().addBefore("packet_handler", key.asString(), object : ChannelDuplexHandler() {
val connection = channel.pipeline()["packet_handler"] as Connection
override fun channelRead(ctx: ChannelHandlerContext, packet: Any) {
(packet as? Packet<*>)?.let { intercept(it)?.let { ctx.fireChannelRead(it) } }
val player = if (connection.packetListener is ServerGamePacketListenerImpl) connection.player.bukkitEntity else null
if (packet is Packet<*>) intercept(packet, player)?.let { ctx.fireChannelRead(it) }
else ctx.fireChannelRead(packet)
}
})
}
Expand Down

0 comments on commit a65b7c1

Please sign in to comment.