Added regex system and fixed ghost players bug (#176)

* Added regex system for TabGroup's servers.
Fixed ghost player after kick/disconnect.

* Fixed config docs with missing entries

* Bumped version
This commit is contained in:
AlexDev_ 2024-03-11 19:43:34 +01:00 committed by GitHub
parent c0abf481c1
commit 4e2749ac9e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 99 additions and 37 deletions

View File

@ -17,6 +17,8 @@ The config file is located in `/plugins/velocitab/config.yml` and the tab groups
check_for_updates: true
# Whether to remove nametag from players' heads if the nametag associated with their server group is empty.
remove_nametags: true
# Whether to disable header and footer if they are empty and let backend servers handle them.
disable_header_footer_if_empty: true
# Which text formatter to use (MINEDOWN, MINIMESSAGE, or LEGACY)
formatter: MINEDOWN
# All servers which are not in other groups will be put in the fallback group.
@ -33,16 +35,21 @@ server_display_names:
# 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)
papi_cache_time: 200
papi_cache_time: 30000
# If you are using MINIMESSAGE formatting, enable this to support MiniPlaceholders in formatting.
enable_mini_placeholders_hook: true
# Whether to send scoreboard teams packets. Required for player list sorting and nametag formatting.
# Turn this off if you're using scoreboard teams on backend servers.
send_scoreboard_packets: true
# If built-in placeholders return a blank string, fallback to Placeholder API equivalents.
# For example, if %prefix% returns a blank string, use %luckperms_prefix%. Requires PAPIProxyBridge.
fallback_to_papi_if_placeholder_blank: false
# Whether to sort players in the TAB list.
sort_players: true
# Remove gamemode spectator effect for other players in the TAB list.
remove_spectator_effect: false
# Whether to enable the Plugin Message API (allows backend plugins to perform certain operations)
enable_plugin_message_api: true
```
</details>

View File

@ -3,7 +3,7 @@ javaVersion=17
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
org.gradle.daemon=true
plugin_version=1.6.3
plugin_version=1.6.4
plugin_archive=velocitab
plugin_description=A beautiful and versatile TAB list plugin for Velocity proxies

View File

@ -19,6 +19,7 @@
package net.william278.velocitab.config;
import com.google.common.collect.Sets;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import net.william278.velocitab.Velocitab;
@ -27,9 +28,12 @@ import net.william278.velocitab.tab.Nametag;
import org.apache.commons.text.StringEscapeUtils;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
@SuppressWarnings("unused")
public record Group(
@ -38,8 +42,8 @@ public record Group(
List<String> footers,
String format,
Nametag nametag,
List<String> servers,
List<String> sortingPlaceholders,
Set<String> servers,
Set<String> sortingPlaceholders,
boolean collisions,
int headerFooterUpdateRate,
int placeholderUpdateRate
@ -58,15 +62,35 @@ public record Group(
}
@NotNull
public List<RegisteredServer> registeredServers(Velocitab plugin) {
public Set<RegisteredServer> registeredServers(@NotNull Velocitab plugin) {
if (isDefault() && plugin.getSettings().isFallbackEnabled()) {
return new ArrayList<>(plugin.getServer().getAllServers());
return Sets.newHashSet(plugin.getServer().getAllServers());
}
return servers.stream()
.map(plugin.getServer()::getServer)
.filter(Optional::isPresent)
.map(Optional::get)
.toList();
return getRegexServers(plugin);
}
@NotNull
private Set<RegisteredServer> getRegexServers(@NotNull Velocitab plugin) {
final Set<RegisteredServer> totalServers = Sets.newHashSet();
for (String server : servers) {
if (!server.contains("*") && !server.contains(".")) {
plugin.getServer().getServer(server).ifPresent(totalServers::add);
continue;
}
try {
final Matcher matcher = Pattern.compile(server
.replace(".", "\\.")
.replace("*", ".*"))
.matcher("");
plugin.getServer().getAllServers().stream()
.filter(registeredServer -> matcher.reset(registeredServer.getServerInfo().getName()).matches())
.forEach(totalServers::add);
} catch (PatternSyntaxException ignored) {
plugin.getServer().getServer(server).ifPresent(totalServers::add);
}
}
return totalServers;
}
public boolean isDefault() {
@ -74,8 +98,8 @@ public record Group(
}
@NotNull
public List<Player> getPlayers(Velocitab plugin) {
List<Player> players = new ArrayList<>();
public Set<Player> getPlayers(@NotNull Velocitab plugin) {
Set<Player> players = Sets.newHashSet();
for (RegisteredServer server : registeredServers(plugin)) {
players.addAll(server.getPlayersConnected());
}
@ -83,12 +107,12 @@ public record Group(
}
@NotNull
public List<TabPlayer> getTabPlayers(Velocitab plugin) {
public Set<TabPlayer> getTabPlayers(@NotNull Velocitab plugin) {
return plugin.getTabList().getPlayers()
.values()
.stream()
.filter(tabPlayer -> tabPlayer.getGroup().equals(this))
.toList();
.collect(Collectors.toSet());
}
@Override

View File

@ -50,6 +50,9 @@ public class Settings implements ConfigValidator {
@Comment("Whether to remove nametag from players' heads if the nametag associated with their server group is empty.")
private boolean removeNametags = false;
@Comment("Whether to disable header and footer if they are empty and let backend servers handle them.")
private boolean disableHeaderFooterIfEmpty = true;
@Comment("Which text formatter to use (MINEDOWN, MINIMESSAGE, or LEGACY)")
private Formatter formatter = Formatter.MINEDOWN;

View File

@ -19,6 +19,7 @@
package net.william278.velocitab.config;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import de.exlll.configlib.Configuration;
@ -29,10 +30,7 @@ import net.william278.velocitab.Velocitab;
import net.william278.velocitab.tab.Nametag;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.*;
@SuppressWarnings("FieldMayBeFinal")
@Getter
@ -54,8 +52,8 @@ public class TabGroups implements ConfigValidator {
List.of("[There are currently %players_online%/%max_players_online% players online](gray)"),
"&7[%server%] &f%prefix%%username%",
new Nametag("&f%prefix%", "&f%suffix%"),
List.of("lobby", "survival", "creative", "minigames", "skyblock", "prison", "hub"),
List.of("%role_weight%", "%username_lower%"),
Set.of("lobby", "survival", "creative", "minigames", "skyblock", "prison", "hub"),
Set.of("%role_weight%", "%username_lower%"),
false,
1000,
1000
@ -113,7 +111,7 @@ public class TabGroups implements ConfigValidator {
@NotNull
private Multimap<Group, String> getMissingKeys() {
final Multimap<Group, String> missingKeys = Multimaps.newSetMultimap(new HashMap<>(), HashSet::new);
final Multimap<Group, String> missingKeys = Multimaps.newSetMultimap(Maps.newHashMap(), HashSet::new);
for (Group group : groups) {
if (group.format() == null) {

View File

@ -113,7 +113,7 @@ public class ScoreboardManager {
if (teamName == null) {
return;
}
final List<RegisteredServer> siblings = tabPlayer.getGroup().registeredServers(plugin);
final Set<RegisteredServer> siblings = tabPlayer.getGroup().registeredServers(plugin);
final Optional<Nametag> cachedTag = Optional.ofNullable(nametags.getOrDefault(teamName, null));
cachedTag.ifPresent(nametag -> {
@ -175,7 +175,7 @@ public class ScoreboardManager {
}
final Player player = tabPlayer.getPlayer();
final List<RegisteredServer> siblings = tabPlayer.getGroup().registeredServers(plugin);
final Set<RegisteredServer> siblings = tabPlayer.getGroup().registeredServers(plugin);
final List<Player> players = siblings.stream()
.map(RegisteredServer::getPlayersConnected)
.flatMap(Collection::stream)
@ -253,7 +253,7 @@ public class ScoreboardManager {
return;
}
final List<RegisteredServer> siblings = tabPlayer.getGroup().registeredServers(plugin);
final Set<RegisteredServer> siblings = tabPlayer.getGroup().registeredServers(plugin);
siblings.forEach(server -> server.getPlayersConnected().forEach(connected -> {
try {
final boolean canSee = plugin.getVanishManager().canSee(connected.getUsername(), player.getUsername());

View File

@ -41,6 +41,7 @@ import java.util.concurrent.CompletableFuture;
@ToString
public final class TabPlayer implements Comparable<TabPlayer> {
private final Velocitab plugin;
private final Player player;
@Setter
private Role role;
@ -65,7 +66,9 @@ public final class TabPlayer implements Comparable<TabPlayer> {
@Setter
private boolean loaded;
public TabPlayer(@NotNull Player player, @NotNull Role role, @NotNull Group group) {
public TabPlayer(@NotNull Velocitab plugin, @NotNull Player player,
@NotNull Role role, @NotNull Group group) {
this.plugin = plugin;
this.player = player;
this.role = role;
this.group = group;
@ -134,11 +137,22 @@ public final class TabPlayer implements Comparable<TabPlayer> {
}
public CompletableFuture<Void> sendHeaderAndFooter(@NotNull PlayerTabList tabList) {
return tabList.getHeader(this).thenCompose(header -> tabList.getFooter(this)
.thenAccept(footer -> {
return tabList.getHeader(this).thenCompose(header -> tabList.getFooter(this).thenAccept(footer -> {
final boolean disabled = plugin.getSettings().isDisableHeaderFooterIfEmpty();
if (disabled) {
if (!Component.empty().equals(header)) {
lastHeader = header;
player.sendPlayerListHeader(header);
}
if (!Component.empty().equals(footer)) {
lastFooter = footer;
player.sendPlayerListFooter(footer);
}
} else {
lastHeader = header;
lastFooter = footer;
player.sendPlayerListHeaderAndFooter(header, footer);
}
}));
}

View File

@ -129,7 +129,7 @@ public class PlayerTabList {
return;
}
final List<RegisteredServer> serversInGroup = Lists.newArrayList(tabPlayer.getGroup().registeredServers(plugin));
final Set<RegisteredServer> serversInGroup = tabPlayer.getGroup().registeredServers(plugin);
if (serversInGroup.isEmpty()) {
return;
}
@ -283,7 +283,7 @@ public class PlayerTabList {
@NotNull
public TabPlayer createTabPlayer(@NotNull Player player, @NotNull Group group) {
return new TabPlayer(player,
return new TabPlayer(plugin, player,
plugin.getLuckPermsHook().map(hook -> hook.getPlayerRole(player)).orElse(Role.DEFAULT_ROLE),
group
);
@ -379,7 +379,7 @@ public class PlayerTabList {
* @param incrementIndexes Whether to increment the header and footer indexes.
*/
private void updateGroupPlayers(@NotNull Group group, boolean all, boolean incrementIndexes) {
List<TabPlayer> groupPlayers = group.getTabPlayers(plugin);
Set<TabPlayer> groupPlayers = group.getTabPlayers(plugin);
if (groupPlayers.isEmpty()) {
return;
}

View File

@ -114,6 +114,7 @@ public class TabListListener {
@Subscribe(order = PostOrder.LAST)
public void onPlayerQuit(@NotNull DisconnectEvent event) {
if (event.getLoginStatus() != DisconnectEvent.LoginStatus.SUCCESSFUL_LOGIN) {
checkDelayedDisconnect(event);
return;
}
@ -124,6 +125,21 @@ public class TabListListener {
tabList.removePlayer(event.getPlayer());
}
private void checkDelayedDisconnect(@NotNull DisconnectEvent event) {
final Player player = event.getPlayer();
plugin.getServer().getScheduler().buildTask(plugin, () -> {
final Optional<Player> actualPlayer = plugin.getServer().getPlayer(player.getUniqueId());
if (actualPlayer.isPresent() && !actualPlayer.get().equals(player)) {
return;
}
if (player.getCurrentServer().isPresent()) {
return;
}
tabList.removePlayer(player);
plugin.log("Player " + player.getUsername() + " was not removed from the tab list, removing now.");
}).delay(500, TimeUnit.MILLISECONDS).schedule();
}
@Subscribe
public void proxyReload(@NotNull ProxyReloadEvent event) {
plugin.loadConfigs();

View File

@ -24,8 +24,8 @@ import net.william278.velocitab.Velocitab;
import net.william278.velocitab.player.TabPlayer;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
/**
@ -79,7 +79,7 @@ public class VanishTabList {
*/
public void recalculateVanishForPlayer(@NotNull TabPlayer tabPlayer) {
final Player player = tabPlayer.getPlayer();
final List<String> serversInGroup = tabPlayer.getGroup().servers();
final Set<String> serversInGroup = tabPlayer.getGroup().servers();
plugin.getServer().getAllPlayers().forEach(p -> {
if (p.equals(player)) {