mirror of
				https://github.com/GayPizzaSpecifications/foundation.git
				synced 2025-11-04 11:39:39 +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