Skip to content

Commit

Permalink
feat: 1.21 support
Browse files Browse the repository at this point in the history
  • Loading branch information
Boy0000 committed Jun 16, 2024
1 parent 7df4a83 commit e002fb8
Show file tree
Hide file tree
Showing 6 changed files with 313 additions and 2 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ dependencies {
paperweight.paperDevBundle("1.20.6-R0.1-SNAPSHOT") //NMS
implementation(project(path = ":core"))
implementation(project(path = ":v1_20_R4"))
implementation(project(path = ":v1_21_R1"))
}

tasks {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.mineinabyss.emojy.EmojyPlugin

object EmojyNMSHandlers {

private val SUPPORTED_VERSION = arrayOf("v1_20_R4")
private val SUPPORTED_VERSION = arrayOf("v1_20_R4", "v1_21_R1")

fun setup(emojy: EmojyPlugin): IEmojyNMSHandler {
SUPPORTED_VERSION.forEach { version ->
Expand Down
2 changes: 1 addition & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ dependencyResolutionManagement {
}
}

include("core", "v1_20_R4")
include("core", "v1_20_R4", "v1_21_R1")
48 changes: 48 additions & 0 deletions v1_21_R1/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import io.papermc.paperweight.userdev.ReobfArtifactConfiguration

plugins {
id("com.mineinabyss.conventions.kotlin.jvm")
id("com.mineinabyss.conventions.autoversion")
id("io.papermc.paperweight.userdev") version "1.7.1"
}

repositories {
gradlePluginPortal()
maven("https://repo.mineinabyss.com/releases")
maven("https://repo.mineinabyss.com/snapshots")
maven("https://repo.papermc.io/repository/maven-public/")
google()
}

paperweight.reobfArtifactConfiguration.set(ReobfArtifactConfiguration.MOJANG_PRODUCTION)

dependencies {
// MineInAbyss platform
compileOnly(idofrontLibs.kotlinx.serialization.json)
compileOnly(idofrontLibs.kotlinx.serialization.kaml)
compileOnly(idofrontLibs.kotlinx.coroutines)
compileOnly(idofrontLibs.minecraft.mccoroutine)

// Shaded
implementation(idofrontLibs.bundles.idofront.core)
implementation(project(":core"))
paperweight.paperDevBundle("1.21-R0.1-SNAPSHOT") //NMS
}

tasks {

build {
dependsOn(reobfJar)
}

compileJava {
options.encoding = Charsets.UTF_8.name()
options.release.set(21)
}
javadoc {
options.encoding = Charsets.UTF_8.name()
}
processResources {
filteringCharset = Charsets.UTF_8.name()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.mineinabyss.emojy.nms.v1_21_R1

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() {
result = result?.editItemMeta {
if (inventory.renameText == null || result?.itemMeta?.hasDisplayName() != true) {
persistentDataContainer.remove(ORIGINAL_ITEM_RENAME_TEXT) }
else persistentDataContainer.set(ORIGINAL_ITEM_RENAME_TEXT, DataType.STRING, inventory.renameText!!)
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
@file:Suppress("unused")

package com.mineinabyss.emojy.nms.v1_21_R1

import com.jeff_media.morepersistentdatatypes.DataType
import com.mineinabyss.emojy.*
import com.mineinabyss.emojy.nms.IEmojyNMSHandler
import com.mineinabyss.idofront.items.editItemMeta
import com.mineinabyss.idofront.plugin.listeners
import com.mineinabyss.idofront.textcomponents.miniMsg
import com.mineinabyss.idofront.textcomponents.serialize
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.AdventureComponent
import io.papermc.paper.adventure.PaperAdventure
import io.papermc.paper.network.ChannelInitializeListenerHolder
import net.minecraft.core.NonNullList
import net.minecraft.network.Connection
import net.minecraft.network.chat.ChatType
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MessageSignature
import net.minecraft.network.chat.SignedMessageBody
import net.minecraft.network.protocol.game.*
import net.minecraft.network.syncher.EntityDataSerializer
import net.minecraft.network.syncher.SynchedEntityData
import net.minecraft.world.item.ItemStack
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 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 ClientboundSetScorePacket -> ClientboundSetScorePacket(packet.owner, packet.objectiveName, packet.score, packet.display.map { it.transformEmotes(connection.locale()) }, packet.numberFormat)
is ClientboundServerDataPacket -> ClientboundServerDataPacket(packet.motd.transformEmotes(connection.locale()), packet.iconBytes)
is ClientboundDisguisedChatPacket -> ClientboundDisguisedChatPacket(packet.message.transformEmotes(connection.locale(), true).unescapeEmoteIds(), packet.chatType)
is ClientboundPlayerChatPacket -> ClientboundPlayerChatPacket(packet.sender, packet.index, packet.signature, packet.body, (packet.unsignedContent ?: PaperAdventure.asVanilla(packet.body.content.miniMsg()))?.transformEmotes(connection.locale(), true)?.unescapeEmoteIds(), packet.filterMask, ChatType.bind(packet.chatType.chatType.unwrapKey().get(), connection.player.registryAccess(), packet.chatType.name.transformEmotes(connection.locale(), true)))
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>,
AdventureComponent(value.`adventure$component`().transformEmotes(connection.locale()))
)
} ?: it
})
is ClientboundContainerSetSlotPacket -> ClientboundContainerSetSlotPacket(packet.containerId, packet.stateId, packet.slot, packet.item.transformItemNameLore())
is ClientboundContainerSetContentPacket -> ClientboundContainerSetContentPacket(
packet.containerId, packet.stateId, NonNullList.of(packet.items.first(),
*packet.items.map {
val inv = connection.player.bukkitEntity.openInventory.topInventory
val bukkit = CraftItemStack.asBukkitCopy(it)

// If item is firstItem in AnvilInventory we want to set it to have the plain-text displayname
if (inv is AnvilInventory && inv.firstItem == bukkit)
bukkit.itemMeta?.persistentDataContainer?.get(ORIGINAL_ITEM_RENAME_TEXT, DataType.STRING)?.let { og ->
CraftItemStack.asNMSCopy(bukkit.editItemMeta {
setDisplayName(og)
})
} ?: it.transformItemNameLore()
else it.transformItemNameLore()
}.toTypedArray()), packet.carriedItem)
is ClientboundBossEventPacket -> {
// Access the private field 'operation'
val operationField = ClientboundBossEventPacket::class.java.getDeclaredField("operation").apply { isAccessible = true }
val operation = operationField.get(packet)

when (operation::class.java.simpleName) {
"AddOperation" -> {
val nameField = operation::class.java.getDeclaredField("name").apply { isAccessible = true }
// Get the component, serialize it and replace "\\<" as it might be escaped if not an AdventureBossbar
val name = PaperAdventure.asAdventure(nameField.get(operation) as Component).serialize().replace("\\<", "<").miniMsg()
nameField.set(operation, PaperAdventure.asVanilla(name.transformEmotes(connection.locale())))
}
"UpdateNameOperation" -> {
val accessorMethod = operation::class.java.methods.find { it.name == "name" }
accessorMethod?.isAccessible = true
if (accessorMethod != null) {
val name = PaperAdventure.asAdventure(accessorMethod.invoke(operation) as Component)
.serialize().replace("\\<", "<")
.miniMsg().transformEmotes(connection.locale())

val updateNameOperationClass = operation::class.java.enclosingClass.declaredClasses.find {
it.simpleName == "UpdateNameOperation"
} ?: throw IllegalStateException("UpdateNameOperation class not found")

val constructor = updateNameOperationClass.getDeclaredConstructor(Component::class.java).apply { isAccessible = true }
// Create a new instance of UpdateNameOperation with the modified name

val updatedOperation = constructor.newInstance(PaperAdventure.asVanilla(name))

// Set the updated operation in the packet
operationField.set(packet, updatedOperation)
}
}
}

packet
}
is ClientboundPlayerInfoUpdatePacket ->
ClientboundPlayerInfoUpdatePacket(packet.actions(), packet.entries().map {
ClientboundPlayerInfoUpdatePacket.Entry(
it.profileId, it.profile, it.listed, it.latency, it.gameMode,
it.displayName?.transformEmotes(connection.locale()), it.chatSession
)
})
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
})
}

