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