Merge remote-tracking branch 'origin/master'

This commit is contained in:
William 2024-08-11 13:32:20 +01:00
commit 4d7ffd75db
No known key found for this signature in database
19 changed files with 229 additions and 166 deletions

View File

@ -27,6 +27,7 @@ repositories {
maven { url = 'https://repo.william278.net/velocity/' } maven { url = 'https://repo.william278.net/velocity/' }
maven { url = 'https://repo.papermc.io/repository/maven-public/' } maven { url = 'https://repo.papermc.io/repository/maven-public/' }
maven { url = 'https://repo.william278.net/releases/' } maven { url = 'https://repo.william278.net/releases/' }
maven { url = 'https://repo.william278.net/snapshots/' }
maven { url = 'https://jitpack.io/' } maven { url = 'https://jitpack.io/' }
maven { url = 'https://repo.minebench.de/' } maven { url = 'https://repo.minebench.de/' }
} }
@ -50,6 +51,7 @@ dependencies {
implementation 'de.exlll:configlib-yaml:4.5.0' implementation 'de.exlll:configlib-yaml:4.5.0'
implementation 'org.apache.commons:commons-jexl3:3.4.0' implementation 'org.apache.commons:commons-jexl3:3.4.0'
implementation 'net.jodah:expiringmap:0.5.11' implementation 'net.jodah:expiringmap:0.5.11'
implementation("net.william278:velocityscoreboardapi:1.0.0-b2d9518")
annotationProcessor 'org.projectlombok:lombok:1.18.34' annotationProcessor 'org.projectlombok:lombok:1.18.34'
} }

View File

