From 24c29e632f45993969cd7176fe40d238b329991a Mon Sep 17 00:00:00 2001 From: AlexDev_ <56083016+alexdev03@users.noreply.github.com> Date: Tue, 3 Dec 2024 22:08:24 +0100 Subject: [PATCH] 1.7.3 - Placeholders Replacements & 1.21.4 (#236) * Added PlaceholderReplacements * Removed server display names * Fixed disconnect problem * Added support for 1.21.4 --- build.gradle | 2 +- docs/Config-File.md | 15 ++- docs/Placeholders-Replaments.md | 88 ++++++++++++ gradle.properties | 2 +- .../william278/velocitab/config/Group.java | 2 + .../velocitab/config/Placeholder.java | 125 +++++++++++++----- .../config/PlaceholderReplacement.java | 25 ++++ .../william278/velocitab/config/Settings.java | 15 --- .../velocitab/config/TabGroups.java | 12 ++ .../velocitab/hook/PAPIProxyBridgeHook.java | 17 +++ .../hook/VelocitabMiniExpansion.java | 2 +- .../miniconditions/MiniConditionManager.java | 2 +- .../velocitab/packet/ScoreboardManager.java | 2 +- .../velocitab/player/TabPlayer.java | 14 +- .../net/william278/velocitab/tab/Nametag.java | 4 +- .../velocitab/tab/PlayerTabList.java | 1 + .../velocitab/tab/TabListListener.java | 4 + 17 files changed, 259 insertions(+), 73 deletions(-) create mode 100644 docs/Placeholders-Replaments.md create mode 100644 src/main/java/net/william278/velocitab/config/PlaceholderReplacement.java diff --git a/build.gradle b/build.gradle index c6967a7..5d33a47 100644 --- a/build.gradle +++ b/build.gradle @@ -165,7 +165,7 @@ tasks { velocityVersion("${velocity_api_version}-SNAPSHOT") downloadPlugins { - modrinth ("papiproxybridge", papi) + github ("WiIIiam278", "PAPIProxyBridge", "1.7.1", "PAPIProxyBridge-Velocity-1.7.1.jar") modrinth ("miniplaceholders", "2.2.4") } } diff --git a/docs/Config-File.md b/docs/Config-File.md index 4621be5..737ea34 100644 --- a/docs/Config-File.md +++ b/docs/Config-File.md @@ -28,10 +28,6 @@ fallback_enabled: true fallback_group: default # Whether to show all players from all groups in the TAB list. show_all_players_from_all_groups: false -# Define custom names to be shown in the TAB list for specific server names. -# If no custom display name is provided for a server, its original name will be used. -server_display_names: - very-long-server-name: VLSN # Whether to enable the PAPIProxyBridge hook for PAPI support enable_papi_hook: true # How long in seconds to cache PAPI placeholders for, in milliseconds. (0 to disable) @@ -101,6 +97,14 @@ groups: sorting_placeholders: - '%role_weight%' - '%username_lower%' + placeholder_replaments: + '%current_date_weekday_en-US%': + - placeholder: Monday + replacement: Monday + - placeholder: Tuesday + replacement: Tuesday + - placeholder: Else + replacement: Other day collisions: false header_footer_update_rate: 1000 placeholder_update_rate: 1000 @@ -127,3 +131,6 @@ You can use various placeholders that will be replaced with values (for example, ### Server Links For Minecraft 1.21+ clients, Velocitab supports specifying a list of URLs that will be sent to display in the player pause menu. See [[Server Links]] for more information. + +### Placeholder Replaments +Velocitab supports replacing values of placeholders with other values. See [[Placeholders Replaments]] for more information. \ No newline at end of file diff --git a/docs/Placeholders-Replaments.md b/docs/Placeholders-Replaments.md new file mode 100644 index 0000000..f1e0bd5 --- /dev/null +++ b/docs/Placeholders-Replaments.md @@ -0,0 +1,88 @@ + +Velocitab supports placeholder replacements, which allow you to replace a placeholder with a different value. This is useful for things like changing the text of a date placeholder to a localized version, changing the text of a biome placeholder to a color or you can use a vanish placeholder to show a player's vanish status if the placeholder returns just a boolean (true/false). + + +## Configuring + +Placeholder replacements are configured in the `placeholder_replaments` section of the every Tab Group. +You can specify a list of replacements for a placeholder, and the replacements will be applied in the order they are listed. + +The replacements are specified as a list of objects with two properties: `placeholder` and `replacement`. +`placeholder` is the placeholder to replace, and `replacement` is the replacement text. + +### Example section +```yaml +placeholder_replaments: + '%current_date_weekday_en-US%': + - placeholder: Monday + replacement: Monday + - placeholder: Tuesday + replacement: Tuesday + - placeholder: Else + replacement: Other day + '%player_world_type%': + - placeholder: Overworld + replacement: 'Overworld' + - placeholder: Nether + replacement: 'Nether' + - placeholder: End + replacement: 'End' + '%player_biome%': + - placeholder: PLAINS + replacement: Plains + - placeholder: DESERT + replacement: Desert + - placeholder: RIVER + replacement: River +``` + +## Specified cases + +### Vanish status +If you want to show a player's vanish status, for example, you can use the `%advancedvanish_is_vanished%` placeholder. +This placeholder returns a boolean value, so you can use it to show a player's vanish status. + +For example, if you wanted to show a player's vanish status as a color, you could use the following replacements: +```yaml +placeholder_replaments: + '%advancedvanish_is_vanished%': + - placeholder: Yes + replacement: Vanished + - placeholder: No + replacement: Not vanished +``` + +### Else clause +If you don't want to specify every possible value for a placeholder, you can use the `ELSE` placeholder. +This placeholder will be replaced with the replacement text of the first replacement that doesn't have a placeholder. + +For example, if you wanted to show the current date as a color, you could use the following replacements: +```yaml +placeholder_replaments: + '%current_date_weekday_en-US%': + - placeholder: Monday + replacement: Monday + - placeholder: Tuesday + replacement: Tuesday + - placeholder: ELSE + replacement: Other day +``` + +### Placeholder not present in a server +If you have a group with multiple servers, and you have a placeholder that is not present in one of the servers, you can use the `%%` as a placeholder it will handle the case where the placeholder is not present in the server. + +```yaml +placeholder_replaments: + '%huskhomes_homes_count%': + - placeholder: '%huskhomes_homes_count%' + replacement: No homes in this server +``` + +If you want you can also set the replacement as an empty string, which will be replaced with the empty string. + +```yaml +placeholder_replaments: + '%huskhomes_homes_count%': + - placeholder: '%huskhomes_homes_count%' + replacement: '' +``` \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 72b8ddd..df8a289 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ javaVersion=17 org.gradle.jvmargs='-Dfile.encoding=UTF-8' org.gradle.daemon=true -plugin_version=1.7.2 +plugin_version=1.7.3 plugin_archive=velocitab plugin_description=A beautiful and versatile TAB list plugin for Velocity proxies diff --git a/src/main/java/net/william278/velocitab/config/Group.java b/src/main/java/net/william278/velocitab/config/Group.java index b2672fe..439b575 100644 --- a/src/main/java/net/william278/velocitab/config/Group.java +++ b/src/main/java/net/william278/velocitab/config/Group.java @@ -31,6 +31,7 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.event.Level; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -46,6 +47,7 @@ public record Group( Nametag nametag, Set servers, List sortingPlaceholders, + Map> placeholderReplaments, boolean collisions, int headerFooterUpdateRate, int placeholderUpdateRate, diff --git a/src/main/java/net/william278/velocitab/config/Placeholder.java b/src/main/java/net/william278/velocitab/config/Placeholder.java index 7e2a98a..598f2d2 100644 --- a/src/main/java/net/william278/velocitab/config/Placeholder.java +++ b/src/main/java/net/william278/velocitab/config/Placeholder.java @@ -19,6 +19,8 @@ package net.william278.velocitab.config; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.server.RegisteredServer; import it.unimi.dsi.fastutil.Pair; @@ -36,8 +38,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; -import java.util.Locale; -import java.util.Map; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; import java.util.regex.Matcher; @@ -95,7 +96,7 @@ public enum Placeholder { }), USERNAME((plugin, player) -> player.getCustomName().orElse(player.getPlayer().getUsername())), USERNAME_LOWER((plugin, player) -> player.getCustomName().orElse(player.getPlayer().getUsername()).toLowerCase()), - SERVER((plugin, player) -> player.getServerDisplayName(plugin)), + SERVER((plugin, player) -> player.getServerName()), PING((plugin, player) -> Long.toString(player.getPlayer().getPing())), PREFIX((plugin, player) -> player.getRole().getPrefix() .orElse(getPlaceholderFallback(plugin, "%luckperms_prefix%"))), @@ -126,6 +127,9 @@ public enum Placeholder { "*LESS*", "*LESS2*", "*GREATER*", "*GREATER2*" ); + private final static String VEL_PLACEHOLDER = " result = processRelationalPlaceholders(format, plugin); - format = result.right(); - format = replacePlaceholders(format, plugin, player); - - return format; + public static Pair> replaceInternal(@NotNull String format, @NotNull Velocitab plugin, @Nullable TabPlayer player) { + format = processRelationalPlaceholders(format, plugin); + return replacePlaceholders(format, plugin, player); } - private static Pair processRelationalPlaceholders(@NotNull String format, @NotNull Velocitab plugin) { - boolean foundRelational = false; - if (plugin.getFormatter().equals(Formatter.MINIMESSAGE) && format.contains(" entry : SYMBOL_SUBSTITUTES_2.entrySet()) { @@ -223,25 +222,58 @@ public enum Placeholder { } } - return Pair.of(foundRelational, format); + return format; } @NotNull - private static String replacePlaceholders(@NotNull String format, @NotNull Velocitab plugin, @Nullable TabPlayer player) { + private static Pair> replacePlaceholders(@NotNull String format, @NotNull Velocitab plugin, + @Nullable TabPlayer player) { + final Map replacedPlaceholders = Maps.newHashMap(); for (Placeholder placeholder : values()) { - Matcher matcher = placeholder.pattern.matcher(format); + final Matcher matcher = placeholder.pattern.matcher(format); if (placeholder.parameterised) { - format = matcher.replaceAll(matchResult -> - Matcher.quoteReplacement( - placeholder.replacer.apply(StringUtils.chop(matchResult.group().replace("%" + placeholder.name().toLowerCase(), "") - .replaceFirst("_", "")) - , plugin, player) - )); + format = matcher.replaceAll(matchResult -> { + final String replacement = placeholder.replacer.apply(StringUtils.chop(matchResult.group().replace("%" + placeholder.name().toLowerCase(), "") + .replaceFirst("_", "")), plugin, player); + replacedPlaceholders.put(matchResult.group(), replacement); + return Matcher.quoteReplacement(replacement); + }); } else { - format = matcher.replaceAll(matchResult -> Matcher.quoteReplacement(placeholder.replacer.apply(null, plugin, player))); + format = matcher.replaceAll(matchResult -> { + final String replacement = placeholder.replacer.apply(null, plugin, player); + replacedPlaceholders.put(matchResult.group(), replacement); + return Matcher.quoteReplacement(replacement); + }); } } - return format; + return Pair.of(format, replacedPlaceholders); + } + + @NotNull + private static String applyPlaceholderReplacements(@NotNull String text, @NotNull TabPlayer player, + @NotNull Map parsed) { + for (final Map.Entry> entry : player.getGroup().placeholderReplaments().entrySet()) { + if (!parsed.containsKey(entry.getKey())) { + continue; + } + + final String replaced = parsed.get(entry.getKey()); + final Optional replacement = entry.getValue().stream() + .filter(r -> r.placeholder().equalsIgnoreCase(replaced)) + .findFirst(); + + if (replacement.isPresent()) { + text = text.replace(entry.getKey(), replacement.get().replacement()); + } else { + final Optional elseReplacement = entry.getValue().stream() + .filter(r -> r.placeholder().equalsIgnoreCase(ELSE_PLACEHOLDER)) + .findFirst(); + if (elseReplacement.isPresent()) { + text = text.replace(entry.getKey(), elseReplacement.get().replacement()); + } + } + } + return applyPlaceholders(text, parsed); } public static CompletableFuture replace(@NotNull String format, @NotNull Velocitab plugin, @@ -251,22 +283,47 @@ public enum Placeholder { return CompletableFuture.completedFuture(""); } - final String replaced = replaceInternal(format, plugin, player); - - if (!PLACEHOLDER_PATTERN.matcher(replaced).find()) { - return CompletableFuture.completedFuture(replaced); + final Pair> replaced = replaceInternal(format, plugin, player); + if (!PLACEHOLDER_PATTERN.matcher(replaced.first()).find()) { + return CompletableFuture.completedFuture(replaced.first()); } + final List placeholders = extractPlaceholders(replaced.first()); return plugin.getPAPIProxyBridgeHook() - .map(hook -> hook.formatPlaceholders(replaced, player.getPlayer()) + .map(hook -> hook.parsePlaceholders(placeholders, player.getPlayer()) .exceptionally(e -> { plugin.log(Level.ERROR, "An error occurred whilst parsing placeholders: " + e.getMessage()); - return replaced; + return Map.of(); }) ) - .orElse(CompletableFuture.completedFuture(replaced)).exceptionally(e -> { + .orElse(CompletableFuture.completedFuture(Maps.newHashMap())).exceptionally(e -> { plugin.log(Level.ERROR, "An error occurred whilst parsing placeholders: " + e.getMessage()); - return replaced; - }); + return Map.of(); + }) + .thenApply(m -> applyPlaceholderReplacements(format, player, mergeMaps(m, replaced.second()))); + } + + @NotNull + private static String applyPlaceholders(@NotNull String text, @NotNull Map replacements) { + for (Map.Entry entry : replacements.entrySet()) { + text = text.replace(entry.getKey(), entry.getValue()); + } + return text; + } + + @NotNull + private static Map mergeMaps(@NotNull Map map1, @NotNull Map map2) { + map1.putAll(map2); + return map1; + } + + @NotNull + private static List extractPlaceholders(@NotNull String text) { + final List placeholders = Lists.newArrayList(); + final Matcher matcher = PLACEHOLDER_PATTERN.matcher(text); + while (matcher.find()) { + placeholders.add(matcher.group()); + } + return placeholders; } } diff --git a/src/main/java/net/william278/velocitab/config/PlaceholderReplacement.java b/src/main/java/net/william278/velocitab/config/PlaceholderReplacement.java new file mode 100644 index 0000000..ea012fe --- /dev/null +++ b/src/main/java/net/william278/velocitab/config/PlaceholderReplacement.java @@ -0,0 +1,25 @@ +/* + * This file is part of Velocitab, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.william278.velocitab.config; + +import org.jetbrains.annotations.NotNull; + +public record PlaceholderReplacement(@NotNull String placeholder, @NotNull String replacement) { +} diff --git a/src/main/java/net/william278/velocitab/config/Settings.java b/src/main/java/net/william278/velocitab/config/Settings.java index 19a769d..584c249 100644 --- a/src/main/java/net/william278/velocitab/config/Settings.java +++ b/src/main/java/net/william278/velocitab/config/Settings.java @@ -67,10 +67,6 @@ public class Settings implements ConfigValidator { @Comment("Whether to show all players from all groups in the TAB list.") private boolean showAllPlayersFromAllGroups = false; - @Comment("Define custom names to be shown in the TAB list for specific server names." - + "\nIf no custom display name is provided for a server, its original name will be used.") - private Map serverDisplayNames = Map.of("very-long-server-name", "VLSN"); - @Comment("Whether to enable the PAPIProxyBridge hook for PAPI support") private boolean enablePapiHook = true; @@ -112,17 +108,6 @@ public class Settings implements ConfigValidator { ) ); - /** - * Get display name for the server - * - * @param serverName The server name - * @return The display name, or the server name if no display name is defined - */ - @NotNull - public String getServerDisplayName(@NotNull String serverName) { - return serverDisplayNames.getOrDefault(serverName, serverName); - } - @NotNull public List getUrlsForGroup(@NotNull Group group) { return serverLinks.stream() diff --git a/src/main/java/net/william278/velocitab/config/TabGroups.java b/src/main/java/net/william278/velocitab/config/TabGroups.java index f671566..a7adf99 100644 --- a/src/main/java/net/william278/velocitab/config/TabGroups.java +++ b/src/main/java/net/william278/velocitab/config/TabGroups.java @@ -54,6 +54,13 @@ public class TabGroups implements ConfigValidator { new Nametag("", ""), Set.of("lobby", "survival", "creative", "minigames", "skyblock", "prison", "hub"), List.of("%role_weight%", "%username_lower%"), + new LinkedHashMap<>() {{ + put("%current_date_weekday_en-US%", List.of( + new PlaceholderReplacement("Monday", "Monday"), + new PlaceholderReplacement("Tuesday", "Tuesday"), + new PlaceholderReplacement("Else", "Other day") + )); + }}, false, 1000, 1000, @@ -140,6 +147,10 @@ public class TabGroups implements ConfigValidator { if (group.sortingPlaceholders() == null) { missingKeys.put(group, "sortingPlaceholders"); } + + if (group.placeholderReplaments() == null) { + missingKeys.put(group, "placeholderReplaments"); + } } return missingKeys; @@ -160,6 +171,7 @@ public class TabGroups implements ConfigValidator { group.nametag() == null ? DEFAULT_GROUP.nametag() : group.nametag(), group.servers() == null ? DEFAULT_GROUP.servers() : group.servers(), group.sortingPlaceholders() == null ? DEFAULT_GROUP.sortingPlaceholders() : group.sortingPlaceholders(), + group.placeholderReplaments() == null ? DEFAULT_GROUP.placeholderReplaments() : group.placeholderReplaments(), group.collisions(), group.headerFooterUpdateRate(), group.placeholderUpdateRate(), diff --git a/src/main/java/net/william278/velocitab/hook/PAPIProxyBridgeHook.java b/src/main/java/net/william278/velocitab/hook/PAPIProxyBridgeHook.java index 8ac6158..dc36fab 100644 --- a/src/main/java/net/william278/velocitab/hook/PAPIProxyBridgeHook.java +++ b/src/main/java/net/william278/velocitab/hook/PAPIProxyBridgeHook.java @@ -19,11 +19,15 @@ package net.william278.velocitab.hook; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.velocitypowered.api.proxy.Player; import net.william278.papiproxybridge.api.PlaceholderAPI; import net.william278.velocitab.Velocitab; import org.jetbrains.annotations.NotNull; +import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; public class PAPIProxyBridgeHook extends Hook { @@ -41,4 +45,17 @@ public class PAPIProxyBridgeHook extends Hook { return api.formatPlaceholders(input, player.getUniqueId()); } + public CompletableFuture> parsePlaceholders(@NotNull List input, @NotNull Player player) { + final Map map = Maps.newConcurrentMap(); + final List> futures = Lists.newArrayList(); + + for (String s : input) { + final CompletableFuture future = formatPlaceholders(s, player); + futures.add(future); + future.thenAccept(r -> map.put(s, r)); + } + + return CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).thenApply(v -> map); + } + } diff --git a/src/main/java/net/william278/velocitab/hook/VelocitabMiniExpansion.java b/src/main/java/net/william278/velocitab/hook/VelocitabMiniExpansion.java index 8b767d6..34dfba3 100644 --- a/src/main/java/net/william278/velocitab/hook/VelocitabMiniExpansion.java +++ b/src/main/java/net/william278/velocitab/hook/VelocitabMiniExpansion.java @@ -98,7 +98,7 @@ public class VelocitabMiniExpansion { } final String value = fixValue(popAll(queue)); - String replaced = Placeholder.replaceInternal(value, plugin, targetPlayer); + final String replaced = Placeholder.replaceInternal(value, plugin, targetPlayer).first(); return Tag.selfClosingInserting(MiniMessage.miniMessage().deserialize(replaced, MiniPlaceholders.getAudienceGlobalPlaceholders(audience))); })); diff --git a/src/main/java/net/william278/velocitab/hook/miniconditions/MiniConditionManager.java b/src/main/java/net/william278/velocitab/hook/miniconditions/MiniConditionManager.java index 103de61..f8ba8c7 100644 --- a/src/main/java/net/william278/velocitab/hook/miniconditions/MiniConditionManager.java +++ b/src/main/java/net/william278/velocitab/hook/miniconditions/MiniConditionManager.java @@ -110,7 +110,7 @@ public class MiniConditionManager { } - condition = Placeholder.replaceInternal(condition, plugin, tabPlayer.get()); + condition = Placeholder.replaceInternal(condition, plugin, tabPlayer.get()).first(); final String falseValue = processFalseValue(parameters.get(2)); final String expression = buildExpression(condition); return evaluateAndFormatCondition(expression, target, audience, parameters.get(1), falseValue); diff --git a/src/main/java/net/william278/velocitab/packet/ScoreboardManager.java b/src/main/java/net/william278/velocitab/packet/ScoreboardManager.java index 59e8ff7..0c4b1dc 100644 --- a/src/main/java/net/william278/velocitab/packet/ScoreboardManager.java +++ b/src/main/java/net/william278/velocitab/packet/ScoreboardManager.java @@ -422,7 +422,7 @@ public class ScoreboardManager { .mapping(0x5C, MINECRAFT_1_20_2, false) .mapping(0x5E, MINECRAFT_1_20_3, false) .mapping(0x60, MINECRAFT_1_20_5, false) - .mapping(0x67, MINECRAFT_1_21_2, false); + .mapping(0x67, MINECRAFT_1_21_4, false); packetRegistration.register(); } catch (Throwable e) { plugin.log(Level.ERROR, "Failed to register UpdateTeamsPacket", e); diff --git a/src/main/java/net/william278/velocitab/player/TabPlayer.java b/src/main/java/net/william278/velocitab/player/TabPlayer.java index 8e1648e..82bc7cb 100644 --- a/src/main/java/net/william278/velocitab/player/TabPlayer.java +++ b/src/main/java/net/william278/velocitab/player/TabPlayer.java @@ -123,18 +123,6 @@ public final class TabPlayer implements Comparable { return plugin.getTabGroups().getPosition(group); } - /** - * Get the display name of the server the player is currently on. - * Affected by server aliases defined in the config. - * - * @param plugin The plugin instance - * @return The display name of the server - */ - @NotNull - public String getServerDisplayName(@NotNull Velocitab plugin) { - return plugin.getSettings().getServerDisplayName(getServerName()); - } - @NotNull public CompletableFuture getDisplayName(@NotNull Velocitab plugin) { final String format = formatGroup(); @@ -166,7 +154,7 @@ public final class TabPlayer implements Comparable { displayName = displayName.replace(placeholder, value); } - displayName = Placeholder.replaceInternal(displayName, plugin, this); + displayName = Placeholder.replaceInternal(displayName, plugin, this).first(); return lastDisplayName = displayName; } diff --git a/src/main/java/net/william278/velocitab/tab/Nametag.java b/src/main/java/net/william278/velocitab/tab/Nametag.java index 9fef606..75c867d 100644 --- a/src/main/java/net/william278/velocitab/tab/Nametag.java +++ b/src/main/java/net/william278/velocitab/tab/Nametag.java @@ -32,13 +32,13 @@ public record Nametag(@NotNull String prefix, @NotNull String suffix) { @NotNull public Component getPrefixComponent(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer, @NotNull TabPlayer target) { - final String formatted = Placeholder.replaceInternal(prefix, plugin, tabPlayer); + final String formatted = Placeholder.replaceInternal(prefix, plugin, tabPlayer).first(); return plugin.getFormatter().format(formatted, tabPlayer, target, plugin); } @NotNull public Component getSuffixComponent(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer, @NotNull TabPlayer target) { - final String formatted = Placeholder.replaceInternal(suffix, plugin, tabPlayer); + final String formatted = Placeholder.replaceInternal(suffix, plugin, tabPlayer).first(); return plugin.getFormatter().format(formatted, tabPlayer, target, plugin); } diff --git a/src/main/java/net/william278/velocitab/tab/PlayerTabList.java b/src/main/java/net/william278/velocitab/tab/PlayerTabList.java index 1cd54f3..09729ce 100644 --- a/src/main/java/net/william278/velocitab/tab/PlayerTabList.java +++ b/src/main/java/net/william278/velocitab/tab/PlayerTabList.java @@ -26,6 +26,7 @@ import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.player.TabList; import com.velocitypowered.api.proxy.player.TabListEntry; import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.protocol.packet.UpsertPlayerInfoPacket; import com.velocitypowered.proxy.tablist.KeyedVelocityTabList; diff --git a/src/main/java/net/william278/velocitab/tab/TabListListener.java b/src/main/java/net/william278/velocitab/tab/TabListListener.java index d03848a..541bda6 100644 --- a/src/main/java/net/william278/velocitab/tab/TabListListener.java +++ b/src/main/java/net/william278/velocitab/tab/TabListListener.java @@ -160,6 +160,9 @@ public class TabListListener { @SuppressWarnings("deprecation") @Subscribe(order = PostOrder.CUSTOM, priority = Short.MIN_VALUE) public void onPlayerQuit(@NotNull DisconnectEvent event) { + if(event.getLoginStatus() == DisconnectEvent.LoginStatus.CONFLICTING_LOGIN) { + return; + } if (event.getLoginStatus() != DisconnectEvent.LoginStatus.SUCCESSFUL_LOGIN) { checkDelayedDisconnect(event); return; @@ -180,6 +183,7 @@ public class TabListListener { return; } + tabList.removeOfflinePlayer(player); tabList.removeTabListUUID(event.getPlayer().getUniqueId()); }).delay(750, TimeUnit.MILLISECONDS).schedule(); }