forked from Upstream/Velocitab
Sorting System with Placeholders (#94)
* Added regex check for placeholders to avoid useless requests. Added support for custom nametags. Due to minecraft limit only legacy chatcolor are supported. Team names now are unique, so 1 team can have max 1 player. Fixed problem with luckperms event bus while reloading the plugin. * Update src/main/java/net/william278/velocitab/config/Placeholder.java Co-authored-by: William <will27528@gmail.com> * Update src/main/java/net/william278/velocitab/hook/LuckPermsHook.java Co-authored-by: William <will27528@gmail.com> * Update src/main/java/net/william278/velocitab/config/Formatter.java Co-authored-by: William <will27528@gmail.com> * Update src/main/java/net/william278/velocitab/packet/UpdateTeamsPacket.java Co-authored-by: William <will27528@gmail.com> * Fixed problem while updating display names. Changed a few method signature as requested in pr. Applied changes of pr. * Added support for placeholders as sorting system * Code reformat * Update logging, task scheduling and player rosters Modified logging in the ScoreboardManager to represent playerNames as an array for readability. Ensured all tasks scheduled by Velocitab are canceled on proxy shutdown to prevent unwanted behavior. Reworked player roster management in PlayerTabList to correctly update player roles and decrease asynchronicity, enhancing performance and preventing possible race conditions. * Fixed problems after merging with upstream, fixed problem with player team color on join. * Fixed problems with pr-merge. Added sorting system with placeholders. * Update src/main/java/net/william278/velocitab/packet/UpdateTeamsPacket.java Co-authored-by: William <will27528@gmail.com> * Update src/main/java/net/william278/velocitab/packet/ScoreboardManager.java Co-authored-by: William <will27528@gmail.com> * Update src/main/java/net/william278/velocitab/packet/ScoreboardManager.java Co-authored-by: William <will27528@gmail.com> * Update src/main/java/net/william278/velocitab/packet/ScoreboardManager.java Co-authored-by: William <will27528@gmail.com> * Update src/main/java/net/william278/velocitab/config/Formatter.java Co-authored-by: William <will27528@gmail.com> * Update src/main/java/net/william278/velocitab/player/TabPlayer.java Co-authored-by: William <will27528@gmail.com> * Fix username replacement in scoreboard and code typo This commit resolves two issues. Firstly, changed the variable that we split the nametag on in `ScoreboardManager` from a hardcoded string to the player's specific username. This rectifies an issue where incorrect splitting occurred if the username wasn't exactly "%username%". Secondly, fixed a miswritten method call in `Formatter` from '..legacySection()' to '.legacySection()', correcting a syntax error. Lastly, removed superfluous replacement in `TabPlayer's` getNametag method as it was already handled in `ScoreboardManager`. * Reformat code * Changed logic with only one plugin message request. * Update src/main/java/net/william278/velocitab/sorting/SortingManager.java Co-authored-by: William <will27528@gmail.com> * Update src/main/java/net/william278/velocitab/hook/LuckPermsHook.java Co-authored-by: William <will27528@gmail.com> * Update src/main/java/net/william278/velocitab/packet/ScoreboardManager.java Co-authored-by: William <will27528@gmail.com> * Fixed requested changes * Changed docs --------- Co-authored-by: William <will27528@gmail.com>
This commit is contained in:
parent
f3f86f54d0
commit
cb8a50c24f
@ -1,36 +1,34 @@
|
||||
Velocitab can sort players in the TAB list by a number of "sorting elements." Sorting is enabled by default, and can be disabled with the `sort_players` option in the [`config.yml`](Config-File) file.
|
||||
|
||||
## Sortable elements
|
||||
To modify what players are sorted by, modify the `sort_players_by` list in the [`config.yml`](Config-File) file. This option accepts an ordered list; the first element in the list is what players will be sorted by first, with subsequent elements being used to break ties. The default sorting strategy is to sort first by `ROLE_WEIGHT` followed by `ROLE_NAME`.
|
||||
To modify what players are sorted by, modify the `sorting_placeholders` list in the [`config.yml`](Config-File) file. This option accepts an ordered list; the first element in the list is what players will be sorted by first, with subsequent elements being used to break ties. The default sorting strategy is to sort first by `%role_weight%` followed by `%username%`.
|
||||
|
||||
<details>
|
||||
<summary>Sort Players By… (config.yml)</summary>
|
||||
|
||||
```yaml
|
||||
# Ordered list of elements by which players should be sorted. (ROLE_WEIGHT, ROLE_NAME and SERVER are supported)
|
||||
# Ordered list of elements by which players should be sorted. (Correct values are both internal placeholders and (if enabled) PAPI placeholders)
|
||||
sort_players_by:
|
||||
- ROLE_WEIGHT
|
||||
- ROLE_NAME
|
||||
- %role_weight%
|
||||
- %username%
|
||||
```
|
||||
</details>
|
||||
|
||||
### List of elements
|
||||
The following sorting elements are supported:
|
||||
|
||||
| Sorting element | Description |
|
||||
|:-------------------:|---------------------------------------------------------|
|
||||
| `ROLE_WEIGHT` | The weight of the player's primary LuckPerms group |
|
||||
| `ROLE_NAME` | The name of the player's primary LuckPerms group |
|
||||
| `SERVER_NAME` | The name of the server the player is connected to |
|
||||
| `SERVER_GROUP` | The order in which the groups are designated |
|
||||
| `SERVER_GROUP_NAME` | The name of the server group the player is connected to |
|
||||
| Sorting element | Description |
|
||||
|:-----------------------:|----------------------------------------------------------------------------------------------|
|
||||
| `Internal Placeholders` | [Check docs here](https://william278.net/docs/velocitab/placeholders#default-placeholders) |
|
||||
| `PAPI Placeholders` | [Check docs here](https://william278.net/docs/velocitab/placeholders#placeholderapi-support) |
|
||||
|
||||
## Technical details
|
||||
In Minecraft, the TAB list is sorted by the client; the server does not handle the actual display order of names in the list. Players are sorted first by the name of their scoreboard team, then by their name. This is why having a proxy TAB plugin sort players is a surprisingly complex feature request!
|
||||
|
||||
To get the client to correctly sort the TAB list, Velocitab sends fake scoreboard "Update Teams" packets to everyone in order to trick the client into thinking players on the server are members of a fake scoreboard team. The name of the fake team for sorting is based on a number of "sorting elements," which can be customized.
|
||||
|
||||
Velocitab has a few optimizations in place to reduce the number of packets sent; if you have more sorting elements, do note this will lead to more packets being sent between clients and the proxy as the teams will need to be updated more regularly. This can lead to an observable increase in network traffic—listing fewer sorting elements in the `sort_players_by` section will reduce the number of packets sent.
|
||||
Velocitab has a few optimizations in place to reduce the number of packets sent; if you update frequently sorting element placeholders, do note this will lead to more packets being sent between clients and the proxy as the teams will need to be updated more regularly. This can lead to an observable increase in network traffic—listing fewer sorting elements in the `sort_players_by` section will reduce the number of packets sent.
|
||||
|
||||
|
||||
## Compatibility issues
|
||||
There are a few compatibility caveats to bear in mind with sorting players in the TAB list:
|
||||
|
@ -30,6 +30,8 @@ import com.velocitypowered.api.plugin.PluginDescription;
|
||||
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.scheduler.ScheduledTask;
|
||||
import lombok.Getter;
|
||||
import net.william278.annotaml.Annotaml;
|
||||
import net.william278.desertwell.util.UpdateChecker;
|
||||
import net.william278.desertwell.util.Version;
|
||||
@ -43,6 +45,7 @@ import net.william278.velocitab.hook.PAPIProxyBridgeHook;
|
||||
import net.william278.velocitab.packet.ScoreboardManager;
|
||||
import net.william278.velocitab.player.Role;
|
||||
import net.william278.velocitab.player.TabPlayer;
|
||||
import net.william278.velocitab.sorting.SortingManager;
|
||||
import net.william278.velocitab.tab.PlayerTabList;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bstats.velocity.Metrics;
|
||||
@ -72,6 +75,7 @@ public class Velocitab {
|
||||
private PlayerTabList tabList;
|
||||
private List<Hook> hooks;
|
||||
private ScoreboardManager scoreboardManager;
|
||||
private SortingManager sortingManager;
|
||||
|
||||
@Inject
|
||||
public Velocitab(@NotNull ProxyServer server, @NotNull Logger logger, @DataDirectory Path dataDirectory) {
|
||||
@ -84,6 +88,7 @@ public class Velocitab {
|
||||
public void onProxyInitialization(@NotNull ProxyInitializeEvent event) {
|
||||
loadSettings();
|
||||
loadHooks();
|
||||
prepareSortingManager();
|
||||
prepareScoreboardManager();
|
||||
prepareTabList();
|
||||
registerCommands();
|
||||
@ -94,6 +99,7 @@ public class Velocitab {
|
||||
|
||||
@Subscribe
|
||||
public void onProxyShutdown(@NotNull ProxyShutdownEvent event) {
|
||||
server.getScheduler().tasksByPlugin(this).forEach(ScheduledTask::cancel);
|
||||
disableScoreboardManager();
|
||||
getLuckPermsHook().ifPresent(LuckPermsHook::close);
|
||||
logger.info("Successfully disabled Velocitab");
|
||||
@ -155,6 +161,12 @@ public class Velocitab {
|
||||
Hook.AVAILABLE.forEach(availableHook -> availableHook.apply(this).ifPresent(hooks::add));
|
||||
}
|
||||
|
||||
private void prepareSortingManager() {
|
||||
if (settings.isSortPlayers()) {
|
||||
this.sortingManager = new SortingManager(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareScoreboardManager() {
|
||||
if (settings.isSortPlayers()) {
|
||||
this.scoreboardManager = new ScoreboardManager(this);
|
||||
@ -173,6 +185,10 @@ public class Velocitab {
|
||||
return Optional.ofNullable(scoreboardManager);
|
||||
}
|
||||
|
||||
public Optional<SortingManager> getSortingManager() {
|
||||
return Optional.ofNullable(sortingManager);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PlayerTabList getTabList() {
|
||||
return tabList;
|
||||
|
@ -24,11 +24,13 @@ import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import net.william278.velocitab.Velocitab;
|
||||
import net.william278.velocitab.player.TabPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.event.Level;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public enum Placeholder {
|
||||
|
||||
@ -48,12 +50,14 @@ public enum Placeholder {
|
||||
SUFFIX((plugin, player) -> player.getRole().getSuffix().orElse("")),
|
||||
ROLE((plugin, player) -> player.getRole().getName().orElse("")),
|
||||
ROLE_DISPLAY_NAME((plugin, player) -> player.getRole().getDisplayName().orElse("")),
|
||||
DEBUG_TEAM_NAME((plugin, player) -> plugin.getFormatter().escape(player.getTeamName(plugin)));
|
||||
ROLE_WEIGHT((plugin, player) -> Integer.toString(player.getRole().getWeight())),
|
||||
DEBUG_TEAM_NAME((plugin, player) -> plugin.getFormatter().escape(player.getLastTeamName().orElse("")));
|
||||
|
||||
/**
|
||||
* Function to replace placeholders with a real value
|
||||
*/
|
||||
private final BiFunction<Velocitab, TabPlayer, String> replacer;
|
||||
private final static Pattern pattern = Pattern.compile("%.*?%");
|
||||
|
||||
Placeholder(@NotNull BiFunction<Velocitab, TabPlayer, String> replacer) {
|
||||
this.replacer = replacer;
|
||||
@ -65,13 +69,16 @@ public enum Placeholder {
|
||||
}
|
||||
final String replaced = format;
|
||||
|
||||
if (!replaced.matches("%.*?%")) {
|
||||
if (!pattern.matcher(replaced).find()) {
|
||||
return CompletableFuture.completedFuture(replaced);
|
||||
}
|
||||
|
||||
return plugin.getPAPIProxyBridgeHook()
|
||||
.map(hook -> hook.formatPlaceholders(replaced, player.getPlayer()))
|
||||
.orElse(CompletableFuture.completedFuture(replaced));
|
||||
.orElse(CompletableFuture.completedFuture(replaced)).exceptionally(e -> {
|
||||
plugin.log(Level.ERROR, "An error occurred whilst parsing placeholders: " + e.getMessage());
|
||||
return replaced;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ import net.william278.annotaml.YamlComment;
|
||||
import net.william278.annotaml.YamlFile;
|
||||
import net.william278.annotaml.YamlKey;
|
||||
import net.william278.velocitab.Velocitab;
|
||||
import net.william278.velocitab.player.TabPlayer;
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -117,12 +116,12 @@ public class Settings {
|
||||
@YamlComment("Whether to sort players in the TAB list.")
|
||||
private boolean sortPlayers = true;
|
||||
|
||||
@YamlKey("sort_players_by")
|
||||
@YamlKey("sorting_placeholders")
|
||||
@YamlComment("Ordered list of elements by which players should be sorted. " +
|
||||
"(ROLE_WEIGHT, ROLE_NAME, SERVER_NAME, SERVER_GROUP and SERVER_GROUP_NAME are supported)")
|
||||
private List<String> sortPlayersBy = List.of(
|
||||
TabPlayer.SortableElement.ROLE_WEIGHT.name(),
|
||||
TabPlayer.SortableElement.ROLE_NAME.name()
|
||||
"(Correct values are both internal placeholders and (if enabled) PAPI placeholders)")
|
||||
private List<String> sortingPlaceholders = List.of(
|
||||
"%role_weight%",
|
||||
"%username%"
|
||||
);
|
||||
|
||||
@Getter
|
||||
@ -216,11 +215,8 @@ public class Settings {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<TabPlayer.SortableElement> getSortingElementList() {
|
||||
return sortPlayersBy.stream()
|
||||
.map(p -> TabPlayer.SortableElement.parse(p).orElseThrow(() ->
|
||||
new IllegalArgumentException("Invalid sorting element set in config file: " + p)))
|
||||
.toList();
|
||||
public List<String> getSortingElements() {
|
||||
return sortingPlaceholders;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -34,9 +34,7 @@ import net.william278.velocitab.tab.PlayerTabList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class LuckPermsHook extends Hook {
|
||||
@ -44,11 +42,14 @@ public class LuckPermsHook extends Hook {
|
||||
private int highestWeight = Role.DEFAULT_WEIGHT;
|
||||
private final LuckPerms api;
|
||||
private final EventSubscription<UserDataRecalculateEvent> event;
|
||||
private final Map<UUID, Long> lastUpdate;
|
||||
|
||||
public LuckPermsHook(@NotNull Velocitab plugin) throws IllegalStateException {
|
||||
super(plugin);
|
||||
this.api = LuckPermsProvider.get();
|
||||
lastUpdate = new HashMap<>();
|
||||
event = api.getEventBus().subscribe(plugin, UserDataRecalculateEvent.class, this::onLuckPermsGroupUpdate);
|
||||
|
||||
}
|
||||
|
||||
public void close() {
|
||||
@ -76,6 +77,12 @@ public class LuckPermsHook extends Hook {
|
||||
}
|
||||
|
||||
public void onLuckPermsGroupUpdate(@NotNull UserDataRecalculateEvent event) {
|
||||
// Prevent duplicate events
|
||||
if (lastUpdate.getOrDefault(event.getUser().getUniqueId(), 0L) > System.currentTimeMillis() - 100) {
|
||||
return;
|
||||
}
|
||||
lastUpdate.put(event.getUser().getUniqueId(), System.currentTimeMillis());
|
||||
|
||||
final PlayerTabList tabList = plugin.getTabList();
|
||||
plugin.getServer().getPlayer(event.getUser().getUniqueId())
|
||||
.ifPresent(player -> plugin.getServer().getScheduler()
|
||||
|
@ -87,7 +87,12 @@ public class ScoreboardManager {
|
||||
String suffix = split.length > 1 ? split[1] : "";
|
||||
|
||||
if (!createdTeams.getOrDefault(player.getUniqueId(), "").equals(role)) {
|
||||
createdTeams.computeIfAbsent(player.getUniqueId(), k -> role);
|
||||
|
||||
if (createdTeams.containsKey(player.getUniqueId())) {
|
||||
dispatchGroupPacket(UpdateTeamsPacket.removeTeam(plugin, createdTeams.get(player.getUniqueId())), player);
|
||||
}
|
||||
|
||||
createdTeams.put(player.getUniqueId(), role);
|
||||
this.nametags.put(role, prefix + ":::" + suffix);
|
||||
dispatchGroupPacket(UpdateTeamsPacket.create(plugin, role, "", prefix, suffix, name), player);
|
||||
} else if (!this.nametags.getOrDefault(role, "").equals(prefix + ":::" + suffix)) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
package net.william278.velocitab.player;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -27,6 +28,7 @@ import java.util.Optional;
|
||||
public class Role implements Comparable<Role> {
|
||||
public static final int DEFAULT_WEIGHT = 0;
|
||||
public static final Role DEFAULT_ROLE = new Role(DEFAULT_WEIGHT, null, null, null, null);
|
||||
@Getter
|
||||
private final int weight;
|
||||
@Nullable
|
||||
private final String name;
|
||||
|
@ -26,12 +26,12 @@ import net.william278.velocitab.Velocitab;
|
||||
import net.william278.velocitab.config.Placeholder;
|
||||
import net.william278.velocitab.tab.PlayerTabList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.event.Level;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class TabPlayer implements Comparable<TabPlayer> {
|
||||
private final Player player;
|
||||
@ -43,6 +43,7 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
||||
private int footerIndex = 0;
|
||||
@Getter
|
||||
private Component lastDisplayname;
|
||||
private String teamName;
|
||||
|
||||
public TabPlayer(@NotNull Player player, @NotNull Role role, int highestWeight) {
|
||||
this.player = player;
|
||||
@ -123,11 +124,20 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getTeamName(@NotNull Velocitab plugin) {
|
||||
return plugin.getSettings().getSortingElementList().stream()
|
||||
.map(element -> element.resolve(this, plugin))
|
||||
.collect(Collectors.joining("-"))
|
||||
+ getPlayer().getUniqueId().toString().substring(0, 3);
|
||||
public CompletableFuture<String> getTeamName(@NotNull Velocitab plugin) {
|
||||
return plugin.getSortingManager().map(sortingManager -> sortingManager.getTeamName(this))
|
||||
.orElseGet(() -> CompletableFuture.completedFuture(""))
|
||||
.thenApply(teamName -> {
|
||||
this.teamName = teamName;
|
||||
return teamName;
|
||||
}).exceptionally(e -> {
|
||||
plugin.log(Level.ERROR, "Failed to get team name for " + player.getUsername(), e);
|
||||
return "";
|
||||
});
|
||||
}
|
||||
|
||||
public Optional<String> getLastTeamName() {
|
||||
return Optional.ofNullable(teamName);
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.sorting;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import net.william278.velocitab.Velocitab;
|
||||
import net.william278.velocitab.config.Placeholder;
|
||||
import net.william278.velocitab.player.TabPlayer;
|
||||
import org.slf4j.event.Level;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SortingManager {
|
||||
|
||||
private final Velocitab plugin;
|
||||
private static final String DELIMITER = ":::";
|
||||
|
||||
public SortingManager(Velocitab plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public CompletableFuture<String> getTeamName(TabPlayer player) {
|
||||
return Placeholder.replace(String.join(DELIMITER, plugin.getSettings().getSortingElements()), plugin, player)
|
||||
.thenApply(s -> Arrays.asList(s.split(DELIMITER)))
|
||||
.thenApply(v -> v.stream().map(this::adaptValue).collect(Collectors.toList()))
|
||||
.thenApply(v -> handleList(player, v));
|
||||
}
|
||||
|
||||
private String handleList(TabPlayer player, List<String> values) {
|
||||
String result = String.join("", values);
|
||||
|
||||
if (result.length() > 12) {
|
||||
result = result.substring(0, 12);
|
||||
plugin.log(Level.WARN, "Sorting element list is too long, truncating to 16 characters");
|
||||
}
|
||||
|
||||
result += player.getPlayer().getUniqueId().toString().substring(0, 4);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String adaptValue(String value) {
|
||||
if (value.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
if (value.matches("[0-9]+")) {
|
||||
int integer = Integer.parseInt(value);
|
||||
int intSortSize = 3;
|
||||
return (integer >= 0 ? 0 : 1) + String.format("%0" + intSortSize + "d", Integer.parseInt(Strings.repeat("9", intSortSize)) - Math.abs(integer));
|
||||
}
|
||||
|
||||
if (value.length() > 6) {
|
||||
return value.substring(0, 4);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
@ -65,6 +65,7 @@ public class PlayerTabList {
|
||||
final Player joined = event.getPlayer();
|
||||
plugin.getScoreboardManager().ifPresent(manager -> manager.resetCache(joined));
|
||||
|
||||
|
||||
// Remove the player from the tracking list if they are switching servers
|
||||
final RegisteredServer previousServer = event.getPreviousServer();
|
||||
if (previousServer == null) {
|
||||
@ -106,12 +107,15 @@ public class PlayerTabList {
|
||||
() -> createEntry(player, tabList).thenAccept(tabList::addEntry)
|
||||
);
|
||||
addPlayerToTabList(player, tabPlayer);
|
||||
|
||||
player.sendHeaderAndFooter(this);
|
||||
|
||||
}
|
||||
|
||||
plugin.getScoreboardManager().ifPresent(s -> {
|
||||
s.resendAllNameTags(joined);
|
||||
s.updateRole(joined, plugin.getTabPlayer(joined).getTeamName(plugin));
|
||||
plugin.getTabPlayer(joined).getTeamName(plugin)
|
||||
.thenAccept(t -> s.updateRole(joined, t));
|
||||
});
|
||||
})
|
||||
.delay(500, TimeUnit.MILLISECONDS)
|
||||
@ -182,10 +186,14 @@ public class PlayerTabList {
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.getScoreboardManager().ifPresent(manager -> manager.updateRole(
|
||||
tabPlayer.getPlayer(),
|
||||
tabPlayer.getTeamName(plugin)
|
||||
));
|
||||
tabPlayer.getTeamName(plugin).thenAccept(teamName -> {
|
||||
if (teamName == null) return;
|
||||
|
||||
plugin.getScoreboardManager().ifPresent(manager -> manager.updateRole(
|
||||
tabPlayer.getPlayer(),
|
||||
teamName
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
public void updatePlayerDisplayName(TabPlayer tabPlayer) {
|
||||
|
Loading…
Reference in New Issue
Block a user