private fun ItemStack.transformItemNameLore(): ItemStack {
val player = connection.player.bukkitEntity
val locale = player.locale()
return CraftItemStack.asNMSCopy(CraftItemStack.asBukkitCopy(this).editItemMeta {
itemName(if (hasItemName()) itemName().transformEmotes(locale) else null)
lore(lore()?.map { l -> l.transformEmotes(locale) })
persistentDataContainer.get(ORIGINAL_ITEM_RENAME_TEXT, DataType.STRING)?.let {
displayName(it.miniMsg().escapeEmoteIDs(player).transformEmotes(locale).unescapeEmoteIds())
}
})
}
}
)
}
}

companion object {

fun String.transformEmotes(locale: Locale? = null, insert: Boolean = false): String {
return miniMsg().transformEmotes(locale, insert).serialize()
}

fun Component.transformEmotes(locale: Locale? = null, insert: Boolean = false): Component {
return PaperAdventure.asVanilla(PaperAdventure.asAdventure(this).transformEmotes(locale, insert))
}

fun String.escapeEmoteIDs(player: Player?): String {
return miniMsg().escapeEmoteIDs(player).serialize()
}

fun Component.escapeEmoteIDs(player: Player?): Component {
return PaperAdventure.asVanilla((PaperAdventure.asAdventure(this)).escapeEmoteIDs(player))
}

fun Component.unescapeEmoteIds(): Component {
return PaperAdventure.asVanilla(PaperAdventure.asAdventure(this).unescapeEmoteIds())
}


}

override val supported get() = true
}

0 comments on commit e002fb8

Please sign in to comment.