feat: add show_all_players_from_all_groups config option (#183)

Code refactor
Improved system that handles latency
This commit is contained in:
AlexDev_ 2024-03-30 00:25:38 +01:00 committed by GitHub
parent 5b5e40e7f8
commit b7c353a0ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 118 additions and 46 deletions

View File

@ -26,6 +26,8 @@ formatter: MINEDOWN
fallback_enabled: true
# The formats to use for the fallback group.
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:

View File

@ -27,6 +27,7 @@ import net.william278.velocitab.player.TabPlayer;
import net.william278.velocitab.tab.Nametag;
import org.apache.commons.text.StringEscapeUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.event.Level;
import java.util.List;
@ -65,7 +66,13 @@ public record Group(
@NotNull
public Set<RegisteredServer> registeredServers(@NotNull Velocitab plugin) {
if (isDefault(plugin) && plugin.getSettings().isFallbackEnabled()) {
return registeredServers(plugin, true);
}
@NotNull
public Set<RegisteredServer> registeredServers(@NotNull Velocitab plugin, boolean includeAllPlayers) {
if ((includeAllPlayers && plugin.getSettings().isShowAllPlayersFromAllGroups()) ||
(isDefault(plugin) && plugin.getSettings().isFallbackEnabled())) {
return Sets.newHashSet(plugin.getServer().getAllServers());
}
return getRegexServers(plugin);
@ -103,6 +110,9 @@ public record Group(
@NotNull
public Set<Player> getPlayers(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer) {
if (plugin.getSettings().isShowAllPlayersFromAllGroups()) {
return Sets.newHashSet(plugin.getServer().getAllPlayers());
}
if (onlyListPlayersInSameServer) {
return tabPlayer.getPlayer().getCurrentServer()
.map(s -> Sets.newHashSet(s.getServer().getPlayersConnected()))
@ -111,8 +121,18 @@ public record Group(
return getPlayers(plugin);
}
/**
* Retrieves the set of TabPlayers associated with the given Velocitab plugin instance.
* If the plugin is configured to show all players from all groups, all players will be returned.
*
* @param plugin The Velocitab plugin instance.
* @return A set of TabPlayers.
*/
@NotNull
public Set<TabPlayer> getTabPlayers(@NotNull Velocitab plugin) {
if (plugin.getSettings().isShowAllPlayersFromAllGroups()) {
return Sets.newHashSet(plugin.getTabList().getPlayers().values());
}
return plugin.getTabList().getPlayers()
.values()
.stream()
@ -122,6 +142,9 @@ public record Group(
@NotNull
public Set<TabPlayer> getTabPlayers(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer) {
if (plugin.getSettings().isShowAllPlayersFromAllGroups()) {
return Sets.newHashSet(plugin.getTabList().getPlayers().values());
}
if (onlyListPlayersInSameServer) {
return plugin.getTabList().getPlayers()
.values()
@ -133,7 +156,7 @@ public record Group(
}
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof Group group)) {
return false;
}

View File

@ -63,6 +63,9 @@ public class Settings implements ConfigValidator {
@Comment("The formats to use for the fallback group.")
private String fallbackGroup = "default";
@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<String, String> serverDisplayNames = Map.of("very-long-server-name", "VLSN");

View File

@ -89,7 +89,7 @@ public class TabGroups implements ConfigValidator {
throw new IllegalStateException("No default group found");
}
for (Group group : groups) {
if (group.registeredServers(plugin)
if (group.registeredServers(plugin, false)
.stream()
.anyMatch(s -> s.getServerInfo().getName().equalsIgnoreCase(server))) {
return group;

View File

@ -24,17 +24,18 @@ import net.william278.velocitab.Velocitab;
import net.william278.velocitab.config.Placeholder;
import net.william278.velocitab.player.TabPlayer;
import org.jetbrains.annotations.NotNull;
import org.slf4j.event.Level;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class SortingManager {
private final Velocitab plugin;
private static final String DELIMITER = ":::";
private static final Pattern NUMBER_PATTERN = Pattern.compile("^-?[0-9]\\d*(\\.\\d+)?$");
public SortingManager(@NotNull Velocitab plugin) {
this.plugin = plugin;
@ -71,7 +72,7 @@ public class SortingManager {
return "";
}
if (value.matches("^-?[0-9]\\d*(\\.\\d+)?$")) {
if (NUMBER_PATTERN.matcher(value).matches()) {
double parsed = Double.parseDouble(value);
parsed = Math.max(0, parsed);
return compressNumber(Integer.MAX_VALUE / 4d - parsed);

View File

@ -0,0 +1,39 @@
/*
* This file is part of Velocitab, licensed under the Apache License 2.0.
*
* Copyright (c) William278 <will27528@gmail.com>
* 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.tab;
import com.velocitypowered.api.scheduler.ScheduledTask;
import org.jetbrains.annotations.Nullable;
public record GroupTasks(@Nullable ScheduledTask updateTask, @Nullable ScheduledTask headerFooterTask, @Nullable ScheduledTask latencyTask) {
public void cancel() {
if (updateTask != null) {
updateTask.cancel();
}
if (headerFooterTask != null) {
headerFooterTask.cancel();
}
if (latencyTask != null) {
latencyTask.cancel();
}
}
}

View File

@ -25,7 +25,6 @@ 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.proxy.server.ServerInfo;
import com.velocitypowered.api.scheduler.ScheduledTask;
import lombok.AccessLevel;
import lombok.Getter;
@ -36,7 +35,6 @@ import net.william278.velocitab.config.Group;
import net.william278.velocitab.config.Placeholder;
import net.william278.velocitab.player.Role;
import net.william278.velocitab.player.TabPlayer;
import org.apache.commons.lang3.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.event.Level;
@ -54,15 +52,13 @@ public class PlayerTabList {
private final VanishTabList vanishTabList;
@Getter(value = AccessLevel.PUBLIC)
private final Map<UUID, TabPlayer> players;
private final Map<Group, ScheduledTask> placeholderTasks;
private final Map<Group, ScheduledTask> headerFooterTasks;
private final Map<Group, GroupTasks> groupTasks;
public PlayerTabList(@NotNull Velocitab plugin) {
this.plugin = plugin;
this.vanishTabList = new VanishTabList(plugin, this);
this.players = Maps.newConcurrentMap();
this.placeholderTasks = Maps.newConcurrentMap();
this.headerFooterTasks = Maps.newConcurrentMap();
this.groupTasks = Maps.newConcurrentMap();
this.reloadUpdate();
this.registerListener();
}
@ -104,7 +100,9 @@ public class PlayerTabList {
final String serverName = server.get().getServerInfo().getName();
final Group group = getGroup(serverName);
final boolean isDefault = group.registeredServers(plugin).stream().noneMatch(s -> s.getServerInfo().getName().equals(serverName));
final boolean isDefault = group.registeredServers(plugin)
.stream()
.noneMatch(s -> s.getServerInfo().getName().equals(serverName));
if (isDefault && !plugin.getSettings().isFallbackEnabled()) {
return;
@ -119,10 +117,7 @@ public class PlayerTabList {
* Removes the player's entry from the tab list of all other players on the same group servers.
*/
public void close() {
placeholderTasks.values().forEach(ScheduledTask::cancel);
placeholderTasks.clear();
headerFooterTasks.values().forEach(ScheduledTask::cancel);
headerFooterTasks.clear();
groupTasks.values().forEach(GroupTasks::cancel);
plugin.getServer().getAllPlayers().forEach(p -> {
final Optional<ServerConnection> server = p.getCurrentServer();
if (server.isEmpty()) return;
@ -345,10 +340,7 @@ public class PlayerTabList {
player.getPlayer().getTabList().getEntries().stream()
.filter(e -> e.getProfile().getId().equals(tabPlayer.getPlayer().getUniqueId())).findFirst()
.ifPresent(entry -> {
entry.setDisplayName(displayName);
entry.setLatency(Math.max((int) tabPlayer.getPlayer().getPing(), 0));
});
.ifPresent(entry -> entry.setDisplayName(displayName));
});
});
}
@ -375,26 +367,54 @@ public class PlayerTabList {
}
// Update the tab list periodically
private void updatePeriodically(Group group) {
private void updatePeriodically(@NotNull Group group) {
cancelTasks(group);
ScheduledTask headerFooterTask = null;
ScheduledTask updateTask = null;
ScheduledTask latencyTask;
if (group.headerFooterUpdateRate() > 0) {
final ScheduledTask headerFooterTask = plugin.getServer().getScheduler()
headerFooterTask = plugin.getServer().getScheduler()
.buildTask(plugin, () -> updateGroupPlayers(group, false, true))
.delay(1, TimeUnit.SECONDS)
.repeat(Math.max(200, group.headerFooterUpdateRate()), TimeUnit.MILLISECONDS)
.schedule();
headerFooterTasks.put(group, headerFooterTask);
}
if (group.placeholderUpdateRate() > 0) {
final ScheduledTask updateTask = plugin.getServer().getScheduler()
updateTask = plugin.getServer().getScheduler()
.buildTask(plugin, () -> updateGroupPlayers(group, true, false))
.delay(1, TimeUnit.SECONDS)
.repeat(Math.max(200, group.placeholderUpdateRate()), TimeUnit.MILLISECONDS)
.schedule();
placeholderTasks.put(group, updateTask);
}
latencyTask = plugin.getServer().getScheduler()
.buildTask(plugin, () -> updateLatency(group))
.delay(1, TimeUnit.SECONDS)
.repeat(3, TimeUnit.SECONDS)
.schedule();
groupTasks.put(group, new GroupTasks(headerFooterTask, updateTask, latencyTask));
}
private void updateLatency(@NotNull Group group) {
final Set<TabPlayer> groupPlayers = group.getTabPlayers(plugin);
if (groupPlayers.isEmpty()) {
return;
}
groupPlayers.stream()
.filter(player -> player.getPlayer().isActive())
.forEach(player -> {
final int latency = (int) player.getPlayer().getPing();
final Set<TabPlayer> players = group.getTabPlayers(plugin, player);
players.forEach(p -> {
p.getPlayer().getTabList().getEntries().stream()
.filter(e -> e.getProfile().getId().equals(player.getPlayer().getUniqueId())).findFirst()
.ifPresent(entry -> entry.setLatency(Math.max(latency, 0)));
});
});
}
/**
@ -425,25 +445,11 @@ public class PlayerTabList {
}
}
private void cancelTasks(Group group) {
ScheduledTask task = placeholderTasks.entrySet().stream()
.filter(entry -> entry.getKey().equals(group))
.map(Map.Entry::getValue)
.findFirst()
.orElse(null);
if (task != null) {
task.cancel();
placeholderTasks.remove(group);
}
task = headerFooterTasks.entrySet().stream()
.filter(entry -> entry.getKey().equals(group))
.map(Map.Entry::getValue)
.findFirst()
.orElse(null);
if (task != null) {
task.cancel();
headerFooterTasks.remove(group);
private void cancelTasks(@NotNull Group group) {
final GroupTasks tasks = groupTasks.get(group);
if (tasks != null) {
tasks.cancel();
groupTasks.remove(group);
}
}
@ -451,8 +457,6 @@ public class PlayerTabList {
* Update the TAB list for all players when a plugin or proxy reload is performed
*/
public void reloadUpdate() {
placeholderTasks.values().forEach(ScheduledTask::cancel);
placeholderTasks.clear();
plugin.getTabGroups().getGroups().forEach(this::updatePeriodically);
if (players.isEmpty()) {