From 77ee036cbf4f1c580cb563265befced551a27e0d Mon Sep 17 00:00:00 2001 From: Vankka Date: Sun, 30 Apr 2023 13:55:33 +0300 Subject: [PATCH] Implement nms advancement listener --- .../award/BukkitAdvancementListener.java | 130 +++++++++++++++++- 1 file changed, 125 insertions(+), 5 deletions(-) diff --git a/bukkit/bukkit1_12/src/main/java/com/discordsrv/bukkit/listener/award/BukkitAdvancementListener.java b/bukkit/bukkit1_12/src/main/java/com/discordsrv/bukkit/listener/award/BukkitAdvancementListener.java index 94eb84e6..8c5d6818 100644 --- a/bukkit/bukkit1_12/src/main/java/com/discordsrv/bukkit/listener/award/BukkitAdvancementListener.java +++ b/bukkit/bukkit1_12/src/main/java/com/discordsrv/bukkit/listener/award/BukkitAdvancementListener.java @@ -1,22 +1,142 @@ package com.discordsrv.bukkit.listener.award; import com.discordsrv.common.DiscordSRV; +import com.discordsrv.common.component.util.ComponentUtil; +import net.kyori.adventure.platform.bukkit.BukkitComponentSerializer; +import org.bukkit.Bukkit; +import org.bukkit.advancement.Advancement; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.player.PlayerAdvancementDoneEvent; +import java.lang.reflect.Method; +import java.util.Arrays; + public class BukkitAdvancementListener extends AbstractBukkitAwardListener { + private final NMS nms; + public BukkitAdvancementListener(DiscordSRV discordSRV, IBukkitAwardForwarder forwarder) { super(discordSRV, forwarder); + + String className = Bukkit.getServer().getClass().getName(); + String[] packageParts = className.split("\\."); + if (packageParts.length != 5) { + this.nms = null; + logger.error("Server does not have NMS, incompatible with advancements."); + return; + } + + String version = packageParts[3]; + NMS nms = null; + try { + if ((version.startsWith("v1_19") && !version.startsWith("v1_19_R1") && !version.startsWith("v1_19_R2")) + || version.startsWith("v1_2")) { + // 1.19.4+ + nms = new NMS("org.bukkit.craftbukkit." + version + ".advancement.CraftAdvancement", + "k", "d", "i", "a"); + } else { + // <1.19.4 + nms = new NMS("org.bukkit.craftbukkit." + version + ".advancement.CraftAdvancement", + "j", "c", "i", "a"); + } + } catch (Throwable t) { + logger.error("Could not get NMS methods for advancements."); + logger.debug(t); + } + this.nms = nms; } @EventHandler(priority = EventPriority.MONITOR) public void onPlayerAdvancementDone(PlayerAdvancementDoneEvent event) { - // TODO: - // - NMS: check if the advancement should be broadcasted to chat, returning if not - // - run checkIfShouldSkip from parent - // - NMS: get advancement and/or 'advancement award message' - // - run forwarder.publishEvent + if (nms == null) { + return; + } + + try { + ReturnData data = nms.getData(event.getAdvancement()); + if (data == null || checkIfShouldSkip(event.getPlayer())) { + return; + } + + forwarder.publishEvent( + event.getPlayer(), + data.nameJson != null ? ComponentUtil.toAPI(BukkitComponentSerializer.gson().deserialize(data.nameJson)) : null, + data.titleJson != null ? ComponentUtil.toAPI(BukkitComponentSerializer.gson().deserialize(data.titleJson)) : null, + false + ); + } catch (ReflectiveOperationException e) { + logger.debug("Failed to get advancement data", e); + } + } + + private static class NMS { + + private final Method handleMethod; + private final Method advancementNameMethod; + private final Method advancementDisplayMethod; + private final Method broadcastToChatMethod; + private final Method titleMethod; + private final Method toJsonMethod; + + public NMS( + String craftAdvancementClassName, + String nameMethodName, + String displayMethodName, + String broadcastToChatMethodName, + String titleMethodName + ) throws ReflectiveOperationException { + Class clazz = Class.forName(craftAdvancementClassName); + handleMethod = clazz.getDeclaredMethod("getHandle"); + Class nmsClass = handleMethod.getReturnType(); + advancementNameMethod = nmsClass.getDeclaredMethod(nameMethodName); + advancementDisplayMethod = nmsClass.getDeclaredMethod(displayMethodName); + Class displayClass = advancementDisplayMethod.getReturnType(); + broadcastToChatMethod = displayClass.getDeclaredMethod(broadcastToChatMethodName); + titleMethod = displayClass.getDeclaredMethod(titleMethodName); + + Class serializer = Class.forName(titleMethod.getReturnType().getName() + "$ChatSerializer"); + toJsonMethod = Arrays.stream(serializer.getDeclaredMethods()) + .filter(method -> method.getReturnType().equals(String.class)) + .findAny().orElseThrow(() -> new NoSuchMethodException("ChatSerializer toJson")); + } + + public ReturnData getData(Advancement advancement) throws ReflectiveOperationException { + Object nms = handleMethod.invoke(advancement); + Object display = advancementDisplayMethod.invoke(nms); + if (display == null) { + // Not something that would be displayed in chat + return null; + } + + boolean broadcastToChat = (boolean) broadcastToChatMethod.invoke(display); + if (!broadcastToChat) { + // Not something that would be displayed in chat + return null; + } + + Object nameChat = advancementNameMethod.invoke(nms); + Object titleChat = titleMethod.invoke(display); + + return new ReturnData( + toJson(nameChat), + toJson(titleChat) + ); + } + + private String toJson(Object chat) throws ReflectiveOperationException { + return (String) toJsonMethod.invoke(chat); + } + } + + private static class ReturnData { + + private final String nameJson; + private final String titleJson; + + public ReturnData(String nameJson, String titleJson) { + this.nameJson = nameJson; + this.titleJson = titleJson; + } } }