Add optional support for MiniMessage and hook for MiniPlaceholders (#18)

This commit is contained in:
William 2023-03-11 20:23:47 +00:00 committed by GitHub
parent cb011c4e66
commit b431ee2165
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 168 additions and 43 deletions

View File

@ -46,6 +46,7 @@ jobs:
protocolize | depends | 2.2.5 protocolize | depends | 2.2.5
luckperms | suggests | * luckperms | suggests | *
papiproxybridge | suggests | * papiproxybridge | suggests | *
miniplaceholders | suggests | *
game-versions: | game-versions: |
1.16.5 1.16.5
1.17.1 1.17.1

View File

@ -26,8 +26,10 @@ dependencies {
compileOnly 'net.luckperms:api:5.4' compileOnly 'net.luckperms:api:5.4'
compileOnly 'dev.simplix:protocolize-api:2.2.5' compileOnly 'dev.simplix:protocolize-api:2.2.5'
compileOnly 'io.netty:netty-codec-http:4.1.89.Final' compileOnly 'io.netty:netty-codec-http:4.1.89.Final'
compileOnly 'io.github.miniplaceholders:miniplaceholders-api:2.0.0'
compileOnly 'net.william278:PAPIProxyBridge:1.0' compileOnly 'net.william278:PAPIProxyBridge:1.0'
compileOnly 'org.projectlombok:lombok:1.18.26' compileOnly 'org.projectlombok:lombok:1.18.26'
compileOnly 'net.kyori:adventure-text-minimessage:4.12.0'
implementation 'org.apache.commons:commons-text:1.10.0' implementation 'org.apache.commons:commons-text:1.10.0'
implementation 'net.william278:Annotaml:2.0.1' implementation 'net.william278:Annotaml:2.0.1'

View File

@ -3,5 +3,5 @@ javaVersion=16
org.gradle.jvmargs='-Dfile.encoding=UTF-8' org.gradle.jvmargs='-Dfile.encoding=UTF-8'
org.gradle.daemon=true org.gradle.daemon=true
plugin_version=1.1.1 plugin_version=1.2
plugin_archive=velocitab plugin_archive=velocitab

View File

@ -7,9 +7,14 @@ import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import de.themoep.minedown.adventure.MineDown;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.william278.annotaml.Annotaml; import net.william278.annotaml.Annotaml;
import net.william278.velocitab.config.Settings; import net.william278.velocitab.config.Settings;
import net.william278.velocitab.hook.Hook;
import net.william278.velocitab.hook.LuckPermsHook; import net.william278.velocitab.hook.LuckPermsHook;
import net.william278.velocitab.hook.MiniPlaceholdersHook;
import net.william278.velocitab.hook.PapiHook; import net.william278.velocitab.hook.PapiHook;
import net.william278.velocitab.packet.ScoreboardManager; import net.william278.velocitab.packet.ScoreboardManager;
import net.william278.velocitab.player.Role; import net.william278.velocitab.player.Role;
@ -22,12 +27,12 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Optional; import java.util.Optional;
@Plugin( @Plugin(id = "velocitab")
id = "velocitab"
)
public class Velocitab { public class Velocitab {
private Settings settings; private Settings settings;
@ -35,8 +40,7 @@ public class Velocitab {
private final Logger logger; private final Logger logger;
private final Path dataDirectory; private final Path dataDirectory;
private PlayerTabList tabList; private PlayerTabList tabList;
private LuckPermsHook luckPerms; private List<Hook> hooks;
private PapiHook papiHook;
private ScoreboardManager scoreboardManager; private ScoreboardManager scoreboardManager;
@Inject @Inject
@ -76,34 +80,38 @@ public class Velocitab {
} }
} }
private <H extends Hook> Optional<H> getHook(@NotNull Class<H> hookType) {
return hooks.stream()
.filter(hook -> hook.getClass().equals(hookType))
.map(hookType::cast)
.findFirst();
}
public Optional<LuckPermsHook> getLuckPerms() { public Optional<LuckPermsHook> getLuckPerms() {
return Optional.ofNullable(luckPerms); return getHook(LuckPermsHook.class);
} }
public Optional<PapiHook> getPapiHook() { public Optional<PapiHook> getPapiHook() {
return Optional.ofNullable(papiHook); return getHook(PapiHook.class);
}
public Optional<MiniPlaceholdersHook> getMiniPlaceholdersHook() {
return getHook(MiniPlaceholdersHook.class);
} }
private void loadHooks() { private void loadHooks() {
// If LuckPerms is present, load the hook this.hooks = new ArrayList<>();
if (server.getPluginManager().getPlugin("luckperms").isPresent()) { Hook.AVAILABLE.forEach(availableHook -> availableHook.apply(this).ifPresent(hooks::add));
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 @NotNull
if (settings.isPapiHookEnabled() && server.getPluginManager().getPlugin("papiproxybridge").isPresent()) { public Component formatText(@NotNull String text, @NotNull TabPlayer player) {
try { return switch (getSettings().getFormatter()) {
papiHook = new PapiHook(); case MINEDOWN -> new MineDown(text).toComponent();
logger.info("Successfully hooked into PAPIProxyBridge"); case MINIMESSAGE -> getMiniPlaceholdersHook()
} catch (IllegalArgumentException e) { .map(hook -> hook.format(text, player.getPlayer()))
logger.warn("PAPIProxyBridge was not loaded: " + e.getMessage(), e); .orElse(MiniMessage.miniMessage().deserialize(text));
} };
}
} }
private void prepareScoreboardManager() { private void prepareScoreboardManager() {

View File

@ -20,27 +20,42 @@ import java.util.Map;
Placeholders: %players_online%, %max_players_online%, %local_players_online%, %current_date%, %current_time%, %username%, %server%, %ping%, %prefix%, %suffix%, %role%""") Placeholders: %players_online%, %max_players_online%, %local_players_online%, %current_date%, %current_time%, %username%, %server%, %ping%, %prefix%, %suffix%, %role%""")
public class Settings { public class Settings {
@YamlKey("headers") @YamlKey("headers")
private Map<String, String> headers = Map.of("default", "&rainbow&Running Velocitab by William278"); private Map<String, String> headers = Map.of("default", "&rainbow&Running Velocitab by William278");
@YamlKey("footers") @YamlKey("footers")
private Map<String, String> footers = Map.of("default", "[There are currently %players_online%/%max_players_online% players online](gray)"); private Map<String, String> footers = Map.of("default", "[There are currently %players_online%/%max_players_online% players online](gray)");
@YamlKey("formats") @YamlKey("formats")
private Map<String, String> formats = Map.of("default", "&7[%server%] &f%prefix%%username%"); private Map<String, String> formats = Map.of("default", "&7[%server%] &f%prefix%%username%");
@Getter
@YamlComment("Which text formatter to use (MINEDOWN or MINIMESSAGE)")
@YamlKey("formatting_type")
private Formatter formatter = Formatter.MINEDOWN;
@Getter @Getter
@YamlKey("server_groups") @YamlKey("server_groups")
@YamlComment("The servers in each group of servers") @YamlComment("The servers in each group of servers")
private Map<String, List<String>> serverGroups = Map.of("default", List.of("lobby1", "lobby2", "lobby3")); private Map<String, List<String>> serverGroups = Map.of("default", List.of("lobby1", "lobby2", "lobby3"));
@Getter @Getter
@YamlKey("fallback_enabled") @YamlKey("fallback_enabled")
@YamlComment("All servers which are not in other groups will be put in the fallback group.\n\"false\" will exclude them from Velocitab.") @YamlComment("All servers which are not in other groups will be put in the fallback group.\n\"false\" will exclude them from Velocitab.")
private boolean fallbackEnabled = true; private boolean fallbackEnabled = true;
@Getter @Getter
@YamlKey("fallback_group") @YamlKey("fallback_group")
@YamlComment("The formats to use for the fallback group.") @YamlComment("The formats to use for the fallback group.")
private String fallbackGroup = "default"; private String fallbackGroup = "default";
@YamlKey("enable_papi_hook") @YamlKey("enable_papi_hook")
private boolean enablePapiHook = true; private boolean enablePapiHook = true;
@YamlKey("enable_miniplaceholders_hook")
@YamlComment("If you are using MINIMESSAGE formatting, enable this to support MiniPlaceholders in formatting.")
private boolean enableMiniPlaceholdersHook = true;
@YamlKey("update_rate") @YamlKey("update_rate")
@YamlComment("How often to periodically update the TAB list, including header and footer, for all users.\nWill only update on player join/leave if set to 0.") @YamlComment("How often to periodically update the TAB list, including header and footer, for all users.\nWill only update on player join/leave if set to 0.")
private int updateRate = 0; private int updateRate = 0;
@ -50,8 +65,10 @@ public class Settings {
plugin.getServer().getAllServers().stream().map(server -> server.getServerInfo().getName()).toList() plugin.getServer().getAllServers().stream().map(server -> server.getServerInfo().getName()).toList()
); );
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public Settings(){} public Settings() {
}
@NotNull @NotNull
public String getHeader(String serverGroup) { public String getHeader(String serverGroup) {
@ -87,7 +104,19 @@ public class Settings {
return enablePapiHook; return enablePapiHook;
} }
public boolean isMiniPlaceholdersHookEnabled() {
return enableMiniPlaceholdersHook;
}
public int getUpdateRate() { public int getUpdateRate() {
return updateRate; return updateRate;
} }
/**
* Different formatting markup options for the TAB list
*/
public enum Formatter {
MINEDOWN,
MINIMESSAGE
}
} }

View File

@ -0,0 +1,58 @@
package net.william278.velocitab.hook;
import net.william278.velocitab.Velocitab;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
public abstract class Hook {
public static final List<Function<Velocitab, Optional<Hook>>> AVAILABLE = List.of(
(plugin -> {
if (isPluginAvailable(plugin, "luckperms")) {
try {
plugin.log("Successfully hooked into LuckPerms");
return Optional.of(new LuckPermsHook(plugin));
} catch (Exception e) {
plugin.log("LuckPerms hook was not loaded: " + e.getMessage(), e);
}
}
return Optional.empty();
}),
(plugin -> {
if (isPluginAvailable(plugin, "papiproxybridge") && plugin.getSettings().isPapiHookEnabled()) {
try {
plugin.log("Successfully hooked into PAPIProxyBridge");
return Optional.of(new PapiHook(plugin));
} catch (Exception e) {
plugin.log("PAPIProxyBridge hook was not loaded: " + e.getMessage(), e);
}
}
return Optional.empty();
}),
(plugin -> {
if (isPluginAvailable(plugin, "miniplaceholders") && plugin.getSettings().isMiniPlaceholdersHookEnabled()) {
try {
plugin.log("Successfully hooked into MiniPlaceholders");
return Optional.of(new MiniPlaceholdersHook(plugin));
} catch (Exception e) {
plugin.log("MiniPlaceholders hook was not loaded: " + e.getMessage(), e);
}
}
return Optional.empty();
})
);
protected final Velocitab plugin;
public Hook(@NotNull Velocitab plugin) {
this.plugin = plugin;
}
private static boolean isPluginAvailable(@NotNull Velocitab plugin, @NotNull String id) {
return plugin.getServer().getPluginManager().getPlugin(id).isPresent();
}
}

View File

@ -16,14 +16,13 @@ import org.jetbrains.annotations.Nullable;
import java.util.OptionalInt; import java.util.OptionalInt;
import java.util.UUID; import java.util.UUID;
public class LuckPermsHook { public class LuckPermsHook extends Hook {
private int highestWeight = Role.DEFAULT_WEIGHT; private int highestWeight = Role.DEFAULT_WEIGHT;
private final Velocitab plugin;
private final LuckPerms api; private final LuckPerms api;
public LuckPermsHook(@NotNull Velocitab plugin) throws IllegalStateException { public LuckPermsHook(@NotNull Velocitab plugin) throws IllegalStateException {
this.plugin = plugin; super(plugin);
this.api = LuckPermsProvider.get(); this.api = LuckPermsProvider.get();
api.getEventBus().subscribe(plugin, UserDataRecalculateEvent.class, this::onLuckPermsGroupUpdate); api.getEventBus().subscribe(plugin, UserDataRecalculateEvent.class, this::onLuckPermsGroupUpdate);
} }

View File

@ -0,0 +1,21 @@
package net.william278.velocitab.hook;
import io.github.miniplaceholders.api.MiniPlaceholders;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.william278.velocitab.Velocitab;
import org.jetbrains.annotations.NotNull;
public class MiniPlaceholdersHook extends Hook {
public MiniPlaceholdersHook(@NotNull Velocitab plugin) {
super(plugin);
}
@NotNull
public Component format(@NotNull String text, @NotNull Audience player) {
return MiniMessage.miniMessage().deserialize(text, MiniPlaceholders.getAudienceGlobalPlaceholders(player));
}
}

View File

@ -2,15 +2,17 @@ package net.william278.velocitab.hook;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import net.william278.papiproxybridge.api.PlaceholderAPI; import net.william278.papiproxybridge.api.PlaceholderAPI;
import net.william278.velocitab.Velocitab;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class PapiHook { public class PapiHook extends Hook {
private final PlaceholderAPI api; private final PlaceholderAPI api;
public PapiHook() { public PapiHook(@NotNull Velocitab plugin) {
super(plugin);
this.api = PlaceholderAPI.getInstance(); this.api = PlaceholderAPI.getInstance();
} }

View File

@ -53,7 +53,11 @@ public class ScoreboardManager {
} }
private void dispatchPacket(@NotNull UpdateTeamsPacket packet, @NotNull Player player) { private void dispatchPacket(@NotNull UpdateTeamsPacket packet, @NotNull Player player) {
Protocolize.playerProvider().player(player.getUniqueId()).sendPacket(packet); try {
Protocolize.playerProvider().player(player.getUniqueId()).sendPacket(packet);
} catch (Exception e) {
plugin.log("Failed to dispatch packet (is the client or server modded or using an illegal version?)", e);
}
} }
public void registerPacket() { public void registerPacket() {

View File

@ -1,7 +1,6 @@
package net.william278.velocitab.player; package net.william278.velocitab.player;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import de.themoep.minedown.adventure.MineDown;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.william278.velocitab.Velocitab; import net.william278.velocitab.Velocitab;
import net.william278.velocitab.config.Placeholder; import net.william278.velocitab.config.Placeholder;
@ -40,10 +39,9 @@ public final class TabPlayer implements Comparable<TabPlayer> {
@NotNull @NotNull
public CompletableFuture<Component> getDisplayName(@NotNull Velocitab plugin) { public CompletableFuture<Component> getDisplayName(@NotNull Velocitab plugin) {
return Placeholder.format(plugin.getSettings().getFormat( final String serverGroup = plugin.getSettings().getServerGroup(getServerName());
plugin.getSettings().getServerGroup(getServerName()) return Placeholder.format(plugin.getSettings().getFormat(serverGroup), plugin, this)
), plugin, this) .thenApply(formatted -> plugin.formatText(formatted, this));
.thenApply(formatted -> new MineDown(formatted).toComponent());
} }

View File

@ -8,7 +8,6 @@ import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.player.TabList; import com.velocitypowered.api.proxy.player.TabList;
import com.velocitypowered.api.proxy.player.TabListEntry; import com.velocitypowered.api.proxy.player.TabListEntry;
import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.proxy.server.ServerInfo;
import de.themoep.minedown.adventure.MineDown;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.william278.velocitab.Velocitab; import net.william278.velocitab.Velocitab;
import net.william278.velocitab.config.Placeholder; import net.william278.velocitab.config.Placeholder;
@ -74,7 +73,7 @@ public class PlayerTabList {
final Map<String, String> playerRoles = new HashMap<>(); final Map<String, String> playerRoles = new HashMap<>();
for (TabPlayer player : players) { for (TabPlayer player : players) {
if (!serversInGroup.get().contains(player.getServerName())) { if (serversInGroup.isPresent() && !serversInGroup.get().contains(player.getServerName())) {
continue; // Skip players on other servers continue; // Skip players on other servers
} }
playerRoles.put(player.getPlayer().getUsername(), player.getTeamName()); playerRoles.put(player.getPlayer().getUsername(), player.getTeamName());
@ -158,14 +157,14 @@ public class PlayerTabList {
public CompletableFuture<Component> getHeader(@NotNull TabPlayer player) { public CompletableFuture<Component> getHeader(@NotNull TabPlayer player) {
return Placeholder.format(plugin.getSettings().getHeader( return Placeholder.format(plugin.getSettings().getHeader(
plugin.getSettings().getServerGroup(player.getServerName())), plugin, player) plugin.getSettings().getServerGroup(player.getServerName())), plugin, player)
.thenApply(header -> new MineDown(header).toComponent()); .thenApply(header -> plugin.formatText(header, player));
} }
public CompletableFuture<Component> getFooter(@NotNull TabPlayer player) { public CompletableFuture<Component> getFooter(@NotNull TabPlayer player) {
return Placeholder.format(plugin.getSettings().getFooter( return Placeholder.format(plugin.getSettings().getFooter(
plugin.getSettings().getServerGroup(player.getServerName())), plugin, player) plugin.getSettings().getServerGroup(player.getServerName())), plugin, player)
.thenApply(header -> new MineDown(header).toComponent()); .thenApply(footer -> plugin.formatText(footer, player));
} }

View File

@ -19,6 +19,10 @@
{ {
"id": "papiproxybridge", "id": "papiproxybridge",
"optional": true "optional": true
},
{
"id": "miniplaceholders",
"optional": true
} }
], ],
"main": "net.william278.velocitab.Velocitab" "main": "net.william278.velocitab.Velocitab"