Delayed "real" player removing, spoofed user fixes, sort by server

This commit is contained in:
William 2023-02-19 01:30:46 +00:00
parent 6caf720b5d
commit 500c647ecc
No known key found for this signature in database
8 changed files with 151 additions and 66 deletions

View File

@ -18,14 +18,19 @@ dependencies {
compileOnly 'com.velocitypowered:velocity-api:3.1.1'
compileOnly 'net.luckperms:api:5.4'
implementation 'org.apache.commons:commons-text:1.10.0'
implementation 'net.william278:Annotaml:2.0.1'
implementation 'dev.dejvokep:boosted-yaml:1.3.1'
implementation 'de.themoep:minedown-adventure:1.7.1-SNAPSHOT'
annotationProcessor 'com.velocitypowered:velocity-api:3.1.1'
}
shadowJar {
relocate 'org.apache.commons.text', 'net.william278.husktowns.libraries.commons.text'
relocate 'org.apache.commons.lang3', 'net.william278.husktowns.libraries.commons.lang3'
relocate 'de.themoep', 'net.william278.velocitab.libraries'
relocate 'dev.dejvokep.boostedyaml', 'net.william278.velocitab.libraries'
relocate 'net.william278.annotaml', 'net.william278.velocitab.libraries.annotaml'
dependencies {

View File

@ -53,6 +53,7 @@ public class Velocitab {
loadSettings();
loadHooks();
prepareTabList();
logger.info("Successfully enabled Velocitab v" + BuildConstants.VERSION);
}
@NotNull
@ -84,12 +85,18 @@ public class Velocitab {
// If LuckPerms is present, load the hook
try {
luckPerms = new LuckPermsHook();
luckPerms = new LuckPermsHook(this);
server.getEventManager().register(this, luckPerms);
} catch (IllegalArgumentException e) {
logger.warn("LuckPerms was not loaded: " + e.getMessage(), e);
}
}
@NotNull
public PlayerTabList getTabList() {
return tabList;
}
private void prepareTabList() {
this.tabList = new PlayerTabList(this);
server.getEventManager().register(this, new PlayerTabList(this));

View File

@ -2,7 +2,6 @@ package net.william278.velocitab.config;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import net.william278.velocitab.Velocitab;
import net.william278.velocitab.player.TabPlayer;
import org.jetbrains.annotations.NotNull;
@ -15,20 +14,19 @@ public enum Placeholder {
PLAYERS_ONLINE((plugin, player) -> Integer.toString(plugin.getServer().getPlayerCount())),
MAX_PLAYERS_ONLINE((plugin, player) -> Integer.toString(plugin.getServer().getConfiguration().getShowMaxPlayers())),
LOCAL_PLAYERS_ONLINE((plugin, player) -> player.player().getCurrentServer()
LOCAL_PLAYERS_ONLINE((plugin, player) -> player.getPlayer().getCurrentServer()
.map(ServerConnection::getServer)
.map(RegisteredServer::getPlayersConnected)
.map(players -> Integer.toString(players.size()))
.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) -> player.player().getUsername()),
SERVER((plugin, player) -> player.player().getCurrentServer()
.map(ServerConnection::getServerInfo)
.map(ServerInfo::getName).orElse("")),
PING((plugin, player) -> Long.toString(player.player().getPing())),
PREFIX((plugin, player) -> player.role().getPrefix().orElse("")),
SUFFIX((plugin, player) -> player.role().getSuffix().orElse(""));
USERNAME((plugin, player) -> player.getPlayer().getUsername()),
SERVER((plugin, player) -> player.getServerName()),
PING((plugin, player) -> Long.toString(player.getPlayer().getPing())),
PREFIX((plugin, player) -> player.getRole().getPrefix().orElse("")),
SUFFIX((plugin, player) -> player.getRole().getSuffix().orElse("")),
ROLE((plugin, player) -> player.getRole().getName().orElse(""));
private final BiFunction<Velocitab, TabPlayer, String> formatter;

View File

@ -1,46 +1,42 @@
package net.william278.velocitab.config;
import net.william278.annotaml.YamlComment;
import net.william278.annotaml.YamlFile;
import net.william278.annotaml.YamlKey;
import net.william278.velocitab.BuildConstants;
import org.apache.commons.text.StringEscapeUtils;
import org.jetbrains.annotations.NotNull;
@YamlFile(header = "Velocitab Config File")
@YamlFile(header = """
Velocitab Config
Developed by William278
Placeholders: %players_online%, %max_players_online%, %local_players_online%, %current_date%, %current_time%, %username%, %server%, %ping%, %prefix%, %suffix%, %role%""")
public class Settings {
@YamlKey("tab_list.header")
private String header = "Welcome to the server!";
@YamlKey("tab_list.footer")
private String footer = "Welcome to the server!";
@YamlKey("tab_list.format")
private String format;
@YamlComment("Use LuckPerms for tab list formatting (if installed)")
@YamlKey("use_luckperms")
private boolean useLuckPerms = true;
@YamlKey("header")
private String header = "&rainbow&Running Velocitab v" + BuildConstants.VERSION + " by William278";
@YamlKey("footer")
private String footer = "[There are currently %players_online%/%max_players_online% players online](gray)";
@YamlKey("format")
private String format = "&7[%server%] &f%prefix%%username%";
private Settings() {
}
@NotNull
public String getHeader() {
return header;
return StringEscapeUtils.unescapeJava(header);
}
@NotNull
public String getFooter() {
return footer;
return StringEscapeUtils.unescapeJava(footer);
}
@NotNull
public String getFormat() {
return format;
}
public boolean isUseLuckPerms() {
return useLuckPerms;
return StringEscapeUtils.unescapeJava(format);
}
}

View File

@ -1,12 +1,16 @@
package net.william278.velocitab.luckperms;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.proxy.Player;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.cacheddata.CachedMetaData;
import net.luckperms.api.event.user.UserDataRecalculateEvent;
import net.luckperms.api.model.group.Group;
import net.luckperms.api.model.user.User;
import net.william278.velocitab.Velocitab;
import net.william278.velocitab.player.Role;
import net.william278.velocitab.tab.PlayerTabList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -15,9 +19,11 @@ import java.util.UUID;
public class LuckPermsHook {
private final Velocitab plugin;
private final LuckPerms api;
public LuckPermsHook() throws IllegalStateException {
public LuckPermsHook(@NotNull Velocitab plugin) throws IllegalStateException {
this.plugin = plugin;
this.api = LuckPermsProvider.get();
}
@ -36,6 +42,16 @@ public class LuckPermsHook {
return Role.DEFAULT_ROLE;
}
@Subscribe
public void onLuckPermsGroupUpdate(@NotNull UserDataRecalculateEvent event) {
plugin.getServer().getPlayer(event.getUser().getUniqueId()).ifPresent(player -> {
final PlayerTabList tabList = plugin.getTabList();
tabList.removePlayer(player);
tabList.addPlayer(plugin.getTabPlayer(player));
tabList.refreshHeaderAndFooter();
});
}
private OptionalInt getWeight(@Nullable String groupName) {
final Group group;
if (groupName == null || (group = api.getGroupManager().getGroup(groupName)) == null) {

View File

@ -42,6 +42,6 @@ public class Role implements Comparable<Role> {
@NotNull
public String getStringComparableWeight() {
return String.format("%05d", weight);
return String.format("%03d", weight);
}
}

View File

@ -11,20 +11,61 @@ import net.william278.velocitab.config.Placeholder;
import net.william278.velocitab.tab.PlayerTabList;
import org.jetbrains.annotations.NotNull;
public record TabPlayer(@NotNull Player player, @NotNull Role role) implements Comparable<TabPlayer> {
import java.util.Arrays;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public final class TabPlayer implements Comparable<TabPlayer> {
private static final int DEFAULT_LATENCY = 3;
private final Player player;
private final Role role;
private final GameProfile profile;
public TabPlayer(@NotNull Player player, @NotNull Role role) {
this.player = player;
this.role = role;
final String profileName = role.getStringComparableWeight() + "-" + getServerName() + "-" + player.getUsername();
this.profile = new GameProfile(
new UUID(0, new Random().nextLong()),
profileName.length() > 16 ? profileName.substring(0, 16) : profileName,
player.getGameProfileProperties()
);
}
@NotNull
private Component getFormattedEntry(@NotNull Velocitab plugin) {
public Player getPlayer() {
return player;
}
@NotNull
public Role getRole() {
return role;
}
@NotNull
public GameProfile getProfile() {
return profile;
}
@NotNull
public String getServerName() {
return player.getCurrentServer()
.map(serverConnection -> serverConnection.getServerInfo().getName())
.orElse("Unknown");
}
@NotNull
private Component getDisplayName(@NotNull Velocitab plugin) {
return new MineDown(Placeholder.format(plugin.getSettings().getFormat(), plugin, this)).toComponent();
}
private TabListEntry getEntry(@NotNull Velocitab plugin, @NotNull TabList list) {
private TabListEntry getEntry(@NotNull Velocitab plugin, @NotNull TabList tabList) {
return TabListEntry.builder()
.displayName(getFormattedEntry(plugin))
.latency((int) player.getPing())
.tabList(list)
.profile(new GameProfile(player.getUniqueId(),
role.getStringComparableWeight() + " " + player.getUsername(),
player.getGameProfileProperties()))
.displayName(getDisplayName(plugin))
.latency(DEFAULT_LATENCY)
.profile(profile)
.tabList(tabList)
.build();
}
@ -34,10 +75,19 @@ public record TabPlayer(@NotNull Player player, @NotNull Role role) implements C
public void addPlayer(@NotNull TabPlayer player, @NotNull Velocitab plugin) {
this.player.getTabList().addEntry(player.getEntry(plugin, this.player.getTabList()));
removeUuidPlayer(plugin, player.getPlayer().getUniqueId());
}
public void removePlayer(@NotNull TabPlayer player) {
this.player.getTabList().removeEntry(player.player().getUniqueId());
public void removePlayer(@NotNull TabPlayer player, @NotNull Velocitab plugin) {
this.player.getTabList().removeEntry(player.getProfile().getId());
removeUuidPlayer(plugin, player.getPlayer().getUniqueId());
}
public void removeUuidPlayer(@NotNull Velocitab plugin, @NotNull UUID... uuid) {
plugin.getServer().getScheduler()
.buildTask(plugin, () -> Arrays.stream(uuid).forEach(this.player.getTabList()::removeEntry))
.delay(500, TimeUnit.MILLISECONDS)
.schedule();
}

View File

@ -14,6 +14,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public class PlayerTabList {
private final Velocitab plugin;
@ -24,6 +25,31 @@ public class PlayerTabList {
this.players = new ArrayList<>();
}
@SuppressWarnings("UnstableApiUsage")
@Subscribe
public void onPlayerJoin(@NotNull ServerPostConnectEvent event) {
final TabPlayer player = plugin.getTabPlayer(event.getPlayer());
// Reset existing tab list
player.getPlayer().getTabList().clearHeaderAndFooter();
player.getPlayer().getTabList().getEntries().clear();
// Show existing list to new player
players.forEach(listPlayer -> player.addPlayer(listPlayer, plugin));
addPlayer(player);
refreshHeaderAndFooter();
}
@Subscribe
public void onPlayerQuit(@NotNull DisconnectEvent event) {
try {
removePlayer(event.getPlayer());
refreshHeaderAndFooter();
} catch (Exception ignored) {
// Ignore when server shutting down
}
}
@NotNull
public Component getHeader(@NotNull TabPlayer player) {
return new MineDown(Placeholder.format(plugin.getSettings().getHeader(), plugin, player)).toComponent();
@ -34,35 +60,22 @@ public class PlayerTabList {
return new MineDown(Placeholder.format(plugin.getSettings().getFooter(), plugin, player)).toComponent();
}
@SuppressWarnings("UnstableApiUsage")
@Subscribe
public void onPlayerJoin(@NotNull ServerPostConnectEvent event) {
final TabPlayer player = plugin.getTabPlayer(event.getPlayer());
// Show existing list to new player
players.forEach(listPlayer -> player.addPlayer(listPlayer, plugin));
// Update list for all players with new player
// Add a new tab player to the list and update for online players
public void addPlayer(@NotNull TabPlayer player) {
players.add(player);
players.forEach(tabPlayer -> {
tabPlayer.addPlayer(player, plugin);
tabPlayer.sendHeaderAndFooter(this);
});
players.forEach(tabPlayer -> tabPlayer.addPlayer(player, plugin));
}
@Subscribe
public void onPlayerQuit(@NotNull DisconnectEvent event) {
final Player quitPlayer = event.getPlayer();
public void removePlayer(@NotNull Player playerToRemove) {
final Optional<TabPlayer> quitTabPlayer = players.stream()
.filter(player -> player.player().equals(quitPlayer)).findFirst();
.filter(player -> player.getPlayer().equals(playerToRemove)).findFirst();
if (quitTabPlayer.isPresent()) {
players.remove(quitTabPlayer.get());
players.forEach(tabPlayer -> {
tabPlayer.removePlayer(quitTabPlayer.get());
tabPlayer.sendHeaderAndFooter(this);
});
players.forEach(tabPlayer -> tabPlayer.removePlayer(quitTabPlayer.get(), plugin));
}
}
public void refreshHeaderAndFooter() {
players.forEach(tabPlayer -> tabPlayer.sendHeaderAndFooter(this));
}
}