Skip to content

Commit

Permalink
feat: add show_all_players_from_all_groups config option (#183)
Browse files Browse the repository at this point in the history
Code refactor
Improved system that handles latency
  • Loading branch information
alexdev03 authored Mar 29, 2024
1 parent 5b5e40e commit b7c353a
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 46 deletions.
2 changes: 2 additions & 0 deletions docs/Config-File.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
27 changes: 25 additions & 2 deletions src/main/java/net/william278/velocitab/config/Group.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
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;
Expand Down Expand Up @@ -65,7 +66,13 @@ public String getFooter(int index) {

@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);
Expand Down Expand Up @@ -103,6 +110,9 @@ public Set<Player> getPlayers(@NotNull Velocitab plugin) {

@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()))
Expand All @@ -111,8 +121,18 @@ public Set<Player> getPlayers(@NotNull Velocitab plugin, @NotNull TabPlayer tabP
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()
Expand All @@ -122,6 +142,9 @@ public Set<TabPlayer> getTabPlayers(@NotNull Velocitab plugin) {

@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()
Expand All @@ -133,7 +156,7 @@ public Set<TabPlayer> getTabPlayers(@NotNull Velocitab plugin, @NotNull TabPlaye
}

@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof Group group)) {
return false;
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/net/william278/velocitab/config/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public Group getGroupFromServer(@NotNull String server, @NotNull Velocitab plugi
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,18 @@
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;
Expand Down Expand Up @@ -71,7 +72,7 @@ private String adaptValue(@NotNull String value) {
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);
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/net/william278/velocitab/tab/GroupTasks.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* This file is part of Velocitab, licensed under the Apache License 2.0.
*
* Copyright (c) William278 <[email protected]>
* 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();
}
}

}
86 changes: 45 additions & 41 deletions src/main/java/net/william278/velocitab/tab/PlayerTabList.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
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;
Expand All @@ -36,7 +35,6 @@
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;
Expand All @@ -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();
}
Expand Down Expand Up @@ -104,7 +100,9 @@ public void load() {

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;
Expand All @@ -119,10 +117,7 @@ public void load() {
* 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;
Expand Down Expand Up @@ -345,10 +340,7 @@ public void updatePlayerDisplayName(@NotNull TabPlayer tabPlayer) {

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));
});
});
}
Expand All @@ -375,26 +367,54 @@ public CompletableFuture<Component> getFooter(@NotNull TabPlayer player) {
}

// 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)));
});
});
}

/**
Expand Down Expand Up @@ -425,34 +445,18 @@ private void updateGroupPlayers(@NotNull Group group, boolean all, boolean incre
}
}

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);
}
}

/**
* 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()) {
Expand Down

0 comments on commit b7c353a

Please sign in to comment.