From 8f02e210173e3229018f6eb1ebd7878925b648c1 Mon Sep 17 00:00:00 2001 From: Boy0000 Date: Thu, 30 May 2024 11:48:51 +0200 Subject: [PATCH] feat: 1.20.5+ support (#13) * chore: Update geary, idofront, publish action * feat: 1.20.5+ support * refactor: edit codec instead * fix: use packets to handle title/subtitle/actionbar with no permission checks * fix: space-ids not being transformed * fix: use packetlistener for inventory-titles & entity data * fix: signs formatting minimessage and emotes * fix: tablist footer/header parsing emotes * fix: handle anvil renaming by escaping in client-sent RenameItem packet and transforming in server-side event * fix: more chat client events * fix: sign handling moved to events * fix: anvil renaming storing original rename name for next rename * fix: properly unescape escaped emoteids * chore: bump idofront * fix: emotes in chat not containing inserts * refactor: remove old version impl --------- Co-authored-by: Danielle Voznyy --- .github/workflows/build.yml | 2 +- .github/workflows/publish.yml | 9 +- build.gradle.kts | 51 ++-- core/build.gradle.kts | 17 +- .../com/mineinabyss/emojy/EmojyContext.kt | 2 + .../com/mineinabyss/emojy/EmojyGenerator.kt | 6 +- .../com/mineinabyss/emojy/EmojyHelpers.kt | 156 ++++++----- .../com/mineinabyss/emojy/EmojyListener.kt | 45 --- .../com/mineinabyss/emojy/EmojyPlugin.kt | 31 +- .../mineinabyss/emojy/config/EmojyConfig.kt | 5 +- .../mineinabyss/emojy/nms/EmojyNMSHandlers.kt | 62 +--- .../mineinabyss/emojy/nms/IEmojyNMSHandler.kt | 6 - gradle.properties | 4 +- gradle/{emojyLibs.toml => libs.versions.toml} | 0 gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 7 +- v1_19_R1/build.gradle.kts | 43 --- .../emojy/nms/v1_19_R1/EmojyNMSHandler.kt | 247 ---------------- v1_19_R2/build.gradle.kts | 43 --- .../emojy/nms/v1_19_R2/EmojyNMSHandler.kt | 249 ----------------- v1_19_R3/build.gradle.kts | 43 --- .../emojy/nms/v1_19_R3/EmojyNMSHandler.kt | 264 ------------------ .../emojy/nms/v1_20_R1/EmojyNMSHandler.kt | 239 ---------------- v1_20_R2/build.gradle.kts | 43 --- .../emojy/nms/v1_20_R2/EmojyNMSHandler.kt | 250 ----------------- v1_20_R3/build.gradle.kts | 43 --- .../emojy/nms/v1_20_R3/EmojyNMSHandler.kt | 258 ----------------- {v1_20_R1 => v1_20_R4}/build.gradle.kts | 21 +- .../emojy/nms/v1_20_R4/EmojyListener.kt | 89 ++++++ .../emojy/nms/v1_20_R4/EmojyNMSHandler.kt | 130 +++++++++ 30 files changed, 386 insertions(+), 1981 deletions(-) delete mode 100644 core/src/main/kotlin/com/mineinabyss/emojy/EmojyListener.kt rename gradle/{emojyLibs.toml => libs.versions.toml} (100%) delete mode 100644 v1_19_R1/build.gradle.kts delete mode 100644 v1_19_R1/src/main/kotlin/com/mineinabyss/emojy/nms/v1_19_R1/EmojyNMSHandler.kt delete mode 100644 v1_19_R2/build.gradle.kts delete mode 100644 v1_19_R2/src/main/kotlin/com/mineinabyss/emojy/nms/v1_19_R2/EmojyNMSHandler.kt delete mode 100644 v1_19_R3/build.gradle.kts delete mode 100644 v1_19_R3/src/main/kotlin/com/mineinabyss/emojy/nms/v1_19_R3/EmojyNMSHandler.kt delete mode 100644 v1_20_R1/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R1/EmojyNMSHandler.kt delete mode 100644 v1_20_R2/build.gradle.kts delete mode 100644 v1_20_R2/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R2/EmojyNMSHandler.kt delete mode 100644 v1_20_R3/build.gradle.kts delete mode 100644 v1_20_R3/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R3/EmojyNMSHandler.kt rename {v1_20_R1 => v1_20_R4}/build.gradle.kts (51%) create mode 100644 v1_20_R4/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R4/EmojyListener.kt 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/.github/workflows/publish.yml b/.github/workflows/publish.yml index 35ef6bc..48ebe24 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -12,13 +12,14 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 - - uses: MineInAbyss/publish-action@master + - uses: MineInAbyss/publish-action@develop with: maven-metadata-url: https://repo.mineinabyss.com/releases/com/mineinabyss/emojy/maven-metadata.xml - pages-path: build/dokka/htmlMultiModule/ - dokka: dokkaHtmlMultiModule + maven-snapshot-metadata-url: https://repo.mineinabyss.com/snapshots/com/mineinabyss/emojy/maven-metadata.xml maven-username: ${{ secrets.MAVEN_PUBLISH_USERNAME }} maven-password: ${{ secrets.MAVEN_PUBLISH_PASSWORD }} release-files: | diff --git a/build.gradle.kts b/build.gradle.kts index 142244f..60879fe 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,40 +9,40 @@ 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") + maven("https://hub.jeff-media.com/nexus/repository/jeff-media-public/") mavenLocal() } + + dependencies { + implementation("com.jeff_media:MorePersistentDataTypes:2.4.0") + } } dependencies { // MineInAbyss platform - compileOnly(libs.bundles.idofront.core) - compileOnly(libs.kotlinx.serialization.json) - compileOnly(libs.kotlinx.serialization.kaml) - compileOnly(libs.kotlinx.coroutines) - compileOnly(libs.minecraft.mccoroutine) + compileOnly(idofrontLibs.bundles.idofront.core) + compileOnly(idofrontLibs.kotlinx.serialization.json) + compileOnly(idofrontLibs.kotlinx.serialization.kaml) + compileOnly(idofrontLibs.kotlinx.coroutines) + 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_20_R4")) } tasks { @@ -52,7 +53,7 @@ tasks { compileJava { options.encoding = Charsets.UTF_8.name() - options.release.set(17) + options.release.set(21) } javadoc { @@ -65,16 +66,14 @@ tasks { } runServer { - minecraftVersion("1.20.2") + minecraftVersion("1.20.6") } 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_20_R4:reobfJar") + + relocate("com.jeff_media.morepersistentdatatypes", "com.mineinabyss.shaded.morepersistentdatatypes") + archiveFileName.set("Emojy.jar") } @@ -88,7 +87,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 9920f3f..af2933d 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -2,12 +2,13 @@ plugins { id("com.mineinabyss.conventions.kotlin.jvm") id("com.mineinabyss.conventions.papermc") id("com.mineinabyss.conventions.autoversion") - alias(libs.plugins.kotlinx.serialization) + alias(idofrontLibs.plugins.kotlinx.serialization) } 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() @@ -15,14 +16,14 @@ repositories { dependencies { // MineInAbyss platform - compileOnly(libs.bundles.idofront.core) - compileOnly(libs.kotlinx.serialization.json) - compileOnly(libs.kotlinx.serialization.kaml) - compileOnly(libs.kotlinx.coroutines) - compileOnly(libs.minecraft.mccoroutine) + compileOnly(idofrontLibs.bundles.idofront.core) + compileOnly(idofrontLibs.kotlinx.serialization.json) + compileOnly(idofrontLibs.kotlinx.serialization.kaml) + compileOnly(idofrontLibs.kotlinx.coroutines) + compileOnly(idofrontLibs.minecraft.mccoroutine) - compileOnly(libs.creative.api) - compileOnly(libs.creative.serializer.minecraft) + compileOnly(idofrontLibs.creative.api) + compileOnly(idofrontLibs.creative.serializer.minecraft) // Shaded implementation("com.aaaaahhhhh.bananapuncher714:GifConverter:1.0") diff --git a/core/src/main/kotlin/com/mineinabyss/emojy/EmojyContext.kt b/core/src/main/kotlin/com/mineinabyss/emojy/EmojyContext.kt index e0b89ee..a4d540c 100644 --- a/core/src/main/kotlin/com/mineinabyss/emojy/EmojyContext.kt +++ b/core/src/main/kotlin/com/mineinabyss/emojy/EmojyContext.kt @@ -8,6 +8,7 @@ import com.mineinabyss.emojy.nms.EmojyNMSHandlers import com.mineinabyss.emojy.nms.IEmojyNMSHandler import com.mineinabyss.emojy.translator.EmojyLanguage import com.mineinabyss.idofront.di.DI +import com.mineinabyss.idofront.messaging.ComponentLogger val emojy by DI.observe() val emojyConfig by DI.observe() @@ -18,4 +19,5 @@ interface EmojyContext { val gifs: Set val languages: Set val handler: IEmojyNMSHandler + val logger: ComponentLogger } diff --git a/core/src/main/kotlin/com/mineinabyss/emojy/EmojyGenerator.kt b/core/src/main/kotlin/com/mineinabyss/emojy/EmojyGenerator.kt index b3005db..6628ac1 100644 --- a/core/src/main/kotlin/com/mineinabyss/emojy/EmojyGenerator.kt +++ b/core/src/main/kotlin/com/mineinabyss/emojy/EmojyGenerator.kt @@ -33,13 +33,13 @@ object EmojyGenerator { resourcePack.font(font.toBuilder().addProvider(FontProvider.space().advance("\uE101", -1).build()).build()) // If the font has already added an entry for the emote, skip it font.providers().any { it is BitMapFontProvider && it.file() == emote.texture } -> - return@forEach if (emojyConfig.debug) logWarn("Skipping ${emote.id}-font because it is a bitmap and already added") else {} + return@forEach emojy.logger.d("Skipping ${emote.id}-font because it is a bitmap and already added") } resourcePack.font(emote.appendFont(resourcePack)) emotesFolder.listFiles()?.find { f -> f.nameWithoutExtension == emote.texture.value().substringAfterLast("/").removeSuffix(".png") }?.let { resourcePack.texture(Texture.texture(emote.texture, Writable.file(it))) - } ?: if (emojyConfig.debug) logWarn("Could not find texture for ${emote.id}") else {} + } ?: emojy.logger.d("Could not find texture for ${emote.id}") } emojy.gifs.forEach { it.generateSplitGif(resourcePack) @@ -58,7 +58,7 @@ object EmojyGenerator { Texture.texture(Key.key("${framePath.asString()}${it.name}"), Writable.file(it)) }?.forEach(resourcePack::texture) }.onFailure { - if (emojyConfig.debug) logError("Could not generate split gif for ${id}.gif: ${it.message}") + emojy.logger.d("Could not generate split gif for ${id}.gif: ${it.message}") } } } diff --git a/core/src/main/kotlin/com/mineinabyss/emojy/EmojyHelpers.kt b/core/src/main/kotlin/com/mineinabyss/emojy/EmojyHelpers.kt index 17af042..6b9a6e1 100644 --- a/core/src/main/kotlin/com/mineinabyss/emojy/EmojyHelpers.kt +++ b/core/src/main/kotlin/com/mineinabyss/emojy/EmojyHelpers.kt @@ -9,28 +9,76 @@ import net.kyori.adventure.text.Component import net.kyori.adventure.text.TextReplacementConfig import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer import net.kyori.adventure.translation.GlobalTranslator +import org.bukkit.NamespacedKey import org.bukkit.entity.Player import java.util.* -fun Component.transform(player: Player?, insert: Boolean, unescape: Boolean = true) = player?.let { escapeEmoteIDs(it) } ?: transformEmoteIDs(player, insert, unescape) +val spaceRegex: Regex = "(? + + val colorable = colorableRegex in match.value + val bitmapIndex = bitmapIndexRegex.find(match.value)?.groupValues?.get(1)?.toIntOrNull() ?: -1 + + component = component.replaceText( + TextReplacementConfig.builder() + .match(emote.baseRegex.pattern).once() + .replacement( + emote.formattedUnicode( + insert = insert, + colorable = colorable, + bitmapIndex = bitmapIndex + ) + ) + .build() + ) + } + + for (gif in emojy.gifs) gif.baseRegex.findAll(serialized).forEach { _ -> + component = component.replaceText( + TextReplacementConfig.builder() + .match(gif.baseRegex.pattern).once() + .replacement(gif.formattedUnicode(insert = false)) + .build() + ) + } + + spaceRegex.findAll(serialized).forEach { match -> + val space = match.groupValues[1].toIntOrNull() ?: return@forEach + val spaceRegex = "(? if (emote.checkPermission(player)) return@forEach - msg = msg.replaceText( + component = component.replaceText( TextReplacementConfig.builder() .matchLiteral(match.value).once() .replacement("\\${match.value}".miniMsg()) @@ -51,7 +99,7 @@ fun Component.escapeEmoteIDs(player: Player?): Component { for (gif in emojy.gifs) gif.baseRegex.findAll(serialized).forEach { match -> if (gif.checkPermission(player)) return@forEach - msg = msg.replaceText( + component = component.replaceText( TextReplacementConfig.builder() .matchLiteral(match.value).once() .replacement("\\${match.value}".miniMsg()) @@ -63,7 +111,7 @@ fun Component.escapeEmoteIDs(player: Player?): Component { if (player?.hasPermission(SPACE_PERMISSION) != false) return@forEach val space = match.groupValues[1].toIntOrNull() ?: return@forEach - msg = msg.replaceText( + component = component.replaceText( TextReplacementConfig.builder() .matchLiteral(match.value).once() .replacement("\\:space_$space:".miniMsg()) @@ -71,75 +119,33 @@ fun Component.escapeEmoteIDs(player: Player?): Component { ) } - return msg + return component } -/** - * Formats emote-ids in a component to their unicode representation, ignoring escaped emote-ids. - * This is because we handle with a player-context first, and escape that in-which should not be formatted. - */ -fun Component.transformEmoteIDs(player: Player?, insert: Boolean = true, unescape: Boolean): Component { - val locale = emojy.languages.find { it.locale == player?.locale() }?.locale ?: Locale.US - var msg = GlobalTranslator.render(this, locale) +fun Component.unescapeEmoteIds(): Component { + var component = this val serialized = this.serialize() - for (emote in emojy.emotes) { - emote.baseRegex.findAll(serialized).forEach { match -> - - val colorable = colorableRegex in match.value - val bitmapIndex = bitmapIndexRegex.find(match.value)?.groupValues?.get(1)?.toIntOrNull() ?: -1 - - msg = msg.replaceText( - TextReplacementConfig.builder() - .match(emote.baseRegex.pattern).once() - .replacement(emote.formattedUnicode(insert = insert, colorable = colorable, bitmapIndex = bitmapIndex)) - .build() - ) - } - - if (unescape) emote.escapedRegex.findAll(serialized).forEach { match -> - msg = msg.replaceText( - TextReplacementConfig.builder() - .match(emote.escapedRegex.pattern).once() - .replacement(match.value.removePrefix("\\")) - .build() - ) - } - } - - for (gif in emojy.gifs) { - gif.baseRegex.findAll(serialized).forEach { match -> - msg = msg.replaceText( - TextReplacementConfig.builder() - .match(gif.baseRegex.pattern).once() - .replacement(gif.formattedUnicode(insert = insert)) - .build() - ) - } - - if (unescape) gif.escapedRegex.findAll(serialized).forEach { match -> - msg = msg.replaceText( - TextReplacementConfig.builder() - .match(gif.escapedRegex.pattern).once() - .replacement(match.value.removePrefix("\\")) - .build() - ) - } + for (emote in emojy.emotes) emote.escapedRegex.findAll(serialized).forEach { match -> + component = component.replaceText( + TextReplacementConfig.builder() + .match(emote.escapedRegex.pattern).once() + .replacement(match.value.removePrefix("\\")) + .build() + ) } - spaceRegex.findAll(serialized).forEach { match -> - val space = match.groupValues[1].toIntOrNull() ?: return@forEach - val spaceRegex = "(? + component = component.replaceText( TextReplacementConfig.builder() - .match(spaceRegex.pattern).once() - .replacement(spaceComponent(space)) + .match(gif.escapedRegex.pattern).once() + .replacement(match.value.removePrefix("\\")) .build() ) } - if (unescape) escapedSpaceRegex.findAll(serialized).forEach { match -> - msg = msg.replaceText( + escapedSpaceRegex.findAll(serialized).forEach { match -> + component = component.replaceText( TextReplacementConfig.builder() .match(match.value).once() .replacement(match.value.removePrefix("\\")) @@ -147,7 +153,5 @@ fun Component.transformEmoteIDs(player: Player?, insert: Boolean = true, unescap ) } - return msg + return component } - -fun spaceComponent(space: Int) = Component.textOfChildren(Component.text(Space.of(space)).font(emojyConfig.spaceFont)) diff --git a/core/src/main/kotlin/com/mineinabyss/emojy/EmojyListener.kt b/core/src/main/kotlin/com/mineinabyss/emojy/EmojyListener.kt deleted file mode 100644 index 6324a9b..0000000 --- a/core/src/main/kotlin/com/mineinabyss/emojy/EmojyListener.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.mineinabyss.emojy - -import com.mineinabyss.emojy.nms.EmojyNMSHandlers -import com.mineinabyss.idofront.items.editItemMeta -import com.mineinabyss.idofront.messaging.logError -import com.mineinabyss.idofront.messaging.logVal -import com.mineinabyss.idofront.textcomponents.miniMsg -import com.mineinabyss.idofront.textcomponents.serialize -import io.papermc.paper.event.player.AsyncChatDecorateEvent -import io.papermc.paper.event.player.AsyncChatEvent -import net.kyori.adventure.text.Component -import org.bukkit.Bukkit -import org.bukkit.entity.Player -import org.bukkit.event.EventHandler -import org.bukkit.event.EventPriority -import org.bukkit.event.Listener -import org.bukkit.event.inventory.PrepareAnvilEvent -import org.bukkit.event.player.PlayerJoinEvent -import org.bukkit.inventory.AnvilInventory - -@Suppress("UnstableApiUsage") -class EmojyListener : Listener { - - @EventHandler - fun PlayerJoinEvent.injectPlayer() { - emojy.handler.inject(player) - } - - // Replace with result not original message to avoid borking other chat formatting - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) - fun AsyncChatDecorateEvent.onPlayerChat() { - result(result().transform(player(), true, true)) - } - - @EventHandler - fun PrepareAnvilEvent.onAnvil() { - if (result?.itemMeta?.hasDisplayName() != true) return - val inventory = inventory as? AnvilInventory ?: return - val displayName = ((inventory.renameText?.miniMsg() ?: inventory.firstItem?.itemMeta?.displayName())?.transform(inventory.viewers.firstOrNull() as? Player, false)) - result = result?.editItemMeta { - displayName(displayName) - } - } -} - diff --git a/core/src/main/kotlin/com/mineinabyss/emojy/EmojyPlugin.kt b/core/src/main/kotlin/com/mineinabyss/emojy/EmojyPlugin.kt index e579ee6..7663aec 100644 --- a/core/src/main/kotlin/com/mineinabyss/emojy/EmojyPlugin.kt +++ b/core/src/main/kotlin/com/mineinabyss/emojy/EmojyPlugin.kt @@ -10,6 +10,10 @@ import com.mineinabyss.emojy.translator.EmojyLanguage import com.mineinabyss.emojy.translator.EmojyTranslator import com.mineinabyss.idofront.config.config import com.mineinabyss.idofront.di.DI +import com.mineinabyss.idofront.messaging.ComponentLogger +import com.mineinabyss.idofront.messaging.injectLogger +import com.mineinabyss.idofront.messaging.observeLogger +import com.mineinabyss.idofront.plugin.dataPath import com.mineinabyss.idofront.plugin.listeners import net.kyori.adventure.key.Key import net.kyori.adventure.translation.GlobalTranslator @@ -31,39 +35,30 @@ class EmojyPlugin : JavaPlugin() { EmojyGenerator.generateResourcePack() - listeners(EmojyListener()) - - server.onlinePlayers.forEach { - emojy.handler.inject(it) - } - EmojyCommands() } - override fun onDisable() { - server.onlinePlayers.forEach { - emojy.handler.uninject(it) - } - } - fun createEmojyContext() { DI.remove() - DI.add(config("config", dataFolder.toPath(), EmojyConfig()).getOrLoad()) + DI.add(config("config", dataPath, EmojyConfig(), onLoad = { + this@EmojyPlugin.injectLogger(ComponentLogger.forPlugin(this@EmojyPlugin, it.logLevel)) + }).getOrLoad()) DI.remove() - DI.add(config("templates", dataFolder.toPath(), EmojyTemplates()).getOrLoad()) + DI.add(config("templates", dataPath, EmojyTemplates()).getOrLoad()) DI.remove() DI.add(object : EmojyContext { override val plugin: EmojyPlugin = this@EmojyPlugin - override val emotes: Set = config("emotes", dataFolder.toPath(), Emotes()).getOrLoad().emotes - override val gifs: Set = config("gifs", dataFolder.toPath(), Gifs()).getOrLoad().gifs + override val emotes: Set = config("emotes", dataPath, Emotes()).getOrLoad().emotes + override val gifs: Set = config("gifs", dataPath, Gifs()).getOrLoad().gifs override val languages: Set = emojyConfig.supportedLanguages.map { EmojyLanguage(it.split("_").let { l -> Locale(l.first(), l.last().uppercase()) }, - config>(it, dataFolder.toPath() / "languages", mapOf()).getOrLoad()) + config>(it, dataPath / "languages", mapOf()).getOrLoad()) }.toSet() - override val handler: IEmojyNMSHandler = EmojyNMSHandlers.setup() + override val logger by plugin.observeLogger() + override val handler: IEmojyNMSHandler = EmojyNMSHandlers.setup(this@EmojyPlugin) }) GlobalTranslator.translator().sources().filter { it.name() == EmojyTranslator.key }.forEach(GlobalTranslator.translator()::removeSource) diff --git a/core/src/main/kotlin/com/mineinabyss/emojy/config/EmojyConfig.kt b/core/src/main/kotlin/com/mineinabyss/emojy/config/EmojyConfig.kt index 446b88f..5c1e87a 100644 --- a/core/src/main/kotlin/com/mineinabyss/emojy/config/EmojyConfig.kt +++ b/core/src/main/kotlin/com/mineinabyss/emojy/config/EmojyConfig.kt @@ -2,6 +2,7 @@ package com.mineinabyss.emojy.config +import co.touchlab.kermit.Severity import com.mineinabyss.emojy.* import com.mineinabyss.idofront.messaging.* import com.mineinabyss.idofront.serialization.KeySerializer @@ -39,7 +40,7 @@ data class EmojyConfig( val requirePermissions: Boolean = true, val supportForceUnicode: Boolean = true, - val debug: Boolean = true, + val logLevel: Severity = Severity.Debug, val emojyList: EmojyList = EmojyList(), val supportedLanguages: Set = mutableSetOf("en_us"), ) { @@ -206,7 +207,7 @@ data class Gifs(val gifs: Set = mutableSetOf()) { aspectRatio = reader.getAspectRatio(0) reader.getNumImages(true) }.onFailure { - if (emojyConfig.debug) logError("Could not get frame count for ${id}.gif") + emojy.logger.d("Could not get frame count for ${id}.gif") }.getOrNull() ?: run { aspectRatio = 1f 0 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..bd7358a 100644 --- a/core/src/main/kotlin/com/mineinabyss/emojy/nms/EmojyNMSHandlers.kt +++ b/core/src/main/kotlin/com/mineinabyss/emojy/nms/EmojyNMSHandlers.kt @@ -1,69 +1,19 @@ package com.mineinabyss.emojy.nms -import com.google.gson.JsonObject -import com.google.gson.JsonParser -import com.mineinabyss.emojy.escapeEmoteIDs -import com.mineinabyss.emojy.transform -import com.mineinabyss.emojy.transformEmoteIDs -import com.mineinabyss.idofront.textcomponents.miniMsg -import com.mineinabyss.idofront.textcomponents.serialize -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer -import org.bukkit.entity.Player +import com.mineinabyss.emojy.EmojyPlugin 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_20_R4") - fun setup(): IEmojyNMSHandler { + fun setup(emojy: EmojyPlugin): IEmojyNMSHandler { SUPPORTED_VERSION.forEach { version -> runCatching { - Class.forName("org.bukkit.craftbukkit.$version.entity.CraftPlayer") - return Class.forName("com.mineinabyss.emojy.nms.${version}.EmojyNMSHandler").getConstructor() - .newInstance() as IEmojyNMSHandler - } + return Class.forName("com.mineinabyss.emojy.nms.${version}.EmojyNMSHandler").getConstructor(EmojyPlugin::class.java) + .newInstance(emojy) as IEmojyNMSHandler + }.onFailure { it.printStackTrace() } } throw IllegalStateException("Unsupported server version") } - - private val gson = GsonComponentSerializer.gson() - private val plain = PlainTextComponentSerializer.plainText() - //TODO toPlainText fixes the anvil issue with tags being escaped - // It does break all other formatting everywhere else though by removing tags - // serialize() doesnt but keeps the escaped tag from gson - // anvil goes like this, inputItem -> readUtf -> renameField -> writeNbt from raw string, which is why it breaks - fun JsonObject.formatString(player: Player? = null) : String { - return if (this.has("args") || this.has("text") || this.has("extra") || this.has("translate")) { - gson.serialize(gson.deserializeFromTree(this)/*.toPlainText()*/.serialize().miniMsg().transform(player, true, true)) - } else this.toString() - } - - fun writeTransformer(player: Player?, insert: Boolean, unescape: Boolean) = { string: String -> - runCatching { - val jsonObject = JsonParser.parseString(string).takeIf { it.isJsonObject }?.asJsonObject ?: return@runCatching string - if (jsonObject.has("args") || jsonObject.has("text") || jsonObject.has("extra") || jsonObject.has("translate")) { - val formatted = gson.deserializeFromTree(jsonObject).transformEmoteIDs(player, insert, unescape) - gson.serialize(formatted) - } else string - }.getOrNull() ?: string - } - - fun readTransformer(player: Player?) = { string: String -> - runCatching { - val jsonObject = JsonParser.parseString(string).takeIf { it.isJsonObject }?.asJsonObject ?: return@runCatching string - if (jsonObject.has("args") || jsonObject.has("text") || jsonObject.has("extra") || jsonObject.has("translate")) { - val formatted = gson.deserializeFromTree(jsonObject).escapeEmoteIDs(player) - gson.serialize(formatted) - } else string - }.getOrNull() ?: string - } - - fun transformer(player: Player? = null) = { string: String -> - runCatching { - val element = JsonParser.parseString(string) - if (element.isJsonObject) element.asJsonObject.formatString(player) - else string - }.getOrNull() ?: string - } } diff --git a/core/src/main/kotlin/com/mineinabyss/emojy/nms/IEmojyNMSHandler.kt b/core/src/main/kotlin/com/mineinabyss/emojy/nms/IEmojyNMSHandler.kt index a25cb37..961e8c8 100644 --- a/core/src/main/kotlin/com/mineinabyss/emojy/nms/IEmojyNMSHandler.kt +++ b/core/src/main/kotlin/com/mineinabyss/emojy/nms/IEmojyNMSHandler.kt @@ -1,12 +1,6 @@ package com.mineinabyss.emojy.nms -import org.bukkit.entity.Player - interface IEmojyNMSHandler { - fun inject(player: Player) - - fun uninject(player: Player) - val supported get() = false } diff --git a/gradle.properties b/gradle.properties index e409055..e14b3ee 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ group=com.mineinabyss -version=0.8 -idofrontVersion=0.21.2 +version=0.9 +idofrontVersion=0.24.0 diff --git a/gradle/emojyLibs.toml b/gradle/libs.versions.toml similarity index 100% rename from gradle/emojyLibs.toml rename to gradle/libs.versions.toml 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 58b5595..f8e20d7 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,15 +24,15 @@ dependencyResolutionManagement { repositories { maven("https://repo.mineinabyss.com/releases") + maven("https://repo.mineinabyss.com/snapshots") mavenLocal() } versionCatalogs { - create("libs"){ + create("idofrontLibs"){ from("com.mineinabyss:catalog:$idofrontVersion") } - create("emojyLibs").from(files("gradle/emojyLibs.toml")) } } -include("core", "v1_19_R1", "v1_19_R2", "v1_19_R3", "v1_20_R1", "v1_20_R2", "v1_20_R3") +include("core", "v1_20_R4") diff --git a/v1_19_R1/build.gradle.kts b/v1_19_R1/build.gradle.kts deleted file mode 100644 index 8798848..0000000 --- a/v1_19_R1/build.gradle.kts +++ /dev/null @@ -1,43 +0,0 @@ -plugins { - id("com.mineinabyss.conventions.kotlin.jvm") - id("com.mineinabyss.conventions.autoversion") - id("io.papermc.paperweight.userdev") version "1.5.11" -} - -repositories { - gradlePluginPortal() - maven("https://repo.mineinabyss.com/releases") - maven("https://repo.papermc.io/repository/maven-public/") - google() -} - -dependencies { - // MineInAbyss platform - compileOnly(libs.kotlinx.serialization.json) - compileOnly(libs.kotlinx.serialization.kaml) - compileOnly(libs.kotlinx.coroutines) - compileOnly(libs.minecraft.mccoroutine) - - // Shaded - implementation(libs.bundles.idofront.core) - implementation(project(":core")) - paperweight.paperDevBundle("1.19.2-R0.1-SNAPSHOT") //NMS -} - -tasks { - - build { - dependsOn(reobfJar) - } - - compileJava { - options.encoding = Charsets.UTF_8.name() - options.release.set(17) - } - javadoc { - options.encoding = Charsets.UTF_8.name() - } - processResources { - filteringCharset = Charsets.UTF_8.name() - } -} diff --git a/v1_19_R1/src/main/kotlin/com/mineinabyss/emojy/nms/v1_19_R1/EmojyNMSHandler.kt b/v1_19_R1/src/main/kotlin/com/mineinabyss/emojy/nms/v1_19_R1/EmojyNMSHandler.kt deleted file mode 100644 index ba16ce9..0000000 --- a/v1_19_R1/src/main/kotlin/com/mineinabyss/emojy/nms/v1_19_R1/EmojyNMSHandler.kt +++ /dev/null @@ -1,247 +0,0 @@ -@file:Suppress("unused") - -package com.mineinabyss.emojy.nms.v1_19_R1 - -import com.github.shynixn.mccoroutine.bukkit.launch -import com.github.shynixn.mccoroutine.bukkit.minecraftDispatcher -import com.google.gson.JsonObject -import com.google.gson.JsonParser -import com.mineinabyss.emojy.emojy -import com.mineinabyss.emojy.legacy -import com.mineinabyss.emojy.nms.IEmojyNMSHandler -import com.mineinabyss.emojy.transform -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.kyori.adventure.text.serializer.gson.GsonComponentSerializer -import net.minecraft.nbt.CompoundTag -import net.minecraft.nbt.ListTag -import net.minecraft.nbt.StringTag -import net.minecraft.network.* -import net.minecraft.network.protocol.Packet -import net.minecraft.network.protocol.PacketFlow -import net.minecraft.server.MinecraftServer -import net.minecraft.server.network.ServerConnectionListener -import org.bukkit.Bukkit -import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer -import org.bukkit.entity.Player -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: List = 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) { - try { - // This can take a while, so we need to stop the main thread from interfering - synchronized(connections) { - // Stop injecting channels - channel.eventLoop().submit { channel.inject() } - } - } catch (e: java.lang.Exception) { - e.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) - } - } - - try { - bind(channelFutures, serverChannelHandler) - } catch (e: IllegalArgumentException) { - emojy.plugin.launch(emojy.plugin.minecraftDispatcher) { - bind(channelFutures, serverChannelHandler) - } - } - } - - 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") !is CustomPacketEncoder) - encoder[this] = this.pipeline().replace("encoder", "encoder", CustomPacketEncoder(player)) - - if (this !in decoder.keys && this.pipeline().get("decoder") !is CustomPacketDecoder) - decoder[this] = this.pipeline().replace("decoder", "decoder", CustomPacketDecoder(player)) - - } - - 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 fun Channel.uninject() { - if (this in encoder.keys) { - val prevHandler = encoder.remove(this) - val handler = if (prevHandler is PacketEncoder) PacketEncoder(PacketFlow.CLIENTBOUND) 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(PacketFlow.SERVERBOUND) else prevHandler - handler?.let { this.pipeline().replace("decoder", "decoder", handler) } - } - } - - private class CustomPacketEncoder(val player: Player?) : MessageToByteEncoder>() { - private val protocolDirection = PacketFlow.CLIENTBOUND - - override fun encode(ctx: ChannelHandlerContext, msg: Packet<*>, out: ByteBuf) { - val enumProt = ctx.channel()?.attr(Connection.ATTRIBUTE_PROTOCOL)?.get() - ?: throw RuntimeException("ConnectionProtocol unknown: $out") - val int = msg.let { enumProt.getPacketId(this.protocolDirection, it) } - ?: throw IOException("Can't serialize unregistered packet") - val packetDataSerializer: FriendlyByteBuf = CustomDataSerializer(player, out) - packetDataSerializer.writeVarInt(int) - - try { - val int2 = packetDataSerializer.writerIndex() - msg.write(packetDataSerializer) - val int3 = packetDataSerializer.writerIndex() - int2 - if (int3 > 8388608) { - throw IllegalArgumentException("Packet too big (is $int3, should be less than 8388608): $msg") - } - } catch (e: Exception) { - if (msg.isSkippable) - throw SkipPacketException(e) - throw e - } - } - } - - private class CustomPacketDecoder(val player: Player?) : ByteToMessageDecoder() { - - override fun decode(ctx: ChannelHandlerContext, msg: ByteBuf, out: MutableList) { - if (msg.readableBytes() == 0) return - - val dataSerializer = CustomDataSerializer(player, msg) - val packetID = dataSerializer.readVarInt() - val packet = ctx.channel().attr(Connection.ATTRIBUTE_PROTOCOL).get() - .createPacket(PacketFlow.SERVERBOUND, packetID, dataSerializer) - ?: throw IOException("Bad packet id $packetID") - - if (dataSerializer.readableBytes() > 0) { - throw IOException("Packet $packetID ($packet) was larger than I expected, found ${dataSerializer.readableBytes()} bytes extra whilst reading packet $packetID") - } - out.add(packet) - } - } - - private class CustomDataSerializer(val player: Player?, bytebuf: ByteBuf) : FriendlyByteBuf(bytebuf) { - val gson = GsonComponentSerializer.gson() - - override fun writeUtf(string: String, maxLength: Int): FriendlyByteBuf { - runCatching { - val element = JsonParser.parseString(string) - if (element.isJsonObject) - return super.writeUtf(element.asJsonObject.returnFormattedString(), maxLength) - } - - return super.writeUtf(string, maxLength) - } - - override fun writeNbt(compound: CompoundTag?): FriendlyByteBuf { - return super.writeNbt(compound?.apply { - transform(this, Function { string: String -> - try { - val element = JsonParser.parseString(string) - if (element.isJsonObject) - return@Function element.asJsonObject.returnFormattedString() - } catch (ignored: Exception) { - } - string - }) - }) - } - - private fun JsonObject.returnFormattedString(insert: Boolean = true): String { - return if (this.has("args") || this.has("text") || this.has("extra") || this.has("translate")) { - gson.serialize(gson.deserialize(this.toString()).transform(null, insert)) - } else this.toString() - } - - 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) { - for (base in list) when (base) { - is CompoundTag -> transform(base, transformer) - is ListTag -> transform(base, transformer) - is StringTag -> list.indexOf(base).let { index -> - list.add(index, StringTag.valueOf(transformer.apply(base.asString))) - list.removeAt(index + 1) - } - } - } - - override fun readUtf(maxLength: Int): String { - return super.readUtf(maxLength).let { string -> - runCatching { string.miniMsg() }.recover { legacy.deserialize(string) } - .getOrNull()?.transform(player, true)?.serialize() ?: string - } - } - - - - } - - override val supported get() = true -} diff --git a/v1_19_R2/build.gradle.kts b/v1_19_R2/build.gradle.kts deleted file mode 100644 index 478258e..0000000 --- a/v1_19_R2/build.gradle.kts +++ /dev/null @@ -1,43 +0,0 @@ -plugins { - id("com.mineinabyss.conventions.kotlin.jvm") - id("com.mineinabyss.conventions.autoversion") - id("io.papermc.paperweight.userdev") version "1.5.11" -} - -repositories { - gradlePluginPortal() - maven("https://repo.mineinabyss.com/releases") - maven("https://repo.papermc.io/repository/maven-public/") - google() -} - -dependencies { - // MineInAbyss platform - compileOnly(libs.kotlinx.serialization.json) - compileOnly(libs.kotlinx.serialization.kaml) - compileOnly(libs.kotlinx.coroutines) - compileOnly(libs.minecraft.mccoroutine) - - // Shaded - implementation(libs.bundles.idofront.core) - implementation(project(":core")) - paperweight.paperDevBundle("1.19.3-R0.1-SNAPSHOT") //NMS -} - -tasks { - - build { - dependsOn(reobfJar) - } - - compileJava { - options.encoding = Charsets.UTF_8.name() - options.release.set(17) - } - javadoc { - options.encoding = Charsets.UTF_8.name() - } - processResources { - filteringCharset = Charsets.UTF_8.name() - } -} diff --git a/v1_19_R2/src/main/kotlin/com/mineinabyss/emojy/nms/v1_19_R2/EmojyNMSHandler.kt b/v1_19_R2/src/main/kotlin/com/mineinabyss/emojy/nms/v1_19_R2/EmojyNMSHandler.kt deleted file mode 100644 index 4faf8d1..0000000 --- a/v1_19_R2/src/main/kotlin/com/mineinabyss/emojy/nms/v1_19_R2/EmojyNMSHandler.kt +++ /dev/null @@ -1,249 +0,0 @@ -@file:Suppress("unused") - -package com.mineinabyss.emojy.nms.v1_19_R2 - -import com.github.shynixn.mccoroutine.bukkit.launch -import com.github.shynixn.mccoroutine.bukkit.minecraftDispatcher -import com.google.gson.JsonObject -import com.google.gson.JsonParser -import com.mineinabyss.emojy.emojy -import com.mineinabyss.emojy.legacy -import com.mineinabyss.emojy.nms.IEmojyNMSHandler -import com.mineinabyss.emojy.transform -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.kyori.adventure.text.serializer.gson.GsonComponentSerializer -import net.minecraft.nbt.CompoundTag -import net.minecraft.nbt.ListTag -import net.minecraft.nbt.StringTag -import net.minecraft.network.* -import net.minecraft.network.protocol.Packet -import net.minecraft.network.protocol.PacketFlow -import net.minecraft.server.MinecraftServer -import net.minecraft.server.network.ServerConnectionListener -import org.bukkit.Bukkit -import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer -import org.bukkit.entity.Player -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: List = 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) { - try { - // This can take a while, so we need to stop the main thread from interfering - synchronized(connections) { - // Stop injecting channels - channel.eventLoop().submit { channel.inject() } - } - } catch (e: java.lang.Exception) { - e.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) - } - } - - try { - bind(channelFutures, serverChannelHandler) - } catch (e: IllegalArgumentException) { - emojy.plugin.launch(emojy.plugin.minecraftDispatcher) { - bind(channelFutures, serverChannelHandler) - } - } - } - - 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") !is CustomPacketEncoder) - encoder[this] = this.pipeline().replace("encoder", "encoder", CustomPacketEncoder(player)) - - if (this !in decoder.keys && this.pipeline().get("decoder") !is CustomPacketDecoder) - decoder[this] = this.pipeline().replace("decoder", "decoder", CustomPacketDecoder(player)) - - } - - 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 fun Channel.uninject() { - if (this in encoder.keys) { - val prevHandler = encoder.remove(this) - val handler = if (prevHandler is PacketEncoder) PacketEncoder(PacketFlow.CLIENTBOUND) 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(PacketFlow.SERVERBOUND) else prevHandler - handler?.let { this.pipeline().replace("decoder", "decoder", handler) } - } - } - - private class CustomPacketEncoder(val player: Player?) : MessageToByteEncoder>() { - private val protocolDirection = PacketFlow.CLIENTBOUND - - override fun encode(ctx: ChannelHandlerContext, msg: Packet<*>, out: ByteBuf) { - val enumProt = ctx.channel()?.attr(Connection.ATTRIBUTE_PROTOCOL)?.get() - ?: throw RuntimeException("ConnectionProtocol unknown: $out") - val int = msg.let { enumProt.getPacketId(this.protocolDirection, it) } - ?: throw IOException("Can't serialize unregistered packet") - val packetDataSerializer: FriendlyByteBuf = CustomDataSerializer(player, out) - packetDataSerializer.writeVarInt(int) - - try { - val int2 = packetDataSerializer.writerIndex() - msg.write(packetDataSerializer) - val int3 = packetDataSerializer.writerIndex() - int2 - if (int3 > 8388608) { - throw IllegalArgumentException("Packet too big (is $int3, should be less than 8388608): $msg") - } - } catch (e: Exception) { - if (msg.isSkippable) - throw SkipPacketException(e) - throw e - } - } - } - - private class CustomPacketDecoder(val player: Player?) : ByteToMessageDecoder() { - - override fun decode(ctx: ChannelHandlerContext, msg: ByteBuf, out: MutableList) { - if (msg.readableBytes() == 0) return - - val dataSerializer = CustomDataSerializer(player, msg) - val packetID = dataSerializer.readVarInt() - val packet = ctx.channel().attr(Connection.ATTRIBUTE_PROTOCOL).get() - .createPacket(PacketFlow.SERVERBOUND, packetID, dataSerializer) - ?: throw IOException("Bad packet id $packetID") - - if (dataSerializer.readableBytes() > 0) { - throw IOException("Packet $packetID ($packet) was larger than I expected, found ${dataSerializer.readableBytes()} bytes extra whilst reading packet $packetID") - } - out.add(packet) - } - } - - private class CustomDataSerializer(val player: Player?, bytebuf: ByteBuf) : FriendlyByteBuf(bytebuf) { - val gson = GsonComponentSerializer.gson() - - override fun writeUtf(string: String, maxLength: Int): FriendlyByteBuf { - try { - val element = JsonParser.parseString(string) - if (element.isJsonObject) - return super.writeUtf(element.asJsonObject.returnFormattedString(), maxLength) - } catch (_: Exception) { - } - - return super.writeUtf(string, maxLength) - } - - override fun writeNbt(compound: CompoundTag?): FriendlyByteBuf { - return super.writeNbt(compound?.apply { - transform(this, Function { string: String -> - try { - val element = JsonParser.parseString(string) - if (element.isJsonObject) - return@Function element.asJsonObject.returnFormattedString() - } catch (ignored: Exception) { - } - string - }) - }) - } - - private fun JsonObject.returnFormattedString(): String { - return if (this.has("args") || this.has("text") || this.has("extra") || this.has("translate")) { - gson.serialize(gson.deserialize(this.toString()).transform(null, true)) - } else this.toString() - } - - 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) { - for (base in list) when (base) { - is CompoundTag -> transform(base, transformer) - is ListTag -> transform(base, transformer) - is StringTag -> list.indexOf(base).let { index -> - list.add(index, StringTag.valueOf(transformer.apply(base.asString))) - list.removeAt(index + 1) - } - } - } - - override fun readUtf(maxLength: Int): String { - return super.readUtf(maxLength).let { string -> - runCatching { string.miniMsg() }.recover { legacy.deserialize(string) } - .getOrNull()?.transform(player, true)?.serialize() ?: string - } - } - - } - - override val supported get() = true -} diff --git a/v1_19_R3/build.gradle.kts b/v1_19_R3/build.gradle.kts deleted file mode 100644 index 1626f96..0000000 --- a/v1_19_R3/build.gradle.kts +++ /dev/null @@ -1,43 +0,0 @@ -plugins { - id("com.mineinabyss.conventions.kotlin.jvm") - id("com.mineinabyss.conventions.autoversion") - id("io.papermc.paperweight.userdev") version "1.5.11" -} - -repositories { - gradlePluginPortal() - maven("https://repo.mineinabyss.com/releases") - maven("https://repo.papermc.io/repository/maven-public/") - google() -} - -dependencies { - // MineInAbyss platform - compileOnly(libs.kotlinx.serialization.json) - compileOnly(libs.kotlinx.serialization.kaml) - compileOnly(libs.kotlinx.coroutines) - compileOnly(libs.minecraft.mccoroutine) - - // Shaded - implementation(libs.bundles.idofront.core) - implementation(project(":core")) - paperweight.paperDevBundle("1.19.4-R0.1-SNAPSHOT") //NMS -} - -tasks { - - build { - dependsOn(reobfJar) - } - - compileJava { - options.encoding = Charsets.UTF_8.name() - options.release.set(17) - } - javadoc { - options.encoding = Charsets.UTF_8.name() - } - processResources { - filteringCharset = Charsets.UTF_8.name() - } -} diff --git a/v1_19_R3/src/main/kotlin/com/mineinabyss/emojy/nms/v1_19_R3/EmojyNMSHandler.kt b/v1_19_R3/src/main/kotlin/com/mineinabyss/emojy/nms/v1_19_R3/EmojyNMSHandler.kt deleted file mode 100644 index 741324e..0000000 --- a/v1_19_R3/src/main/kotlin/com/mineinabyss/emojy/nms/v1_19_R3/EmojyNMSHandler.kt +++ /dev/null @@ -1,264 +0,0 @@ -@file:Suppress("unused") - -package com.mineinabyss.emojy.nms.v1_19_R3 - -import com.github.shynixn.mccoroutine.bukkit.launch -import com.github.shynixn.mccoroutine.bukkit.minecraftDispatcher -import com.google.gson.JsonObject -import com.google.gson.JsonParser -import com.mineinabyss.emojy.emojy -import com.mineinabyss.emojy.legacy -import com.mineinabyss.emojy.nms.IEmojyNMSHandler -import com.mineinabyss.emojy.transform -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.kyori.adventure.text.serializer.gson.GsonComponentSerializer -import net.minecraft.nbt.CompoundTag -import net.minecraft.nbt.ListTag -import net.minecraft.nbt.StringTag -import net.minecraft.network.Connection -import net.minecraft.network.FriendlyByteBuf -import net.minecraft.network.PacketDecoder -import net.minecraft.network.PacketEncoder -import net.minecraft.network.SkipPacketException -import net.minecraft.network.protocol.Packet -import net.minecraft.network.protocol.PacketFlow -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.v1_19_R3.entity.CraftPlayer -import org.bukkit.entity.Player -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: List = 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) { - try { - // This can take a while, so we need to stop the main thread from interfering - synchronized(connections) { - // Stop injecting channels - channel.eventLoop().submit { channel.inject() } - } - } catch (e: java.lang.Exception) { - e.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) - } - } - - try { - bind(channelFutures, serverChannelHandler) - } catch (e: IllegalArgumentException) { - emojy.plugin.launch(emojy.plugin.minecraftDispatcher) { - bind(channelFutures, serverChannelHandler) - } - } - } - - private fun bind(channelFutures: List, serverChannelHandler: ChannelInboundHandlerAdapter) { - channelFutures.forEach { future -> - future.channel().pipeline().addFirst(serverChannelHandler) - } - - Bukkit.getOnlinePlayers().forEach(::inject) - } - - 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 fun Channel.inject(player: Player? = null) { - if (this !in encoder.keys && this.pipeline().get("encoder") !is CustomPacketEncoder) - encoder[this] = this.pipeline().replace("encoder", "encoder", CustomPacketEncoder(player)) - - if (this !in decoder.keys && this.pipeline().get("decoder") !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(PacketFlow.CLIENTBOUND) 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(PacketFlow.SERVERBOUND) else prevHandler - handler?.let { this.pipeline().replace("decoder", "decoder", handler) } - } - } - - private class CustomPacketEncoder(val player: Player?) : MessageToByteEncoder>() { - private val protocolDirection = PacketFlow.CLIENTBOUND - - override fun encode(ctx: ChannelHandlerContext, msg: Packet<*>, out: ByteBuf) { - val enumProt = ctx.channel()?.attr(Connection.ATTRIBUTE_PROTOCOL)?.get() - ?: throw RuntimeException("ConnectionProtocol unknown: $out") - val int = msg.let { enumProt.getPacketId(this.protocolDirection, it) } - val packetDataSerializer: FriendlyByteBuf = CustomDataSerializer(player, out) - packetDataSerializer.writeVarInt(int) - - try { - val int2 = packetDataSerializer.writerIndex() - msg.write(packetDataSerializer) - val int3 = packetDataSerializer.writerIndex() - int2 - if (int3 > 8388608) { - throw IllegalArgumentException("Packet too big (is $int3, should be less than 8388608): $msg") - } - } catch (e: Exception) { - if (msg.isSkippable) - throw SkipPacketException(e) - throw e - } - } - } - - private class CustomPacketDecoder(val player: Player?) : ByteToMessageDecoder() { - - override fun decode(ctx: ChannelHandlerContext, buffer: ByteBuf, out: MutableList) { - val buffferCopy = buffer.copy() - if (buffer.readableBytes() == 0) return - - val dataSerializer: FriendlyByteBuf = CustomDataSerializer(player, buffer) - val packetID = dataSerializer.readVarInt() - val protocol = ctx.channel().attr(Connection.ATTRIBUTE_PROTOCOL).get() - var packet = protocol.createPacket(PacketFlow.SERVERBOUND, packetID, dataSerializer) ?: throw IOException("Bad packet id $packetID") - - when { - dataSerializer.readableBytes() > 0 -> throw IOException("Packet $packetID ($packet) was larger than I expected, found ${dataSerializer.readableBytes()} bytes extra whilst reading packet $packetID") - packet is ServerboundChatPacket -> { - val serializer = FriendlyByteBuf(buffferCopy) - serializer.readVarInt() - packet = protocol.createPacket(PacketFlow.SERVERBOUND, packetID, serializer) ?: throw IOException("Bad packet id $packetID") - } - } - - out += packet - } - } - - private class CustomDataSerializer(val player: Player?, bytebuf: ByteBuf) : FriendlyByteBuf(bytebuf) { - val gson = GsonComponentSerializer.gson() - - override fun writeUtf(string: String, maxLength: Int): FriendlyByteBuf { - try { - val element = JsonParser.parseString(string) - if (element.isJsonObject) - return super.writeUtf(element.asJsonObject.returnFormattedString(), maxLength) - } catch (_: Exception) { - } - - return super.writeUtf(string, maxLength) - } - - override fun writeNbt(compound: CompoundTag?): FriendlyByteBuf { - return super.writeNbt(compound?.apply { - transform(this, Function { string: String -> - try { - val element = JsonParser.parseString(string) - if (element.isJsonObject) - return@Function element.asJsonObject.returnFormattedString() - } catch (ignored: Exception) { - } - string - }) - }) - } - - private fun JsonObject.returnFormattedString(): String { - return if (this.has("args") || this.has("text") || this.has("extra") || this.has("translate")) { - gson.serialize(gson.deserialize(this.toString()).transform(player, true)) - } else this.toString() - } - - 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) { - for (base in list) when (base) { - is CompoundTag -> transform(base, transformer) - is ListTag -> transform(base, transformer) - is StringTag -> list.indexOf(base).let { index -> - list.add(index, StringTag.valueOf(transformer.apply(base.asString))) - list.removeAt(index + 1) - } - } - } - - override fun readUtf(maxLength: Int): String { - return super.readUtf(maxLength).let { string -> - runCatching { string.miniMsg() }.recover { legacy.deserialize(string) } - .getOrNull()?.transform(player, true)?.serialize() ?: string - } - } - - - } - - override val supported get() = true -} diff --git a/v1_20_R1/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R1/EmojyNMSHandler.kt b/v1_20_R1/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R1/EmojyNMSHandler.kt deleted file mode 100644 index 83265ac..0000000 --- a/v1_20_R1/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R1/EmojyNMSHandler.kt +++ /dev/null @@ -1,239 +0,0 @@ -@file:Suppress("unused") - -package com.mineinabyss.emojy.nms.v1_20_R1 - -import com.google.gson.JsonParser -import com.mineinabyss.emojy.emojy -import com.mineinabyss.emojy.legacy -import com.mineinabyss.emojy.nms.EmojyNMSHandlers -import com.mineinabyss.emojy.nms.EmojyNMSHandlers.formatString -import com.mineinabyss.emojy.nms.IEmojyNMSHandler -import com.mineinabyss.emojy.transform -import com.mineinabyss.idofront.messaging.broadcast -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.nbt.CompoundTag -import net.minecraft.nbt.ListTag -import net.minecraft.nbt.StringTag -import net.minecraft.network.* -import net.minecraft.network.protocol.Packet -import net.minecraft.network.protocol.PacketFlow -import net.minecraft.network.protocol.game.ClientboundPlayerChatPacket -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.v1_20_R1.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(PacketFlow.CLIENTBOUND) 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(PacketFlow.SERVERBOUND) 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) : FriendlyByteBuf(bytebuf) { - override fun writeComponent(component: net.kyori.adventure.text.Component): FriendlyByteBuf { - return super.writeComponent(component.transform(null, true, true)) - } - - override fun writeUtf(string: String, maxLength: Int): FriendlyByteBuf { - runCatching { - val element = JsonParser.parseString(string) - if (element.isJsonObject) return super.writeUtf(element.asJsonObject.formatString(), maxLength) - } - - return super.writeUtf(string, maxLength) - } - - override fun writeNbt(compound: CompoundTag?): FriendlyByteBuf { - return super.writeNbt(compound?.apply { - transform(this, EmojyNMSHandlers.transformer()) - }) - } - - 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) { - for (base in list) when (base) { - is CompoundTag -> transform(base, transformer) - is ListTag -> transform(base, transformer) - is StringTag -> list.indexOf(base).let { index -> - list.add(index, StringTag.valueOf(transformer.apply(base.asString))) - list.removeAt(index + 1) - } - } - } - - override fun readUtf(maxLength: Int): String { - return super.readUtf(maxLength).let { string -> - runCatching { string.miniMsg() }.recover { legacy.deserialize(string) } - .getOrNull()?.transform(player, true)?.serialize() ?: string - } - } - - } - - private class CustomPacketEncoder(val player: Player?) : MessageToByteEncoder>() { - private val protocolDirection = PacketFlow.CLIENTBOUND - - override fun encode(ctx: ChannelHandlerContext, msg: Packet<*>, out: ByteBuf) { - val enumProt = ctx.channel()?.attr(Connection.ATTRIBUTE_PROTOCOL)?.get() ?: throw RuntimeException("ConnectionProtocol unknown: $out") - val int = msg.let { enumProt.getPacketId(this.protocolDirection, it) } - val packetDataSerializer: FriendlyByteBuf = CustomDataSerializer(player, out) - packetDataSerializer.writeVarInt(int) - - runCatching { - val int2 = packetDataSerializer.writerIndex() - msg.write(packetDataSerializer) - val int3 = packetDataSerializer.writerIndex() - int2 - if (int3 > 8388608) { - throw IllegalArgumentException("Packet too big (is $int3, should be less than 8388608): $msg") - } - }.onFailure { - if (msg.isSkippable) throw SkipPacketException(it) - throw it - } - } - } - - private class CustomPacketDecoder(val player: Player?) : ByteToMessageDecoder() { - - override fun decode(ctx: ChannelHandlerContext, buffer: ByteBuf, out: MutableList) { - val buffferCopy = buffer.copy() - if (buffer.readableBytes() == 0) return - - val customDataSerializer: FriendlyByteBuf = CustomDataSerializer(player, buffer) - val packetID = customDataSerializer.readVarInt() - val protocol = ctx.channel().attr(Connection.ATTRIBUTE_PROTOCOL).get() - val packet = protocol.createPacket(PacketFlow.SERVERBOUND, packetID, customDataSerializer) ?: throw IOException("Bad packet id $packetID") - - out += when { - customDataSerializer.readableBytes() > 0 -> throw IOException("Packet $packetID ($packet) was larger than I expected, found ${customDataSerializer.readableBytes()} bytes extra whilst reading packet $packetID") - packet is ServerboundChatPacket || packet is ClientboundPlayerChatPacket -> { - broadcast((player?.name ?: "null") + ": " + packet.javaClass.name) - val baseSerializer = FriendlyByteBuf(buffferCopy) - val basePacketId = baseSerializer.readVarInt() - protocol.createPacket(PacketFlow.SERVERBOUND, basePacketId, baseSerializer) ?: throw IOException("Bad packet id $basePacketId") - } - else -> packet - } - } - } - - override val supported get() = true -} diff --git a/v1_20_R2/build.gradle.kts b/v1_20_R2/build.gradle.kts deleted file mode 100644 index 2cb241f..0000000 --- a/v1_20_R2/build.gradle.kts +++ /dev/null @@ -1,43 +0,0 @@ -plugins { - id("com.mineinabyss.conventions.kotlin.jvm") - id("com.mineinabyss.conventions.autoversion") - id("io.papermc.paperweight.userdev") version "1.5.11" -} - -repositories { - gradlePluginPortal() - maven("https://repo.mineinabyss.com/releases") - maven("https://repo.papermc.io/repository/maven-public/") - google() -} - -dependencies { - // MineInAbyss platform - compileOnly(libs.kotlinx.serialization.json) - compileOnly(libs.kotlinx.serialization.kaml) - compileOnly(libs.kotlinx.coroutines) - compileOnly(libs.minecraft.mccoroutine) - - // Shaded - implementation(libs.bundles.idofront.core) - implementation(project(":core")) - paperweight.paperDevBundle("1.20.2-R0.1-SNAPSHOT") //NMS -} - -tasks { - - build { - dependsOn(reobfJar) - } - - compileJava { - options.encoding = Charsets.UTF_8.name() - options.release.set(17) - } - javadoc { - options.encoding = Charsets.UTF_8.name() - } - processResources { - filteringCharset = 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 deleted file mode 100644 index 8fb1d39..0000000 --- a/v1_20_R2/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R2/EmojyNMSHandler.kt +++ /dev/null @@ -1,250 +0,0 @@ -@file:Suppress("unused") - -package com.mineinabyss.emojy.nms.v1_20_R2 - -import com.google.gson.JsonParser -import com.mineinabyss.emojy.emojy -import com.mineinabyss.emojy.legacy -import com.mineinabyss.emojy.nms.EmojyNMSHandlers -import com.mineinabyss.emojy.nms.EmojyNMSHandlers.formatString -import com.mineinabyss.emojy.nms.IEmojyNMSHandler -import com.mineinabyss.emojy.transform -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 io.papermc.paper.adventure.PaperAdventure -import net.kyori.adventure.text.Component -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.ServerboundChatPacket -import net.minecraft.server.MinecraftServer -import net.minecraft.server.network.ServerConnectionListener -import org.bukkit.Bukkit -import org.bukkit.craftbukkit.v1_20_R2.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(Connection.ATTRIBUTE_CLIENTBOUND_PROTOCOL) 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(Connection.ATTRIBUTE_SERVERBOUND_PROTOCOL) 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) : FriendlyByteBuf(bytebuf) { - override fun writeComponent(component: Component): FriendlyByteBuf { - return super.writeComponent(component.transform(null, true, false)) - } - - override fun readComponent(): net.minecraft.network.chat.Component { - return PaperAdventure.asVanilla(PaperAdventure.asAdventure(super.readComponent()).transform(player, true, false)) - } - - override fun writeUtf(string: String, maxLength: Int): FriendlyByteBuf { - runCatching { - val element = JsonParser.parseString(string) - if (element.isJsonObject) return super.writeUtf(element.asJsonObject.formatString(), maxLength) - } - - return super.writeUtf(string, maxLength) - } - - override fun readUtf(maxLength: Int): String { - return super.readUtf(maxLength).let { string -> - runCatching { string.miniMsg() }.recover { legacy.deserialize(string) } - .getOrNull()?.transform(player, true)?.serialize() ?: string - } - } - - override fun writeNbt(compound: Tag?): FriendlyByteBuf { - return super.writeNbt(compound?.apply { - transform(this as CompoundTag, EmojyNMSHandlers.transformer()) - }) - } - - override fun readNbt(): CompoundTag? { - return super.readNbt()?.apply { - transform(this, EmojyNMSHandlers.transformer(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 class CustomPacketEncoder(val player: Player? = null) : MessageToByteEncoder>() { - private val protocolDirection = PacketFlow.CLIENTBOUND - - override fun encode(ctx: ChannelHandlerContext, msg: Packet<*>, out: ByteBuf) { - val enumProt = ctx.channel()?.attr(Connection.ATTRIBUTE_CLIENTBOUND_PROTOCOL)?.get() ?: throw RuntimeException("ConnectionProtocol unknown: $out") - val packetID = enumProt.protocol().codec(protocolDirection).packetId(msg) - val packetDataSerializer: FriendlyByteBuf = CustomDataSerializer(player, out) - packetDataSerializer.writeVarInt(packetID) - - runCatching { - val int2 = packetDataSerializer.writerIndex() - msg.write(packetDataSerializer) - val int3 = packetDataSerializer.writerIndex() - int2 - if (int3 > 8388608) { - throw IllegalArgumentException("Packet too big (is $int3, should be less than 8388608): $msg") - } - }.onFailure { - if (msg.isSkippable) throw SkipPacketException(it) - throw it - } - } - } - - 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 packetID = customDataSerializer.readVarInt() - val attribute = ctx.channel().attr(Connection.ATTRIBUTE_SERVERBOUND_PROTOCOL) - val packet = attribute.get().createPacket(packetID, customDataSerializer) ?: throw IOException("Bad packet id $packetID") - //ObfHelper.INSTANCE.deobfClassName(packet.javaClass.name).logVal("Packet: ") - out += when { - customDataSerializer.readableBytes() > 0 -> throw IOException("Packet $packetID ($packet) was larger than I expected, found ${customDataSerializer.readableBytes()} bytes extra whilst reading packet $packetID") - packet is ServerboundChatPacket -> { - val baseSerializer = FriendlyByteBuf(bufferCopy) - val basePacketID = baseSerializer.readVarInt() - attribute.get().createPacket(basePacketID, baseSerializer) ?: throw IOException("Bad packet id $basePacketID") - } - else -> packet - } - ProtocolSwapHandler.swapProtocolIfNeeded(attribute, packet) - } - } - - override val supported get() = true -} diff --git a/v1_20_R3/build.gradle.kts b/v1_20_R3/build.gradle.kts deleted file mode 100644 index 2214a76..0000000 --- a/v1_20_R3/build.gradle.kts +++ /dev/null @@ -1,43 +0,0 @@ -plugins { - id("com.mineinabyss.conventions.kotlin.jvm") - id("com.mineinabyss.conventions.autoversion") - id("io.papermc.paperweight.userdev") version "1.5.11" -} - -repositories { - gradlePluginPortal() - maven("https://repo.mineinabyss.com/releases") - maven("https://repo.papermc.io/repository/maven-public/") - google() -} - -dependencies { - // MineInAbyss platform - compileOnly(libs.kotlinx.serialization.json) - compileOnly(libs.kotlinx.serialization.kaml) - compileOnly(libs.kotlinx.coroutines) - compileOnly(libs.minecraft.mccoroutine) - - // Shaded - implementation(libs.bundles.idofront.core) - implementation(project(":core")) - paperweight.paperDevBundle("1.20.4-R0.1-SNAPSHOT") //NMS -} - -tasks { - - build { - dependsOn(reobfJar) - } - - compileJava { - options.encoding = Charsets.UTF_8.name() - options.release.set(17) - } - javadoc { - options.encoding = Charsets.UTF_8.name() - } - processResources { - filteringCharset = Charsets.UTF_8.name() - } -} diff --git a/v1_20_R3/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R3/EmojyNMSHandler.kt b/v1_20_R3/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R3/EmojyNMSHandler.kt deleted file mode 100644 index 5d0ea47..0000000 --- a/v1_20_R3/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R3/EmojyNMSHandler.kt +++ /dev/null @@ -1,258 +0,0 @@ -@file:Suppress("unused") - -package com.mineinabyss.emojy.nms.v1_20_R3 - -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.emojy.transformEmoteIDs -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 io.papermc.paper.adventure.AdventureComponent -import io.papermc.paper.adventure.PaperAdventure -import net.kyori.adventure.text.Component -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.ServerboundChatPacket -import net.minecraft.server.MinecraftServer -import net.minecraft.server.network.ServerConnectionListener -import org.bukkit.Bukkit -import org.bukkit.craftbukkit.v1_20_R3.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(Connection.ATTRIBUTE_CLIENTBOUND_PROTOCOL) 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(Connection.ATTRIBUTE_SERVERBOUND_PROTOCOL) 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) : FriendlyByteBuf(bytebuf) { - - override fun writeComponent(component: Component): FriendlyByteBuf { - return super.writeComponent(component.transformEmoteIDs(player, true, true)) - } - - override fun writeComponent(text: net.minecraft.network.chat.Component): FriendlyByteBuf { - if (text is AdventureComponent) return writeComponent(text.deepConverted()) - return writeComponent(PaperAdventure.asAdventure(text).transformEmoteIDs(player, true, false)) - } - - override fun readComponent(): net.minecraft.network.chat.Component { - return PaperAdventure.asVanilla(PaperAdventure.asAdventure(super.readComponentTrusted()).escapeEmoteIDs(player)) - } - - 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) { - val enumProt = ctx.channel()?.attr(Connection.ATTRIBUTE_CLIENTBOUND_PROTOCOL)?.get() ?: throw RuntimeException("ConnectionProtocol unknown: $out") - val packetID = enumProt.protocol().codec(protocolDirection).packetId(msg) - val packetDataSerializer: FriendlyByteBuf = CustomDataSerializer(player, out) - packetDataSerializer.writeVarInt(packetID) - - runCatching { - val int2 = packetDataSerializer.writerIndex() - msg.write(packetDataSerializer) - val int3 = packetDataSerializer.writerIndex() - int2 - if (int3 > 8388608) { - throw IllegalArgumentException("Packet too big (is $int3, should be less than 8388608): $msg") - } - }.onFailure { - if (msg.isSkippable) throw SkipPacketException(it) - throw it - } - } - } - - 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 packetID = customDataSerializer.readVarInt() - val attribute = ctx.channel().attr(Connection.ATTRIBUTE_SERVERBOUND_PROTOCOL) - val packet = attribute.get().createPacket(packetID, customDataSerializer) ?: throw IOException("Bad packet id $packetID") - - out += when { - customDataSerializer.readableBytes() > 0 -> throw IOException("Packet $packetID ($packet) was larger than I expected, found ${customDataSerializer.readableBytes()} bytes extra whilst reading packet $packetID") - packet is ServerboundChatPacket -> { - val baseSerializer = FriendlyByteBuf(bufferCopy) - val basePacketID = baseSerializer.readVarInt() - attribute.get().createPacket(basePacketID, baseSerializer) ?: throw IOException("Bad packet id $basePacketID") - } - else -> packet - } - ProtocolSwapHandler.swapProtocolIfNeeded(attribute, packet) - } - } - - override val supported get() = true -} diff --git a/v1_20_R1/build.gradle.kts b/v1_20_R4/build.gradle.kts similarity index 51% rename from v1_20_R1/build.gradle.kts rename to v1_20_R4/build.gradle.kts index b0a079d..11b747f 100644 --- a/v1_20_R1/build.gradle.kts +++ b/v1_20_R4/build.gradle.kts @@ -1,27 +1,32 @@ +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.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() } +paperweight.reobfArtifactConfiguration.set(ReobfArtifactConfiguration.MOJANG_PRODUCTION) + dependencies { // MineInAbyss platform - compileOnly(libs.kotlinx.serialization.json) - compileOnly(libs.kotlinx.serialization.kaml) - compileOnly(libs.kotlinx.coroutines) - compileOnly(libs.minecraft.mccoroutine) + compileOnly(idofrontLibs.kotlinx.serialization.json) + compileOnly(idofrontLibs.kotlinx.serialization.kaml) + compileOnly(idofrontLibs.kotlinx.coroutines) + compileOnly(idofrontLibs.minecraft.mccoroutine) // Shaded - implementation(libs.bundles.idofront.core) + implementation(idofrontLibs.bundles.idofront.core) implementation(project(":core")) - paperweight.paperDevBundle("1.20-R0.1-SNAPSHOT") //NMS + paperweight.paperDevBundle("1.20.6-R0.1-SNAPSHOT") //NMS } tasks { @@ -32,7 +37,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/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R4/EmojyListener.kt b/v1_20_R4/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R4/EmojyListener.kt new file mode 100644 index 0000000..ec88073 --- /dev/null +++ b/v1_20_R4/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R4/EmojyListener.kt @@ -0,0 +1,89 @@ +package com.mineinabyss.emojy.nms.v1_20_R4 + +import com.github.shynixn.mccoroutine.bukkit.launch +import com.github.shynixn.mccoroutine.bukkit.ticks +import com.jeff_media.morepersistentdatatypes.DataType +import com.mineinabyss.emojy.* +import com.mineinabyss.idofront.items.editItemMeta +import com.mineinabyss.idofront.textcomponents.miniMsg +import com.mineinabyss.idofront.textcomponents.serialize +import io.papermc.paper.event.player.AsyncChatDecorateEvent +import io.papermc.paper.event.player.AsyncChatEvent +import io.papermc.paper.event.player.PlayerOpenSignEvent +import kotlinx.coroutines.delay +import net.minecraft.core.BlockPos +import net.minecraft.world.level.block.entity.BlockEntityType +import org.bukkit.block.Sign +import org.bukkit.block.sign.Side +import org.bukkit.craftbukkit.entity.CraftPlayer +import org.bukkit.event.EventHandler +import org.bukkit.event.EventPriority +import org.bukkit.event.Listener +import org.bukkit.event.block.SignChangeEvent +import org.bukkit.event.inventory.PrepareAnvilEvent + +@Suppress("UnstableApiUsage") +class EmojyListener : Listener { + + // Replace with result not original message to avoid borking other chat formatting + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + fun AsyncChatDecorateEvent.onPlayerChat() { + result(result().escapeEmoteIDs(player())) + } + + @EventHandler + fun SignChangeEvent.onSign() { + val state = (block.state as Sign) + val type = DataType.asList(DataType.STRING) + val sideLines = lines().map { it.serialize() }.toList() + val frontLines = if (side == Side.FRONT) sideLines else state.persistentDataContainer.getOrDefault(ORIGINAL_SIGN_FRONT_LINES, type, mutableListOf("", "", "", "")) + val backLines = if (side == Side.BACK) sideLines else state.persistentDataContainer.getOrDefault(ORIGINAL_SIGN_BACK_LINES, type, mutableListOf("", "", "", "")) + + state.persistentDataContainer.set(ORIGINAL_SIGN_FRONT_LINES, type, frontLines) + state.persistentDataContainer.set(ORIGINAL_SIGN_BACK_LINES, type, backLines) + state.update(true) + + lines().forEachIndexed { index, s -> + line(index, s?.escapeEmoteIDs(player)?.transformEmotes()) + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + fun PlayerOpenSignEvent.onSignEdit() { + if (cause == PlayerOpenSignEvent.Cause.PLACE) return + + sign.persistentDataContainer.get(when (sign.getInteractableSideFor(player)) { + Side.FRONT -> ORIGINAL_SIGN_FRONT_LINES + Side.BACK -> ORIGINAL_SIGN_BACK_LINES + }, DataType.asList(DataType.STRING))?.forEachIndexed { index, s -> + sign.getSide(side).line(index, s.miniMsg()) + } + sign.update(true) + isCancelled = true + emojy.plugin.launch { + delay(2.ticks) + (player as CraftPlayer).handle.level().getBlockEntity(BlockPos(sign.x, sign.y, sign.z), BlockEntityType.SIGN).ifPresent { + it.setAllowedPlayerEditor(player.uniqueId) + (player as CraftPlayer).handle.openTextEdit(it, side == Side.FRONT) + } + } + } + + @EventHandler + fun PrepareAnvilEvent.onAnvil() { + if (result?.itemMeta?.hasDisplayName() != true || inventory.renameText == null) { + result?.editItemMeta { persistentDataContainer.remove(ORIGINAL_ITEM_RENAME_TEXT) } + return + } + val displayName = inventory.renameText?.miniMsg()?.transformEmotes(null, false)?.unescapeEmoteIds() ?: run { + result?.editItemMeta { persistentDataContainer.remove(ORIGINAL_ITEM_RENAME_TEXT) } + return + } + + result = result?.editItemMeta { + persistentDataContainer.set(ORIGINAL_ITEM_RENAME_TEXT, DataType.STRING, inventory.renameText!!) + displayName(displayName) + } + } +} + 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..0b86c85 --- /dev/null +++ b/v1_20_R4/src/main/kotlin/com/mineinabyss/emojy/nms/v1_20_R4/EmojyNMSHandler.kt @@ -0,0 +1,130 @@ +@file:Suppress("unused") + +package com.mineinabyss.emojy.nms.v1_20_R4 + +import com.jeff_media.morepersistentdatatypes.DataType +import com.mineinabyss.emojy.* +import com.mineinabyss.emojy.config.SPACE_PERMISSION +import com.mineinabyss.emojy.nms.IEmojyNMSHandler +import com.mineinabyss.idofront.items.editItemMeta +import com.mineinabyss.idofront.messaging.logVal +import com.mineinabyss.idofront.plugin.listeners +import com.mineinabyss.idofront.textcomponents.miniMsg +import com.mineinabyss.idofront.textcomponents.serialize +import com.mojang.serialization.Codec +import io.netty.channel.Channel +import io.netty.channel.ChannelDuplexHandler +import io.netty.channel.ChannelHandlerContext +import io.netty.channel.ChannelPromise +import io.papermc.paper.adventure.AdventureCodecs +import io.papermc.paper.adventure.AdventureComponent +import io.papermc.paper.adventure.PaperAdventure +import io.papermc.paper.network.ChannelInitializeListenerHolder +import net.kyori.adventure.key.Key +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.TextReplacementConfig +import net.kyori.adventure.translation.GlobalTranslator +import net.minecraft.core.NonNullList +import net.minecraft.network.Connection +import net.minecraft.network.protocol.game.* +import net.minecraft.network.syncher.EntityDataSerializer +import net.minecraft.network.syncher.SynchedEntityData +import org.bukkit.NamespacedKey +import org.bukkit.craftbukkit.inventory.CraftItemStack +import org.bukkit.entity.Player +import org.bukkit.inventory.AnvilInventory +import java.util.* + +class EmojyNMSHandler(emojy: EmojyPlugin) : IEmojyNMSHandler { + + + init { + emojy.listeners(EmojyListener()) + + val codecs = (PaperAdventure::class.java.getDeclaredField("LOCALIZED_CODECS").apply { isAccessible = true } + .get(null) as MutableMap>) + + /*for (locale in Locale.getAvailableLocales()) { + codecs[locale] = AdventureCodecs.COMPONENT_CODEC.xmap( + { component -> component }, // decode + { component -> GlobalTranslator.render(component, locale) } // encode + ) + }*/ + + val key = NamespacedKey.fromString("packet_listener", emojy) + ChannelInitializeListenerHolder.addListener(key!!) { channel: Channel -> + channel.pipeline().addBefore("packet_handler", key.toString(), object : ChannelDuplexHandler() { + private val connection = channel.pipeline()["packet_handler"] as Connection + fun Connection.locale() = player.bukkitEntity.locale() + + override fun write(ctx: ChannelHandlerContext, packet: Any, promise: ChannelPromise) { + ctx.write( + when (packet) { + is ClientboundPlayerChatPacket -> ClientboundPlayerChatPacket(packet.sender, packet.index, packet.signature, packet.body, packet.unsignedContent?.transformEmotes(connection.locale(), true)?.unescapeEmoteIds(), packet.filterMask, packet.chatType) + is ClientboundDisguisedChatPacket -> ClientboundDisguisedChatPacket(packet.message.transformEmotes(connection.locale(), true).unescapeEmoteIds(), packet.chatType) + is ClientboundSystemChatPacket -> ClientboundSystemChatPacket(packet.content.transformEmotes(connection.locale(), true).unescapeEmoteIds(), packet.overlay) + is ClientboundSetTitleTextPacket -> ClientboundSetTitleTextPacket(packet.text.transformEmotes(connection.locale())) + is ClientboundSetSubtitleTextPacket -> ClientboundSetSubtitleTextPacket(packet.text.transformEmotes(connection.locale())) + is ClientboundSetActionBarTextPacket -> ClientboundSetActionBarTextPacket(packet.text.transformEmotes(connection.locale())) + is ClientboundOpenScreenPacket -> ClientboundOpenScreenPacket(packet.containerId, packet.type, packet.title.transformEmotes(connection.locale())) + is ClientboundTabListPacket -> ClientboundTabListPacket(packet.header.transformEmotes(connection.locale()), packet.footer.transformEmotes(connection.locale())) + is ClientboundSetEntityDataPacket -> ClientboundSetEntityDataPacket(packet.id, packet.packedItems.map { + (it.value as? AdventureComponent)?.let { value -> + SynchedEntityData.DataValue(it.id, it.serializer as EntityDataSerializer, + AdventureComponent(value.`adventure$component`().transformEmotes(connection.locale())) + ) + } ?: it + }) + is ClientboundContainerSetContentPacket -> { + (connection.player.bukkitEntity.openInventory.topInventory as? AnvilInventory)?.let { + ClientboundContainerSetContentPacket(packet.containerId, packet.stateId, + NonNullList.of(packet.items.first(), *packet.items.map { + CraftItemStack.asNMSCopy(CraftItemStack.asBukkitCopy(it.copy()).editItemMeta { + setDisplayName(persistentDataContainer.get(ORIGINAL_ITEM_RENAME_TEXT, DataType.STRING) ?: return@editItemMeta) + }) + }.toTypedArray()), packet.carriedItem) + } ?: packet + } + else -> packet + }, promise + ) + } + + override fun channelRead(ctx: ChannelHandlerContext, packet: Any) { + ctx.fireChannelRead(when (packet) { + is ServerboundRenameItemPacket -> ServerboundRenameItemPacket(packet.name.escapeEmoteIDs(connection.player.bukkitEntity)) + else -> packet + }) + } + } + ) + } + } + + companion object { + + fun String.transformEmotes(locale: Locale? = null, insert: Boolean = false): String { + return miniMsg().transformEmotes(locale, insert).serialize() + } + + fun net.minecraft.network.chat.Component.transformEmotes(locale: Locale? = null, insert: Boolean = false): net.minecraft.network.chat.Component { + return PaperAdventure.asVanilla(PaperAdventure.asAdventure(this).transformEmotes(locale, insert)) + } + + fun String.escapeEmoteIDs(player: Player?): String { + return miniMsg().escapeEmoteIDs(player).serialize() + } + + fun net.minecraft.network.chat.Component.escapeEmoteIDs(player: Player?): net.minecraft.network.chat.Component { + return PaperAdventure.asVanilla((PaperAdventure.asAdventure(this)).escapeEmoteIDs(player)) + } + + fun net.minecraft.network.chat.Component.unescapeEmoteIds(): net.minecraft.network.chat.Component { + return PaperAdventure.asVanilla(PaperAdventure.asAdventure(this).unescapeEmoteIds()) + } + + + } + + override val supported get() = true +}