forked from Upstream/Velocitab
feat: add show_all_players_from_all_groups
config option (#183)
Code refactor Improved system that handles latency
This commit is contained in:
parent
5b5e40e7f8
commit
b7c353a0ec
@ -26,6 +26,8 @@ formatter: MINEDOWN
|
|||||||
fallback_enabled: true
|
fallback_enabled: true
|
||||||
# The formats to use for the fallback group.
|
# The formats to use for the fallback group.
|
||||||
fallback_group: default
|
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.
|
# 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.
|
# If no custom display name is provided for a server, its original name will be used.
|
||||||
server_display_names:
|
server_display_names:
|
||||||
|
@ -27,6 +27,7 @@ import net.william278.velocitab.player.TabPlayer;
|
|||||||
import net.william278.velocitab.tab.Nametag;
|
import net.william278.velocitab.tab.Nametag;
|
||||||
import org.apache.commons.text.StringEscapeUtils;
|
import org.apache.commons.text.StringEscapeUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.event.Level;
|
import org.slf4j.event.Level;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -65,7 +66,13 @@ public record Group(
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public Set<RegisteredServer> registeredServers(@NotNull Velocitab plugin) {
|
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 Sets.newHashSet(plugin.getServer().getAllServers());
|
||||||
}
|
}
|
||||||
return getRegexServers(plugin);
|
return getRegexServers(plugin);
|
||||||
@ -103,6 +110,9 @@ public record Group(
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public Set<Player> getPlayers(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer) {
|
public Set<Player> getPlayers(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer) {
|
||||||
|
if (plugin.getSettings().isShowAllPlayersFromAllGroups()) {
|
||||||
|
return Sets.newHashSet(plugin.getServer().getAllPlayers());
|
||||||
|
}
|
||||||
if (onlyListPlayersInSameServer) {
|
if (onlyListPlayersInSameServer) {
|
||||||
return tabPlayer.getPlayer().getCurrentServer()
|
return tabPlayer.getPlayer().getCurrentServer()
|
||||||
.map(s -> Sets.newHashSet(s.getServer().getPlayersConnected()))
|
.map(s -> Sets.newHashSet(s.getServer().getPlayersConnected()))
|
||||||
@ -111,8 +121,18 @@ public record Group(
|
|||||||
return getPlayers(plugin);
|
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
|
@NotNull
|
||||||
public Set<TabPlayer> getTabPlayers(@NotNull Velocitab plugin) {
|
public Set<TabPlayer> getTabPlayers(@NotNull Velocitab plugin) {
|
||||||
|
if (plugin.getSettings().isShowAllPlayersFromAllGroups()) {
|
||||||
|
return Sets.newHashSet(plugin.getTabList().getPlayers().values());
|
||||||
|
}
|
||||||
return plugin.getTabList().getPlayers()
|
return plugin.getTabList().getPlayers()
|
||||||
.values()
|
.values()
|
||||||
.stream()
|
.stream()
|
||||||
@ -122,6 +142,9 @@ public record Group(
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public Set<TabPlayer> getTabPlayers(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer) {
|
public Set<TabPlayer> getTabPlayers(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer) {
|
||||||
|
if (plugin.getSettings().isShowAllPlayersFromAllGroups()) {
|
||||||
|
return Sets.newHashSet(plugin.getTabList().getPlayers().values());
|
||||||
|
}
|
||||||
if (onlyListPlayersInSameServer) {
|
if (onlyListPlayersInSameServer) {
|
||||||
return plugin.getTabList().getPlayers()
|
return plugin.getTabList().getPlayers()
|
||||||
.values()
|
.values()
|
||||||
@ -133,7 +156,7 @@ public record Group(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
if (!(obj instanceof Group group)) {
|
if (!(obj instanceof Group group)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,9 @@ public class Settings implements ConfigValidator {
|
|||||||
@Comment("The formats to use for the fallback group.")
|
@Comment("The formats to use for the fallback group.")
|
||||||
private String fallbackGroup = "default";
|
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."
|
@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.")
|
+ "\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");
|
private Map<String, String> serverDisplayNames = Map.of("very-long-server-name", "VLSN");
|
||||||
|
@ -89,7 +89,7 @@ public class TabGroups implements ConfigValidator {
|
|||||||
throw new IllegalStateException("No default group found");
|
throw new IllegalStateException("No default group found");
|
||||||
}
|
}
|
||||||
for (Group group : groups) {
|
for (Group group : groups) {
|
||||||
if (group.registeredServers(plugin)
|
if (group.registeredServers(plugin, false)
|
||||||
.stream()
|
.stream()
|
||||||
.anyMatch(s -> s.getServerInfo().getName().equalsIgnoreCase(server))) {
|
.anyMatch(s -> s.getServerInfo().getName().equalsIgnoreCase(server))) {
|
||||||
return group;
|
return group;
|
||||||
|
@ -24,17 +24,18 @@ import net.william278.velocitab.Velocitab;
|
|||||||
import net.william278.velocitab.config.Placeholder;
|
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;
|
||||||
import org.slf4j.event.Level;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class SortingManager {
|
public class SortingManager {
|
||||||
|
|
||||||
private final Velocitab plugin;
|
private final Velocitab plugin;
|
||||||
private static final String DELIMITER = ":::";
|
private static final String DELIMITER = ":::";
|
||||||
|
private static final Pattern NUMBER_PATTERN = Pattern.compile("^-?[0-9]\\d*(\\.\\d+)?$");
|
||||||
|
|
||||||
public SortingManager(@NotNull Velocitab plugin) {
|
public SortingManager(@NotNull Velocitab plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
@ -71,7 +72,7 @@ public class SortingManager {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.matches("^-?[0-9]\\d*(\\.\\d+)?$")) {
|
if (NUMBER_PATTERN.matcher(value).matches()) {
|
||||||
double parsed = Double.parseDouble(value);
|
double parsed = Double.parseDouble(value);
|
||||||
parsed = Math.max(0, parsed);
|
parsed = Math.max(0, parsed);
|
||||||
return compressNumber(Integer.MAX_VALUE / 4d - parsed);
|
return compressNumber(Integer.MAX_VALUE / 4d - parsed);
|
||||||
|
39
src/main/java/net/william278/velocitab/tab/GroupTasks.java
Normal file
39
src/main/java/net/william278/velocitab/tab/GroupTasks.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -25,7 +25,6 @@ import com.velocitypowered.api.proxy.ServerConnection;
|
|||||||
import com.velocitypowered.api.proxy.player.TabList;
|
import com.velocitypowered.api.proxy.player.TabList;
|
||||||
import com.velocitypowered.api.proxy.player.TabListEntry;
|
import com.velocitypowered.api.proxy.player.TabListEntry;
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
|
||||||
import com.velocitypowered.api.scheduler.ScheduledTask;
|
import com.velocitypowered.api.scheduler.ScheduledTask;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@ -36,7 +35,6 @@ import net.william278.velocitab.config.Group;
|
|||||||
import net.william278.velocitab.config.Placeholder;
|
import net.william278.velocitab.config.Placeholder;
|
||||||
import net.william278.velocitab.player.Role;
|
import net.william278.velocitab.player.Role;
|
||||||
import net.william278.velocitab.player.TabPlayer;
|
import net.william278.velocitab.player.TabPlayer;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.event.Level;
|
import org.slf4j.event.Level;
|
||||||
@ -54,15 +52,13 @@ public class PlayerTabList {
|
|||||||
private final VanishTabList vanishTabList;
|
private final VanishTabList vanishTabList;
|
||||||
@Getter(value = AccessLevel.PUBLIC)
|
@Getter(value = AccessLevel.PUBLIC)
|
||||||
private final Map<UUID, TabPlayer> players;
|
private final Map<UUID, TabPlayer> players;
|
||||||
private final Map<Group, ScheduledTask> placeholderTasks;
|
private final Map<Group, GroupTasks> groupTasks;
|
||||||
private final Map<Group, ScheduledTask> headerFooterTasks;
|
|
||||||
|
|
||||||
public PlayerTabList(@NotNull Velocitab plugin) {
|
public PlayerTabList(@NotNull Velocitab plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.vanishTabList = new VanishTabList(plugin, this);
|
this.vanishTabList = new VanishTabList(plugin, this);
|
||||||
this.players = Maps.newConcurrentMap();
|
this.players = Maps.newConcurrentMap();
|
||||||
this.placeholderTasks = Maps.newConcurrentMap();
|
this.groupTasks = Maps.newConcurrentMap();
|
||||||
this.headerFooterTasks = Maps.newConcurrentMap();
|
|
||||||
this.reloadUpdate();
|
this.reloadUpdate();
|
||||||
this.registerListener();
|
this.registerListener();
|
||||||
}
|
}
|
||||||
@ -104,7 +100,9 @@ public class PlayerTabList {
|
|||||||
|
|
||||||
final String serverName = server.get().getServerInfo().getName();
|
final String serverName = server.get().getServerInfo().getName();
|
||||||
final Group group = getGroup(serverName);
|
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()) {
|
if (isDefault && !plugin.getSettings().isFallbackEnabled()) {
|
||||||
return;
|
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.
|
* Removes the player's entry from the tab list of all other players on the same group servers.
|
||||||
*/
|
*/
|
||||||
public void close() {
|
public void close() {
|
||||||
placeholderTasks.values().forEach(ScheduledTask::cancel);
|
groupTasks.values().forEach(GroupTasks::cancel);
|
||||||
placeholderTasks.clear();
|
|
||||||
headerFooterTasks.values().forEach(ScheduledTask::cancel);
|
|
||||||
headerFooterTasks.clear();
|
|
||||||
plugin.getServer().getAllPlayers().forEach(p -> {
|
plugin.getServer().getAllPlayers().forEach(p -> {
|
||||||
final Optional<ServerConnection> server = p.getCurrentServer();
|
final Optional<ServerConnection> server = p.getCurrentServer();
|
||||||
if (server.isEmpty()) return;
|
if (server.isEmpty()) return;
|
||||||
@ -345,10 +340,7 @@ public class PlayerTabList {
|
|||||||
|
|
||||||
player.getPlayer().getTabList().getEntries().stream()
|
player.getPlayer().getTabList().getEntries().stream()
|
||||||
.filter(e -> e.getProfile().getId().equals(tabPlayer.getPlayer().getUniqueId())).findFirst()
|
.filter(e -> e.getProfile().getId().equals(tabPlayer.getPlayer().getUniqueId())).findFirst()
|
||||||
.ifPresent(entry -> {
|
.ifPresent(entry -> entry.setDisplayName(displayName));
|
||||||
entry.setDisplayName(displayName);
|
|
||||||
entry.setLatency(Math.max((int) tabPlayer.getPlayer().getPing(), 0));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -375,26 +367,54 @@ public class PlayerTabList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the tab list periodically
|
// Update the tab list periodically
|
||||||
private void updatePeriodically(Group group) {
|
private void updatePeriodically(@NotNull Group group) {
|
||||||
cancelTasks(group);
|
cancelTasks(group);
|
||||||
|
|
||||||
|
ScheduledTask headerFooterTask = null;
|
||||||
|
ScheduledTask updateTask = null;
|
||||||
|
ScheduledTask latencyTask;
|
||||||
|
|
||||||
if (group.headerFooterUpdateRate() > 0) {
|
if (group.headerFooterUpdateRate() > 0) {
|
||||||
final ScheduledTask headerFooterTask = plugin.getServer().getScheduler()
|
headerFooterTask = plugin.getServer().getScheduler()
|
||||||
.buildTask(plugin, () -> updateGroupPlayers(group, false, true))
|
.buildTask(plugin, () -> updateGroupPlayers(group, false, true))
|
||||||
.delay(1, TimeUnit.SECONDS)
|
.delay(1, TimeUnit.SECONDS)
|
||||||
.repeat(Math.max(200, group.headerFooterUpdateRate()), TimeUnit.MILLISECONDS)
|
.repeat(Math.max(200, group.headerFooterUpdateRate()), TimeUnit.MILLISECONDS)
|
||||||
.schedule();
|
.schedule();
|
||||||
headerFooterTasks.put(group, headerFooterTask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group.placeholderUpdateRate() > 0) {
|
if (group.placeholderUpdateRate() > 0) {
|
||||||
final ScheduledTask updateTask = plugin.getServer().getScheduler()
|
updateTask = plugin.getServer().getScheduler()
|
||||||
.buildTask(plugin, () -> updateGroupPlayers(group, true, false))
|
.buildTask(plugin, () -> updateGroupPlayers(group, true, false))
|
||||||
.delay(1, TimeUnit.SECONDS)
|
.delay(1, TimeUnit.SECONDS)
|
||||||
.repeat(Math.max(200, group.placeholderUpdateRate()), TimeUnit.MILLISECONDS)
|
.repeat(Math.max(200, group.placeholderUpdateRate()), TimeUnit.MILLISECONDS)
|
||||||
.schedule();
|
.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) {
|
private void cancelTasks(@NotNull Group group) {
|
||||||
ScheduledTask task = placeholderTasks.entrySet().stream()
|
final GroupTasks tasks = groupTasks.get(group);
|
||||||
.filter(entry -> entry.getKey().equals(group))
|
if (tasks != null) {
|
||||||
.map(Map.Entry::getValue)
|
tasks.cancel();
|
||||||
.findFirst()
|
groupTasks.remove(group);
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,8 +457,6 @@ public class PlayerTabList {
|
|||||||
* Update the TAB list for all players when a plugin or proxy reload is performed
|
* Update the TAB list for all players when a plugin or proxy reload is performed
|
||||||
*/
|
*/
|
||||||
public void reloadUpdate() {
|
public void reloadUpdate() {
|
||||||
placeholderTasks.values().forEach(ScheduledTask::cancel);
|
|
||||||
placeholderTasks.clear();
|
|
||||||
plugin.getTabGroups().getGroups().forEach(this::updatePeriodically);
|
plugin.getTabGroups().getGroups().forEach(this::updatePeriodically);
|
||||||
|
|
||||||
if (players.isEmpty()) {
|
if (players.isEmpty()) {
|
||||||
|
Loading…
Reference in New Issue
Block a user