forked from Upstream/Velocitab
Added VelocitabAPI, Vanish integration system (#106)
* Added support for VanishIntegration
* Merged
* Added API and improved vanish system
* Fixed problem with API
* Fixed import problem
* Commit with requested changes
* first test
* Added NotNull missing annotations
* Fixed all requested changes
* Fixed logic problem
* Revert "first test"
This reverts commit 1be3c47d9c
.
* Hide nametag if the prefix & suffix are empty.
* Fixes for conversations.
* Added missing @NotNull
* Adjust repo order; use `elytrium` over `exceptionflug`
---------
Co-authored-by: William <will27528@gmail.com>
This commit is contained in:
parent
1e2aff4cf0
commit
b128f36efa
@ -23,8 +23,8 @@ repositories {
|
||||
maven { url = 'https://repo.william278.net/releases/' }
|
||||
maven { url = 'https://jitpack.io/' }
|
||||
maven { url = 'https://repo.minebench.de/' }
|
||||
maven { url = 'https://maven.elytrium.net/repo/' }
|
||||
maven { url = 'https://mvn.exceptionflug.de/repository/exceptionflug-public/' }
|
||||
maven { url = "https://maven.elytrium.net/repo/" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -28,12 +28,13 @@ import com.velocitypowered.api.plugin.Plugin;
|
||||
import com.velocitypowered.api.plugin.PluginContainer;
|
||||
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;
|
||||
import net.william278.velocitab.api.VelocitabAPI;
|
||||
import net.william278.velocitab.commands.VelocitabCommand;
|
||||
import net.william278.velocitab.config.Formatter;
|
||||
import net.william278.velocitab.config.Settings;
|
||||
@ -42,10 +43,9 @@ import net.william278.velocitab.hook.LuckPermsHook;
|
||||
import net.william278.velocitab.hook.MiniPlaceholdersHook;
|
||||
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 net.william278.velocitab.vanish.VanishManager;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bstats.velocity.Metrics;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -75,6 +75,8 @@ public class Velocitab {
|
||||
private List<Hook> hooks;
|
||||
private ScoreboardManager scoreboardManager;
|
||||
private SortingManager sortingManager;
|
||||
@Getter
|
||||
private VanishManager vanishManager;
|
||||
|
||||
@Inject
|
||||
public Velocitab(@NotNull ProxyServer server, @NotNull Logger logger, @DataDirectory Path dataDirectory) {
|
||||
@ -90,9 +92,11 @@ public class Velocitab {
|
||||
prepareScoreboardManager();
|
||||
prepareTabList();
|
||||
prepareSortingManager();
|
||||
prepareVanishManager();
|
||||
registerCommands();
|
||||
registerMetrics();
|
||||
checkForUpdates();
|
||||
prepareAPI();
|
||||
logger.info("Successfully enabled Velocitab");
|
||||
}
|
||||
|
||||
@ -101,6 +105,7 @@ public class Velocitab {
|
||||
server.getScheduler().tasksByPlugin(this).forEach(ScheduledTask::cancel);
|
||||
disableScoreboardManager();
|
||||
getLuckPermsHook().ifPresent(LuckPermsHook::close);
|
||||
VelocitabAPI.unregister();
|
||||
logger.info("Successfully disabled Velocitab");
|
||||
}
|
||||
|
||||
@ -173,6 +178,10 @@ public class Velocitab {
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareVanishManager() {
|
||||
this.vanishManager = new VanishManager(this);
|
||||
}
|
||||
|
||||
private void prepareSortingManager() {
|
||||
this.sortingManager = new SortingManager(this);
|
||||
}
|
||||
@ -197,16 +206,8 @@ public class Velocitab {
|
||||
server.getEventManager().register(this, tabList);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public TabPlayer getTabPlayer(@NotNull Player player) {
|
||||
return new TabPlayer(player,
|
||||
getLuckPermsHook().map(hook -> hook.getPlayerRole(player)).orElse(Role.DEFAULT_ROLE)
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Optional<TabPlayer> getTabPlayer(String name) {
|
||||
return server.getPlayer(name).map(this::getTabPlayer);
|
||||
private void prepareAPI() {
|
||||
VelocitabAPI.register(this);
|
||||
}
|
||||
|
||||
private void registerCommands() {
|
||||
|
181
src/main/java/net/william278/velocitab/api/VelocitabAPI.java
Normal file
181
src/main/java/net/william278/velocitab/api/VelocitabAPI.java
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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.api;
|
||||
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import net.william278.velocitab.Velocitab;
|
||||
import net.william278.velocitab.player.TabPlayer;
|
||||
import net.william278.velocitab.tab.PlayerTabList;
|
||||
import net.william278.velocitab.vanish.VanishIntegration;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
/**
|
||||
* The Velocitab API class.
|
||||
* Retrieve an instance of the API class via {@link #getInstance()}.
|
||||
*/ public class VelocitabAPI {
|
||||
|
||||
// Instance of the plugin
|
||||
private final Velocitab plugin;
|
||||
private static VelocitabAPI instance;
|
||||
|
||||
@ApiStatus.Internal
|
||||
protected VelocitabAPI(@NotNull Velocitab plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entrypoint to the {@link VelocitabAPI} API - returns an instance of the API
|
||||
*
|
||||
* @return instance of the HuskSync API
|
||||
* @since 1.5.2
|
||||
*/
|
||||
@NotNull
|
||||
public static VelocitabAPI getInstance() {
|
||||
if (instance == null) {
|
||||
throw new NotRegisteredException();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>(Internal use only)</b> - Register the API.
|
||||
*
|
||||
* @param plugin the plugin instance
|
||||
* @since 3.0
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public static void register(@NotNull Velocitab plugin) {
|
||||
instance = new VelocitabAPI(plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>(Internal use only)</b> - Unregister the API.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public static void unregister() {
|
||||
instance = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an option of {@link TabPlayer} instance for the given Velocity {@link Player}.
|
||||
*
|
||||
* @param player the Velocity player to get the {@link TabPlayer} instance for
|
||||
* @return the {@link TabPlayer} instance for the given player or an empty optional if the player is not in a group server
|
||||
* @since 2.0
|
||||
*/
|
||||
public Optional<TabPlayer> getUser(@NotNull Player player) {
|
||||
return plugin.getTabList().getTabPlayer(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the custom name for a player.
|
||||
* This will only be visible in the tab list and not in the nametag.
|
||||
*
|
||||
* @param player The player for whom to set the custom name
|
||||
* @param name The custom name to set
|
||||
*/
|
||||
public void setCustomPlayerName(@NotNull Player player, @Nullable String name) {
|
||||
getUser(player).ifPresent(tabPlayer -> {
|
||||
tabPlayer.setCustomName(name);
|
||||
plugin.getTabList().updatePlayerDisplayName(tabPlayer);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the custom name of the TabPlayer, if it has been set.
|
||||
*
|
||||
* @param player The player for whom to get the custom name
|
||||
* @return An Optional object containing the custom name, or empty if no custom name has been set.
|
||||
*/
|
||||
public Optional<String> getCustomPlayerName(@NotNull Player player) {
|
||||
return getUser(player).flatMap(TabPlayer::getCustomName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link PlayerTabList} handles the tab list for all players on the server groups.
|
||||
*
|
||||
* @return the {@link PlayerTabList} global instance.
|
||||
*/
|
||||
@NotNull
|
||||
public PlayerTabList getTabList() {
|
||||
return plugin.getTabList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the VanishIntegration for the VelocitabAPI.
|
||||
*
|
||||
* @param vanishIntegration the VanishIntegration to set
|
||||
*/
|
||||
public void setVanishIntegration(@NotNull VanishIntegration vanishIntegration) {
|
||||
plugin.getVanishManager().setIntegration(vanishIntegration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the VanishIntegration associated with the VelocitabAPI instance.
|
||||
* This integration allows checking if a player can see another player and if a player is vanished.
|
||||
*
|
||||
* @return The VanishIntegration instance associated with the VelocitabAPI
|
||||
*/
|
||||
@NotNull
|
||||
public VanishIntegration getVanishIntegration() {
|
||||
return plugin.getVanishManager().getIntegration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vanishes the player by hiding them from the tab list and scoreboard if enabled.
|
||||
*
|
||||
* @param player The player to vanish
|
||||
*/
|
||||
public void vanishPlayer(@NotNull Player player) {
|
||||
plugin.getVanishManager().vanishPlayer(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Un-vanishes the given player by showing them in the tab list and scoreboard if enabled.
|
||||
*
|
||||
* @param player The player to unvanish
|
||||
*/
|
||||
public void unVanishPlayer(@NotNull Player player) {
|
||||
plugin.getVanishManager().unVanishPlayer(player);
|
||||
}
|
||||
|
||||
|
||||
static final class NotRegisteredException extends IllegalStateException {
|
||||
|
||||
private static final String MESSAGE = """
|
||||
Could not access the Velocitab API as it has not yet been registered. This could be because:
|
||||
1) Velocitab has failed to enable successfully
|
||||
2) You are attempting to access Velocitab on plugin construction/before your plugin has enabled.
|
||||
3) You have shaded Velocitab into your plugin jar and need to fix your maven/gradle/build script
|
||||
to only include Velocitab as a dependency and not as a shaded dependency.""";
|
||||
|
||||
NotRegisteredException() {
|
||||
super(MESSAGE);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -20,15 +20,21 @@
|
||||
package net.william278.velocitab.commands;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.velocitypowered.api.command.BrigadierCommand;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.william278.desertwell.about.AboutMenu;
|
||||
import net.william278.velocitab.Velocitab;
|
||||
import net.william278.velocitab.player.TabPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public final class VelocitabCommand {
|
||||
private static final TextColor MAIN_COLOR = TextColor.color(0x00FB9A);
|
||||
private final AboutMenu aboutMenu;
|
||||
@ -69,6 +75,29 @@ public final class VelocitabCommand {
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})
|
||||
)
|
||||
.then(LiteralArgumentBuilder.<CommandSource>literal("name")
|
||||
.then(RequiredArgumentBuilder.<CommandSource, String>argument("name", StringArgumentType.word())
|
||||
.executes(ctx -> {
|
||||
if (!(ctx.getSource() instanceof Player player)) {
|
||||
ctx.getSource().sendMessage(Component.text("You must be a player to use this command!", MAIN_COLOR));
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
String name = StringArgumentType.getString(ctx, "name");
|
||||
Optional<TabPlayer> tabPlayer = plugin.getTabList().getTabPlayer(player);
|
||||
|
||||
if (tabPlayer.isEmpty()) {
|
||||
ctx.getSource().sendMessage(Component.text("You must in a correct server!", MAIN_COLOR));
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
tabPlayer.get().setCustomName(name);
|
||||
plugin.getTabList().updatePlayerDisplayName(tabPlayer.get());
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})
|
||||
)
|
||||
)
|
||||
.then(LiteralArgumentBuilder.<CommandSource>literal("reload")
|
||||
.requires(src -> src.hasPermission("velocitab.command.reload"))
|
||||
.executes(ctx -> {
|
||||
@ -91,7 +120,7 @@ public final class VelocitabCommand {
|
||||
}
|
||||
ctx.getSource().sendMessage(Component
|
||||
.text("An update for velocitab is available. " +
|
||||
"Please update to " + checked.getLatestVersion(), MAIN_COLOR));
|
||||
"Please update to " + checked.getLatestVersion(), MAIN_COLOR));
|
||||
});
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})
|
||||
|
@ -43,7 +43,7 @@ public enum Placeholder {
|
||||
.orElse("")),
|
||||
CURRENT_DATE((plugin, player) -> DateTimeFormatter.ofPattern("dd MMM yyyy").format(LocalDateTime.now())),
|
||||
CURRENT_TIME((plugin, player) -> DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now())),
|
||||
USERNAME((plugin, player) -> plugin.getFormatter().escape(player.getPlayer().getUsername())),
|
||||
USERNAME((plugin, player) -> plugin.getFormatter().escape(player.getCustomName().orElse(player.getPlayer().getUsername()))),
|
||||
SERVER((plugin, player) -> player.getServerDisplayName(plugin)),
|
||||
PING((plugin, player) -> Long.toString(player.getPlayer().getPing())),
|
||||
PREFIX((plugin, player) -> player.getRole().getPrefix().orElse("")),
|
||||
@ -83,10 +83,4 @@ public enum Placeholder {
|
||||
return replaced;
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String formatSortableInt(int value, int maxValue) {
|
||||
return String.format("%0" + Integer.toString(maxValue).length() + "d", maxValue - value);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -34,12 +34,14 @@ import net.william278.velocitab.tab.PlayerTabList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
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;
|
||||
@ -87,13 +89,9 @@ public class LuckPermsHook extends Hook {
|
||||
plugin.getServer().getPlayer(event.getUser().getUniqueId())
|
||||
.ifPresent(player -> plugin.getServer().getScheduler()
|
||||
.buildTask(plugin, () -> {
|
||||
final TabPlayer updatedPlayer = new TabPlayer(
|
||||
player,
|
||||
getRoleFromMetadata(event.getData().getMetaData())
|
||||
);
|
||||
tabList.replacePlayer(updatedPlayer);
|
||||
tabList.updatePlayer(updatedPlayer);
|
||||
tabList.updatePlayerDisplayName(updatedPlayer);
|
||||
final TabPlayer tabPlayer = tabList.getTabPlayer(player).orElseThrow();
|
||||
tabPlayer.setRole(getRoleFromMetadata(event.getData().getMetaData()));
|
||||
tabList.updatePlayerDisplayName(tabPlayer);
|
||||
})
|
||||
.delay(500, TimeUnit.MILLISECONDS)
|
||||
.schedule());
|
||||
|
@ -74,6 +74,63 @@ public class ScoreboardManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void vanishPlayer(@NotNull Player player) {
|
||||
if (!plugin.getSettings().isSortPlayers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Optional<ServerConnection> optionalServerConnection = player.getCurrentServer();
|
||||
if (optionalServerConnection.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final RegisteredServer serverInfo = optionalServerConnection.get().getServer();
|
||||
final List<RegisteredServer> siblings = plugin.getTabList().getGroupServers(serverInfo.getServerInfo().getName());
|
||||
UpdateTeamsPacket packet = UpdateTeamsPacket.removeTeam(plugin, createdTeams.get(player.getUniqueId()));
|
||||
|
||||
siblings.forEach(server -> server.getPlayersConnected().forEach(connected -> {
|
||||
boolean canSee = !plugin.getVanishManager().isVanished(connected.getUsername())
|
||||
|| plugin.getVanishManager().canSee(player.getUsername(), player.getUsername());
|
||||
|
||||
if (!canSee) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatchPacket(packet, connected);
|
||||
}));
|
||||
}
|
||||
|
||||
public void unVanishPlayer(@NotNull Player player) {
|
||||
if (!plugin.getSettings().isSortPlayers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Optional<ServerConnection> optionalServerConnection = player.getCurrentServer();
|
||||
if (optionalServerConnection.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final RegisteredServer serverInfo = optionalServerConnection.get().getServer();
|
||||
final List<RegisteredServer> siblings = plugin.getTabList().getGroupServers(serverInfo.getServerInfo().getName());
|
||||
|
||||
final String role = createdTeams.getOrDefault(player.getUniqueId(), "");
|
||||
if (role.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String nametag = nametags.getOrDefault(role, "");
|
||||
if (nametag.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String[] split = nametag.split(NAMETAG_DELIMITER, 2);
|
||||
final String prefix = split[0];
|
||||
final String suffix = split.length > 1 ? split[1] : "";
|
||||
final UpdateTeamsPacket packet = UpdateTeamsPacket.create(plugin, createdTeams.get(player.getUniqueId()), "", prefix, suffix, player.getUsername());
|
||||
|
||||
siblings.forEach(server -> server.getPlayersConnected().forEach(connected -> dispatchPacket(packet, connected)));
|
||||
}
|
||||
|
||||
public void updateRole(@NotNull Player player, @NotNull String role) {
|
||||
if (!player.isActive()) {
|
||||
plugin.getTabList().removeOfflinePlayer(player);
|
||||
@ -81,11 +138,11 @@ public class ScoreboardManager {
|
||||
}
|
||||
|
||||
final String name = player.getUsername();
|
||||
final TabPlayer tabPlayer = plugin.getTabPlayer(player);
|
||||
final TabPlayer tabPlayer = plugin.getTabList().getTabPlayer(player).orElseThrow();
|
||||
tabPlayer.getNametag(plugin).thenAccept(nametag -> {
|
||||
String[] split = nametag.split(player.getUsername(), 2);
|
||||
String prefix = split[0];
|
||||
String suffix = split.length > 1 ? split[1] : "";
|
||||
final String[] split = nametag.split(player.getUsername(), 2);
|
||||
final String prefix = split[0];
|
||||
final String suffix = split.length > 1 ? split[1] : "";
|
||||
|
||||
if (!createdTeams.getOrDefault(player.getUniqueId(), "").equals(role)) {
|
||||
if (createdTeams.containsKey(player.getUniqueId())) {
|
||||
@ -106,8 +163,8 @@ public class ScoreboardManager {
|
||||
}
|
||||
|
||||
|
||||
public void resendAllNameTags(Player player) {
|
||||
if (!plugin.getSettings().doNametags()) {
|
||||
public void resendAllTeams(@NotNull Player player) {
|
||||
if (!plugin.getSettings().isSendScoreboardPackets()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -122,16 +179,31 @@ public class ScoreboardManager {
|
||||
.map(RegisteredServer::getPlayersConnected)
|
||||
.flatMap(Collection::stream)
|
||||
.toList();
|
||||
|
||||
final List<String> roles = new ArrayList<>();
|
||||
|
||||
players.forEach(p -> {
|
||||
if (p == player || !p.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.getVanishManager().isVanished(p.getUsername()) ||
|
||||
!plugin.getVanishManager().canSee(player.getUsername(), p.getUsername())) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String role = createdTeams.getOrDefault(p.getUniqueId(), "");
|
||||
if (role.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent duplicate packets
|
||||
if (roles.contains(role)) {
|
||||
return;
|
||||
}
|
||||
|
||||
roles.add(role);
|
||||
|
||||
final String nametag = nametags.getOrDefault(role, "");
|
||||
if (nametag.isEmpty()) {
|
||||
return;
|
||||
@ -168,6 +240,12 @@ public class ScoreboardManager {
|
||||
final List<RegisteredServer> siblings = plugin.getTabList().getGroupServers(serverInfo.getServerInfo().getName());
|
||||
siblings.forEach(server -> server.getPlayersConnected().forEach(connected -> {
|
||||
try {
|
||||
final boolean canSee = !plugin.getVanishManager().isVanished(connected.getUsername())
|
||||
|| plugin.getVanishManager().canSee(player.getUsername(), player.getUsername());
|
||||
if (!canSee) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ConnectedPlayer connectedPlayer = (ConnectedPlayer) connected;
|
||||
connectedPlayer.getConnection().write(packet);
|
||||
} catch (Throwable e) {
|
||||
|
@ -69,7 +69,7 @@ public class UpdateTeamsPacket implements MinecraftPacket {
|
||||
.mode(UpdateMode.CREATE_TEAM)
|
||||
.displayName(displayName)
|
||||
.friendlyFlags(List.of(FriendlyFlag.CAN_HURT_FRIENDLY))
|
||||
.nameTagVisibility(NameTagVisibility.ALWAYS)
|
||||
.nameTagVisibility(isNametagPresent(prefix, suffix) ? NameTagVisibility.ALWAYS : NameTagVisibility.NEVER)
|
||||
.collisionRule(CollisionRule.ALWAYS)
|
||||
.color(getLastColor(prefix))
|
||||
.prefix(prefix == null ? "" : prefix)
|
||||
@ -77,6 +77,10 @@ public class UpdateTeamsPacket implements MinecraftPacket {
|
||||
.entities(Arrays.asList(teamMembers));
|
||||
}
|
||||
|
||||
private static boolean isNametagPresent(@Nullable String prefix, @Nullable String suffix) {
|
||||
return prefix != null && !prefix.isEmpty() || suffix != null && !suffix.isEmpty();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected static UpdateTeamsPacket changeNameTag(@NotNull Velocitab plugin, @NotNull String teamName,
|
||||
@Nullable String prefix, @Nullable String suffix) {
|
||||
@ -85,7 +89,7 @@ public class UpdateTeamsPacket implements MinecraftPacket {
|
||||
.mode(UpdateMode.UPDATE_INFO)
|
||||
.displayName(teamName)
|
||||
.friendlyFlags(List.of(FriendlyFlag.CAN_HURT_FRIENDLY))
|
||||
.nameTagVisibility(NameTagVisibility.ALWAYS)
|
||||
.nameTagVisibility(isNametagPresent(prefix, suffix) ? NameTagVisibility.ALWAYS : NameTagVisibility.NEVER)
|
||||
.collisionRule(CollisionRule.ALWAYS)
|
||||
.color(getLastColor(prefix))
|
||||
.prefix(prefix == null ? "" : prefix)
|
||||
|
@ -21,18 +21,21 @@ package net.william278.velocitab.player;
|
||||
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.william278.velocitab.Velocitab;
|
||||
import net.william278.velocitab.config.Placeholder;
|
||||
import net.william278.velocitab.tab.PlayerTabList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public final class TabPlayer implements Comparable<TabPlayer> {
|
||||
private final Player player;
|
||||
private final Role role;
|
||||
@Setter
|
||||
private Role role;
|
||||
@Getter
|
||||
private int headerIndex = 0;
|
||||
@Getter
|
||||
@ -40,6 +43,8 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
||||
@Getter
|
||||
private Component lastDisplayname;
|
||||
private String teamName;
|
||||
@Nullable
|
||||
private String customName;
|
||||
|
||||
public TabPlayer(@NotNull Player player, @NotNull Role role) {
|
||||
this.player = player;
|
||||
@ -153,6 +158,24 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the custom name of the TabPlayer, if it has been set.
|
||||
*
|
||||
* @return An Optional object containing the custom name, or empty if no custom name has been set.
|
||||
*/
|
||||
public Optional<String> getCustomName() {
|
||||
return Optional.ofNullable(customName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the custom name of the TabPlayer.
|
||||
*
|
||||
* @param customName The custom name to set
|
||||
*/
|
||||
public void setCustomName(@Nullable String customName) {
|
||||
this.customName = customName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull TabPlayer o) {
|
||||
final int roleDifference = role.compareTo(o.role);
|
||||
|
@ -33,6 +33,7 @@ import com.velocitypowered.api.scheduler.ScheduledTask;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.william278.velocitab.Velocitab;
|
||||
import net.william278.velocitab.config.Placeholder;
|
||||
import net.william278.velocitab.player.Role;
|
||||
import net.william278.velocitab.player.TabPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -63,17 +64,17 @@ public class PlayerTabList {
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<TabPlayer> getTabPlayer(@NotNull Player player) {
|
||||
return players.stream().filter(tabPlayer -> tabPlayer.getPlayer().getUniqueId().equals(player.getUniqueId())).findFirst();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@Subscribe
|
||||
public void onPlayerJoin(@NotNull ServerPostConnectEvent event) {
|
||||
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) {
|
||||
players.removeIf(player -> player.getPlayer().getUniqueId().equals(joined.getUniqueId()));
|
||||
}
|
||||
|
||||
// Get the servers in the group from the joined server name
|
||||
// If the server is not in a group, use fallback
|
||||
@ -90,10 +91,11 @@ public class PlayerTabList {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the player to the tracking list
|
||||
final TabPlayer tabPlayer = plugin.getTabPlayer(joined);
|
||||
// Add the player to the tracking list if they are not already listed
|
||||
final TabPlayer tabPlayer = getTabPlayer(joined).orElseGet(() -> createTabPlayer(joined));
|
||||
players.add(tabPlayer);
|
||||
|
||||
final boolean isVanished = plugin.getVanishManager().isVanished(joined.getUsername());
|
||||
// Update lists
|
||||
plugin.getServer().getScheduler()
|
||||
.buildTask(plugin, () -> {
|
||||
@ -104,22 +106,31 @@ public class PlayerTabList {
|
||||
&& !serversInGroup.get().contains(player.getServerName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create or update TAB list entries for all players
|
||||
tabList.getEntries().stream()
|
||||
.filter(e -> e.getProfile().getId().equals(player.getPlayer().getUniqueId()))
|
||||
.findFirst().ifPresentOrElse(
|
||||
entry -> player.getDisplayName(plugin).thenAccept(entry::setDisplayName),
|
||||
() -> createEntry(player, tabList).thenAccept(tabList::addEntry)
|
||||
);
|
||||
addPlayerToTabList(player, tabPlayer);
|
||||
// check if current player can see the joined player
|
||||
if (!isVanished || plugin.getVanishManager().canSee(player.getPlayer().getUsername(), joined.getUsername())) {
|
||||
addPlayerToTabList(player, tabPlayer);
|
||||
} else {
|
||||
player.getPlayer().getTabList().removeEntry(joined.getUniqueId());
|
||||
}
|
||||
// check if joined player can see current player
|
||||
if ((plugin.getVanishManager().isVanished(player.getPlayer().getUsername()) ||
|
||||
!plugin.getVanishManager().canSee(joined.getUsername(), player.getPlayer().getUsername())) && player.getPlayer() != joined) {
|
||||
tabList.removeEntry(player.getPlayer().getUniqueId());
|
||||
} else {
|
||||
tabList.getEntries().stream()
|
||||
.filter(e -> e.getProfile().getId().equals(player.getPlayer().getUniqueId())).findFirst()
|
||||
.ifPresentOrElse(
|
||||
entry -> player.getDisplayName(plugin).thenAccept(entry::setDisplayName),
|
||||
() -> createEntry(player, tabList).thenAccept(tabList::addEntry)
|
||||
);
|
||||
}
|
||||
|
||||
player.sendHeaderAndFooter(this);
|
||||
}
|
||||
|
||||
plugin.getScoreboardManager().ifPresent(s -> {
|
||||
s.resendAllNameTags(joined);
|
||||
plugin.getTabPlayer(joined).getTeamName(plugin).thenAccept(t -> s.updateRole(joined, t));
|
||||
s.resendAllTeams(joined);
|
||||
tabPlayer.getTeamName(plugin).thenAccept(t -> s.updateRole(joined, t));
|
||||
});
|
||||
})
|
||||
.delay(500, TimeUnit.MILLISECONDS)
|
||||
@ -136,6 +147,16 @@ public class PlayerTabList {
|
||||
.build());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private TabListEntry createEntry(@NotNull TabPlayer player, @NotNull TabList tabList, Component displayName) {
|
||||
return TabListEntry.builder()
|
||||
.profile(player.getPlayer().getGameProfile())
|
||||
.displayName(displayName)
|
||||
.latency(0)
|
||||
.tabList(tabList)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void addPlayerToTabList(@NotNull TabPlayer player, @NotNull TabPlayer newPlayer) {
|
||||
if (newPlayer.getPlayer().getUniqueId().equals(player.getPlayer().getUniqueId())) {
|
||||
return;
|
||||
@ -152,6 +173,20 @@ public class PlayerTabList {
|
||||
|
||||
}
|
||||
|
||||
private void addPlayerToTabList(@NotNull TabPlayer player, @NotNull TabPlayer newPlayer, TabListEntry entry) {
|
||||
if (newPlayer.getPlayer().getUniqueId().equals(player.getPlayer().getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean isPresent = player.getPlayer()
|
||||
.getTabList().getEntries().stream()
|
||||
.noneMatch(e -> e.getProfile().getId().equals(newPlayer.getPlayer().getUniqueId()));
|
||||
|
||||
if (isPresent) {
|
||||
player.getPlayer().getTabList().addEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPlayerQuit(@NotNull DisconnectEvent event) {
|
||||
if (event.getLoginStatus() != DisconnectEvent.LoginStatus.SUCCESSFUL_LOGIN) {
|
||||
@ -181,10 +216,11 @@ public class PlayerTabList {
|
||||
|
||||
}
|
||||
|
||||
// Replace a player in the tab list
|
||||
public void replacePlayer(@NotNull TabPlayer tabPlayer) {
|
||||
players.removeIf(player -> player.getPlayer().getUniqueId().equals(tabPlayer.getPlayer().getUniqueId()));
|
||||
players.add(tabPlayer);
|
||||
@NotNull
|
||||
public TabPlayer createTabPlayer(@NotNull Player player) {
|
||||
return new TabPlayer(player,
|
||||
plugin.getLuckPermsHook().map(hook -> hook.getPlayerRole(player)).orElse(Role.DEFAULT_ROLE)
|
||||
);
|
||||
}
|
||||
|
||||
// Update a player's name in the tab list
|
||||
@ -204,19 +240,25 @@ public class PlayerTabList {
|
||||
});
|
||||
}
|
||||
|
||||
public void updatePlayerDisplayName(TabPlayer tabPlayer) {
|
||||
public void updatePlayerDisplayName(@NotNull TabPlayer tabPlayer) {
|
||||
final Component lastDisplayName = tabPlayer.getLastDisplayname();
|
||||
tabPlayer.getDisplayName(plugin).thenAccept(displayName -> {
|
||||
if (displayName == null || displayName.equals(lastDisplayName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
players.forEach(player ->
|
||||
player.getPlayer().getTabList().getEntries().stream()
|
||||
.filter(e -> e.getProfile().getId().equals(tabPlayer.getPlayer().getUniqueId())).findFirst()
|
||||
.ifPresent(entry -> entry.setDisplayName(displayName)));
|
||||
});
|
||||
boolean isVanished = plugin.getVanishManager().isVanished(tabPlayer.getPlayer().getUsername());
|
||||
|
||||
players.forEach(player -> {
|
||||
if (isVanished && !plugin.getVanishManager().canSee(player.getPlayer().getUsername(), tabPlayer.getPlayer().getUsername())) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.getPlayer().getTabList().getEntries().stream()
|
||||
.filter(e -> e.getProfile().getId().equals(tabPlayer.getPlayer().getUniqueId())).findFirst()
|
||||
.ifPresent(entry -> entry.setDisplayName(displayName));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Update the display names of all listed players
|
||||
@ -293,7 +335,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
|
||||
*/
|
||||
@NotNull
|
||||
public Optional<List<String>> getGroupNames(String serverName) {
|
||||
public Optional<List<String>> getGroupNames(@NotNull String serverName) {
|
||||
return plugin.getSettings().getServerGroups().values().stream()
|
||||
.filter(servers -> servers.contains(serverName))
|
||||
.findFirst()
|
||||
@ -319,7 +361,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
|
||||
*/
|
||||
@NotNull
|
||||
public List<RegisteredServer> getGroupServers(String serverName) {
|
||||
public List<RegisteredServer> getGroupServers(@NotNull String serverName) {
|
||||
return plugin.getServer().getAllServers().stream()
|
||||
.filter(server -> plugin.getSettings().getServerGroups().values().stream()
|
||||
.filter(servers -> servers.contains(serverName))
|
||||
@ -342,4 +384,33 @@ public class PlayerTabList {
|
||||
public void removeOfflinePlayer(@NotNull Player player) {
|
||||
players.removeIf(tabPlayer -> tabPlayer.getPlayer().getUniqueId().equals(player.getUniqueId()));
|
||||
}
|
||||
|
||||
public void vanishPlayer(@NotNull TabPlayer tabPlayer) {
|
||||
players.forEach(p -> {
|
||||
if (p.getPlayer().equals(tabPlayer.getPlayer())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!plugin.getVanishManager().canSee(p.getPlayer().getUsername(), tabPlayer.getPlayer().getUsername())) {
|
||||
p.getPlayer().getTabList().removeEntry(tabPlayer.getPlayer().getUniqueId());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void unVanishPlayer(@NotNull TabPlayer tabPlayer) {
|
||||
final UUID uuid = tabPlayer.getPlayer().getUniqueId();
|
||||
|
||||
tabPlayer.getDisplayName(plugin).thenAccept(c -> {
|
||||
players.forEach(p -> {
|
||||
if (p.getPlayer().equals(tabPlayer.getPlayer())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!p.getPlayer().getTabList().containsEntry(uuid)) {
|
||||
createEntry(tabPlayer, p.getPlayer().getTabList(), c);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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.vanish;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
public final class DefaultVanishIntegration implements VanishIntegration {
|
||||
|
||||
@Override
|
||||
public boolean canSee(@NotNull String name, @NotNull String otherName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVanished(@NotNull String name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.vanish;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface VanishIntegration {
|
||||
|
||||
boolean canSee(@NotNull String name, @NotNull String otherName);
|
||||
|
||||
boolean isVanished(@NotNull String name);
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.vanish;
|
||||
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import net.william278.velocitab.Velocitab;
|
||||
import net.william278.velocitab.player.TabPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class VanishManager {
|
||||
|
||||
private final Velocitab plugin;
|
||||
private VanishIntegration integration;
|
||||
|
||||
public VanishManager(@NotNull Velocitab plugin) {
|
||||
this.plugin = plugin;
|
||||
setIntegration(new DefaultVanishIntegration());
|
||||
}
|
||||
|
||||
public void setIntegration(@NotNull VanishIntegration integration) {
|
||||
this.integration = integration;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public VanishIntegration getIntegration() {
|
||||
return integration;
|
||||
}
|
||||
|
||||
public boolean canSee(@NotNull String name, @NotNull String otherName) {
|
||||
return integration.canSee(name, otherName);
|
||||
}
|
||||
|
||||
public boolean isVanished(@NotNull String name) {
|
||||
return integration.isVanished(name);
|
||||
}
|
||||
|
||||
public void vanishPlayer(@NotNull Player player) {
|
||||
final Optional<TabPlayer> tabPlayer = plugin.getTabList().getTabPlayer(player);
|
||||
if (tabPlayer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.getTabList().vanishPlayer(tabPlayer.get());
|
||||
plugin.getScoreboardManager().ifPresent(scoreboardManager -> scoreboardManager.vanishPlayer(player));
|
||||
}
|
||||
|
||||
public void unVanishPlayer(@NotNull Player player) {
|
||||
final Optional<TabPlayer> tabPlayer = plugin.getTabList().getTabPlayer(player);
|
||||
if (tabPlayer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.getTabList().unVanishPlayer(tabPlayer.get());
|
||||
plugin.getScoreboardManager().ifPresent(scoreboardManager -> scoreboardManager.unVanishPlayer(player));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user