forked from Upstream/Velocitab
Added support for nametags and fixed a few problems (#84)
* 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. * Fixed problems after merging with upstream, fixed problem with player team color on join. * 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`. --------- Co-authored-by: William <will27528@gmail.com>
This commit is contained in:
parent
8ae25521dd
commit
75d9f32010
@ -95,6 +95,7 @@ public class Velocitab {
|
|||||||
@Subscribe
|
@Subscribe
|
||||||
public void onProxyShutdown(@NotNull ProxyShutdownEvent event) {
|
public void onProxyShutdown(@NotNull ProxyShutdownEvent event) {
|
||||||
disableScoreboardManager();
|
disableScoreboardManager();
|
||||||
|
getLuckPermsHook().ifPresent(LuckPermsHook::close);
|
||||||
logger.info("Successfully disabled Velocitab");
|
logger.info("Successfully disabled Velocitab");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +120,12 @@ public class Velocitab {
|
|||||||
new File(dataDirectory.toFile(), "config.yml"),
|
new File(dataDirectory.toFile(), "config.yml"),
|
||||||
new Settings(this)
|
new Settings(this)
|
||||||
).get();
|
).get();
|
||||||
|
|
||||||
|
settings.getNametags().values().stream()
|
||||||
|
.filter(nametag -> !nametag.contains("%username%")).forEach(nametag -> {
|
||||||
|
logger.warn("Nametag '" + nametag + "' does not contain %username% - removing");
|
||||||
|
settings.getNametags().remove(nametag);
|
||||||
|
});
|
||||||
} catch (IOException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
|
} catch (IOException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
|
||||||
logger.error("Failed to load config file: " + e.getMessage(), e);
|
logger.error("Failed to load config file: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@ -156,7 +163,7 @@ public class Velocitab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void disableScoreboardManager() {
|
private void disableScoreboardManager() {
|
||||||
if (scoreboardManager !=null && settings.isSortPlayers()) {
|
if (scoreboardManager != null && settings.isSortPlayers()) {
|
||||||
scoreboardManager.unregisterPacket();
|
scoreboardManager.unregisterPacket();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,6 +191,10 @@ public class Velocitab {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<TabPlayer> getTabPlayer(String name) {
|
||||||
|
return server.getPlayer(name).map(this::getTabPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
private void registerCommands() {
|
private void registerCommands() {
|
||||||
final BrigadierCommand command = new VelocitabCommand(this).command();
|
final BrigadierCommand command = new VelocitabCommand(this).command();
|
||||||
server.getCommandManager().register(
|
server.getCommandManager().register(
|
||||||
|
@ -79,6 +79,11 @@ public enum Formatter {
|
|||||||
return formatter.apply(text, player, plugin);
|
return formatter.apply(text, player, plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public String formatLegacySymbols(@NotNull String text, @NotNull TabPlayer player, @NotNull Velocitab plugin) {
|
||||||
|
return LegacyComponentSerializer.legacySection()
|
||||||
|
.serialize(format(text, player, plugin));
|
||||||
|
}
|
||||||
@NotNull
|
@NotNull
|
||||||
public String escape(@NotNull String text) {
|
public String escape(@NotNull String text) {
|
||||||
return escaper.apply(text);
|
return escaper.apply(text);
|
||||||
|
@ -65,6 +65,10 @@ public enum Placeholder {
|
|||||||
}
|
}
|
||||||
final String replaced = format;
|
final String replaced = format;
|
||||||
|
|
||||||
|
if (!replaced.matches("%.*?%")) {
|
||||||
|
return CompletableFuture.completedFuture(replaced);
|
||||||
|
}
|
||||||
|
|
||||||
return plugin.getPAPIProxyBridgeHook()
|
return plugin.getPAPIProxyBridgeHook()
|
||||||
.map(hook -> hook.formatPlaceholders(replaced, player.getPlayer()))
|
.map(hook -> hook.formatPlaceholders(replaced, player.getPlayer()))
|
||||||
.orElse(CompletableFuture.completedFuture(replaced));
|
.orElse(CompletableFuture.completedFuture(replaced));
|
||||||
|
@ -60,6 +60,11 @@ public class Settings {
|
|||||||
@YamlKey("formats")
|
@YamlKey("formats")
|
||||||
private Map<String, String> formats = Map.of("default", "&7[%server%] &f%prefix%%username%");
|
private Map<String, String> formats = Map.of("default", "&7[%server%] &f%prefix%%username%");
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@YamlKey("nametags")
|
||||||
|
@YamlComment("Nametag(s) to display above players' heads for each server group. To disable, set to empty")
|
||||||
|
private Map<String, String> nametags = Map.of("default", "&f%prefix%%username%&f%suffix%");
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@YamlComment("Which text formatter to use (MINEDOWN, MINIMESSAGE, or LEGACY)")
|
@YamlComment("Which text formatter to use (MINEDOWN, MINIMESSAGE, or LEGACY)")
|
||||||
@YamlKey("formatting_type")
|
@YamlKey("formatting_type")
|
||||||
@ -165,6 +170,16 @@ public class Settings {
|
|||||||
formats.getOrDefault(serverGroup, "%username%"));
|
formats.getOrDefault(serverGroup, "%username%"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public String getNametag(@NotNull String serverGroup) {
|
||||||
|
return StringEscapeUtils.unescapeJava(
|
||||||
|
nametags.getOrDefault(serverGroup, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean areNametagsEnabled() {
|
||||||
|
return !nametags.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get display name for the server
|
* Get display name for the server
|
||||||
*
|
*
|
||||||
|
@ -23,6 +23,7 @@ import com.velocitypowered.api.proxy.Player;
|
|||||||
import net.luckperms.api.LuckPerms;
|
import net.luckperms.api.LuckPerms;
|
||||||
import net.luckperms.api.LuckPermsProvider;
|
import net.luckperms.api.LuckPermsProvider;
|
||||||
import net.luckperms.api.cacheddata.CachedMetaData;
|
import net.luckperms.api.cacheddata.CachedMetaData;
|
||||||
|
import net.luckperms.api.event.EventSubscription;
|
||||||
import net.luckperms.api.event.user.UserDataRecalculateEvent;
|
import net.luckperms.api.event.user.UserDataRecalculateEvent;
|
||||||
import net.luckperms.api.model.group.Group;
|
import net.luckperms.api.model.group.Group;
|
||||||
import net.luckperms.api.model.user.User;
|
import net.luckperms.api.model.user.User;
|
||||||
@ -42,11 +43,16 @@ public class LuckPermsHook extends Hook {
|
|||||||
|
|
||||||
private int highestWeight = Role.DEFAULT_WEIGHT;
|
private int highestWeight = Role.DEFAULT_WEIGHT;
|
||||||
private final LuckPerms api;
|
private final LuckPerms api;
|
||||||
|
private final EventSubscription<UserDataRecalculateEvent> event;
|
||||||
|
|
||||||
public LuckPermsHook(@NotNull Velocitab plugin) throws IllegalStateException {
|
public LuckPermsHook(@NotNull Velocitab plugin) throws IllegalStateException {
|
||||||
super(plugin);
|
super(plugin);
|
||||||
this.api = LuckPermsProvider.get();
|
this.api = LuckPermsProvider.get();
|
||||||
api.getEventBus().subscribe(plugin, UserDataRecalculateEvent.class, this::onLuckPermsGroupUpdate);
|
event = api.getEventBus().subscribe(plugin, UserDataRecalculateEvent.class, this::onLuckPermsGroupUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
event.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -81,6 +87,7 @@ public class LuckPermsHook extends Hook {
|
|||||||
);
|
);
|
||||||
tabList.replacePlayer(updatedPlayer);
|
tabList.replacePlayer(updatedPlayer);
|
||||||
tabList.updatePlayer(updatedPlayer);
|
tabList.updatePlayer(updatedPlayer);
|
||||||
|
tabList.updatePlayerDisplayName(updatedPlayer);
|
||||||
})
|
})
|
||||||
.delay(500, TimeUnit.MILLISECONDS)
|
.delay(500, TimeUnit.MILLISECONDS)
|
||||||
.schedule());
|
.schedule());
|
||||||
|
@ -32,7 +32,7 @@ public class PAPIProxyBridgeHook extends Hook {
|
|||||||
|
|
||||||
public PAPIProxyBridgeHook(@NotNull Velocitab plugin) {
|
public PAPIProxyBridgeHook(@NotNull Velocitab plugin) {
|
||||||
super(plugin);
|
super(plugin);
|
||||||
this.api = PlaceholderAPI.getInstance();
|
this.api = PlaceholderAPI.createInstance();
|
||||||
this.api.setCacheExpiry(Math.max(0, plugin.getSettings().getPapiCacheTime()));
|
this.api.setCacheExpiry(Math.max(0, plugin.getSettings().getPapiCacheTime()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,15 +21,18 @@ package net.william278.velocitab.packet;
|
|||||||
|
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
|
import com.velocitypowered.api.proxy.ServerConnection;
|
||||||
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||||
import net.william278.velocitab.Velocitab;
|
import net.william278.velocitab.Velocitab;
|
||||||
|
import net.william278.velocitab.player.TabPlayer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.slf4j.event.Level;
|
import org.slf4j.event.Level;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import static com.velocitypowered.api.network.ProtocolVersion.*;
|
import static com.velocitypowered.api.network.ProtocolVersion.*;
|
||||||
|
|
||||||
@ -37,14 +40,14 @@ public class ScoreboardManager {
|
|||||||
|
|
||||||
private PacketRegistration<UpdateTeamsPacket> packetRegistration;
|
private PacketRegistration<UpdateTeamsPacket> packetRegistration;
|
||||||
private final Velocitab plugin;
|
private final Velocitab plugin;
|
||||||
private final Map<UUID, List<String>> createdTeams;
|
|
||||||
private final Map<UUID, Map<String, String>> roleMappings;
|
|
||||||
private final Set<TeamsPacketAdapter> versions;
|
private final Set<TeamsPacketAdapter> versions;
|
||||||
|
private final Map<UUID, String> createdTeams;
|
||||||
|
private final Map<String, String> nametags;
|
||||||
|
|
||||||
public ScoreboardManager(@NotNull Velocitab velocitab) {
|
public ScoreboardManager(@NotNull Velocitab velocitab) {
|
||||||
this.plugin = velocitab;
|
this.plugin = velocitab;
|
||||||
this.createdTeams = new HashMap<>();
|
this.createdTeams = new ConcurrentHashMap<>();
|
||||||
this.roleMappings = new HashMap<>();
|
this.nametags = new ConcurrentHashMap<>();
|
||||||
this.versions = new HashSet<>();
|
this.versions = new HashSet<>();
|
||||||
this.registerVersions();
|
this.registerVersions();
|
||||||
}
|
}
|
||||||
@ -64,44 +67,78 @@ public class ScoreboardManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void resetCache(@NotNull Player player) {
|
public void resetCache(@NotNull Player player) {
|
||||||
createdTeams.remove(player.getUniqueId());
|
String team = createdTeams.remove(player.getUniqueId());
|
||||||
roleMappings.remove(player.getUniqueId());
|
if (team != null) {
|
||||||
|
dispatchGroupPacket(UpdateTeamsPacket.removeTeam(plugin, team), player);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRoles(@NotNull Player player, @NotNull Map<String, String> playerRoles) {
|
public void updateRole(@NotNull Player player, @NotNull String role) {
|
||||||
if (!player.isActive()) {
|
if (!player.isActive()) {
|
||||||
plugin.getTabList().removeOfflinePlayer(player);
|
plugin.getTabList().removeOfflinePlayer(player);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
playerRoles.entrySet().stream()
|
|
||||||
.collect(Collectors.groupingBy(
|
final String name = player.getUsername();
|
||||||
Map.Entry::getValue,
|
final TabPlayer tabPlayer = plugin.getTabPlayer(player);
|
||||||
Collectors.mapping(Map.Entry::getKey, Collectors.toList())
|
tabPlayer.getNametag(plugin).thenAccept(nametag -> {
|
||||||
))
|
String[] split = nametag.split(player.getUsername(), 2);
|
||||||
.forEach((role, players) -> updateRoles(player, role, players.toArray(new String[0])));
|
String prefix = split[0];
|
||||||
|
String suffix = split.length > 1 ? split[1] : "";
|
||||||
|
|
||||||
|
if (!createdTeams.getOrDefault(player.getUniqueId(), "").equals(role)) {
|
||||||
|
createdTeams.computeIfAbsent(player.getUniqueId(), k -> 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)) {
|
||||||
|
this.nametags.put(role, prefix + ":::" + suffix);
|
||||||
|
dispatchGroupPacket(UpdateTeamsPacket.changeNameTag(plugin, role, prefix, suffix), player);
|
||||||
|
}
|
||||||
|
}).exceptionally(e -> {
|
||||||
|
plugin.log(Level.ERROR, "Failed to update role for " + player.getUsername(), e);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateRoles(@NotNull Player player, @NotNull String role, @NotNull String... playerNames) {
|
|
||||||
if (!player.isActive()) {
|
public void resendAllNameTags(Player player) {
|
||||||
plugin.getTabList().removeOfflinePlayer(player);
|
|
||||||
|
if(!plugin.getSettings().areNametagsEnabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!createdTeams.getOrDefault(player.getUniqueId(), List.of()).contains(role)) {
|
|
||||||
dispatchPacket(UpdateTeamsPacket.create(plugin, role, playerNames), player);
|
final Optional<ServerConnection> optionalServerConnection = player.getCurrentServer();
|
||||||
createdTeams.computeIfAbsent(player.getUniqueId(), k -> new ArrayList<>()).add(role);
|
if (optionalServerConnection.isEmpty()) {
|
||||||
roleMappings.computeIfAbsent(player.getUniqueId(), k -> new HashMap<>()).put(player.getUsername(), role);
|
return;
|
||||||
} else {
|
|
||||||
roleMappings.getOrDefault(player.getUniqueId(), Map.of())
|
|
||||||
.entrySet().stream()
|
|
||||||
.filter((entry) -> List.of(playerNames).contains(entry.getKey()))
|
|
||||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
|
|
||||||
.forEach((playerName, oldRole) -> dispatchPacket(
|
|
||||||
UpdateTeamsPacket.removeFromTeam(plugin, oldRole, playerName),
|
|
||||||
player
|
|
||||||
));
|
|
||||||
dispatchPacket(UpdateTeamsPacket.addToTeam(plugin, role, playerNames), player);
|
|
||||||
roleMappings.computeIfAbsent(player.getUniqueId(), k -> new HashMap<>()).put(player.getUsername(), role);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RegisteredServer serverInfo = optionalServerConnection.get().getServer();
|
||||||
|
|
||||||
|
List<RegisteredServer> siblings = plugin.getTabList().getGroupServers(serverInfo.getServerInfo().getName());
|
||||||
|
|
||||||
|
List<Player> players = siblings.stream().map(RegisteredServer::getPlayersConnected).flatMap(Collection::stream).toList();
|
||||||
|
|
||||||
|
players.forEach(p -> {
|
||||||
|
if (p == player || !p.isActive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String role = createdTeams.getOrDefault(p.getUniqueId(), "");
|
||||||
|
if (role.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String nametag = nametags.getOrDefault(role, "");
|
||||||
|
if (nametag.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] split = nametag.split(":::", 2);
|
||||||
|
String prefix = split[0];
|
||||||
|
String suffix = split.length > 1 ? split[1] : "";
|
||||||
|
|
||||||
|
dispatchPacket(UpdateTeamsPacket.create(plugin, role, "", prefix, suffix, p.getUsername()), player);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dispatchPacket(@NotNull UpdateTeamsPacket packet, @NotNull Player player) {
|
private void dispatchPacket(@NotNull UpdateTeamsPacket packet, @NotNull Player player) {
|
||||||
@ -118,6 +155,29 @@ public class ScoreboardManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void dispatchGroupPacket(@NotNull UpdateTeamsPacket packet, @NotNull Player player) {
|
||||||
|
Optional<ServerConnection> optionalServerConnection = player.getCurrentServer();
|
||||||
|
|
||||||
|
if (optionalServerConnection.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisteredServer serverInfo = optionalServerConnection.get().getServer();
|
||||||
|
|
||||||
|
List<RegisteredServer> siblings = plugin.getTabList().getGroupServers(serverInfo.getServerInfo().getName());
|
||||||
|
|
||||||
|
siblings.forEach(s -> {
|
||||||
|
s.getPlayersConnected().forEach(p -> {
|
||||||
|
try {
|
||||||
|
final ConnectedPlayer connectedPlayer = (ConnectedPlayer) p;
|
||||||
|
connectedPlayer.getConnection().write(packet);
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.log(Level.ERROR, "Failed to dispatch packet (is the client or server modded or using an illegal version?)", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void registerPacket() {
|
public void registerPacket() {
|
||||||
try {
|
try {
|
||||||
packetRegistration = PacketRegistration.of(UpdateTeamsPacket.class)
|
packetRegistration = PacketRegistration.of(UpdateTeamsPacket.class)
|
||||||
|
@ -30,6 +30,7 @@ import net.william278.velocitab.Velocitab;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -61,18 +62,32 @@ public class UpdateTeamsPacket implements MinecraftPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
protected static UpdateTeamsPacket create(@NotNull Velocitab plugin, @NotNull String teamName, @NotNull String... teamMembers) {
|
protected static UpdateTeamsPacket create(@NotNull Velocitab plugin, @NotNull String teamName, @NotNull String displayName, @Nullable String prefix, @Nullable String suffix, @NotNull String... teamMembers) {
|
||||||
return new UpdateTeamsPacket(plugin)
|
return new UpdateTeamsPacket(plugin)
|
||||||
.teamName(teamName.length() > 16 ? teamName.substring(0, 16) : teamName)
|
.teamName(teamName.length() > 16 ? teamName.substring(0, 16) : teamName)
|
||||||
.mode(UpdateMode.CREATE_TEAM)
|
.mode(UpdateMode.CREATE_TEAM)
|
||||||
|
.displayName(displayName)
|
||||||
|
.friendlyFlags(List.of(FriendlyFlag.CAN_HURT_FRIENDLY))
|
||||||
|
.nameTagVisibility(NameTagVisibility.ALWAYS)
|
||||||
|
.collisionRule(CollisionRule.ALWAYS)
|
||||||
|
.color(getLastColor(prefix))
|
||||||
|
.prefix(prefix == null ? "" : prefix)
|
||||||
|
.suffix(suffix == null ? "" : suffix)
|
||||||
|
.entities(Arrays.asList(teamMembers));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected static UpdateTeamsPacket changeNameTag(@NotNull Velocitab plugin, @NotNull String teamName, @Nullable String prefix, @Nullable String suffix) {
|
||||||
|
return new UpdateTeamsPacket(plugin)
|
||||||
|
.teamName(teamName.length() > 16 ? teamName.substring(0, 16) : teamName)
|
||||||
|
.mode(UpdateMode.UPDATE_INFO)
|
||||||
.displayName(teamName)
|
.displayName(teamName)
|
||||||
.friendlyFlags(List.of(FriendlyFlag.CAN_HURT_FRIENDLY))
|
.friendlyFlags(List.of(FriendlyFlag.CAN_HURT_FRIENDLY))
|
||||||
.nameTagVisibility(NameTagVisibility.ALWAYS)
|
.nameTagVisibility(NameTagVisibility.ALWAYS)
|
||||||
.collisionRule(CollisionRule.ALWAYS)
|
.collisionRule(CollisionRule.ALWAYS)
|
||||||
.color(15)
|
.color(getLastColor(prefix))
|
||||||
.prefix("")
|
.prefix(prefix == null ? "" : prefix)
|
||||||
.suffix("")
|
.suffix(suffix == null ? "" : suffix);
|
||||||
.entities(Arrays.asList(teamMembers));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -91,6 +106,64 @@ public class UpdateTeamsPacket implements MinecraftPacket {
|
|||||||
.entities(Arrays.asList(teamMembers));
|
.entities(Arrays.asList(teamMembers));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected static UpdateTeamsPacket removeTeam(@NotNull Velocitab plugin, @NotNull String teamName) {
|
||||||
|
return new UpdateTeamsPacket(plugin)
|
||||||
|
.teamName(teamName.length() > 16 ? teamName.substring(0, 16) : teamName)
|
||||||
|
.mode(UpdateMode.REMOVE_TEAM);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getLastColor(@Nullable String text) {
|
||||||
|
if (text == null) {
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
int intvar = text.lastIndexOf("§");
|
||||||
|
|
||||||
|
if (intvar == -1 || intvar == text.length() - 1) {
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
String last = text.substring(intvar, intvar + 2);
|
||||||
|
return TeamColor.getColorId(last.charAt(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TeamColor {
|
||||||
|
BLACK('0', 0),
|
||||||
|
DARK_BLUE('1', 1),
|
||||||
|
DARK_GREEN('2', 2),
|
||||||
|
DARK_AQUA('3', 3),
|
||||||
|
DARK_RED('4', 4),
|
||||||
|
DARK_PURPLE('5', 5),
|
||||||
|
GOLD('6', 6),
|
||||||
|
GRAY('7', 7),
|
||||||
|
DARK_GRAY('8', 8),
|
||||||
|
BLUE('9', 9),
|
||||||
|
GREEN('a', 10),
|
||||||
|
AQUA('b', 11),
|
||||||
|
RED('c', 12),
|
||||||
|
LIGHT_PURPLE('d', 13),
|
||||||
|
YELLOW('e', 14),
|
||||||
|
WHITE('f', 15),
|
||||||
|
OBFUSCATED('k', 16),
|
||||||
|
BOLD('l', 17),
|
||||||
|
STRIKETHROUGH('m', 18),
|
||||||
|
UNDERLINED('n', 19),
|
||||||
|
ITALIC('o', 20),
|
||||||
|
RESET('r', 21);
|
||||||
|
|
||||||
|
private final char colorChar;
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
TeamColor(char colorChar, int id) {
|
||||||
|
this.colorChar= colorChar;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getColorId(char var) {
|
||||||
|
return Arrays.stream(values()).filter(color -> color.colorChar == var).map(c -> c.id).findFirst().orElse(15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void decode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
public void decode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||||
throw new UnsupportedOperationException("Operation not supported");
|
throw new UnsupportedOperationException("Operation not supported");
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
package net.william278.velocitab.player;
|
package net.william278.velocitab.player;
|
||||||
|
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
|
import lombok.Getter;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.william278.velocitab.Velocitab;
|
import net.william278.velocitab.Velocitab;
|
||||||
import net.william278.velocitab.config.Placeholder;
|
import net.william278.velocitab.config.Placeholder;
|
||||||
@ -36,8 +37,12 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
|||||||
private final Player player;
|
private final Player player;
|
||||||
private final Role role;
|
private final Role role;
|
||||||
private final int highestWeight;
|
private final int highestWeight;
|
||||||
|
@Getter
|
||||||
private int headerIndex = 0;
|
private int headerIndex = 0;
|
||||||
|
@Getter
|
||||||
private int footerIndex = 0;
|
private int footerIndex = 0;
|
||||||
|
@Getter
|
||||||
|
private Component lastDisplayname;
|
||||||
|
|
||||||
public TabPlayer(@NotNull Player player, @NotNull Role role, int highestWeight) {
|
public TabPlayer(@NotNull Player player, @NotNull Role role, int highestWeight) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
@ -105,7 +110,15 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
|||||||
public CompletableFuture<Component> getDisplayName(@NotNull Velocitab plugin) {
|
public CompletableFuture<Component> getDisplayName(@NotNull Velocitab plugin) {
|
||||||
final String serverGroup = plugin.getSettings().getServerGroup(getServerName());
|
final String serverGroup = plugin.getSettings().getServerGroup(getServerName());
|
||||||
return Placeholder.replace(plugin.getSettings().getFormat(serverGroup), plugin, this)
|
return Placeholder.replace(plugin.getSettings().getFormat(serverGroup), plugin, this)
|
||||||
.thenApply(formatted -> plugin.getFormatter().format(formatted, this, plugin));
|
.thenApply(formatted -> plugin.getFormatter().format(formatted, this, plugin))
|
||||||
|
.thenApply(c -> this.lastDisplayname = c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public CompletableFuture<String> getNametag(@NotNull Velocitab plugin) {
|
||||||
|
final String serverGroup = plugin.getSettings().getServerGroup(getServerName());
|
||||||
|
return Placeholder.replace(plugin.getSettings().getNametag(serverGroup), plugin, this)
|
||||||
|
.thenApply(formatted -> plugin.getFormatter().formatLegacySymbols(formatted, this, plugin));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,18 +126,16 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
|||||||
public String getTeamName(@NotNull Velocitab plugin) {
|
public String getTeamName(@NotNull Velocitab plugin) {
|
||||||
return plugin.getSettings().getSortingElementList().stream()
|
return plugin.getSettings().getSortingElementList().stream()
|
||||||
.map(element -> element.resolve(this, plugin))
|
.map(element -> element.resolve(this, plugin))
|
||||||
.collect(Collectors.joining("-"));
|
.collect(Collectors.joining("-"))
|
||||||
|
+ getPlayer().getUniqueId().toString().substring(0, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void sendHeaderAndFooter(@NotNull PlayerTabList tabList) {
|
public void sendHeaderAndFooter(@NotNull PlayerTabList tabList) {
|
||||||
tabList.getHeader(this).thenAccept(header -> tabList.getFooter(this)
|
tabList.getHeader(this).thenAccept(header -> tabList.getFooter(this)
|
||||||
.thenAccept(footer -> player.sendPlayerListHeaderAndFooter(header, footer)));
|
.thenAccept(footer -> player.sendPlayerListHeaderAndFooter(header, footer)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getHeaderIndex() {
|
|
||||||
return headerIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void incrementHeaderIndex(@NotNull Velocitab plugin) {
|
public void incrementHeaderIndex(@NotNull Velocitab plugin) {
|
||||||
headerIndex++;
|
headerIndex++;
|
||||||
if (headerIndex >= plugin.getSettings().getHeaderListSize(getServerGroup(plugin))) {
|
if (headerIndex >= plugin.getSettings().getHeaderListSize(getServerGroup(plugin))) {
|
||||||
@ -132,10 +143,6 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFooterIndex() {
|
|
||||||
return footerIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void incrementFooterIndex(@NotNull Velocitab plugin) {
|
public void incrementFooterIndex(@NotNull Velocitab plugin) {
|
||||||
footerIndex++;
|
footerIndex++;
|
||||||
if (footerIndex >= plugin.getSettings().getFooterListSize(getServerGroup(plugin))) {
|
if (footerIndex >= plugin.getSettings().getFooterListSize(getServerGroup(plugin))) {
|
||||||
@ -174,7 +181,7 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
|||||||
? String.format("%0" + Integer.toString(orderSize).length() + "d", position)
|
? String.format("%0" + Integer.toString(orderSize).length() + "d", position)
|
||||||
: String.valueOf(orderSize);
|
: String.valueOf(orderSize);
|
||||||
}),
|
}),
|
||||||
SERVER_GROUP_NAME((player, plugin) -> player.getServerGroup(plugin));
|
SERVER_GROUP_NAME(TabPlayer::getServerGroup);
|
||||||
|
|
||||||
private final BiFunction<TabPlayer, Velocitab, String> elementResolver;
|
private final BiFunction<TabPlayer, Velocitab, String> elementResolver;
|
||||||
|
|
||||||
|
@ -36,9 +36,7 @@ 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 java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
@ -67,7 +65,6 @@ public class PlayerTabList {
|
|||||||
final Player joined = event.getPlayer();
|
final Player joined = event.getPlayer();
|
||||||
plugin.getScoreboardManager().ifPresent(manager -> manager.resetCache(joined));
|
plugin.getScoreboardManager().ifPresent(manager -> manager.resetCache(joined));
|
||||||
|
|
||||||
|
|
||||||
// Remove the player from the tracking list if they are switching servers
|
// Remove the player from the tracking list if they are switching servers
|
||||||
final RegisteredServer previousServer = event.getPreviousServer();
|
final RegisteredServer previousServer = event.getPreviousServer();
|
||||||
if (previousServer == null) {
|
if (previousServer == null) {
|
||||||
@ -76,7 +73,7 @@ public class PlayerTabList {
|
|||||||
|
|
||||||
// Get the servers in the group from the joined server name
|
// Get the servers in the group from the joined server name
|
||||||
// If the server is not in a group, use fallback
|
// If the server is not in a group, use fallback
|
||||||
Optional<List<String>> serversInGroup = getSiblings(joined.getCurrentServer()
|
Optional<List<String>> serversInGroup = getGroupNames(joined.getCurrentServer()
|
||||||
.map(ServerConnection::getServerInfo)
|
.map(ServerConnection::getServerInfo)
|
||||||
.map(ServerInfo::getName)
|
.map(ServerInfo::getName)
|
||||||
.orElse("?"));
|
.orElse("?"));
|
||||||
@ -91,14 +88,10 @@ public class PlayerTabList {
|
|||||||
final TabPlayer tabPlayer = plugin.getTabPlayer(joined);
|
final TabPlayer tabPlayer = plugin.getTabPlayer(joined);
|
||||||
players.add(tabPlayer);
|
players.add(tabPlayer);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Update lists
|
// Update lists
|
||||||
plugin.getServer().getScheduler()
|
plugin.getServer().getScheduler()
|
||||||
.buildTask(plugin, () -> {
|
.buildTask(plugin, () -> {
|
||||||
final TabList tabList = joined.getTabList();
|
final TabList tabList = joined.getTabList();
|
||||||
final Map<String, String> playerRoles = new HashMap<>();
|
|
||||||
|
|
||||||
for (TabPlayer player : players) {
|
for (TabPlayer player : players) {
|
||||||
// Skip players on other servers if the setting is enabled
|
// Skip players on other servers if the setting is enabled
|
||||||
if (plugin.getSettings().isOnlyListPlayersInSameGroup() && serversInGroup.isPresent()
|
if (plugin.getSettings().isOnlyListPlayersInSameGroup() && serversInGroup.isPresent()
|
||||||
@ -106,7 +99,6 @@ public class PlayerTabList {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
playerRoles.put(player.getPlayer().getUsername(), player.getTeamName(plugin));
|
|
||||||
tabList.getEntries().stream()
|
tabList.getEntries().stream()
|
||||||
.filter(e -> e.getProfile().getId().equals(player.getPlayer().getUniqueId())).findFirst()
|
.filter(e -> e.getProfile().getId().equals(player.getPlayer().getUniqueId())).findFirst()
|
||||||
.ifPresentOrElse(
|
.ifPresentOrElse(
|
||||||
@ -114,13 +106,13 @@ public class PlayerTabList {
|
|||||||
() -> createEntry(player, tabList).thenAccept(tabList::addEntry)
|
() -> createEntry(player, tabList).thenAccept(tabList::addEntry)
|
||||||
);
|
);
|
||||||
addPlayerToTabList(player, tabPlayer);
|
addPlayerToTabList(player, tabPlayer);
|
||||||
|
|
||||||
player.sendHeaderAndFooter(this);
|
player.sendHeaderAndFooter(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugin.getScoreboardManager().ifPresent(s -> {
|
||||||
plugin.getScoreboardManager().ifPresent(manager -> manager.setRoles(joined, playerRoles));
|
s.resendAllNameTags(joined);
|
||||||
|
s.updateRole(joined, plugin.getTabPlayer(joined).getTeamName(plugin));
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.delay(500, TimeUnit.MILLISECONDS)
|
.delay(500, TimeUnit.MILLISECONDS)
|
||||||
.schedule();
|
.schedule();
|
||||||
@ -149,11 +141,6 @@ public class PlayerTabList {
|
|||||||
() -> createEntry(newPlayer, player.getPlayer().getTabList())
|
() -> createEntry(newPlayer, player.getPlayer().getTabList())
|
||||||
.thenAccept(entry -> player.getPlayer().getTabList().addEntry(entry))
|
.thenAccept(entry -> player.getPlayer().getTabList().addEntry(entry))
|
||||||
);
|
);
|
||||||
plugin.getScoreboardManager().ifPresent(manager -> manager.updateRoles(
|
|
||||||
player.getPlayer(),
|
|
||||||
newPlayer.getTeamName(plugin),
|
|
||||||
newPlayer.getPlayer().getUsername()
|
|
||||||
));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,6 +164,9 @@ public class PlayerTabList {
|
|||||||
}))
|
}))
|
||||||
.delay(500, TimeUnit.MILLISECONDS)
|
.delay(500, TimeUnit.MILLISECONDS)
|
||||||
.schedule();
|
.schedule();
|
||||||
|
// Delete player team
|
||||||
|
plugin.getScoreboardManager().ifPresent(manager -> manager.resetCache(event.getPlayer()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace a player in the tab list
|
// Replace a player in the tab list
|
||||||
@ -192,16 +182,27 @@ public class PlayerTabList {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
players.forEach(player -> tabPlayer.getDisplayName(plugin).thenAccept(displayName -> {
|
plugin.getScoreboardManager().ifPresent(manager -> manager.updateRole(
|
||||||
|
tabPlayer.getPlayer(),
|
||||||
|
tabPlayer.getTeamName(plugin)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePlayerDisplayName(TabPlayer tabPlayer) {
|
||||||
|
Component lastDisplayName = tabPlayer.getLastDisplayname();
|
||||||
|
tabPlayer.getDisplayName(plugin).thenAccept(displayName -> {
|
||||||
|
if (displayName == null || displayName.equals(lastDisplayName)) return;
|
||||||
|
|
||||||
|
players.forEach(player ->
|
||||||
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 -> entry.setDisplayName(displayName));
|
.ifPresent(entry -> entry.setDisplayName(displayName)));
|
||||||
plugin.getScoreboardManager().ifPresent(manager -> manager.updateRoles(
|
});
|
||||||
player.getPlayer(),
|
|
||||||
tabPlayer.getTeamName(plugin),
|
}
|
||||||
tabPlayer.getPlayer().getUsername()
|
|
||||||
));
|
public void updateDisplayNames() {
|
||||||
}));
|
players.forEach(this::updatePlayerDisplayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Component> getHeader(@NotNull TabPlayer player) {
|
public CompletableFuture<Component> getHeader(@NotNull TabPlayer player) {
|
||||||
@ -231,6 +232,7 @@ public class PlayerTabList {
|
|||||||
this.updatePlayer(player);
|
this.updatePlayer(player);
|
||||||
player.sendHeaderAndFooter(this);
|
player.sendHeaderAndFooter(this);
|
||||||
});
|
});
|
||||||
|
updateDisplayNames();
|
||||||
})
|
})
|
||||||
.repeat(Math.max(200, updateRate), TimeUnit.MILLISECONDS)
|
.repeat(Math.max(200, updateRate), TimeUnit.MILLISECONDS)
|
||||||
.schedule();
|
.schedule();
|
||||||
@ -255,6 +257,7 @@ public class PlayerTabList {
|
|||||||
this.updatePlayer(player);
|
this.updatePlayer(player);
|
||||||
player.sendHeaderAndFooter(this);
|
player.sendHeaderAndFooter(this);
|
||||||
});
|
});
|
||||||
|
updateDisplayNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -269,7 +272,7 @@ public class PlayerTabList {
|
|||||||
* @return The servers in the same group as the given server, empty if the server is not in a group and fallback is disabled
|
* @return The servers in the same group as the given server, empty if the server is not in a group and fallback is disabled
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public Optional<List<String>> getSiblings(String serverName) {
|
public Optional<List<String>> getGroupNames(String serverName) {
|
||||||
return plugin.getSettings().getServerGroups().values().stream()
|
return plugin.getSettings().getServerGroups().values().stream()
|
||||||
.filter(servers -> servers.contains(serverName))
|
.filter(servers -> servers.contains(serverName))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
@ -285,6 +288,24 @@ public class PlayerTabList {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the servers in the same group as the given server, as an optional list of {@link ServerInfo}
|
||||||
|
* <p>
|
||||||
|
* If the server is not in a group, use the fallback group
|
||||||
|
* If the fallback is disabled, return an empty optional
|
||||||
|
*
|
||||||
|
* @param serverName The server name
|
||||||
|
* @return The servers in the same group as the given server, empty if the server is not in a group and fallback is disabled
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public List<RegisteredServer> getGroupServers(String serverName) {
|
||||||
|
return plugin.getServer().getAllServers().stream()
|
||||||
|
.filter(server -> plugin.getSettings().getServerGroups().values().stream()
|
||||||
|
.filter(servers -> servers.contains(serverName))
|
||||||
|
.anyMatch(servers -> servers.contains(server.getServerInfo().getName())))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void proxyReload(@NotNull ProxyReloadEvent event) {
|
public void proxyReload(@NotNull ProxyReloadEvent event) {
|
||||||
plugin.loadSettings();
|
plugin.loadSettings();
|
||||||
|
Loading…
Reference in New Issue
Block a user