forked from Upstream/Velocitab
Move to using Protocolize for scoreboard team handling using fake scoreboard teams
This commit is contained in:
parent
8ad443f4fc
commit
51c7853b7b
@ -1,4 +1,4 @@
|
|||||||
# Velocitab
|
# Velocitab
|
||||||
[](https://discord.gg/tVYhJfyDWG)
|
[](https://discord.gg/tVYhJfyDWG)
|
||||||
|
|
||||||
A very simple (sorted) Velocity TAB plugin
|
A very simple (sorted) Velocity TAB plugin. Requires [Protocolize](https://github.com/Exceptionflug/protocolize) v2.2.5 to be installed on your proxy.
|
@ -12,18 +12,23 @@ repositories {
|
|||||||
maven { url = 'https://repo.papermc.io/repository/maven-public/' }
|
maven { url = 'https://repo.papermc.io/repository/maven-public/' }
|
||||||
maven { url = 'https://jitpack.io' }
|
maven { url = 'https://jitpack.io' }
|
||||||
maven { url = 'https://repo.minebench.de/' }
|
maven { url = 'https://repo.minebench.de/' }
|
||||||
|
maven { url = 'https://mvn.exceptionflug.de/repository/exceptionflug-public/' }
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly 'com.velocitypowered:velocity-api:3.1.1'
|
compileOnly 'com.velocitypowered:velocity-api:3.1.1'
|
||||||
compileOnly 'net.luckperms:api:5.4'
|
compileOnly 'net.luckperms:api:5.4'
|
||||||
|
compileOnly 'dev.simplix:protocolize-api:2.2.5'
|
||||||
|
compileOnly 'io.netty:netty-codec-http:4.1.86.Final'
|
||||||
|
|
||||||
implementation 'org.apache.commons:commons-text:1.10.0'
|
implementation 'org.apache.commons:commons-text:1.10.0'
|
||||||
implementation 'net.william278:Annotaml:2.0.1'
|
implementation 'net.william278:Annotaml:2.0.1'
|
||||||
implementation 'dev.dejvokep:boosted-yaml:1.3.1'
|
implementation 'dev.dejvokep:boosted-yaml:1.3.1'
|
||||||
implementation 'de.themoep:minedown-adventure:1.7.1-SNAPSHOT'
|
implementation 'de.themoep:minedown-adventure:1.7.1-SNAPSHOT'
|
||||||
|
implementation 'org.projectlombok:lombok:1.18.26'
|
||||||
|
|
||||||
annotationProcessor 'com.velocitypowered:velocity-api:3.1.1'
|
annotationProcessor 'com.velocitypowered:velocity-api:3.1.1'
|
||||||
|
annotationProcessor 'org.projectlombok:lombok:1.18.26'
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
|
@ -11,6 +11,7 @@ import com.velocitypowered.api.proxy.ProxyServer;
|
|||||||
import net.william278.annotaml.Annotaml;
|
import net.william278.annotaml.Annotaml;
|
||||||
import net.william278.velocitab.config.Settings;
|
import net.william278.velocitab.config.Settings;
|
||||||
import net.william278.velocitab.luckperms.LuckPermsHook;
|
import net.william278.velocitab.luckperms.LuckPermsHook;
|
||||||
|
import net.william278.velocitab.packet.ScoreboardManager;
|
||||||
import net.william278.velocitab.player.Role;
|
import net.william278.velocitab.player.Role;
|
||||||
import net.william278.velocitab.player.TabPlayer;
|
import net.william278.velocitab.player.TabPlayer;
|
||||||
import net.william278.velocitab.tab.PlayerTabList;
|
import net.william278.velocitab.tab.PlayerTabList;
|
||||||
@ -30,7 +31,10 @@ import java.util.Optional;
|
|||||||
description = "Simple velocity TAB menu plugin",
|
description = "Simple velocity TAB menu plugin",
|
||||||
url = "https://william278.net/",
|
url = "https://william278.net/",
|
||||||
authors = {"William278"},
|
authors = {"William278"},
|
||||||
dependencies = {@Dependency(id = "luckperms", optional = true)}
|
dependencies = {
|
||||||
|
@Dependency(id = "protocolize"),
|
||||||
|
@Dependency(id = "luckperms", optional = true)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
public class Velocitab {
|
public class Velocitab {
|
||||||
|
|
||||||
@ -40,6 +44,7 @@ public class Velocitab {
|
|||||||
private final Path dataDirectory;
|
private final Path dataDirectory;
|
||||||
private PlayerTabList tabList;
|
private PlayerTabList tabList;
|
||||||
private LuckPermsHook luckPerms;
|
private LuckPermsHook luckPerms;
|
||||||
|
private ScoreboardManager scoreboardManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Velocitab(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
|
public Velocitab(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
|
||||||
@ -52,6 +57,7 @@ public class Velocitab {
|
|||||||
public void onProxyInitialization(ProxyInitializeEvent event) {
|
public void onProxyInitialization(ProxyInitializeEvent event) {
|
||||||
loadSettings();
|
loadSettings();
|
||||||
loadHooks();
|
loadHooks();
|
||||||
|
prepareScoreboardManager();
|
||||||
prepareTabList();
|
prepareTabList();
|
||||||
logger.info("Successfully enabled Velocitab v" + BuildConstants.VERSION);
|
logger.info("Successfully enabled Velocitab v" + BuildConstants.VERSION);
|
||||||
}
|
}
|
||||||
@ -92,6 +98,16 @@ public class Velocitab {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void prepareScoreboardManager() {
|
||||||
|
this.scoreboardManager = new ScoreboardManager(this);
|
||||||
|
scoreboardManager.registerPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ScoreboardManager getScoreboardManager() {
|
||||||
|
return scoreboardManager;
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public PlayerTabList getTabList() {
|
public PlayerTabList getTabList() {
|
||||||
return tabList;
|
return tabList;
|
||||||
|
@ -10,7 +10,6 @@ import net.luckperms.api.model.group.Group;
|
|||||||
import net.luckperms.api.model.user.User;
|
import net.luckperms.api.model.user.User;
|
||||||
import net.william278.velocitab.Velocitab;
|
import net.william278.velocitab.Velocitab;
|
||||||
import net.william278.velocitab.player.Role;
|
import net.william278.velocitab.player.Role;
|
||||||
import net.william278.velocitab.tab.PlayerTabList;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@ -45,12 +44,8 @@ public class LuckPermsHook {
|
|||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onLuckPermsGroupUpdate(@NotNull UserDataRecalculateEvent event) {
|
public void onLuckPermsGroupUpdate(@NotNull UserDataRecalculateEvent event) {
|
||||||
plugin.getServer().getPlayer(event.getUser().getUniqueId()).ifPresent(player -> {
|
plugin.getServer().getPlayer(event.getUser().getUniqueId())
|
||||||
final PlayerTabList tabList = plugin.getTabList();
|
.ifPresent(player -> plugin.getTabList().updatePlayer(plugin.getTabPlayer(player)));
|
||||||
tabList.removePlayer(player);
|
|
||||||
tabList.addPlayer(plugin.getTabPlayer(player));
|
|
||||||
tabList.refreshHeaderAndFooter();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private OptionalInt getWeight(@Nullable String groupName) {
|
private OptionalInt getWeight(@Nullable String groupName) {
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
package net.william278.velocitab.packet;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.proxy.Player;
|
||||||
|
import dev.simplix.protocolize.api.PacketDirection;
|
||||||
|
import dev.simplix.protocolize.api.Protocol;
|
||||||
|
import dev.simplix.protocolize.api.Protocolize;
|
||||||
|
import net.william278.velocitab.Velocitab;
|
||||||
|
import net.william278.velocitab.player.TabPlayer;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class ScoreboardManager {
|
||||||
|
|
||||||
|
private final Velocitab plugin;
|
||||||
|
|
||||||
|
private final HashMap<UUID, String> fauxTeams;
|
||||||
|
|
||||||
|
public ScoreboardManager(@NotNull Velocitab velocitab) {
|
||||||
|
this.plugin = velocitab;
|
||||||
|
this.fauxTeams = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerPacket() {
|
||||||
|
Protocolize.protocolRegistration().registerPacket(
|
||||||
|
UpdateTeamsPacket.MAPPINGS,
|
||||||
|
Protocol.PLAY,
|
||||||
|
PacketDirection.CLIENTBOUND,
|
||||||
|
UpdateTeamsPacket.class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlayerTeam(@NotNull TabPlayer player) {
|
||||||
|
removeTeam(player.getPlayer());
|
||||||
|
createTeam(player.getTeamName(), player.getPlayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTeam(@NotNull String teamName, @NotNull Player member) {
|
||||||
|
final UUID uuid = member.getUniqueId();
|
||||||
|
final UpdateTeamsPacket createTeamPacket = UpdateTeamsPacket.create(teamName, member.getUsername());
|
||||||
|
plugin.getServer().getAllPlayers().stream()
|
||||||
|
.map(Player::getUniqueId)
|
||||||
|
.map(Protocolize.playerProvider()::player)
|
||||||
|
.forEach(protocolPlayer -> protocolPlayer.sendPacket(createTeamPacket));
|
||||||
|
fauxTeams.put(uuid, teamName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeTeam(@NotNull Player member) {
|
||||||
|
final UUID uuid = member.getUniqueId();
|
||||||
|
if (!fauxTeams.containsKey(uuid)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final UpdateTeamsPacket removeTeamPacket = UpdateTeamsPacket.remove(fauxTeams.get(uuid));
|
||||||
|
plugin.getServer().getAllPlayers().stream()
|
||||||
|
.map(Player::getUniqueId)
|
||||||
|
.map(Protocolize.playerProvider()::player)
|
||||||
|
.forEach(protocolPlayer -> protocolPlayer.sendPacket(removeTeamPacket));
|
||||||
|
fauxTeams.remove(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,226 @@
|
|||||||
|
package net.william278.velocitab.packet;
|
||||||
|
|
||||||
|
import dev.simplix.protocolize.api.PacketDirection;
|
||||||
|
import dev.simplix.protocolize.api.mapping.AbstractProtocolMapping;
|
||||||
|
import dev.simplix.protocolize.api.mapping.ProtocolIdMapping;
|
||||||
|
import dev.simplix.protocolize.api.packet.AbstractPacket;
|
||||||
|
import dev.simplix.protocolize.api.util.ProtocolUtil;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import lombok.*;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import org.apache.commons.text.StringEscapeUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static dev.simplix.protocolize.api.util.ProtocolVersions.MINECRAFT_1_19;
|
||||||
|
import static dev.simplix.protocolize.api.util.ProtocolVersions.MINECRAFT_1_19_3;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@ToString
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Accessors(fluent = true)
|
||||||
|
public class UpdateTeamsPacket extends AbstractPacket {
|
||||||
|
|
||||||
|
protected static final List<ProtocolIdMapping> MAPPINGS = List.of(
|
||||||
|
AbstractProtocolMapping.rangedIdMapping(MINECRAFT_1_19, MINECRAFT_1_19_3, 0x56)
|
||||||
|
);
|
||||||
|
|
||||||
|
private String teamName;
|
||||||
|
private UpdateMode mode;
|
||||||
|
private String displayName;
|
||||||
|
private List<FriendlyFlag> friendlyFlags;
|
||||||
|
private NameTagVisibility nameTagVisibility;
|
||||||
|
private CollisionRule collisionRule;
|
||||||
|
private int color;
|
||||||
|
private String prefix;
|
||||||
|
private String suffix;
|
||||||
|
private List<String> entities;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static UpdateTeamsPacket create(@NotNull String teamName, @NotNull String member) {
|
||||||
|
if (teamName.length() > 16) {
|
||||||
|
teamName = teamName.substring(0, 16);
|
||||||
|
}
|
||||||
|
final UpdateTeamsPacket updateTeamsPacket = new UpdateTeamsPacket();
|
||||||
|
updateTeamsPacket.teamName(teamName);
|
||||||
|
updateTeamsPacket.mode(UpdateMode.CREATE);
|
||||||
|
updateTeamsPacket.displayName(getChatString(teamName));
|
||||||
|
updateTeamsPacket.friendlyFlags(List.of(FriendlyFlag.CAN_HURT_FRIENDLY));
|
||||||
|
updateTeamsPacket.nameTagVisibility(NameTagVisibility.ALWAYS);
|
||||||
|
updateTeamsPacket.collisionRule(CollisionRule.ALWAYS);
|
||||||
|
updateTeamsPacket.color(0);
|
||||||
|
updateTeamsPacket.prefix(getChatString(""));
|
||||||
|
updateTeamsPacket.suffix(getChatString(""));
|
||||||
|
updateTeamsPacket.entities(List.of(member));
|
||||||
|
return updateTeamsPacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static UpdateTeamsPacket remove(@NotNull String teamName) {
|
||||||
|
if (teamName.length() > 16) {
|
||||||
|
teamName = teamName.substring(0, 16);
|
||||||
|
}
|
||||||
|
final UpdateTeamsPacket updateTeamsPacket = new UpdateTeamsPacket();
|
||||||
|
updateTeamsPacket.teamName(teamName);
|
||||||
|
updateTeamsPacket.mode(UpdateMode.REMOVE);
|
||||||
|
return updateTeamsPacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuf byteBuf, PacketDirection packetDirection, int i) {
|
||||||
|
teamName = ProtocolUtil.readString(byteBuf);
|
||||||
|
mode = UpdateMode.byId(byteBuf.readByte());
|
||||||
|
if (mode == UpdateMode.REMOVE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mode == UpdateMode.CREATE || mode == UpdateMode.UPDATE_INFO) {
|
||||||
|
displayName = ProtocolUtil.readString(byteBuf);
|
||||||
|
friendlyFlags = FriendlyFlag.fromBitMask(byteBuf.readByte());
|
||||||
|
nameTagVisibility = NameTagVisibility.byId(ProtocolUtil.readString(byteBuf));
|
||||||
|
collisionRule = CollisionRule.byId(ProtocolUtil.readString(byteBuf));
|
||||||
|
color = byteBuf.readByte();
|
||||||
|
prefix = ProtocolUtil.readString(byteBuf);
|
||||||
|
suffix = ProtocolUtil.readString(byteBuf);
|
||||||
|
}
|
||||||
|
if (mode == UpdateMode.CREATE || mode == UpdateMode.ADD_PLAYERS || mode == UpdateMode.REMOVE_PLAYERS) {
|
||||||
|
int entityCount = ProtocolUtil.readVarInt(byteBuf);
|
||||||
|
for (int j = 0; j < entityCount; j++) {
|
||||||
|
entities.add(ProtocolUtil.readString(byteBuf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf byteBuf, PacketDirection packetDirection, int i) {
|
||||||
|
ProtocolUtil.writeString(byteBuf, teamName);
|
||||||
|
byteBuf.writeByte(mode.id());
|
||||||
|
if (mode == UpdateMode.REMOVE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mode == UpdateMode.CREATE || mode == UpdateMode.UPDATE_INFO) {
|
||||||
|
ProtocolUtil.writeString(byteBuf, displayName);
|
||||||
|
byteBuf.writeByte(FriendlyFlag.toBitMask(friendlyFlags));
|
||||||
|
ProtocolUtil.writeString(byteBuf, nameTagVisibility.id());
|
||||||
|
ProtocolUtil.writeString(byteBuf, collisionRule.id());
|
||||||
|
byteBuf.writeByte(color);
|
||||||
|
ProtocolUtil.writeString(byteBuf, prefix);
|
||||||
|
ProtocolUtil.writeString(byteBuf, suffix);
|
||||||
|
}
|
||||||
|
if (mode == UpdateMode.CREATE || mode == UpdateMode.ADD_PLAYERS || mode == UpdateMode.REMOVE_PLAYERS) {
|
||||||
|
ProtocolUtil.writeVarInt(byteBuf, entities.size());
|
||||||
|
for (String entity : entities) {
|
||||||
|
ProtocolUtil.writeString(byteBuf, entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static String getChatString(@NotNull String string) {
|
||||||
|
return "{\"text\":\"" + StringEscapeUtils.escapeJson(string) + "\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum UpdateMode {
|
||||||
|
CREATE(0),
|
||||||
|
REMOVE(1),
|
||||||
|
UPDATE_INFO(2),
|
||||||
|
ADD_PLAYERS(3),
|
||||||
|
REMOVE_PLAYERS(4);
|
||||||
|
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
UpdateMode(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int id() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UpdateMode byId(int id) {
|
||||||
|
return Arrays.stream(values())
|
||||||
|
.filter(mode -> mode.id == id)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum FriendlyFlag {
|
||||||
|
CAN_HURT_FRIENDLY(0x01),
|
||||||
|
CAN_HURT_FRIENDLY_FIRE(0x02);
|
||||||
|
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
FriendlyFlag(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static List<FriendlyFlag> fromBitMask(int bitMask) {
|
||||||
|
return Arrays.stream(values())
|
||||||
|
.filter(flag -> (bitMask & flag.id) != 0)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int toBitMask(@NotNull List<FriendlyFlag> friendlyFlags) {
|
||||||
|
int bitMask = 0;
|
||||||
|
for (FriendlyFlag friendlyFlag : friendlyFlags) {
|
||||||
|
bitMask |= friendlyFlag.id;
|
||||||
|
}
|
||||||
|
return bitMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum NameTagVisibility {
|
||||||
|
ALWAYS("always"),
|
||||||
|
NEVER("never"),
|
||||||
|
HIDE_FOR_OTHER_TEAMS("hideForOtherTeams"),
|
||||||
|
HIDE_FOR_OWN_TEAM("hideForOwnTeam");
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
NameTagVisibility(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String id() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NameTagVisibility byId(String id) {
|
||||||
|
return Arrays.stream(values())
|
||||||
|
.filter(visibility -> visibility.id.equals(id))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum CollisionRule {
|
||||||
|
ALWAYS("always"),
|
||||||
|
NEVER("never"),
|
||||||
|
PUSH_OTHER_TEAMS("pushOtherTeams"),
|
||||||
|
PUSH_OWN_TEAM("pushOwnTeam");
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
CollisionRule(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String id() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CollisionRule byId(String id) {
|
||||||
|
return Arrays.stream(values())
|
||||||
|
.filter(rule -> rule.id.equals(id))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,6 @@
|
|||||||
package net.william278.velocitab.player;
|
package net.william278.velocitab.player;
|
||||||
|
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
import com.velocitypowered.api.proxy.player.TabList;
|
|
||||||
import com.velocitypowered.api.proxy.player.TabListEntry;
|
|
||||||
import com.velocitypowered.api.util.GameProfile;
|
|
||||||
import de.themoep.minedown.adventure.MineDown;
|
import de.themoep.minedown.adventure.MineDown;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.william278.velocitab.Velocitab;
|
import net.william278.velocitab.Velocitab;
|
||||||
@ -11,26 +8,15 @@ import net.william278.velocitab.config.Placeholder;
|
|||||||
import net.william278.velocitab.tab.PlayerTabList;
|
import net.william278.velocitab.tab.PlayerTabList;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public final class TabPlayer implements Comparable<TabPlayer> {
|
public final class TabPlayer implements Comparable<TabPlayer> {
|
||||||
private static final int DEFAULT_LATENCY = 3;
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private final Role role;
|
private final Role role;
|
||||||
private final GameProfile profile;
|
private final int highestWeight;
|
||||||
|
|
||||||
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;
|
||||||
this.role = role;
|
this.role = role;
|
||||||
final String profileName = role.getStringComparableWeight(highestWeight) + getServerName() + player.getUsername();
|
this.highestWeight = highestWeight;
|
||||||
this.profile = new GameProfile(
|
|
||||||
new UUID(0, new Random().nextLong()),
|
|
||||||
profileName.length() > 16 ? profileName.substring(0, 16) : profileName,
|
|
||||||
player.getGameProfileProperties()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -43,11 +29,6 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
|||||||
return role;
|
return role;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public GameProfile getProfile() {
|
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public String getServerName() {
|
public String getServerName() {
|
||||||
return player.getCurrentServer()
|
return player.getCurrentServer()
|
||||||
@ -56,41 +37,19 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private Component getDisplayName(@NotNull Velocitab plugin) {
|
public Component getDisplayName(@NotNull Velocitab plugin) {
|
||||||
return new MineDown(Placeholder.format(plugin.getSettings().getFormat(), plugin, this)).toComponent();
|
return new MineDown(Placeholder.format(plugin.getSettings().getFormat(), plugin, this)).toComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private TabListEntry getEntry(@NotNull Velocitab plugin, @NotNull TabList tabList) {
|
@NotNull
|
||||||
return TabListEntry.builder()
|
public String getTeamName() {
|
||||||
.displayName(getDisplayName(plugin))
|
return role.getStringComparableWeight(highestWeight) + "-" + getServerName() + "-" + player.getUsername();
|
||||||
.latency(DEFAULT_LATENCY)
|
|
||||||
.profile(profile)
|
|
||||||
.tabList(tabList)
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendHeaderAndFooter(@NotNull PlayerTabList tabList) {
|
public void sendHeaderAndFooter(@NotNull PlayerTabList tabList) {
|
||||||
this.player.sendPlayerListHeaderAndFooter(tabList.getHeader(this), tabList.getFooter(this));
|
this.player.sendPlayerListHeaderAndFooter(tabList.getHeader(this), tabList.getFooter(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
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, @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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(@NotNull TabPlayer o) {
|
public int compareTo(@NotNull TabPlayer o) {
|
||||||
final int roleDifference = role.compareTo(o.role);
|
final int roleDifference = role.compareTo(o.role);
|
||||||
|
@ -3,7 +3,6 @@ package net.william278.velocitab.tab;
|
|||||||
import com.velocitypowered.api.event.Subscribe;
|
import com.velocitypowered.api.event.Subscribe;
|
||||||
import com.velocitypowered.api.event.connection.DisconnectEvent;
|
import com.velocitypowered.api.event.connection.DisconnectEvent;
|
||||||
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
|
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
|
||||||
import de.themoep.minedown.adventure.MineDown;
|
import de.themoep.minedown.adventure.MineDown;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.william278.velocitab.Velocitab;
|
import net.william278.velocitab.Velocitab;
|
||||||
@ -13,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class PlayerTabList {
|
public class PlayerTabList {
|
||||||
private final Velocitab plugin;
|
private final Velocitab plugin;
|
||||||
@ -27,33 +26,66 @@ public class PlayerTabList {
|
|||||||
@SuppressWarnings("UnstableApiUsage")
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onPlayerJoin(@NotNull ServerPostConnectEvent event) {
|
public void onPlayerJoin(@NotNull ServerPostConnectEvent event) {
|
||||||
// Remove previous Tab entries for players when they move servers
|
// Remove the player from the tracking list if they are switching servers
|
||||||
if (event.getPreviousServer() != null) {
|
if (event.getPreviousServer() == null) {
|
||||||
removePlayer(event.getPlayer());
|
players.removeIf(player -> player.getPlayer().getUniqueId().equals(event.getPlayer().getUniqueId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
final TabPlayer player = plugin.getTabPlayer(event.getPlayer());
|
// Add the player to the tracking list
|
||||||
|
players.add(plugin.getTabPlayer(event.getPlayer()));
|
||||||
|
|
||||||
// Reset existing tab list
|
// Update the tab list of all players
|
||||||
player.getPlayer().getTabList().clearHeaderAndFooter();
|
plugin.getServer().getScheduler().buildTask(plugin, this::updateList)
|
||||||
if (!player.getPlayer().getTabList().getEntries().isEmpty()) {
|
.delay(500, TimeUnit.MILLISECONDS)
|
||||||
player.getPlayer().getTabList().getEntries().clear();
|
.schedule();
|
||||||
}
|
|
||||||
|
|
||||||
// Show existing list to new player
|
|
||||||
players.forEach(listPlayer -> player.addPlayer(listPlayer, plugin));
|
|
||||||
addPlayer(player);
|
|
||||||
refreshHeaderAndFooter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onPlayerQuit(@NotNull DisconnectEvent event) {
|
public void onPlayerQuit(@NotNull DisconnectEvent event) {
|
||||||
try {
|
// Remove the player from the tracking list
|
||||||
removePlayer(event.getPlayer());
|
players.removeIf(player -> player.getPlayer().getUniqueId().equals(event.getPlayer().getUniqueId()));
|
||||||
refreshHeaderAndFooter();
|
|
||||||
} catch (Exception ignored) {
|
// Remove the player from the tab list of all other players
|
||||||
// Ignore when server shutting down
|
plugin.getScoreboardManager().removeTeam(event.getPlayer());
|
||||||
|
plugin.getServer().getAllPlayers().forEach(player -> {
|
||||||
|
if (player.getTabList().containsEntry(event.getPlayer().getUniqueId())) {
|
||||||
|
player.getTabList().removeEntry(event.getPlayer().getUniqueId());
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the tab list of all players
|
||||||
|
plugin.getServer().getScheduler().buildTask(plugin, this::updateList)
|
||||||
|
.delay(500, TimeUnit.MILLISECONDS)
|
||||||
|
.schedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePlayer(@NotNull TabPlayer tabPlayer) {
|
||||||
|
// Remove the existing player from the tracking list
|
||||||
|
players.removeIf(player -> player.getPlayer().getUniqueId().equals(tabPlayer.getPlayer().getUniqueId()));
|
||||||
|
|
||||||
|
// Add the player to the tracking list
|
||||||
|
players.add(tabPlayer);
|
||||||
|
|
||||||
|
// Update the player's team sorting
|
||||||
|
plugin.getScoreboardManager().removeTeam(tabPlayer.getPlayer());
|
||||||
|
|
||||||
|
// Update the tab list of all players
|
||||||
|
plugin.getServer().getScheduler().buildTask(plugin, this::updateList)
|
||||||
|
.delay(500, TimeUnit.MILLISECONDS)
|
||||||
|
.schedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateList() {
|
||||||
|
players.forEach(player -> {
|
||||||
|
player.sendHeaderAndFooter(this);
|
||||||
|
player.getPlayer().getTabList().getEntries()
|
||||||
|
.forEach(entry -> players.stream()
|
||||||
|
.filter(p -> p.getPlayer().getGameProfile().getId().equals(entry.getProfile().getId()))
|
||||||
|
.findFirst().ifPresent(tabPlayer -> {
|
||||||
|
entry.setDisplayName(tabPlayer.getDisplayName(plugin));
|
||||||
|
plugin.getScoreboardManager().setPlayerTeam(tabPlayer);
|
||||||
|
}));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -66,22 +98,4 @@ public class PlayerTabList {
|
|||||||
return new MineDown(Placeholder.format(plugin.getSettings().getFooter(), plugin, player)).toComponent();
|
return new MineDown(Placeholder.format(plugin.getSettings().getFooter(), plugin, player)).toComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removePlayer(@NotNull Player playerToRemove) {
|
|
||||||
final Optional<TabPlayer> quitTabPlayer = players.stream()
|
|
||||||
.filter(player -> player.getPlayer().equals(playerToRemove)).findFirst();
|
|
||||||
if (quitTabPlayer.isPresent()) {
|
|
||||||
players.remove(quitTabPlayer.get());
|
|
||||||
players.forEach(tabPlayer -> tabPlayer.removePlayer(quitTabPlayer.get(), plugin));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void refreshHeaderAndFooter() {
|
|
||||||
players.forEach(tabPlayer -> tabPlayer.sendHeaderAndFooter(this));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user