mirror of
https://github.com/GayPizzaSpecifications/foundation.git
synced 2025-09-17 08:51:31 +00:00
Bifrost: Implement player advancement notifications. Oh my god this was hard and it still is ugly.
This commit is contained in:
@ -11,7 +11,7 @@ class FoundationGradlePlugin : Plugin<Project> {
|
|||||||
project.afterEvaluate { ->
|
project.afterEvaluate { ->
|
||||||
setupPaperServer.dependsOn(*project.subprojects
|
setupPaperServer.dependsOn(*project.subprojects
|
||||||
.filter { it.name.startsWith("foundation-") }
|
.filter { it.name.startsWith("foundation-") }
|
||||||
.map { it.tasks.getByName("build") }
|
.map { it.tasks.getByName("shadowJar") }
|
||||||
.toTypedArray()
|
.toTypedArray()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package cloud.kubelet.foundation.bifrost
|
|||||||
import cloud.kubelet.foundation.bifrost.model.BifrostConfig
|
import cloud.kubelet.foundation.bifrost.model.BifrostConfig
|
||||||
import cloud.kubelet.foundation.core.FoundationCorePlugin
|
import cloud.kubelet.foundation.core.FoundationCorePlugin
|
||||||
import cloud.kubelet.foundation.core.Util
|
import cloud.kubelet.foundation.core.Util
|
||||||
|
import cloud.kubelet.foundation.core.util.AdvancementTitleCache
|
||||||
import com.charleskorn.kaml.Yaml
|
import com.charleskorn.kaml.Yaml
|
||||||
import io.papermc.paper.event.player.AsyncChatEvent
|
import io.papermc.paper.event.player.AsyncChatEvent
|
||||||
import net.dv8tion.jda.api.EmbedBuilder
|
import net.dv8tion.jda.api.EmbedBuilder
|
||||||
@ -19,6 +20,7 @@ import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
|
|||||||
import org.bukkit.event.EventHandler
|
import org.bukkit.event.EventHandler
|
||||||
import org.bukkit.event.EventPriority
|
import org.bukkit.event.EventPriority
|
||||||
import org.bukkit.event.entity.PlayerDeathEvent
|
import org.bukkit.event.entity.PlayerDeathEvent
|
||||||
|
import org.bukkit.event.player.PlayerAdvancementDoneEvent
|
||||||
import org.bukkit.event.player.PlayerJoinEvent
|
import org.bukkit.event.player.PlayerJoinEvent
|
||||||
import org.bukkit.event.player.PlayerQuitEvent
|
import org.bukkit.event.player.PlayerQuitEvent
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
@ -162,6 +164,17 @@ class FoundationBifrostPlugin : JavaPlugin(), DiscordEventListener, BukkitEventL
|
|||||||
sendEmbedMessage(Color.YELLOW, deathMessage)
|
sendEmbedMessage(Color.YELLOW, deathMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
private fun onPlayerAdvancementDone(e: PlayerAdvancementDoneEvent) {
|
||||||
|
if (!config.channel.sendPlayerAdvancement) return
|
||||||
|
if (e.advancement.key.key.contains("recipe/")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val display = AdvancementTitleCache.of(e.advancement) ?: return
|
||||||
|
sendEmbedMessage(Color.CYAN, "${e.player.name} completed the advancement '${display}'")
|
||||||
|
}
|
||||||
|
|
||||||
private fun onDiscordReady() {
|
private fun onDiscordReady() {
|
||||||
if (!config.channel.sendStart) return
|
if (!config.channel.sendStart) return
|
||||||
if (isDev) return
|
if (isDev) return
|
||||||
|
@ -22,5 +22,6 @@ data class BifrostChannel(
|
|||||||
val sendShutdown: Boolean = true,
|
val sendShutdown: Boolean = true,
|
||||||
val sendPlayerJoin: Boolean = true,
|
val sendPlayerJoin: Boolean = true,
|
||||||
val sendPlayerQuit: Boolean = true,
|
val sendPlayerQuit: Boolean = true,
|
||||||
val sendPlayerDeath: Boolean = true
|
val sendPlayerDeath: Boolean = true,
|
||||||
|
val sendPlayerAdvancement: Boolean = true
|
||||||
)
|
)
|
||||||
|
@ -19,6 +19,7 @@ channel:
|
|||||||
sendPlayerJoin: true
|
sendPlayerJoin: true
|
||||||
sendPlayerQuit: true
|
sendPlayerQuit: true
|
||||||
sendPlayerDeath: true
|
sendPlayerDeath: true
|
||||||
|
sendPlayerAdvancement: true
|
||||||
|
|
||||||
# Enables logging of what is sent to Discord.
|
# Enables logging of what is sent to Discord.
|
||||||
enableDebugLog: false
|
enableDebugLog: false
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
package cloud.kubelet.foundation.core.util
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer
|
||||||
|
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
|
||||||
|
import org.bukkit.advancement.Advancement
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
private fun Advancement.getInternalHandle(): Any =
|
||||||
|
javaClass.getMethod("getHandle").invoke(this)
|
||||||
|
|
||||||
|
private fun Class<*>.getDeclaredFieldAccessible(name: String): Field {
|
||||||
|
val field = getDeclaredField(name)
|
||||||
|
if (!field.trySetAccessible()) {
|
||||||
|
throw RuntimeException("Failed to set reflection permissions to accessible.")
|
||||||
|
}
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Advancement.getInternalAdvancementDisplay(handle: Any = getInternalHandle()): Any? =
|
||||||
|
handle.javaClass.methods.firstOrNull {
|
||||||
|
it.returnType.simpleName == "AdvancementDisplay" &&
|
||||||
|
it.parameterCount == 0
|
||||||
|
}?.invoke(handle) ?: handle.javaClass.getDeclaredFieldAccessible("c").get(handle)
|
||||||
|
|
||||||
|
private fun Advancement.displayTitleText(): String? {
|
||||||
|
val handle = getInternalHandle()
|
||||||
|
val advancementDisplay = getInternalAdvancementDisplay(handle) ?: return null
|
||||||
|
try {
|
||||||
|
val field = advancementDisplay.javaClass.getDeclaredField("a")
|
||||||
|
field.trySetAccessible()
|
||||||
|
val message = field.get(advancementDisplay)
|
||||||
|
val title = message.javaClass.getMethod("getString").invoke(message)
|
||||||
|
return title.toString()
|
||||||
|
} catch (_: Exception) {
|
||||||
|
}
|
||||||
|
|
||||||
|
val titleComponentField = advancementDisplay.javaClass.declaredFields.firstOrNull {
|
||||||
|
it.type.simpleName == "IChatBaseComponent"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (titleComponentField != null) {
|
||||||
|
titleComponentField.trySetAccessible()
|
||||||
|
val titleChatBaseComponent = titleComponentField.get(advancementDisplay)
|
||||||
|
val title = titleChatBaseComponent.javaClass.getMethod("getText").invoke(titleChatBaseComponent).toString()
|
||||||
|
if (title.isNotBlank()) {
|
||||||
|
return title
|
||||||
|
}
|
||||||
|
|
||||||
|
val chatSerializerClass = titleChatBaseComponent.javaClass.declaredClasses.firstOrNull {
|
||||||
|
it.simpleName == "ChatSerializer"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chatSerializerClass != null) {
|
||||||
|
val componentJson = chatSerializerClass
|
||||||
|
.getMethod("a", titleChatBaseComponent.javaClass)
|
||||||
|
.invoke(null, titleChatBaseComponent).toString()
|
||||||
|
val gson = GsonComponentSerializer.gson().deserialize(componentJson)
|
||||||
|
return LegacyComponentSerializer.legacySection().serialize(gson)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val rawAdvancementName = key.key
|
||||||
|
return rawAdvancementName.substring(rawAdvancementName.lastIndexOf("/") + 1)
|
||||||
|
.lowercase().split("_")
|
||||||
|
.joinToString(" ") { it.substring(0, 1).uppercase() + it.substring(1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
object AdvancementTitleCache {
|
||||||
|
private val cache = ConcurrentHashMap<Advancement, String?>()
|
||||||
|
|
||||||
|
fun of(advancement: Advancement): String? =
|
||||||
|
cache.computeIfAbsent(advancement) { it.displayTitleText() }
|
||||||
|
}
|
Reference in New Issue
Block a user