Skip to content

Commit

Permalink
fix: rework nms logic to mostly fix stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
Boy0000 committed Dec 10, 2023
1 parent d332f6b commit 783451d
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 145 deletions.
132 changes: 103 additions & 29 deletions core/src/main/kotlin/com/mineinabyss/emojy/EmojyHelpers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,140 @@ package com.mineinabyss.emojy

import com.mineinabyss.emojy.config.SPACE_PERMISSION
import com.mineinabyss.idofront.font.Space
import com.mineinabyss.idofront.messaging.broadcast
import com.mineinabyss.idofront.messaging.logInfo
import com.mineinabyss.idofront.textcomponents.miniMsg
import com.mineinabyss.idofront.textcomponents.serialize
import net.kyori.adventure.key.Key
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.ComponentLike
import net.kyori.adventure.text.TextReplacementConfig
import net.kyori.adventure.text.minimessage.tag.Tag
import net.kyori.adventure.translation.GlobalTranslator
import org.bukkit.Bukkit
import org.bukkit.entity.Player
import java.util.*

fun Component.transform(player: Player?, insert: Boolean) = player?.let { replaceEmoteIds(it, insert) } ?: transformEmoteIds(insert)

private val spaceRegex: Regex = "(?<!\\\\):space_(-?\\d+):".toRegex()
private val escapedSpaceRegex: Regex = "\\\\(:space_(-?\\d+):)".toRegex()
private val colorableRegex: Regex = "\\|(c|colorable)".toRegex()
private val bitmapIndexRegex: Regex = "\\|([0-9]+)".toRegex()
//TODO Tags like rainbow and gradient, which split the text into multiple children, will break replacement below
// Above is due to Adventure-issue, nothing on our end for once. https://github.com/KyoriPowered/adventure/issues/872
// Find out why this is called 3 times
fun Component.replaceEmoteIds(player: Player? = null, insert: Boolean = true): Component {
var msg = GlobalTranslator.render(this, player?.locale() ?: Locale.US)
private fun Component.replaceEmoteIds(player: Player, insert: Boolean = true): Component {
var msg = GlobalTranslator.render(this, player.locale())
val serialized = msg.serialize()

emojy.emotes.filter { it.baseRegex in serialized && it.checkPermission(player) }.forEach { emote ->
// if (emote.id == "survival") {
// broadcast(player)
// broadcast(emote.id)
// broadcast(emote.checkPermission(player))
// }
val matches = emote.fullRegex.findAll(serialized)
matches.forEach { match ->
logInfo("replace: $serialized")

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
val replacement =
if (emote.checkPermission(player)) emote.formattedUnicode(insert = insert, colorable = colorable, bitmapIndex = bitmapIndex)
else "\\${match.value}".miniMsg()
msg = msg.replaceText(
TextReplacementConfig.builder()
.matchLiteral(match.value)
.replacement(replacement)
.build()
)
}

for (gif in emojy.gifs) gif.baseRegex.findAll(serialized).forEach { match ->
val replacement = if (gif.checkPermission(player)) gif.formattedUnicode(insert = insert)
else "\\:${gif.id}:".miniMsg()
msg = msg.replaceText(
TextReplacementConfig.builder()
.matchLiteral(match.value)
.replacement(replacement)
.build()
)
}

spaceRegex.findAll(serialized).forEach { match ->
val space = match.groupValues[1].toIntOrNull() ?: return@forEach
val replacement = if (player.hasPermission(SPACE_PERMISSION)) buildSpaceComponents(space) else "\\:space_$space:".miniMsg()

msg = msg.replaceText(
TextReplacementConfig.builder()
.matchLiteral(match.value)
.replacement(replacement)
.build()
)
}

return msg
}

/**
* Formats emote-ids in a component to their unicode representation.
* This does format without a player context, but ignores escaped emote-ids.
* This is because we handle with a player-context first, and escape that in-which should not be formatted.
*/
private fun Component.transformEmoteIds(insert: Boolean = true): Component {
var msg = 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

val replacement = emote.formattedUnicode(insert = insert, colorable = colorable, bitmapIndex = bitmapIndex)
msg = msg.replaceText(
TextReplacementConfig.builder()
.matchLiteral(match.value)
.replacement(replacement)
.replacement(emote.formattedUnicode(insert = insert, colorable = colorable, bitmapIndex = bitmapIndex))
.build()
)
}
}

emojy.gifs.filter { ":${it.id}:" in serialized && it.checkPermission(player) }.forEach { gif ->
msg = msg.replaceText(
TextReplacementConfig.builder()
.matchLiteral(":${gif.id}:")
.replacement(gif.formattedUnicode(insert = insert))
.build()
)
emote.escapedRegex.findAll(serialized).forEach { match ->
msg = msg.replaceText(
TextReplacementConfig.builder()
.matchLiteral(match.value)
.replacement(match.value.removePrefix("\\"))
.build()
)
}
}

if (player?.hasPermission(SPACE_PERMISSION) != false) ":space_(-?\\d+):".toRegex().findAll(serialized)
.mapNotNull { it.groupValues[1].toIntOrNull() }.toSet().forEach { space ->
for (gif in emojy.gifs) {
gif.baseRegex.findAll(serialized).forEach { match ->
msg = msg.replaceText(
TextReplacementConfig.builder()
.matchLiteral(match.value)
.replacement(gif.formattedUnicode(insert = insert))
.build()
)
}

gif.escapedRegex.findAll(serialized).forEach { match ->
msg = msg.replaceText(
TextReplacementConfig.builder()
.matchLiteral(":space_$space:")
.replacement(buildSpaceComponents(space))
.matchLiteral(match.value)
.replacement(match.value.removePrefix("\\"))
.build()
)
}
}

spaceRegex.findAll(serialized).forEach { match ->
val space = match.groupValues[1].toIntOrNull() ?: return@forEach
msg = msg.replaceText(
TextReplacementConfig.builder()
.matchLiteral(match.value)
.replacement(buildSpaceComponents(space))
.build()
)
}

escapedSpaceRegex.findAll(serialized).forEach { match ->
msg = msg.replaceText(
TextReplacementConfig.builder()
.matchLiteral(match.value)
.replacement(match.value.removePrefix("\\"))
.build()
)
}

return msg
}
Expand Down
6 changes: 5 additions & 1 deletion core/src/main/kotlin/com/mineinabyss/emojy/EmojyListener.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package com.mineinabyss.emojy

import com.mineinabyss.emojy.nms.EmojyNMSHandlers
import com.mineinabyss.idofront.messaging.logError
import com.mineinabyss.idofront.messaging.logVal
import com.mineinabyss.idofront.textcomponents.serialize
import io.papermc.paper.event.player.AsyncChatDecorateEvent
import io.papermc.paper.event.player.AsyncChatEvent
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
Expand All @@ -18,7 +22,7 @@ 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().replaceEmoteIds(player()))
result(result().transform(player(), true))
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ data class Emotes(val emotes: Set<Emote> = mutableSetOf()) {
@EncodeDefault(NEVER) val bitmapHeight: Int = template?.bitmapHeight ?: 1,
) {
val isMultiBitmap: Boolean get() = bitmapWidth > 1 || bitmapHeight > 1
@Transient val baseRegex = ":$id(\\|.*?)?:".toRegex()
@Transient val fullRegex = ":$id(\\|(c|colorable|\\d+))*:".toRegex()
@Transient val baseRegex = "(?<!\\\\):$id(\\|(c|colorable|\\d+))*:".toRegex()
@Transient val escapedRegex = "\\\\:$id(\\|(c|colorable|\\d+))*:".toRegex()

// Beginning of Private Use Area \uE000 -> uF8FF
// Option: (Character.toCodePoint('\uE000', '\uFF8F')/37 + getIndex())
Expand Down Expand Up @@ -162,6 +162,8 @@ data class Gifs(val gifs: Set<Gif> = mutableSetOf()) {
@Transient val framePath = Key.key(_framePath.asString().removeSuffix("/") + "/")
@Transient val font = Key.key(framePath.namespace(), id)
@Transient val permission = "emojy.gif.$id"
@Transient val baseRegex = "(?<!\\\\):$id:".toRegex()
@Transient val escapedRegex = "\\\\:$id:".toRegex()

val gifFile get() = emojy.plugin.dataFolder.resolve("gifs/${id}.gif").apply { mkdirs() }
private var aspectRatio by Delegates.notNull<Float>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import com.google.gson.JsonObject
import com.google.gson.JsonParser
import com.mineinabyss.emojy.emojy
import com.mineinabyss.emojy.nms.IEmojyNMSHandler
import com.mineinabyss.emojy.replaceEmoteIds
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
Expand Down Expand Up @@ -101,31 +102,22 @@ class EmojyNMSHandler : IEmojyNMSHandler {
}

private fun bind(channelFutures: List<ChannelFuture>, serverChannelHandler: ChannelInboundHandlerAdapter) {
channelFutures.forEach { future ->
future.channel().pipeline().addFirst(serverChannelHandler)
}

channelFutures.forEach { future -> future.channel().pipeline().addFirst(serverChannelHandler) }
Bukkit.getOnlinePlayers().forEach(::inject)
}

private fun Channel.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())
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())
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() }
channel.pipeline().forEach {
when (val handler = it.value) {
is CustomPacketEncoder -> handler.player = player
is CustomPacketDecoder -> handler.player = player
}
}
channel.eventLoop().submit { channel.inject(player) }
}

