From 53b6355e77b773a2c77807211c5441c7fe1d0b63 Mon Sep 17 00:00:00 2001 From: Boy Date: Sun, 5 May 2024 22:15:46 +0200 Subject: [PATCH] feat: 1.20.5+ support --- .github/workflows/build.yml | 2 +- build.gradle.kts | 43 ++-- core/build.gradle.kts | 1 + .../mineinabyss/emojy/nms/EmojyNMSHandlers.kt | 4 +- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 4 +- v1_19_R1/build.gradle.kts | 5 +- v1_19_R2/build.gradle.kts | 5 +- v1_19_R3/build.gradle.kts | 5 +- v1_20_R1/build.gradle.kts | 5 +- v1_20_R2/build.gradle.kts | 5 +- .../emojy/nms/v1_20_R2/EmojyNMSHandler.kt | 3 +- v1_20_R3/build.gradle.kts | 5 +- v1_20_R4/build.gradle.kts | 48 ++++ .../emojy/nms/v1_20_R4/EmojyNMSHandler.kt | 224 ++++++++++++++++++ 16 files changed, 325 insertions(+), 40 deletions(-) create mode 100644 v1_20_R4/build.gradle.kts create mode 100644 v1_20_R4/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R4/EmojyNMSHandler.kt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1eaaad1..872e257 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin - java-version: 17 + java-version: 21 cache: gradle - name: Build diff --git a/build.gradle.kts b/build.gradle.kts index 76f4b00..9c001fe 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,4 @@ +import io.papermc.paperweight.userdev.ReobfArtifactConfiguration import net.minecrell.pluginyml.paper.PaperPluginDescription plugins { @@ -8,19 +9,19 @@ plugins { id("com.mineinabyss.conventions.autoversion") id("xyz.jpenilla.run-paper") version "2.0.1" // Adds runServer and runMojangMappedServer tasks for testing id("net.minecrell.plugin-yml.paper") version "0.6.0" - id("io.papermc.paperweight.userdev") version "1.5.11" + id("io.papermc.paperweight.userdev") version "1.7.1" } +paperweight.reobfArtifactConfiguration.set(ReobfArtifactConfiguration.MOJANG_PRODUCTION) + allprojects { apply(plugin = "java") version = rootProject.version - dependencies { - compileOnly(kotlin("stdlib-jdk8")) - } - repositories { + maven("https://repo.mineinabyss.com/releases") + maven("https://repo.mineinabyss.com/snapshots") mavenLocal() } } @@ -34,14 +35,15 @@ dependencies { compileOnly(idofrontLibs.minecraft.mccoroutine) // Shaded - paperweight.paperDevBundle("1.20.4-R0.1-SNAPSHOT") //NMS + paperweight.paperDevBundle("1.20.6-R0.1-SNAPSHOT") //NMS implementation(project(path = ":core")) - implementation(project(path = ":v1_19_R1", configuration = "reobf")) - implementation(project(path = ":v1_19_R2", configuration = "reobf")) - implementation(project(path = ":v1_19_R3", configuration = "reobf")) - implementation(project(path = ":v1_20_R1", configuration = "reobf")) - implementation(project(path = ":v1_20_R2", configuration = "reobf")) - implementation(project(path = ":v1_20_R3", configuration = "reobf")) + //implementation(project(path = ":v1_19_R1", configuration = "reobf")) + //implementation(project(path = ":v1_19_R2", configuration = "reobf")) + //implementation(project(path = ":v1_19_R3", configuration = "reobf")) + //implementation(project(path = ":v1_20_R1", configuration = "reobf")) + //implementation(project(path = ":v1_20_R2", configuration = "reobf")) + //implementation(project(path = ":v1_20_R3", configuration = "reobf")) + implementation(project(path = ":v1_20_R4")) } tasks { @@ -52,7 +54,7 @@ tasks { compileJava { options.encoding = Charsets.UTF_8.name() - options.release.set(17) + options.release.set(21) } javadoc { @@ -69,12 +71,13 @@ tasks { } shadowJar { - dependsOn(":v1_19_R1:reobfJar") - dependsOn(":v1_19_R2:reobfJar") - dependsOn(":v1_19_R3:reobfJar") - dependsOn(":v1_20_R1:reobfJar") - dependsOn(":v1_20_R2:reobfJar") - dependsOn(":v1_20_R3:reobfJar") + //dependsOn(":v1_19_R1:reobfJar") + //dependsOn(":v1_19_R2:reobfJar") + //dependsOn(":v1_19_R3:reobfJar") + //dependsOn(":v1_20_R1:reobfJar") + //dependsOn(":v1_20_R2:reobfJar") + //dependsOn(":v1_20_R3:reobfJar") + dependsOn(":v1_20_R4:reobfJar") archiveFileName.set("Emojy.jar") } @@ -88,7 +91,7 @@ paper { val version: String by project this.version = version authors = listOf("boy0000") - apiVersion = "1.19" + apiVersion = "1.20" serverDependencies { register("Idofront") { diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 1956639..af2933d 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -8,6 +8,7 @@ plugins { repositories { gradlePluginPortal() maven("https://repo.mineinabyss.com/releases") + maven("https://repo.mineinabyss.com/snapshots") maven("https://repo.papermc.io/repository/maven-public/") maven("https://repo.unnamed.team/repository/unnamed-public/") google() diff --git a/core/src/main/kotlin/com/mineinabyss/emojy/nms/EmojyNMSHandlers.kt b/core/src/main/kotlin/com/mineinabyss/emojy/nms/EmojyNMSHandlers.kt index b93c672..2acac7e 100644 --- a/core/src/main/kotlin/com/mineinabyss/emojy/nms/EmojyNMSHandlers.kt +++ b/core/src/main/kotlin/com/mineinabyss/emojy/nms/EmojyNMSHandlers.kt @@ -13,12 +13,12 @@ import org.bukkit.entity.Player object EmojyNMSHandlers { - private val SUPPORTED_VERSION = arrayOf("v1_19_R1", "v1_19_R2", "v1_19_R3", "v1_20_R1", "v1_20_R2", "v1_20_R3") + private val SUPPORTED_VERSION = arrayOf(/*"v1_19_R1", "v1_19_R2", "v1_19_R3", "v1_20_R1", "v1_20_R2", "v1_20_R3", */"v1_20_R4") fun setup(): IEmojyNMSHandler { SUPPORTED_VERSION.forEach { version -> runCatching { - Class.forName("org.bukkit.craftbukkit.$version.entity.CraftPlayer") + //Class.forName("org.bukkit.craftbukkit.entity.CraftPlayer") return Class.forName("com.mineinabyss.emojy.nms.${version}.EmojyNMSHandler").getConstructor() .newInstance() as IEmojyNMSHandler } diff --git a/gradle.properties b/gradle.properties index b8debd1..7906906 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ group=com.mineinabyss -version=0.8 -idofrontVersion=0.23.0 +version=0.9 +idofrontVersion=0.24.0-dev.10 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a595206..48c0a02 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index 2db4462..36ee24e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,6 +8,7 @@ pluginManagement { mavenCentral() google() maven("https://repo.mineinabyss.com/releases") + maven("https://repo.mineinabyss.com/snapshots") maven("https://repo.papermc.io/repository/maven-public/") mavenLocal() } @@ -23,6 +24,7 @@ dependencyResolutionManagement { repositories { maven("https://repo.mineinabyss.com/releases") + maven("https://repo.mineinabyss.com/snapshots") mavenLocal() } @@ -33,4 +35,4 @@ dependencyResolutionManagement { } } -include("core", "v1_19_R1", "v1_19_R2", "v1_19_R3", "v1_20_R1", "v1_20_R2", "v1_20_R3") +include("core", /*"v1_19_R1", "v1_19_R2", "v1_19_R3", "v1_20_R1", "v1_20_R2","v1_20_R3", */ "v1_20_R4") diff --git a/v1_19_R1/build.gradle.kts b/v1_19_R1/build.gradle.kts index 58d0d94..4641baa 100644 --- a/v1_19_R1/build.gradle.kts +++ b/v1_19_R1/build.gradle.kts @@ -1,12 +1,13 @@ plugins { id("com.mineinabyss.conventions.kotlin.jvm") id("com.mineinabyss.conventions.autoversion") - id("io.papermc.paperweight.userdev") version "1.5.11" + id("io.papermc.paperweight.userdev") version "1.7.1" } repositories { gradlePluginPortal() maven("https://repo.mineinabyss.com/releases") + maven("https://repo.mineinabyss.com/snapshots") maven("https://repo.papermc.io/repository/maven-public/") google() } @@ -32,7 +33,7 @@ tasks { compileJava { options.encoding = Charsets.UTF_8.name() - options.release.set(17) + options.release.set(21) } javadoc { options.encoding = Charsets.UTF_8.name() diff --git a/v1_19_R2/build.gradle.kts b/v1_19_R2/build.gradle.kts index 0e33bc5..8da5352 100644 --- a/v1_19_R2/build.gradle.kts +++ b/v1_19_R2/build.gradle.kts @@ -1,12 +1,13 @@ plugins { id("com.mineinabyss.conventions.kotlin.jvm") id("com.mineinabyss.conventions.autoversion") - id("io.papermc.paperweight.userdev") version "1.5.11" + id("io.papermc.paperweight.userdev") version "1.7.1" } repositories { gradlePluginPortal() maven("https://repo.mineinabyss.com/releases") + maven("https://repo.mineinabyss.com/snapshots") maven("https://repo.papermc.io/repository/maven-public/") google() } @@ -32,7 +33,7 @@ tasks { compileJava { options.encoding = Charsets.UTF_8.name() - options.release.set(17) + options.release.set(21) } javadoc { options.encoding = Charsets.UTF_8.name() diff --git a/v1_19_R3/build.gradle.kts b/v1_19_R3/build.gradle.kts index ccb8d43..634f20e 100644 --- a/v1_19_R3/build.gradle.kts +++ b/v1_19_R3/build.gradle.kts @@ -1,12 +1,13 @@ plugins { id("com.mineinabyss.conventions.kotlin.jvm") id("com.mineinabyss.conventions.autoversion") - id("io.papermc.paperweight.userdev") version "1.5.11" + id("io.papermc.paperweight.userdev") version "1.7.1" } repositories { gradlePluginPortal() maven("https://repo.mineinabyss.com/releases") + maven("https://repo.mineinabyss.com/snapshots") maven("https://repo.papermc.io/repository/maven-public/") google() } @@ -32,7 +33,7 @@ tasks { compileJava { options.encoding = Charsets.UTF_8.name() - options.release.set(17) + options.release.set(21) } javadoc { options.encoding = Charsets.UTF_8.name() diff --git a/v1_20_R1/build.gradle.kts b/v1_20_R1/build.gradle.kts index 14eea49..000763d 100644 --- a/v1_20_R1/build.gradle.kts +++ b/v1_20_R1/build.gradle.kts @@ -1,12 +1,13 @@ plugins { id("com.mineinabyss.conventions.kotlin.jvm") id("com.mineinabyss.conventions.autoversion") - id("io.papermc.paperweight.userdev") version "1.5.11" + id("io.papermc.paperweight.userdev") version "1.7.1" } repositories { gradlePluginPortal() maven("https://repo.mineinabyss.com/releases") + maven("https://repo.mineinabyss.com/snapshots") maven("https://repo.papermc.io/repository/maven-public/") google() } @@ -32,7 +33,7 @@ tasks { compileJava { options.encoding = Charsets.UTF_8.name() - options.release.set(17) + options.release.set(21) } javadoc { options.encoding = Charsets.UTF_8.name() diff --git a/v1_20_R2/build.gradle.kts b/v1_20_R2/build.gradle.kts index c32e7c5..856d82c 100644 --- a/v1_20_R2/build.gradle.kts +++ b/v1_20_R2/build.gradle.kts @@ -1,12 +1,13 @@ plugins { id("com.mineinabyss.conventions.kotlin.jvm") id("com.mineinabyss.conventions.autoversion") - id("io.papermc.paperweight.userdev") version "1.5.11" + id("io.papermc.paperweight.userdev") version "1.7.1" } repositories { gradlePluginPortal() maven("https://repo.mineinabyss.com/releases") + maven("https://repo.mineinabyss.com/snapshots") maven("https://repo.papermc.io/repository/maven-public/") google() } @@ -32,7 +33,7 @@ tasks { compileJava { options.encoding = Charsets.UTF_8.name() - options.release.set(17) + options.release.set(21) } javadoc { options.encoding = Charsets.UTF_8.name() diff --git a/v1_20_R2/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R2/EmojyNMSHandler.kt b/v1_20_R2/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R2/EmojyNMSHandler.kt index 8fb1d39..6c3588b 100644 --- a/v1_20_R2/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R2/EmojyNMSHandler.kt +++ b/v1_20_R2/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R2/EmojyNMSHandler.kt @@ -17,6 +17,7 @@ import io.netty.handler.codec.ByteToMessageDecoder import io.netty.handler.codec.MessageToByteEncoder import io.papermc.paper.adventure.PaperAdventure import net.kyori.adventure.text.Component +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.ListTag import net.minecraft.nbt.StringTag @@ -161,7 +162,7 @@ class EmojyNMSHandler : IEmojyNMSHandler { override fun readUtf(maxLength: Int): String { return super.readUtf(maxLength).let { string -> - runCatching { string.miniMsg() }.recover { legacy.deserialize(string) } + runCatching { string.miniMsg() }.recover { LegacyComponentSerializer.legacySection().deserialize(string) } .getOrNull()?.transform(player, true)?.serialize() ?: string } } diff --git a/v1_20_R3/build.gradle.kts b/v1_20_R3/build.gradle.kts index 16fe9bf..0478735 100644 --- a/v1_20_R3/build.gradle.kts +++ b/v1_20_R3/build.gradle.kts @@ -1,12 +1,13 @@ plugins { id("com.mineinabyss.conventions.kotlin.jvm") id("com.mineinabyss.conventions.autoversion") - id("io.papermc.paperweight.userdev") version "1.5.11" + id("io.papermc.paperweight.userdev") version "1.7.1" } repositories { gradlePluginPortal() maven("https://repo.mineinabyss.com/releases") + maven("https://repo.mineinabyss.com/snapshots") maven("https://repo.papermc.io/repository/maven-public/") google() } @@ -32,7 +33,7 @@ tasks { compileJava { options.encoding = Charsets.UTF_8.name() - options.release.set(17) + options.release.set(21) } javadoc { options.encoding = Charsets.UTF_8.name() diff --git a/v1_20_R4/build.gradle.kts b/v1_20_R4/build.gradle.kts new file mode 100644 index 0000000..11b747f --- /dev/null +++ b/v1_20_R4/build.gradle.kts @@ -0,0 +1,48 @@ +import io.papermc.paperweight.userdev.ReobfArtifactConfiguration + +plugins { + id("com.mineinabyss.conventions.kotlin.jvm") + id("com.mineinabyss.conventions.autoversion") + id("io.papermc.paperweight.userdev") version "1.7.1" +} + +repositories { + gradlePluginPortal() + maven("https://repo.mineinabyss.com/releases") + maven("https://repo.mineinabyss.com/snapshots") + maven("https://repo.papermc.io/repository/maven-public/") + google() +} + +paperweight.reobfArtifactConfiguration.set(ReobfArtifactConfiguration.MOJANG_PRODUCTION) + +dependencies { + // MineInAbyss platform + compileOnly(idofrontLibs.kotlinx.serialization.json) + compileOnly(idofrontLibs.kotlinx.serialization.kaml) + compileOnly(idofrontLibs.kotlinx.coroutines) + compileOnly(idofrontLibs.minecraft.mccoroutine) + + // Shaded + implementation(idofrontLibs.bundles.idofront.core) + implementation(project(":core")) + paperweight.paperDevBundle("1.20.6-R0.1-SNAPSHOT") //NMS +} + +tasks { + + build { + dependsOn(reobfJar) + } + + compileJava { + options.encoding = Charsets.UTF_8.name() + options.release.set(21) + } + javadoc { + options.encoding = Charsets.UTF_8.name() + } + processResources { + filteringCharset = Charsets.UTF_8.name() + } +} diff --git a/v1_20_R4/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R4/EmojyNMSHandler.kt b/v1_20_R4/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R4/EmojyNMSHandler.kt new file mode 100644 index 0000000..b371553 --- /dev/null +++ b/v1_20_R4/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R4/EmojyNMSHandler.kt @@ -0,0 +1,224 @@ +@file:Suppress("unused") + +package com.mineinabyss.emojy.nms.v1_20_R4 + +import com.mineinabyss.emojy.emojy +import com.mineinabyss.emojy.escapeEmoteIDs +import com.mineinabyss.emojy.legacy +import com.mineinabyss.emojy.nms.EmojyNMSHandlers +import com.mineinabyss.emojy.nms.IEmojyNMSHandler +import com.mineinabyss.idofront.textcomponents.miniMsg +import com.mineinabyss.idofront.textcomponents.serialize +import io.netty.buffer.ByteBuf +import io.netty.channel.* +import io.netty.handler.codec.ByteToMessageDecoder +import io.netty.handler.codec.MessageToByteEncoder +import net.minecraft.core.RegistryAccess +import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.ListTag +import net.minecraft.nbt.StringTag +import net.minecraft.nbt.Tag +import net.minecraft.network.* +import net.minecraft.network.protocol.Packet +import net.minecraft.network.protocol.PacketFlow +import net.minecraft.network.protocol.game.ClientGamePacketListener +import net.minecraft.network.protocol.game.GameProtocols +import net.minecraft.network.protocol.game.ServerboundChatPacket +import net.minecraft.server.MinecraftServer +import net.minecraft.server.network.ServerConnectionListener +import org.bukkit.Bukkit +import org.bukkit.craftbukkit.entity.CraftPlayer +import org.bukkit.entity.Player +import org.bukkit.scheduler.BukkitRunnable +import java.io.IOException +import java.util.* +import java.util.function.Function + +class EmojyNMSHandler : IEmojyNMSHandler { + private val encoder = Collections.synchronizedMap(WeakHashMap()) + private val decoder = Collections.synchronizedMap(WeakHashMap()) + + @Suppress("unused", "UNCHECKED_CAST", "FunctionName") + fun EmojyNMSHandler() { + val connections = MinecraftServer.getServer().connection.connections ?: emptyList() + // Have to set it accessible because unlike connections it is private + val channelFutures = ServerConnectionListener::class.java.getDeclaredField("f").apply { this.isAccessible = true; }.get(MinecraftServer.getServer().connection) as List + + + // Handle connected channels + val endInitProtocol: ChannelInitializer = object : ChannelInitializer() { + override fun initChannel(channel: Channel) { + runCatching { + synchronized(connections) { + // Stop injecting channels + channel.eventLoop().submit { channel.inject() } + } + }.onFailure { it.printStackTrace() } + } + } + + // Handle channels that are connecting + val beginInitProtocol = object : ChannelInitializer() { + override fun initChannel(channel: Channel) { + var handler: ChannelHandler? = null + + channel.pipeline().forEach { + if (it.value.javaClass.name == "com.viaversion.viaversion.bukkit.handlers.BukkitChannelInitializer") { + handler = it.value as ChannelHandler + } + } + handler?.let { + val initChannel = ChannelInitializer::class.java.getDeclaredMethod("initChannel", Channel::class.java).apply { isAccessible = true } + val original = it.javaClass.getDeclaredField("original").apply { this.isAccessible = true } + val initializer = original.get(it) as ChannelInitializer<*> + val miniInit = object : ChannelInitializer() { + override fun initChannel(channel: Channel) { + initChannel.invoke(initializer, channel) + channel.eventLoop().submit { channel.inject() } + } + } + original.set(handler, miniInit) + } ?: channel.pipeline().addLast(endInitProtocol) + } + } + + val serverChannelHandler = object : ChannelInboundHandlerAdapter() { + override fun channelRead(ctx: ChannelHandlerContext, msg: Any) { + (msg as Channel).pipeline().addFirst(beginInitProtocol) + ctx.fireChannelRead(msg) + } + } + + runCatching { + bind(channelFutures, serverChannelHandler) + }.onFailure { + object : BukkitRunnable() { + override fun run() { + bind(channelFutures, serverChannelHandler) + } + }.runTask(emojy.plugin) + } + } + + private fun bind(channelFutures: List, serverChannelHandler: ChannelInboundHandlerAdapter) { + channelFutures.forEach { future -> + future.channel().pipeline().addFirst(serverChannelHandler) + } + + Bukkit.getOnlinePlayers().forEach(::inject) + } + + private fun Channel.inject(player: Player? = null) { + if (this !in encoder.keys && (this.pipeline().get("encoder") as ChannelHandler) !is CustomPacketEncoder) + encoder[this] = this.pipeline().replace("encoder", "encoder", CustomPacketEncoder(player)) + + if (this !in decoder.keys && (this.pipeline().get("decoder") as ChannelHandler) !is CustomPacketDecoder) + decoder[this] = this.pipeline().replace("decoder", "decoder", CustomPacketDecoder(player)) + + } + + private fun Channel.uninject() { + if (this in encoder.keys) { + val prevHandler = encoder.remove(this) + val handler = if (prevHandler is PacketEncoder<*>) PacketEncoder(GameProtocols.CLIENTBOUND.bind(RegistryFriendlyByteBuf.decorator(RegistryAccess.EMPTY))) else prevHandler + handler?.let { this.pipeline().replace("encoder", "encoder", handler) } + } + + if (this in decoder.keys) { + val prevHandler = decoder.remove(this) + val handler = if (prevHandler is PacketDecoder<*>) PacketDecoder(GameProtocols.SERVERBOUND.bind(RegistryFriendlyByteBuf.decorator(RegistryAccess.EMPTY))) else prevHandler + handler?.let { this.pipeline().replace("decoder", "decoder", handler) } + } + } + + override fun inject(player: Player) { + val channel = (player as? CraftPlayer)?.handle?.connection?.connection?.channel ?: return + channel.eventLoop().submit { channel.inject(player) } + } + + override fun uninject(player: Player) { + (player as? CraftPlayer)?.handle?.connection?.connection?.channel?.uninject() + } + + private class CustomDataSerializer(val player: Player?, bytebuf: ByteBuf) : RegistryFriendlyByteBuf(bytebuf, RegistryAccess.EMPTY) { + + override fun writeUtf(string: String, maxLength: Int): FriendlyByteBuf { + return EmojyNMSHandlers.writeTransformer(player, true, true).invoke(string).let { super.writeUtf(it, maxLength) } + } + + override fun readUtf(maxLength: Int): String { + return super.readUtf(maxLength).let { string -> + runCatching { string.miniMsg() }.recover { legacy.deserialize(string) } + .getOrNull()?.escapeEmoteIDs(player)?.serialize() ?: string + } + } + + override fun writeNbt(compound: Tag?): FriendlyByteBuf { + return super.writeNbt(compound?.apply { + when (this) { + is CompoundTag -> transform(this, EmojyNMSHandlers.writeTransformer(player, false, true)) + is StringTag -> transform(this, EmojyNMSHandlers.writeTransformer(player, false,true)) + } + }) + } + + override fun readNbt(): CompoundTag? { + return super.readNbt()?.apply { + transform(this, EmojyNMSHandlers.readTransformer(player)) + } + } + + private fun transform(compound: CompoundTag, transformer: Function) { + for (key in compound.allKeys) when (val base = compound.get(key)) { + is CompoundTag -> transform(base, transformer) + is ListTag -> transform(base, transformer) + is StringTag -> compound.put(key, StringTag.valueOf(transformer.apply(base.asString))) + } + } + + private fun transform(list: ListTag, transformer: Function) { + val listCopy = list.toList() + for (base in listCopy) when (base) { + is CompoundTag -> transform(base, transformer) + is ListTag -> transform(base, transformer) + is StringTag -> list.indexOf(base).let { index -> + list[index] = StringTag.valueOf(transformer.apply(base.asString)) + } + } + } + + private fun transform(string: StringTag, transformer: Function) { + transformer.apply(string.asString) + } + + } + + private class CustomPacketEncoder(val player: Player? = null) : MessageToByteEncoder>() { + private val protocolDirection = PacketFlow.CLIENTBOUND + + override fun encode(ctx: ChannelHandlerContext, msg: Packet<*>, out: ByteBuf) { + GameProtocols.CLIENTBOUND.bind { CustomDataSerializer(player, it) }.codec().encode(out, msg as Packet) + } + } + + private class CustomPacketDecoder(val player: Player? = null) : ByteToMessageDecoder() { + + override fun decode(ctx: ChannelHandlerContext, buffer: ByteBuf, out: MutableList) { + val bufferCopy = buffer.copy() + if (buffer.readableBytes() == 0) return + + val customDataSerializer = CustomDataSerializer(player, buffer) + val codec = GameProtocols.SERVERBOUND.bind { CustomDataSerializer(player, it) }.codec() + var packet = codec.decode(buffer) + + out += when { + customDataSerializer.readableBytes() > 0 -> throw IOException("Packet ($packet) was larger than I expected, found ${customDataSerializer.readableBytes()} bytes extra whilst reading packet") + packet is ServerboundChatPacket -> packet = codec.decode(bufferCopy) + else -> packet + } + ProtocolSwapHandler.handleInboundTerminalPacket(ctx, packet) + } + } + + override val supported get() = true +}