From f7903781820a5c1eaa82858b2bcbb0cc3c9fec62 Mon Sep 17 00:00:00 2001 From: William Date: Tue, 7 Mar 2023 11:39:47 +0000 Subject: [PATCH] Add support for PlaceholderAPI placeholders via PAPIProxyBridge (#17) * Start PAPI hook work * Finish adding PlaceholderAPI support --- .github/workflows/java_ci.yml | 1 + build.gradle | 3 +- .../net/william278/velocitab/Velocitab.java | 35 ++++++++++++----- .../velocitab/config/Placeholder.java | 12 ++++-- .../william278/velocitab/config/Settings.java | 8 ++++ .../{luckperms => hook}/LuckPermsHook.java | 2 +- .../william278/velocitab/hook/PapiHook.java | 22 +++++++++++ .../velocitab/player/TabPlayer.java | 10 +++-- .../velocitab/tab/PlayerTabList.java | 38 ++++++++++--------- 9 files changed, 94 insertions(+), 37 deletions(-) rename src/main/java/net/william278/velocitab/{luckperms => hook}/LuckPermsHook.java (98%) create mode 100644 src/main/java/net/william278/velocitab/hook/PapiHook.java diff --git a/.github/workflows/java_ci.yml b/.github/workflows/java_ci.yml index 1aaef31..3872d17 100644 --- a/.github/workflows/java_ci.yml +++ b/.github/workflows/java_ci.yml @@ -45,6 +45,7 @@ jobs: dependencies: | protocolize | depends | 2.2.5 luckperms | suggests | * + papiproxybridge | suggests | * game-versions: | 1.19 1.19.2 diff --git a/build.gradle b/build.gradle index 67a5e39..877c51b 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ version = "1.0.1-${versionMetadata()}" repositories { mavenCentral() maven { url = 'https://repo.papermc.io/repository/maven-public/' } - maven { url = 'https://jitpack.io' } + maven { url = 'https://jitpack.io/' } maven { url = 'https://repo.minebench.de/' } maven { url = 'https://mvn.exceptionflug.de/repository/exceptionflug-public/' } } @@ -20,6 +20,7 @@ dependencies { compileOnly 'net.luckperms:api:5.4' compileOnly 'dev.simplix:protocolize-api:2.2.5' compileOnly 'io.netty:netty-codec-http:4.1.89.Final' + compileOnly 'net.william278:PAPIProxyBridge:1.0' implementation 'org.apache.commons:commons-text:1.10.0' implementation 'net.william278:Annotaml:2.0.1' diff --git a/src/main/java/net/william278/velocitab/Velocitab.java b/src/main/java/net/william278/velocitab/Velocitab.java index baeb366..18b1387 100644 --- a/src/main/java/net/william278/velocitab/Velocitab.java +++ b/src/main/java/net/william278/velocitab/Velocitab.java @@ -10,7 +10,8 @@ import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; import net.william278.annotaml.Annotaml; import net.william278.velocitab.config.Settings; -import net.william278.velocitab.luckperms.LuckPermsHook; +import net.william278.velocitab.hook.LuckPermsHook; +import net.william278.velocitab.hook.PapiHook; import net.william278.velocitab.packet.ScoreboardManager; import net.william278.velocitab.player.Role; import net.william278.velocitab.player.TabPlayer; @@ -34,7 +35,8 @@ import java.util.Optional; authors = {"William278"}, dependencies = { @Dependency(id = "protocolize"), - @Dependency(id = "luckperms", optional = true) + @Dependency(id = "luckperms", optional = true), + @Dependency(id = "papiproxybridge", optional = true) } ) public class Velocitab { @@ -45,6 +47,7 @@ public class Velocitab { private final Path dataDirectory; private PlayerTabList tabList; private LuckPermsHook luckPerms; + private PapiHook papiHook; private ScoreboardManager scoreboardManager; @Inject @@ -85,17 +88,29 @@ public class Velocitab { return Optional.ofNullable(luckPerms); } + public Optional getPapiHook() { + return Optional.ofNullable(papiHook); + } + private void loadHooks() { - if (server.getPluginManager().getPlugin("luckperms").isEmpty()) { - return; + // If LuckPerms is present, load the hook + if (server.getPluginManager().getPlugin("luckperms").isPresent()) { + try { + luckPerms = new LuckPermsHook(this); + logger.info("Successfully hooked into LuckPerms"); + } catch (IllegalArgumentException e) { + logger.warn("LuckPerms was not loaded: " + e.getMessage(), e); + } } - // If LuckPerms is present, load the hook - try { - luckPerms = new LuckPermsHook(this); - logger.info("Successfully hooked into LuckPerms"); - } catch (IllegalArgumentException e) { - logger.warn("LuckPerms was not loaded: " + e.getMessage(), e); + // If PAPIProxyBridge is present, load the hook + if (settings.isPapiHookEnabled() && server.getPluginManager().getPlugin("papiproxybridge").isPresent()) { + try { + papiHook = new PapiHook(); + logger.info("Successfully hooked into PAPIProxyBridge"); + } catch (IllegalArgumentException e) { + logger.warn("PAPIProxyBridge was not loaded: " + e.getMessage(), e); + } } } diff --git a/src/main/java/net/william278/velocitab/config/Placeholder.java b/src/main/java/net/william278/velocitab/config/Placeholder.java index 7401cab..5043fa8 100644 --- a/src/main/java/net/william278/velocitab/config/Placeholder.java +++ b/src/main/java/net/william278/velocitab/config/Placeholder.java @@ -8,6 +8,7 @@ import org.jetbrains.annotations.NotNull; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; public enum Placeholder { @@ -35,17 +36,20 @@ public enum Placeholder { this.formatter = formatter; } - @NotNull - public static String format(@NotNull String format, @NotNull Velocitab plugin, @NotNull TabPlayer player) { + public static CompletableFuture format(@NotNull String format, @NotNull Velocitab plugin, @NotNull TabPlayer player) { for (Placeholder placeholder : values()) { format = format.replace("%" + placeholder.name().toLowerCase() + "%", placeholder.formatter.apply(plugin, player)); } + final String replaced = format; - return format; + return plugin.getPapiHook() + .map(hook -> hook.formatPapiPlaceholders(replaced, player.getPlayer())) + .orElse(CompletableFuture.completedFuture(replaced)); } + // Replace __ so that it is not seen as underline when the string is formatted. + @NotNull private static String escape(String replace) { - // Replace __ so that it is not seen as underline when the string is formatted. return replace.replace("__", "_\\_"); } } diff --git a/src/main/java/net/william278/velocitab/config/Settings.java b/src/main/java/net/william278/velocitab/config/Settings.java index eb259c0..a2dabcb 100644 --- a/src/main/java/net/william278/velocitab/config/Settings.java +++ b/src/main/java/net/william278/velocitab/config/Settings.java @@ -1,5 +1,6 @@ package net.william278.velocitab.config; +import net.william278.annotaml.YamlComment; import net.william278.annotaml.YamlFile; import net.william278.annotaml.YamlKey; import net.william278.velocitab.BuildConstants; @@ -22,6 +23,9 @@ public class Settings { private String footer = "[There are currently %players_online%/%max_players_online% players online](gray)"; @YamlKey("format") private String format = "&7[%server%] &f%prefix%%username%"; + @YamlComment("Enable the PlaceholderAPI hook (requires PAPIProxyBridge to be installed on proxy & spigot servers)") + @YamlKey("enable_papi_hook") + private boolean enablePapiHook = true; @YamlKey("excluded_servers") private ArrayList excludedServers = new ArrayList<>(); @YamlKey(("update_rate")) @@ -49,6 +53,10 @@ public class Settings { return excludedServers.contains(serverName); } + public boolean isPapiHookEnabled() { + return enablePapiHook; + } + public int getUpdateRate() { return updateRate; } diff --git a/src/main/java/net/william278/velocitab/luckperms/LuckPermsHook.java b/src/main/java/net/william278/velocitab/hook/LuckPermsHook.java similarity index 98% rename from src/main/java/net/william278/velocitab/luckperms/LuckPermsHook.java rename to src/main/java/net/william278/velocitab/hook/LuckPermsHook.java index bb32936..e89d413 100644 --- a/src/main/java/net/william278/velocitab/luckperms/LuckPermsHook.java +++ b/src/main/java/net/william278/velocitab/hook/LuckPermsHook.java @@ -1,4 +1,4 @@ -package net.william278.velocitab.luckperms; +package net.william278.velocitab.hook; import com.velocitypowered.api.proxy.Player; import net.luckperms.api.LuckPerms; diff --git a/src/main/java/net/william278/velocitab/hook/PapiHook.java b/src/main/java/net/william278/velocitab/hook/PapiHook.java new file mode 100644 index 0000000..f15ffea --- /dev/null +++ b/src/main/java/net/william278/velocitab/hook/PapiHook.java @@ -0,0 +1,22 @@ +package net.william278.velocitab.hook; + +import com.velocitypowered.api.proxy.Player; +import net.william278.papiproxybridge.api.PlaceholderAPI; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.CompletableFuture; + +public class PapiHook { + + private final PlaceholderAPI api; + + public PapiHook() { + this.api = PlaceholderAPI.getInstance(); + } + + public CompletableFuture formatPapiPlaceholders(@NotNull String input, @NotNull Player player) { + return api.formatPlaceholders(input, player.getUniqueId()); + } + + +} diff --git a/src/main/java/net/william278/velocitab/player/TabPlayer.java b/src/main/java/net/william278/velocitab/player/TabPlayer.java index 6377bc2..5522183 100644 --- a/src/main/java/net/william278/velocitab/player/TabPlayer.java +++ b/src/main/java/net/william278/velocitab/player/TabPlayer.java @@ -8,6 +8,8 @@ import net.william278.velocitab.config.Placeholder; import net.william278.velocitab.tab.PlayerTabList; import org.jetbrains.annotations.NotNull; +import java.util.concurrent.CompletableFuture; + public final class TabPlayer implements Comparable { private final Player player; private final Role role; @@ -37,8 +39,9 @@ public final class TabPlayer implements Comparable { } @NotNull - public Component getDisplayName(@NotNull Velocitab plugin) { - return new MineDown(Placeholder.format(plugin.getSettings().getFormat(), plugin, this)).toComponent(); + public CompletableFuture getDisplayName(@NotNull Velocitab plugin) { + return Placeholder.format(plugin.getSettings().getFormat(), plugin, this) + .thenApply(formatted -> new MineDown(formatted).toComponent()); } @NotNull @@ -47,7 +50,8 @@ public final class TabPlayer implements Comparable { } public void sendHeaderAndFooter(@NotNull PlayerTabList tabList) { - this.player.sendPlayerListHeaderAndFooter(tabList.getHeader(this), tabList.getFooter(this)); + tabList.getHeader(this).thenAccept(header -> tabList.getFooter(this) + .thenAccept(footer -> player.sendPlayerListHeaderAndFooter(header, footer))); } @Override diff --git a/src/main/java/net/william278/velocitab/tab/PlayerTabList.java b/src/main/java/net/william278/velocitab/tab/PlayerTabList.java index b820188..bf5ad1b 100644 --- a/src/main/java/net/william278/velocitab/tab/PlayerTabList.java +++ b/src/main/java/net/william278/velocitab/tab/PlayerTabList.java @@ -17,6 +17,7 @@ import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeUnit; @@ -67,8 +68,8 @@ public class PlayerTabList { tabList.getEntries().stream() .filter(e -> e.getProfile().getId().equals(player.getPlayer().getUniqueId())).findFirst() .ifPresentOrElse( - entry -> entry.setDisplayName(player.getDisplayName(plugin)), - () -> tabList.addEntry(createEntry(player, tabList)) + entry -> player.getDisplayName(plugin).thenAccept(entry::setDisplayName), + () -> createEntry(player, tabList).thenAccept(tabList::addEntry) ); addPlayerToTabList(player, tabPlayer); player.sendHeaderAndFooter(this); @@ -80,13 +81,13 @@ public class PlayerTabList { } @NotNull - private TabListEntry createEntry(@NotNull TabPlayer player, @NotNull TabList tabList) { - return TabListEntry.builder() + private CompletableFuture createEntry(@NotNull TabPlayer player, @NotNull TabList tabList) { + return player.getDisplayName(plugin).thenApply(name -> TabListEntry.builder() .profile(player.getPlayer().getGameProfile()) - .displayName(player.getDisplayName(plugin)) + .displayName(name) .latency(0) .tabList(tabList) - .build(); + .build()); } private void addPlayerToTabList(@NotNull TabPlayer player, @NotNull TabPlayer newPlayer) { @@ -98,9 +99,9 @@ public class PlayerTabList { .getTabList().getEntries().stream() .filter(e -> e.getProfile().getId().equals(newPlayer.getPlayer().getUniqueId())).findFirst() .ifPresentOrElse( - entry -> entry.setDisplayName(newPlayer.getDisplayName(plugin)), - () -> player.getPlayer().getTabList() - .addEntry(createEntry(newPlayer, player.getPlayer().getTabList())) + entry -> newPlayer.getDisplayName(plugin).thenAccept(entry::setDisplayName), + () -> createEntry(newPlayer, player.getPlayer().getTabList()) + .thenAccept(entry -> player.getPlayer().getTabList().addEntry(entry)) ); plugin.getScoreboardManager().updateRoles( player.getPlayer(), @@ -129,25 +130,26 @@ public class PlayerTabList { public void onUpdate(@NotNull TabPlayer tabPlayer) { plugin.getServer().getScheduler() - .buildTask(plugin, () -> players.forEach(player -> { + .buildTask(plugin, () -> players.forEach(player -> tabPlayer.getDisplayName(plugin).thenAccept(displayName -> { player.getPlayer().getTabList().getEntries().stream() .filter(e -> e.getProfile().getId().equals(tabPlayer.getPlayer().getUniqueId())).findFirst() - .ifPresent(entry -> entry.setDisplayName(tabPlayer.getDisplayName(plugin))); + .ifPresent(entry -> entry.setDisplayName(displayName)); plugin.getScoreboardManager().updateRoles(player.getPlayer(), tabPlayer.getTeamName(), tabPlayer.getPlayer().getUsername()); - })) + }))) .delay(500, TimeUnit.MILLISECONDS) .schedule(); } - @NotNull - public Component getHeader(@NotNull TabPlayer player) { - return new MineDown(Placeholder.format(plugin.getSettings().getHeader(), plugin, player)).toComponent(); + public CompletableFuture getHeader(@NotNull TabPlayer player) { + return Placeholder.format(plugin.getSettings().getHeader(), plugin, player) + .thenApply(header -> new MineDown(header).toComponent()); + } - @NotNull - public Component getFooter(@NotNull TabPlayer player) { - return new MineDown(Placeholder.format(plugin.getSettings().getFooter(), plugin, player)).toComponent(); + public CompletableFuture getFooter(@NotNull TabPlayer player) { + return Placeholder.format(plugin.getSettings().getFooter(), plugin, player) + .thenApply(header -> new MineDown(header).toComponent()); } private void updateTimer(int updateRate) {