Skip to content

Commit

Permalink
feat: 1.21.4 support
Browse files Browse the repository at this point in the history
  • Loading branch information
Boy0000 committed Dec 5, 2024
1 parent 27dd011 commit d402982
Show file tree
Hide file tree
Showing 10 changed files with 338 additions and 6 deletions.
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
id("com.mineinabyss.conventions.autoversion")
id("xyz.jpenilla.run-paper") version "2.3.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.7.4"
id("io.papermc.paperweight.userdev") version "1.7.6"
}

paperweight.reobfArtifactConfiguration.set(ReobfArtifactConfiguration.MOJANG_PRODUCTION)
Expand Down Expand Up @@ -44,6 +44,7 @@ dependencies {
implementation(project(path = ":v1_20_R4"))
implementation(project(path = ":v1_21_R1"))
implementation(project(path = ":v1_21_R2"))
implementation(project(path = ":v1_21_R3"))
}

tasks {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ object EmojyNMSHandlers {
"1.20.5", "1.20.6" -> "v1_20_R4"
"1.21", "1.21.1" -> "v1_21_R1"
"1.21.2", "1.21.3" -> "v1_21_R2"
"1.21.4" -> "v1_21_R3"
else -> throw IllegalStateException("Unsupported server version")
}
runCatching {
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
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", "v1_21_R1", "v1_21_R2")
include("core", "v1_20_R4", "v1_21_R1", "v1_21_R2", "v1_21_R3")
2 changes: 1 addition & 1 deletion v1_20_R4/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ 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.4"
id("io.papermc.paperweight.userdev") version "1.7.6"
}

repositories {
Expand Down
2 changes: 1 addition & 1 deletion v1_21_R1/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ 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.4"
id("io.papermc.paperweight.userdev") version "1.7.6"
}

repositories {
Expand Down
2 changes: 1 addition & 1 deletion v1_21_R2/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ 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.4"
id("io.papermc.paperweight.userdev") version "1.7.6"
}

repositories {
Expand Down
48 changes: 48 additions & 0 deletions v1_21_R3/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.6"
}

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.4-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,101 @@
package com.mineinabyss.emojy.nms.v1_21_R3

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.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
import org.bukkit.event.player.PlayerEditBookEvent

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

@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
fun PlayerEditBookEvent.onBookEdit() {
if (isSigning) newBookMeta = newBookMeta.apply {
pages(pages().map {
it.escapeEmoteIDs(player).transformEmotes().unescapeEmoteIds()
})
}
}
}

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

package com.mineinabyss.emojy.nms.v1_21_R3

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 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.contents.PlainTextContents.LiteralContents
import net.minecraft.network.protocol.Packet
import net.minecraft.network.protocol.common.ClientboundDisconnectPacket
import net.minecraft.network.protocol.common.ClientboundResourcePackPushPacket
import net.minecraft.network.protocol.common.ClientboundServerLinksPacket
import net.minecraft.network.protocol.game.*
import net.minecraft.network.syncher.EntityDataSerializer
import net.minecraft.network.syncher.EntityDataSerializers
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

override fun write(ctx: ChannelHandlerContext, packet: Any, promise: ChannelPromise) {
ctx.write(transformPacket(packet, connection), 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 {
private fun Connection.locale() = player.bukkitEntity.locale()
fun transformPacket(packet: Any, connection: Connection): Any {
return when (packet) {
is ClientboundBundlePacket -> ClientboundBundlePacket(packet.subPackets().map { transformPacket(it, connection) as Packet<in ClientGamePacketListener> })
is ClientboundServerLinksPacket -> ClientboundServerLinksPacket(packet.links.map { net.minecraft.server.ServerLinks.UntrustedEntry(it.type.mapRight { it.transformEmotes(connection.locale()) }, it.link) })
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 ClientboundResourcePackPushPacket -> ClientboundResourcePackPushPacket(packet.id, packet.url, packet.hash, packet.required, packet.prompt.map { it.transformEmotes(connection.locale()) })
is ClientboundDisconnectPacket -> ClientboundDisconnectPacket(packet.reason.transformEmotes(connection.locale()))
is ClientboundSetEntityDataPacket -> ClientboundSetEntityDataPacket(packet.id, packet.packedItems.map {
when (val value = it.value) {
is AdventureComponent -> SynchedEntityData.DataValue(it.id, it.serializer as EntityDataSerializer<AdventureComponent>,
AdventureComponent(value.`adventure$component`().transformEmotes(connection.locale()))
)
is Component -> SynchedEntityData.DataValue(it.id, EntityDataSerializers.COMPONENT,
value.transformEmotes(connection.locale())
)
else -> it
}
})
is ClientboundContainerSetSlotPacket -> ClientboundContainerSetSlotPacket(packet.containerId, packet.stateId, packet.slot, packet.item.transformItemNameLore(connection.player.bukkitEntity))
is ClientboundContainerSetContentPacket -> ClientboundContainerSetContentPacket(
packet.containerId, packet.stateId, NonNullList.of(packet.items.first(),
*packet.items.map {
val player = connection.player.bukkitEntity
val inv = player.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(player)
else it.transformItemNameLore(player)
}.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 }
nameField.set(operation, (nameField.get(operation) as Component).transformEmotes(connection.locale()))
}
"UpdateNameOperation" -> {
val accessorMethod = operation::class.java.methods.find { it.name == "name" }
accessorMethod?.isAccessible = true
if (accessorMethod != null) {
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 }
val name = (accessorMethod.invoke(operation) as Component).transformEmotes(connection.locale())
val updatedOperation = constructor.newInstance(name)

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.listed, it.listOrder, it.chatSession
)
})
else -> packet
}
}

private fun ItemStack.transformItemNameLore(player: Player): ItemStack {
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.escapeEmoteIDs(player).transformEmotes().unescapeEmoteIds().miniMsg())
}
})
}

fun Component.transformEmotes(locale: Locale? = null, insert: Boolean = false): Component {
return when {
// Sometimes a NMS component is partially Literal, so ensure entire thing is just one LiteralContent with no extra data
contents is LiteralContents && style.isEmpty && siblings.isEmpty() -> (contents as LiteralContents).text.miniMsg()
else -> PaperAdventure.asAdventure(this)
}.transformEmotes(locale, insert).let(PaperAdventure::asVanilla)
}

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 d402982

Please sign in to comment.