diff --git a/api/src/main/java/net/momirealms/customnameplates/common/util/ReflectionUtils.java b/api/src/main/java/net/momirealms/customnameplates/common/util/ReflectionUtils.java index 8d8a448..2709fba 100644 --- a/api/src/main/java/net/momirealms/customnameplates/common/util/ReflectionUtils.java +++ b/api/src/main/java/net/momirealms/customnameplates/common/util/ReflectionUtils.java @@ -200,6 +200,30 @@ public class ReflectionUtils { return null; } + @Nullable + public static Method getMethod(final Class clazz, Class returnType, final String[] possibleMethodNames, final Class... parameterTypes) { + outer: + for (Method method : clazz.getMethods()) { + if (method.getParameterCount() != parameterTypes.length) { + continue; + } + Class[] types = method.getParameterTypes(); + for (int i = 0; i < types.length; i++) { + if (types[i] != parameterTypes[i]) { + continue outer; + } + } + for (String name : possibleMethodNames) { + if (name.equals(method.getName())) { + if (returnType.isAssignableFrom(method.getReturnType())) { + return method; + } + } + } + } + return null; + } + @Nullable public static Method getMethod(final Class clazz, Class returnType, int index) { int i = 0; diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/requirement/AbstractRequirementManager.java b/backend/src/main/java/net/momirealms/customnameplates/backend/requirement/AbstractRequirementManager.java index b6a08c1..c4fa1f4 100644 --- a/backend/src/main/java/net/momirealms/customnameplates/backend/requirement/AbstractRequirementManager.java +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/requirement/AbstractRequirementManager.java @@ -66,7 +66,12 @@ public abstract class AbstractRequirementManager implements RequirementManager { } private void registerInternalRequirements() { - this.registerRequirement((args, interval) -> new LaggyRequirement(interval, (int) args), "laggy"); + this.registerRequirement((args, interval) -> new LaggyRequirement(interval, (int) args), "laggy-test"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + Requirement[] requirements = parseRequirements(section); + return new InvertedRequirement(interval, requirements); + }, "inverted"); this.registerRequirement((args, interval) -> { Section section = ConfigUtils.safeCast(args, Section.class); if (section == null) return Requirement.empty(); diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/requirement/builtin/InvertedRequirement.java b/backend/src/main/java/net/momirealms/customnameplates/backend/requirement/builtin/InvertedRequirement.java new file mode 100644 index 0000000..c946de8 --- /dev/null +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/requirement/builtin/InvertedRequirement.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.backend.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.Requirement; +import net.momirealms.customnameplates.backend.requirement.AbstractRequirement; + +public class InvertedRequirement extends AbstractRequirement { + + private final Requirement[] requirements; + + public InvertedRequirement(int refreshInterval, Requirement[] requirements) { + super(refreshInterval); + this.requirements = requirements; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + if (requirements.length == 0) return false; + for (Requirement requirement : requirements) { + if (requirement.isSatisfied(p1, p2)) return false; + } + return true; + } + + @Override + public String type() { + return "inverted"; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + InvertedRequirement that = (InvertedRequirement) object; + int length = requirements.length; + if (that.requirements.length != length) + return false; + for (int i = 0; i < length; i++) { + Object e1 = requirements[i]; + Object e2 = that.requirements[i]; + if (e1 == e2) + continue; + if (!e1.equals(e2)) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + int hash = 271; + for (Requirement requirement : requirements) { + hash += requirement.hashCode(); + } + return hash; + } +} diff --git a/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitPlatform.java b/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitPlatform.java index c82500c..3bc0d38 100644 --- a/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitPlatform.java +++ b/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitPlatform.java @@ -19,6 +19,7 @@ package net.momirealms.customnameplates.bukkit; import it.unimi.dsi.fastutil.ints.IntList; import me.clip.placeholderapi.PlaceholderAPI; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.momirealms.customnameplates.api.CNPlayer; import net.momirealms.customnameplates.api.ConfigManager; import net.momirealms.customnameplates.api.CustomNameplates; @@ -43,6 +44,7 @@ import org.bukkit.Location; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; +import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.function.Consumer; import java.util.regex.Pattern; @@ -56,6 +58,7 @@ public class BukkitPlatform implements Platform { private final boolean geyser; private final boolean floodGate; private final boolean libsDisguises; + private static Object serializer; private static final HashMap> packetFunctions = new HashMap<>(); @@ -87,6 +90,12 @@ public class BukkitPlatform implements Platform { this.geyser = Bukkit.getPluginManager().getPlugin("Geyser-Spigot") != null; this.floodGate = Bukkit.getPluginManager().getPlugin("floodgate") != null; this.libsDisguises = Bukkit.getPluginManager().getPlugin("LibsDisguises") != null; + try { + Object builder = Reflections.method$GsonComponentSerializer$builder.invoke(null); + serializer = Reflections.method$GsonComponentSerializer$Builder$build.invoke(builder); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } } static { @@ -101,19 +110,33 @@ public class BukkitPlatform implements Platform { if (!player.shouldCNTakeOverActionBar()) return; try { // some plugins would send null to clear the actionbar, what a bad solution - Object component = Optional.ofNullable(Reflections.field$ClientboundSetActionBarTextPacket$text.get(packet)).orElse(Reflections.instance$Component$empty); - Object contents = Reflections.method$Component$getContents.invoke(component); - if (contents == null) { - return; + Object component = Reflections.field$ClientboundSetActionBarTextPacket$text.get(packet); + if (component == null && !VersionHelper.isVersionNewerThan1_20_5()) { + // paper api, must be from other plugins + Object adventureComponent = Reflections.field$ClientboundSetActionBarTextPacket$adventure$text.get(packet); + if (adventureComponent != null) { + String json = (String) Reflections.method$ComponentSerializer$serialize.invoke(serializer, adventureComponent); + CustomNameplates.getInstance().getScheduler().async().execute(() -> { + ((ActionBarManagerImpl) CustomNameplates.getInstance().getActionBarManager()).handleActionBarPacket(player, AdventureHelper.jsonToMiniMessage(json)); + }); + } else { + // bungeecord components ? + } + } else { + // mc components + Object contents = Reflections.method$Component$getContents.invoke(component); + if (contents == null) { + return; + } + if (Reflections.clazz$ScoreContents.isAssignableFrom(contents.getClass())) { + //String name = scoreContentNameFunction.apply(Reflections.field$ScoreContents$name.get(contents)); + String objective = (String) Reflections.field$ScoreContents$objective.get(contents); + if ("actionbar".equals(objective)) return; + } + CustomNameplates.getInstance().getScheduler().async().execute(() -> { + ((ActionBarManagerImpl) CustomNameplates.getInstance().getActionBarManager()).handleActionBarPacket(player, AdventureHelper.minecraftComponentToMiniMessage(component)); + }); } - if (Reflections.clazz$ScoreContents.isAssignableFrom(contents.getClass())) { - //String name = scoreContentNameFunction.apply(Reflections.field$ScoreContents$name.get(contents)); - String objective = (String) Reflections.field$ScoreContents$objective.get(contents); - if ("actionbar".equals(objective)) return; - } - CustomNameplates.getInstance().getScheduler().async().execute(() -> { - ((ActionBarManagerImpl) CustomNameplates.getInstance().getActionBarManager()).handleActionBarPacket(player, AdventureHelper.minecraftComponentToMiniMessage(component)); - }); } catch (ReflectiveOperationException e) { CustomNameplates.getInstance().getPluginLogger().severe("Failed to handle ClientboundSetActionBarTextPacket", e); } diff --git a/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/Reflections.java b/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/Reflections.java index eccd291..ca395c5 100644 --- a/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/Reflections.java +++ b/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/Reflections.java @@ -1162,4 +1162,58 @@ public class Reflections { throw new RuntimeException(e); } } + + public static final Class clazz$AdventureComponent = requireNonNull( + ReflectionUtils.getClazz( + "net{}kyori{}adventure{}text{}Component".replace("{}", ".") + ) + ); + + // <= 1.20.4 + public static final Field field$ClientboundSetActionBarTextPacket$adventure$text = + ReflectionUtils.getDeclaredField( + clazz$ClientboundSetActionBarTextPacket, clazz$AdventureComponent, 0 + ); + + public static final Class clazz$ComponentSerializer = requireNonNull( + ReflectionUtils.getClazz( + "net{}kyori{}adventure{}text{}serializer{}ComponentSerializer".replace("{}", ".") + ) + ); + + public static final Class clazz$GsonComponentSerializer = requireNonNull( + ReflectionUtils.getClazz( + "net{}kyori{}adventure{}text{}serializer{}gson{}GsonComponentSerializer".replace("{}", ".") + ) + ); + + public static final Class clazz$GsonComponentSerializer$Builder = requireNonNull( + ReflectionUtils.getClazz( + "net{}kyori{}adventure{}text{}serializer{}gson{}GsonComponentSerializer$Builder".replace("{}", ".") + ) + ); + + public static final Method method$GsonComponentSerializer$builder = requireNonNull( + ReflectionUtils.getMethod( + clazz$GsonComponentSerializer, clazz$GsonComponentSerializer$Builder + ) + ); + + public static final Method method$GsonComponentSerializer$Builder$build = requireNonNull( + ReflectionUtils.getMethod( + clazz$GsonComponentSerializer$Builder, clazz$GsonComponentSerializer + ) + ); + + public static final Method method$ComponentSerializer$serialize = requireNonNull( + ReflectionUtils.getMethod( + clazz$ComponentSerializer, Object.class, new String[] {"serialize"}, clazz$AdventureComponent + ) + ); + + public static final Method method$ComponentSerializer$deserialize = requireNonNull( + ReflectionUtils.getMethod( + clazz$ComponentSerializer, Object.class, new String[] {"deserialize"}, Object.class + ) + ); }