override fun uninject(player: Player) {
Expand All @@ -146,9 +138,8 @@ class EmojyNMSHandler : IEmojyNMSHandler {
}
}

private class CustomPacketEncoder : MessageToByteEncoder<Packet<*>>() {
private class CustomPacketEncoder(val player: Player?) : MessageToByteEncoder<Packet<*>>() {
private val protocolDirection = PacketFlow.CLIENTBOUND
var player: Player? = null

override fun encode(ctx: ChannelHandlerContext, msg: Packet<*>, out: ByteBuf) {
val enumProt = ctx.channel()?.attr(Connection.ATTRIBUTE_PROTOCOL)?.get()
Expand All @@ -173,8 +164,7 @@ class EmojyNMSHandler : IEmojyNMSHandler {
}
}

private class CustomPacketDecoder : ByteToMessageDecoder() {
var player: Player? = null
private class CustomPacketDecoder(val player: Player?) : ByteToMessageDecoder() {

override fun decode(ctx: ChannelHandlerContext, msg: ByteBuf, out: MutableList<Any>) {
if (msg.readableBytes() == 0) return
Expand All @@ -196,11 +186,10 @@ class EmojyNMSHandler : IEmojyNMSHandler {
val gson = GsonComponentSerializer.gson()

override fun writeUtf(string: String, maxLength: Int): FriendlyByteBuf {
try {
runCatching {
val element = JsonParser.parseString(string)
if (element.isJsonObject)
return super.writeUtf(element.asJsonObject.returnFormattedString(), maxLength)
} catch (_: Exception) {
}

return super.writeUtf(string, maxLength)
Expand All @@ -212,7 +201,7 @@ class EmojyNMSHandler : IEmojyNMSHandler {
try {
val element = JsonParser.parseString(string)
if (element.isJsonObject)
return@Function element.asJsonObject.returnFormattedString(false)
return@Function element.asJsonObject.returnFormattedString()
} catch (ignored: Exception) {
}
string
Expand All @@ -222,7 +211,7 @@ class EmojyNMSHandler : IEmojyNMSHandler {

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()).replaceEmoteIds(player, insert))
gson.serialize(gson.deserialize(this.toString()).transform(null, true))
} else this.toString()
}

Expand All @@ -246,9 +235,7 @@ class EmojyNMSHandler : IEmojyNMSHandler {
}

override fun readUtf(maxLength: Int): String {
return super.readUtf(maxLength).apply {
this.miniMsg().replaceEmoteIds(player, false)
}
return super.readUtf(maxLength).miniMsg().transform(player, false).serialize()
}


Expand Down
Loading

0 comments on commit 783451d

Please sign in to comment.