Add support for PlaceholderAPI placeholders via PAPIProxyBridge (#17)

* Start PAPI hook work

* Finish adding PlaceholderAPI support
This commit is contained in:
William 2023-03-07 11:39:47 +00:00 committed by GitHub
parent 7d85ed6cfe
commit f790378182
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 94 additions and 37 deletions

View File

@ -45,6 +45,7 @@ jobs:
dependencies: |
protocolize | depends | 2.2.5
luckperms | suggests | *
papiproxybridge | suggests | *
game-versions: |
1.19
1.19.2

View File

@ -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'

View File

@ -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<PapiHook> 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);
}
}
}

View File

@ -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<String> 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("__", "_\\_");
}
}

View File

@ -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<String> 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;
}

View File

@ -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;

View File

@ -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<String> formatPapiPlaceholders(@NotNull String input, @NotNull Player player) {
return api.formatPlaceholders(input, player.getUniqueId());
}
}

View File

@ -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<TabPlayer> {
private final Player player;
private final Role role;
@ -37,8 +39,9 @@ public final class TabPlayer implements Comparable<TabPlayer> {
}
@NotNull
public Component getDisplayName(@NotNull Velocitab plugin) {
return new MineDown(Placeholder.format(plugin.getSettings().getFormat(), plugin, this)).toComponent();
public CompletableFuture<Component> 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<TabPlayer> {
}
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

View File

@ -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<TabListEntry> 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<Component> 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<Component> getFooter(@NotNull TabPlayer player) {
return Placeholder.format(plugin.getSettings().getFooter(), plugin, player)
.thenApply(header -> new MineDown(header).toComponent());
}
private void updateTimer(int updateRate) {