@ -11,9 +11,9 @@ To add additional frames of animation to a header format for a [server group](se
```yaml ```yaml
headers: headers:
- '&rainbow&Running Velocitab by William278' - '<rainbow>Running Velocitab by William278 & AlexDev_</rainbow>'
- '&rainbow:10&Running Velocitab by William278' - '<rainbow:10>Running Velocitab by William278 & AlexDev_</rainbow>'
- '&rainbow:20&Running Velocitab by William278' - '<rainbow:20>Running Velocitab by William278 & AlexDev_</rainbow>'
``` ```
</details> </details>
@ -35,47 +35,47 @@ Wondering how to make something like the above example? Here's how! This example
Please note this is not a complete tab_groups file; you will need to add the relevant sections to the correct part in your own Velocitab `tab_groups.yml`. Please note this is not a complete tab_groups file; you will need to add the relevant sections to the correct part in your own Velocitab `tab_groups.yml`.
```yaml ```yaml
headers: headers:
- '&rainbow&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:2&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:2>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:4&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:2>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:6&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:2>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:8&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:2>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:10&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:2>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:12&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:2>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:14&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:2>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:16&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:2>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:18&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:18>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:20&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:20>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:22&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:22>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:24&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:24>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:26&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:26>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:28&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:28>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:30&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:30>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:32&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:32>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:34&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:34>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:36&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:36>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:38&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:38>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:40&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:40>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:42&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:42>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:44&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:44>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:46&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:46>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:48&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:48>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:50&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:50>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:52&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:52>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:54&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:54>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:56&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:56>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:58&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:58>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
- '&rainbow:60&Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n' - '<rainbow:60>Velocitab ⭐ A super-simple (sorted!) Velocity TAB menu plugin\n</rainbow>'
footers: footers:
- | - |
\n&7For Velocity proxy servers: \n<gray>For Velocity proxy servers:</gray>
&#1bd96a-#6cffa9&https://modrinth.com/plugin/velocitab <gradient:#1bd96a:#6cffa9>https://modrinth.com/plugin/velocitab</gradient>
&#1bd96a-#6cffa9&https://william278.net/project/veloictab' <gradient:#1bd96a:#6cffa9>https://william278.net/project/veloictab</gradient>'
format: '&#999-#fff&[%server%] &f%username%' format: '<gradient:#999:#fff>[%server%] &f%username%</gradient>'
header_footer_update_rate: 200 header_footer_update_rate: 200
``` ```
In config.yml In config.yml
```yaml ```yaml
formatter: MINEDOWN formatter: MINIMESSAGE
``` ```
</details> </details>

View File

@ -19,8 +19,8 @@ check_for_updates: true
remove_nametags: true remove_nametags: true
# Whether to disable header and footer if they are empty and let backend servers handle them. # Whether to disable header and footer if they are empty and let backend servers handle them.
disable_header_footer_if_empty: true disable_header_footer_if_empty: true
# Which text formatter to use (MINEDOWN, MINIMESSAGE, or LEGACY) # Which text formatter to use (MINIMESSAGE, MINEDOWN or LEGACY)
formatter: MINEDOWN formatter: MINIMESSAGE
# All servers which are not in other groups will be put in the fallback group. # All servers which are not in other groups will be put in the fallback group.
# "false" will exclude them from Velocitab. # "false" will exclude them from Velocitab.
fallback_enabled: true fallback_enabled: true
@ -56,7 +56,7 @@ enable_plugin_message_api: true
# • If you supply a url with a 'bug_report' label, it will be shown if the player is disconnected. # • If you supply a url with a 'bug_report' label, it will be shown if the player is disconnected.
# • Specify a set of server groups each URL should be sent on. Use '*' to show a URL to all groups. # • Specify a set of server groups each URL should be sent on. Use '*' to show a URL to all groups.
server_links: server_links:
- label: '&#00fb9a&About Velocitab' - label: '<#00fb9a>About Velocitab</#00fb9a>'
url: 'https://william278.net/project/velocitab' url: 'https://william278.net/project/velocitab'
groups: groups:
- '*' - '*'
@ -79,29 +79,30 @@ server_links:
# ┗╸ Documentation: https://william278.net/docs/velocitab # ┗╸ Documentation: https://william278.net/docs/velocitab
groups: groups:
- name: default - name: default
headers: headers:
- '&rainbow&Running Velocitab by William278' - <rainbow:!2>Running Velocitab by William278 & AlexDev_</rainbow>
footers: footers:
- '[There are currently %players_online%/%max_players_online% players online](gray)' - <gray>There are currently %players_online%/%max_players_online% players online</gray>
format: '&7[%server%] &f%prefix%%username%' format: <gray>[%server%] %prefix%%username%</gray>
nametag: nametag:
prefix: '&f%prefix%' prefix: <white>%prefix%</white>
suffix: '&f%suffix%' suffix: <white>%suffix%</white>
servers: servers:
- ^lobby[^ ]* - skyblock
- survival - minigames
- creative - survival
- minigames - lobby
- skyblock - prison
- prison - creative
sorting_placeholders: - hub
- '%role_weight%' sorting_placeholders:
- '%username_lower%' - '%role_weight%'
collisions: false - '%username_lower%'
header_footer_update_rate: 1000 collisions: false
placeholder_update_rate: 1000 header_footer_update_rate: 1000
only_list_players_in_same_server: false placeholder_update_rate: 1000
only_list_players_in_same_server: false
``` ```
</details> </details>

View File

@ -1,12 +1,14 @@
Velocitab supports the full range of modern color formatting, including RGB colors and gradients. Both MineDown (_default_), MiniMessage and Legacy formatting are supported. To change which formatter is being used, change the `formatter` value in `config.yml` to `MINEDOWN`, `MINIMESSAGE` or `LEGACY` respectively. Velocitab supports the full range of modern color formatting, including RGB colors and gradients. Both MiniMessage (_default_), MineDown and Legacy formatting are supported. To change which formatter is being used, change the `formatter` value in `config.yml` to `MINEDOWN`, `MINIMESSAGE` or `LEGACY` respectively.
Formatting is applied on header, footer and player text for each server group, and is applied after [[Placeholders]] have been inserted. Formatting is applied on header, footer and player text for each server group, and is applied after [[Placeholders]] have been inserted.
## MineDown syntax reference
MineDown is the default formatter type, enabled by setting `formatter` to `MINEDOWN` in `config.yml`. See the [MineDown Syntax Reference](https://github.com/WiIIiam278/MineDown) on GitHub for the specification of how to format text with it.
## MiniMessage syntax reference ## MiniMessage syntax reference
MiniMessage formatting can be enabled by setting `formatter` to `MINIMESSAGE` in `config.yml`. See the [MiniMessage Syntax Reference](https://docs.advntr.dev/minimessage/format.html) on the Adventure Docs for how to format text with it. Using MiniMessage as the formatter also allows compatibility for using MiniPlaceholders in text. MiniMessage is the default formatter type, enabled by setting `formatter` to `MINIMESSAGE` in `config.yml`. See the [MiniMessage Syntax Reference](https://docs.advntr.dev/minimessage/format.html) on the Adventure Docs for how to format text with it. Using MiniMessage as the formatter also allows compatibility for using MiniPlaceholders in text.
## MineDown syntax reference
MineDown formatting can be enabled by setting `formatter` to `MINEDOWN` in `config.yml`. See the [MineDown Syntax Reference](https://github.com/WiIIiam278/MineDown) on GitHub for the specification of how to format text with it.
## Legacy formatting ## Legacy formatting
> **Warning:** The option for legacy formatting is provided only for backwards compatibility with other plugins. Please consider using the MineDown or MiniMessage options instead! > **Warning:** The option for legacy formatting is provided only for backwards compatibility with other plugins. Please consider using the MineDown or MiniMessage options instead!

View File

@ -41,3 +41,12 @@ Nametags must adhere to the following restrictions:
* Velocitab determines which color to use here based on the last color format used in the configured prefix (displayed before their name), downsampled from RGB if necessary. * Velocitab determines which color to use here based on the last color format used in the configured prefix (displayed before their name), downsampled from RGB if necessary.
* To control this, simply set the prefix format to end with a valid [team color](https://wiki.vg/Text_formatting#Colors) you want to use (e.g. `&4` for dark_red in Minedown formatting). * To control this, simply set the prefix format to end with a valid [team color](https://wiki.vg/Text_formatting#Colors) you want to use (e.g. `&4` for dark_red in Minedown formatting).
* Nametags cannot contain newlines (must be on a single line). * Nametags cannot contain newlines (must be on a single line).
## Bypassing formatting restrictions
UnlimitedNameTags is a spigot plugin that lets you create nametags with unlimited length and lines.
You can use rgb colors and gradients in usernames.
In order to use this plugin you need to disable the nametag feature in Velocitab by setting `remove_nametags` to `true` in the [`config.yml` file](config-file) and put an empty nametag (empty prefix & suffix) in the [`tab_groups.yml` file](Server-Groups.md).
You can find the plugin on [BuiltByBit](https://builtbybit.com/resources/unlimitednametags.46172/?ref=38685) and on [SpigotMC](https://www.spigotmc.org/resources/unlimitednametags.117526/).
![UnlimitedNameTags](https://i.imgur.com/VvHtqlY.gif)

View File

@ -17,9 +17,9 @@ rate to use for the group.
```yaml ```yaml
headers: headers:
- '&rainbow&Running Velocitab by William278' - '<rainbow>Running Velocitab by William278 & AlexDev_</rainbow>'
footers: footers:
- '[There are currently %players_online%/%max_players_online% players online](gray)' - '<gray>There are currently %players_online%/%max_players_online% players online</gray>'
``` ```
</details> </details>
@ -35,7 +35,7 @@ information.
<summary>Example of format</summary> <summary>Example of format</summary>
```yaml ```yaml
format: '&7[%server%] &f%prefix%%username%' format: '<gray>[%server%] %prefix%%username%</gray>'
``` ```
</details> </details>
@ -51,8 +51,8 @@ Player formats may only utilize one line.
```yaml ```yaml
nametag: nametag:
prefix: '&f%prefix%' prefix: '<white>%prefix%</white>'
suffix: '&f%suffix%' suffix: '<white>%suffix%</white>'
``` ```
</details> </details>
@ -148,10 +148,10 @@ placeholders in the TAB list will update. The default is 1000 milliseconds (1 se
groups: groups:
- name: lobbies - name: lobbies
headers: headers:
- '&rainbow&Running Velocitab by William278 on Lobbies!' - '<rainbow:!2>Running Velocitab by William278 & AlexDev_ on Lobbies!</rainbow>'
footers: footers:
- '[There are currently %players_online%/%max_players_online% players online](gray)' - '<gray>There are currently %players_online%/%max_players_online% players online</gray>'
format: '&7[%server%] &f%prefix%%username%' format: '<gray>[%server%] %prefix%%username%</gray>'
servers: servers:
- lobby - lobby
- hub - hub
@ -165,10 +165,10 @@ groups:
placeholder_update_rate: 1000 placeholder_update_rate: 1000
- name: creative - name: creative
headers: headers:
- '&rainbow&Running Velocitab by William278 on Creative!' - '<rainbow:!2>Running Velocitab by William278 & AlexDev_ on Creative!</rainbow>'
footers: footers:
- '[There are currently %players_online%/%max_players_online% players online](gray)' - '<gray>There are currently %players_online%/%max_players_online% players online</gray>'
format: '&7[%server%] &f%prefix%%username%' format: '<gray>[%server%] %prefix%%username%</gray>'
servers: servers:
- creative - creative
sorting_placeholders: sorting_placeholders:
@ -178,10 +178,10 @@ groups:
placeholder_update_rate: 1000 placeholder_update_rate: 1000
- name: survival - name: survival
headers: headers:
- '&rainbow&Running Velocitab by William278 on Survival!' - '<rainbow:!2>Running Velocitab by William278 & AlexDev_ on Survival!</rainbow>'
footers: footers:
- '[There are currently %players_online%/%max_players_online% players online](gray)' - '<gray>There are currently %players_online%/%max_players_online% players online</gray>'
format: '&7[%server%] &f%prefix%%username%' format: '<gray>[%server%] %prefix%%username%</gray>'
servers: servers:
- survival - survival
sorting_placeholders: sorting_placeholders:

View File

@ -114,7 +114,6 @@ public class Velocitab implements ConfigProvider, ScoreboardProvider, LoggerProv
@Subscribe @Subscribe
public void onProxyShutdown(@NotNull ProxyShutdownEvent event) { public void onProxyShutdown(@NotNull ProxyShutdownEvent event) {
// server.getScheduler().tasksByPlugin(this).forEach(ScheduledTask::cancel);
disableScoreboardManager(); disableScoreboardManager();
getLuckPermsHook().ifPresent(LuckPermsHook::closeEvent); getLuckPermsHook().ifPresent(LuckPermsHook::closeEvent);
getMiniPlaceholdersHook().ifPresent(MiniPlaceholdersHook::unregisterExpansion); getMiniPlaceholdersHook().ifPresent(MiniPlaceholdersHook::unregisterExpansion);

View File

@ -48,8 +48,8 @@ public enum Formatter {
), ),
MINIMESSAGE( MINIMESSAGE(
(text, player, viewer, plugin) -> plugin.getMiniPlaceholdersHook() (text, player, viewer, plugin) -> plugin.getMiniPlaceholdersHook()
.filter(hook -> viewer != null) .filter(hook -> player != null)
.map(hook -> hook.format(text, player.getPlayer(), viewer.getPlayer())) .map(hook -> hook.format(text, player.getPlayer(), viewer == null ? null : viewer.getPlayer()))
.orElse(MiniMessage.miniMessage().deserialize(text)), .orElse(MiniMessage.miniMessage().deserialize(text)),
(text) -> MiniMessage.miniMessage().escapeTags(text), (text) -> MiniMessage.miniMessage().escapeTags(text),
"MiniMessage", "MiniMessage",

View File

@ -22,8 +22,8 @@ package net.william278.velocitab.config;
import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.Pair;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.william278.velocitab.Velocitab; import net.william278.velocitab.Velocitab;
import net.william278.velocitab.hook.miniconditions.MiniConditionManager;
import net.william278.velocitab.player.TabPlayer; import net.william278.velocitab.player.TabPlayer;
import net.william278.velocitab.tab.Nametag; import net.william278.velocitab.tab.Nametag;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -114,13 +114,17 @@ public enum Placeholder {
.orElse(getPlaceholderFallback(plugin, "%luckperms_meta_" + param + "%"))); .orElse(getPlaceholderFallback(plugin, "%luckperms_meta_" + param + "%")));
private final static Pattern VELOCITAB_PATTERN = Pattern.compile("<velocitab_.*?>"); private final static Pattern VELOCITAB_PATTERN = Pattern.compile("<velocitab_.*?>");
private final static Pattern TEST = Pattern.compile("<.*?>");
private final static Pattern CONDITION_REPLACER = Pattern.compile("<velocitab_rel_condition:[^:]*:");
private final static Pattern PLACEHOLDER_PATTERN = Pattern.compile("%.*?%"); private final static Pattern PLACEHOLDER_PATTERN = Pattern.compile("%.*?%");
private final static Pattern CONDITIONAL_PATTERN = Pattern.compile("<velocitab_rel_condition:[^:]*:[^:]*:[^:]*>");
private final static String DELIMITER = ":::"; private final static String DELIMITER = ":::";
private final static String REL_SUBSTITUTE = "-REL-"; private final static Map<String, String> SYMBOL_SUBSTITUTES = Map.of(
public final static Map<String, String> SYMBOL_SUBSTITUTES = Map.of( "<", "*LESS*",
"<", "-COND-1", ">", "*GREATER*"
">", "-COND-2" );
private final static Map<String, String> SYMBOL_SUBSTITUTES_2 = Map.of(
"*LESS*", "*LESS2*",
"*GREATER*", "*GREATER2*"
); );
/** /**
@ -163,44 +167,65 @@ public enum Placeholder {
format = result.right(); format = result.right();
format = replacePlaceholders(format, plugin, player); format = replacePlaceholders(format, plugin, player);
if (result.left()) {
format = format.replace(REL_SUBSTITUTE, "%");
}
return format; return format;
} }
private static Pair<Boolean, String> processRelationalPlaceholders(@NotNull String format, @NotNull Velocitab plugin) { private static Pair<Boolean, String> processRelationalPlaceholders(@NotNull String format, @NotNull Velocitab plugin) {
boolean foundRelational = false; boolean foundRelational = false;
if (format.contains("<vel") && plugin.getFormatter().equals(Formatter.MINIMESSAGE)) { if (plugin.getFormatter().equals(Formatter.MINIMESSAGE) && format.contains("<vel")) {
final Matcher velocitabRelationalMatcher = VELOCITAB_PATTERN.matcher(format); final Matcher conditionReplacer = CONDITION_REPLACER.matcher(format);
while (velocitabRelationalMatcher.find()) { while (conditionReplacer.find()) {
if (velocitabRelationalMatcher.group().contains("rel_condition")) {
final String search = conditionReplacer.group().split(":")[1];
String condition = search;
for (Map.Entry<String, String> entry : MiniConditionManager.REPLACE.entrySet()) {
condition = condition.replace(entry.getKey(), entry.getValue());
}
for (Map.Entry<String, String> entry : MiniConditionManager.REPLACE_2.entrySet()) {
condition = condition.replace(entry.getValue(), entry.getKey());
}
format = format.replace(search, condition);
}
final Matcher testMatcher = TEST.matcher(format);
while (testMatcher.find()) {
if(testMatcher.group().startsWith("<velocitab_rel")) {
final Matcher second = TEST.matcher(testMatcher.group().substring(1));
while (second.find()) {
String s = second.group();
for (Map.Entry<String, String> entry : SYMBOL_SUBSTITUTES.entrySet()) {
s = s.replace(entry.getKey(), entry.getValue());
}
format = format.replace(second.group(), s);
}
continue; continue;
} }
String s = testMatcher.group();
for (Map.Entry<String, String> entry : SYMBOL_SUBSTITUTES.entrySet()) {
s = s.replace(entry.getKey(), entry.getValue());
}
format = format.replace(testMatcher.group(), s);
}
final Matcher velocitabRelationalMatcher = VELOCITAB_PATTERN.matcher(format);
while (velocitabRelationalMatcher.find()) {
foundRelational = true; foundRelational = true;
final String relationalPlaceholder = velocitabRelationalMatcher.group().substring(1, velocitabRelationalMatcher.group().length() - 1); final String relationalPlaceholder = velocitabRelationalMatcher.group().substring(1, velocitabRelationalMatcher.group().length() - 1);
final String fixedString = replaceSymbols(relationalPlaceholder); String fixedString = relationalPlaceholder;
for (Map.Entry<String, String> entry : SYMBOL_SUBSTITUTES_2.entrySet()) {
fixedString = fixedString.replace(entry.getKey(), entry.getValue());
}
format = format.replace(relationalPlaceholder, fixedString); format = format.replace(relationalPlaceholder, fixedString);
} }
format = processConditionalPlaceholders(format); for (Map.Entry<String, String> entry : SYMBOL_SUBSTITUTES.entrySet()) {
format = format.replace(entry.getValue(), entry.getKey());
}
} }
return Pair.of(foundRelational, format); return Pair.of(foundRelational, format);
} }
@NotNull
private static String processConditionalPlaceholders(@NotNull String format) {
final Matcher conditionalMatcher = CONDITIONAL_PATTERN.matcher(format);
while (conditionalMatcher.find()) {
String conditionalPlaceholder = conditionalMatcher.group();
conditionalPlaceholder = conditionalPlaceholder.substring(1, conditionalPlaceholder.length() - 1);
final String fixedString = replaceSymbols(conditionalPlaceholder);
format = format.replace(conditionalPlaceholder, fixedString);
}
return format;
}
@NotNull @NotNull
private static String replacePlaceholders(@NotNull String format, @NotNull Velocitab plugin, @Nullable TabPlayer player) { private static String replacePlaceholders(@NotNull String format, @NotNull Velocitab plugin, @Nullable TabPlayer player) {
for (Placeholder placeholder : values()) { for (Placeholder placeholder : values()) {
@ -219,16 +244,6 @@ public enum Placeholder {
return format; return format;
} }
@NotNull
private static String replaceSymbols(@NotNull String input) {
String fixedString = input.replace("%", REL_SUBSTITUTE);
fixedString = MiniMessage.miniMessage().serialize(Formatter.LEGACY.deserialize(fixedString));
for (Map.Entry<String, String> entry : SYMBOL_SUBSTITUTES.entrySet()) {
fixedString = fixedString.replace(entry.getKey(), entry.getValue());
}
return fixedString;
}
public static CompletableFuture<String> replace(@NotNull String format, @NotNull Velocitab plugin, public static CompletableFuture<String> replace(@NotNull String format, @NotNull Velocitab plugin,
@NotNull TabPlayer player) { @NotNull TabPlayer player) {

View File

@ -19,7 +19,6 @@
package net.william278.velocitab.config; package net.william278.velocitab.config;
import com.google.common.collect.Lists;
import com.velocitypowered.api.util.ServerLink; import com.velocitypowered.api.util.ServerLink;
import net.william278.velocitab.Velocitab; import net.william278.velocitab.Velocitab;
import net.william278.velocitab.player.TabPlayer; import net.william278.velocitab.player.TabPlayer;

View File

@ -54,8 +54,8 @@ public class Settings implements ConfigValidator {
@Comment("Whether to disable header and footer if they are empty and let backend servers handle them.") @Comment("Whether to disable header and footer if they are empty and let backend servers handle them.")
private boolean disableHeaderFooterIfEmpty = true; private boolean disableHeaderFooterIfEmpty = true;
@Comment("Which text formatter to use (MINEDOWN, MINIMESSAGE, or LEGACY)") @Comment("Which text formatter to use (MINIMESSAGE, MINEDOWN or LEGACY)")
private Formatter formatter = Formatter.MINEDOWN; private Formatter formatter = Formatter.MINIMESSAGE;
@Comment("All servers which are not in other groups will be put in the fallback group." @Comment("All servers which are not in other groups will be put in the fallback group."
+ "\n\"false\" will exclude them from Velocitab.") + "\n\"false\" will exclude them from Velocitab.")
@ -104,7 +104,7 @@ public class Settings implements ConfigValidator {
"• Specify a set of server groups each URL should be sent on. Use '*' to show a URL to all groups."}) "• Specify a set of server groups each URL should be sent on. Use '*' to show a URL to all groups."})
private List<ServerUrl> serverLinks = List.of( private List<ServerUrl> serverLinks = List.of(
new ServerUrl( new ServerUrl(
"&#00fb9a&About Velocitab", "<#00fb9a>About Velocitab</#00fb9a>",
"https://william278.net/project/velocitab" "https://william278.net/project/velocitab"
) )
); );

View File

@ -48,10 +48,10 @@ public class TabGroups implements ConfigValidator {
private static final Group DEFAULT_GROUP = new Group( private static final Group DEFAULT_GROUP = new Group(
"default", "default",
List.of("&rainbow&Running Velocitab by William278"), List.of("<rainbow:!2>Running Velocitab by William278 & AlexDev_</rainbow>"),
List.of("[There are currently %players_online%/%max_players_online% players online](gray)"), List.of("<gray>There are currently %players_online%/%max_players_online% players online</gray>"),
"&7[%server%] &f%prefix%%username%", "<gray>[%server%] %prefix%%username%</gray>",
new Nametag("&f%prefix%", "&f%suffix%"), new Nametag("<white>%prefix%</white>", "<white>%suffix%</white>"),
Set.of("lobby", "survival", "creative", "minigames", "skyblock", "prison", "hub"), Set.of("lobby", "survival", "creative", "minigames", "skyblock", "prison", "hub"),
List.of("%role_weight%", "%username_lower%"), List.of("%role_weight%", "%username_lower%"),
false, false,

View File

@ -27,15 +27,8 @@ import net.william278.velocitab.Velocitab;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Map;
public class MiniPlaceholdersHook extends Hook { public class MiniPlaceholdersHook extends Hook {
public final static Map<String, String> REPLACE = Map.of(
"\"", "-q-",
"'", "-a-"
);
private final VelocitabMiniExpansion expansion; private final VelocitabMiniExpansion expansion;
public MiniPlaceholdersHook(@NotNull Velocitab plugin) { public MiniPlaceholdersHook(@NotNull Velocitab plugin) {
@ -46,9 +39,6 @@ public class MiniPlaceholdersHook extends Hook {
@NotNull @NotNull
public Component format(@NotNull String text, @NotNull Audience player, @Nullable Audience viewer) { public Component format(@NotNull String text, @NotNull Audience player, @Nullable Audience viewer) {
for (Map.Entry<String, String> entry : REPLACE.entrySet()) {
text = text.replace(entry.getKey(), entry.getValue());
}
if (viewer == null) { if (viewer == null) {
return MiniMessage.miniMessage().deserialize(text, MiniPlaceholders.getAudienceGlobalPlaceholders(player)); return MiniMessage.miniMessage().deserialize(text, MiniPlaceholders.getAudienceGlobalPlaceholders(player));
} }

View File

@ -26,12 +26,13 @@ import io.github.miniplaceholders.api.utils.TagsUtils;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.Tag; import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
import net.william278.velocitab.Velocitab; import net.william278.velocitab.Velocitab;
import net.william278.velocitab.config.Placeholder; import net.william278.velocitab.config.Placeholder;
import net.william278.velocitab.hook.miniconditions.MiniConditionManager; import net.william278.velocitab.hook.miniconditions.MiniConditionManager;
import net.william278.velocitab.player.TabPlayer; import net.william278.velocitab.player.TabPlayer;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
public class VelocitabMiniExpansion { public class VelocitabMiniExpansion {
@ -96,11 +97,9 @@ public class VelocitabMiniExpansion {
return TagsUtils.EMPTY_TAG; return TagsUtils.EMPTY_TAG;
} }
final String value = queue.pop().value(); final String value = fixValue(popAll(queue));
String replaced = Placeholder.replaceInternal(value, plugin, targetPlayer); String replaced = Placeholder.replaceInternal(value, plugin, targetPlayer);
for (final Map.Entry<String, String> entry : Placeholder.SYMBOL_SUBSTITUTES.entrySet()) {
replaced = replaced.replace(entry.getValue(), entry.getKey());
}
return Tag.selfClosingInserting(MiniMessage.miniMessage().deserialize(replaced, MiniPlaceholders.getAudienceGlobalPlaceholders(audience))); return Tag.selfClosingInserting(MiniMessage.miniMessage().deserialize(replaced, MiniPlaceholders.getAudienceGlobalPlaceholders(audience)));
})); }));
builder.relationalPlaceholder("vanish", ((a1, otherAudience, queue, ctx) -> { builder.relationalPlaceholder("vanish", ((a1, otherAudience, queue, ctx) -> {
@ -122,4 +121,23 @@ public class VelocitabMiniExpansion {
expansion.unregister(); expansion.unregister();
} }
@NotNull
private String popAll(@NotNull ArgumentQueue queue) {
final StringBuilder builder = new StringBuilder();
int i = 0;
while (queue.hasNext()) {
if (i > 0) {
builder.append(":");
}
builder.append(queue.pop().value());
i++;
}
return builder.toString();
}
@NotNull
private String fixValue(@NotNull String value) {
return value.replace("*LESS2*", "<").replace("*GREATER2*", ">");
}
} }

View File

@ -26,7 +26,6 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue; import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
import net.william278.velocitab.Velocitab; import net.william278.velocitab.Velocitab;
import net.william278.velocitab.config.Placeholder; import net.william278.velocitab.config.Placeholder;
import net.william278.velocitab.hook.MiniPlaceholdersHook;
import net.william278.velocitab.player.TabPlayer; import net.william278.velocitab.player.TabPlayer;
import org.apache.commons.jexl3.JexlBuilder; import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlContext; import org.apache.commons.jexl3.JexlContext;
@ -43,6 +42,22 @@ import java.util.regex.Pattern;
public class MiniConditionManager { public class MiniConditionManager {
public final static Map<String, String> REPLACE = Map.of(
"\"", "-q-",
"'", "-a-"
);
public final static Map<String, String> REPLACE_2 = Map.of(
"*LESS3*", "<",
"*GREATER3*",">",
"*LESS2*", "<",
"*GREATER2*", ">"
);
private final static Map<String, String> REPLACE_3 = Map.of(
"?dp?", ":"
);
private final Velocitab plugin; private final Velocitab plugin;
private final JexlEngine jexlEngine; private final JexlEngine jexlEngine;
private final JexlContext jexlContext; private final JexlContext jexlContext;
@ -82,6 +97,7 @@ public class MiniConditionManager {
return Component.empty(); return Component.empty();
} }
String condition = decodeCondition(parameters.get(0)); String condition = decodeCondition(parameters.get(0));
if (parameters.size() < 3) { if (parameters.size() < 3) {
plugin.getLogger().warn("Invalid condition: Missing true/false values for condition: {}", condition); plugin.getLogger().warn("Invalid condition: Missing true/false values for condition: {}", condition);
@ -93,8 +109,9 @@ public class MiniConditionManager {
return Component.empty(); return Component.empty();
} }
condition = Placeholder.replaceInternal(condition, plugin, tabPlayer.get()); condition = Placeholder.replaceInternal(condition, plugin, tabPlayer.get());
String falseValue = processFalseValue(parameters.get(2)); final String falseValue = processFalseValue(parameters.get(2));
final String expression = buildExpression(condition); final String expression = buildExpression(condition);
return evaluateAndFormatCondition(expression, target, audience, parameters.get(1), falseValue); return evaluateAndFormatCondition(expression, target, audience, parameters.get(1), falseValue);
} }
@ -103,19 +120,25 @@ public class MiniConditionManager {
private List<String> collectParameters(@NotNull ArgumentQueue queue) { private List<String> collectParameters(@NotNull ArgumentQueue queue) {
final List<String> parameters = Lists.newArrayList(); final List<String> parameters = Lists.newArrayList();
while (queue.hasNext()) { while (queue.hasNext()) {
parameters.add(queue.pop().value()); String param = queue.pop().value();
for (Map.Entry<String, String> entry : REPLACE_2.entrySet()) {
param = param.replace(entry.getKey(), entry.getValue());
}
for (Map.Entry<String, String> entry : REPLACE_3.entrySet()) {
param = param.replace(entry.getKey(), entry.getValue());
}
parameters.add(param);
} }
return parameters; return parameters;
} }
@NotNull @NotNull
private String decodeCondition(@NotNull String condition) { private String decodeCondition(@NotNull String condition) {
condition = condition.replace("?lt;", "<").replace("?gt;", ">"); for (Map.Entry<String, String> entry : REPLACE.entrySet()) {
for (Map.Entry<String, String> entry : MiniPlaceholdersHook.REPLACE.entrySet()) {
condition = condition.replace(entry.getValue(), entry.getKey()); condition = condition.replace(entry.getValue(), entry.getKey());
condition = condition.replace(entry.getKey() + entry.getKey(), entry.getKey()); condition = condition.replace(entry.getKey() + entry.getKey(), entry.getKey());
} }
for (Map.Entry<String, String> entry : Placeholder.SYMBOL_SUBSTITUTES.entrySet()) { for (Map.Entry<String, String> entry : REPLACE_2.entrySet()) {
condition = condition.replace(entry.getValue(), entry.getKey()); condition = condition.replace(entry.getValue(), entry.getKey());
} }
return condition; return condition;

View File

@ -161,16 +161,7 @@ public final class TabPlayer implements Comparable<TabPlayer> {
displayName = displayName.replace(placeholder, value); displayName = displayName.replace(placeholder, value);
} }
displayName = displayName.replace("\n", "");
final boolean isMiniMessage = plugin.getFormatter().equals(Formatter.MINIMESSAGE);
if (isMiniMessage) {
displayName = Formatter.LEGACY.serialize(MiniMessage.miniMessage().deserialize(displayName));
}
displayName = Placeholder.replaceInternal(displayName, plugin, this); displayName = Placeholder.replaceInternal(displayName, plugin, this);
if (isMiniMessage) {
displayName = MiniMessage.miniMessage().serialize(Formatter.LEGACY.deserialize(displayName))
.replace("\\<", "<");
}
return lastDisplayName = displayName; return lastDisplayName = displayName;
} }

View File

@ -21,6 +21,7 @@ package net.william278.velocitab.tab;
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.player.TabPlayer; import net.william278.velocitab.player.TabPlayer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -31,12 +32,14 @@ public record Nametag(@NotNull String prefix, @NotNull String suffix) {
@NotNull @NotNull
public Component getPrefixComponent(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer, @NotNull TabPlayer target) { public Component getPrefixComponent(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer, @NotNull TabPlayer target) {
return plugin.getFormatter().format(prefix, tabPlayer, target, plugin); final String formatted = Placeholder.replaceInternal(prefix, plugin, tabPlayer);
return plugin.getFormatter().format(formatted, tabPlayer, target, plugin);
} }
@NotNull @NotNull
public Component getSuffixComponent(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer, @NotNull TabPlayer target) { public Component getSuffixComponent(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer, @NotNull TabPlayer target) {
return plugin.getFormatter().format(suffix, tabPlayer, target, plugin); final String formatted = Placeholder.replaceInternal(suffix, plugin, tabPlayer);
return plugin.getFormatter().format(formatted, tabPlayer, target, plugin);
} }
@Override @Override

View File

@ -286,6 +286,16 @@ public class PlayerTabList {
removePlayer(target, null); removePlayer(target, null);
} }
/**
* Remove a player from the tab list
* @param uuid
*/
protected void removeTablistUUID(@NotNull UUID uuid) {
getPlayers().forEach((key, value) -> {
value.getPlayer().getTabList().getEntry(uuid).ifPresent(entry -> value.getPlayer().getTabList().removeEntry(uuid));
});
}
protected void removePlayer(@NotNull Player target, @Nullable RegisteredServer server) { protected void removePlayer(@NotNull Player target, @Nullable RegisteredServer server) {
final UUID uuid = target.getUniqueId(); final UUID uuid = target.getUniqueId();
plugin.getServer().getAllPlayers().forEach(player -> player.getTabList().removeEntry(uuid)); plugin.getServer().getAllPlayers().forEach(player -> player.getTabList().removeEntry(uuid));

View File

@ -168,8 +168,9 @@ public class TabListListener {
if (player.getCurrentServer().isPresent()) { if (player.getCurrentServer().isPresent()) {
return; return;
} }
tabList.removePlayer(player);
}).delay(500, TimeUnit.MILLISECONDS).schedule(); tabList.removeTablistUUID(event.getPlayer().getUniqueId());
}).delay(750, TimeUnit.MILLISECONDS).schedule();
} }
@Subscribe @Subscribe