Compare commits

...
57 Commits
Author SHA1 Message Date
NEZNAMY 6b9063c789 4.1.5 2024-05-27 15:58:52 +02:00
NEZNAMY ce0a45b76c [Bukkit] Add support for latest 1.21 snapshot 2024-05-27 13:46:47 +02:00
NEZNAMY 41a0bd37ce [Sorting] Fix number compression bug (#1297) 2024-05-26 12:38:28 +02:00
NEZNAMY 449d3cb3ee [BossBar] Fix error when trying to display bossbar that was not visible on join 2024-05-22 10:14:52 +02:00
NEZNAMY a1a3852b08 [BossBar] Fix Network Protocol Error disconnect on server switch 2024-05-20 12:41:07 +02:00
NEZNAMY ef63df5175 [BossBar] Make %countdown% return 0 after announcement is over instead of going to negative numbers 2024-05-20 11:08:08 +02:00
NEZNAMY 17e57f9d0c [Bug fix] Fix disabled tablist formatting referencing wrong variable 2024-05-16 21:00:01 +02:00
NEZNAMY 949216325f [Nametags] Only make nametags of invisible players invisible for 1.8 players to compensate for the bug instead of everyone (#1287) 2024-05-11 21:29:55 +02:00
NEZNAMY dddf0b35a2 [Code style] Add missing access level to field 2024-05-11 11:43:51 +02:00
NEZNAMY 35ec2bf215 [Code style] Make fields final 2024-05-11 11:43:05 +02:00
NEZNAMY 607b60b323 [Code style] Remove unused method 2024-05-11 11:41:35 +02:00
NEZNAMY bb6ad72962 [Code style] Improve presence check of Player#getPing method 2024-05-10 17:05:47 +02:00
NEZNAMY 4fb8420d69 [Code style] Remove unused method 2024-05-10 16:59:11 +02:00
NEZNAMY 74af19a9d9 [Code style] Move field to a local variable 2024-05-10 16:53:51 +02:00
NEZNAMY 737ac70e6e [Fabric] Update to 24w19a 2024-05-10 16:28:39 +02:00
NEZNAMY bfc72b735c [Fabric] Add another submodule for 1.18.2 to avoid hardcoding method and class names 2024-05-10 16:05:09 +02:00
NEZNAMY 323412f935 [Code style] Decrease field visibility 2024-05-10 14:56:00 +02:00
NEZNAMY db34ba4995 [Fabric] Improve check for Fabric API features availability 2024-05-10 14:54:01 +02:00
NEZNAMY 15d7eb6d91 [Javadoc] Document more methods 2024-05-10 14:50:02 +02:00
NEZNAMY e2b95df2e4 [Bukkit] Explicitly specify constructor to fix compatibility with forge mods that widen a constructor access, causing TAB to choose the wrong one on 1.19.3+ (#1276) 2024-05-10 13:42:50 +02:00
NEZNAMY 1117094412 [Bug fix] Update library to fix error 2024-05-09 14:54:46 +02:00
NEZNAMY 4bab076642 [Bug fix] Fix group change from Bridge not being processed correctly (#1281) 2024-05-09 13:38:36 +02:00
NEZNAMY d602818f67 [Bug fix] Disable tablist containsEntry usage due to a race condition that sometimes makes it not work properly until a better solution is found (#1284) 2024-05-08 09:37:35 +02:00
NEZNAMY 1a76321062 [Fabric] Update to 24w18a 2024-05-04 20:37:08 +02:00
NEZNAMY 4b056529dd [Fix] Add cpu usage of placeholders inside plugin's main thread instead of placeholder thread to avoid incorrect numbers when it's overloaded 2024-05-04 20:29:21 +02:00
NEZNAMY bdbf3ba5b1 [Velocity] Remove message printing if attempting to send plugin message to a player who is not connected to any server (#1248) 2024-05-03 22:35:29 +02:00
NEZNAMY aab9a650b0 Fix startup error when using a plugin that shades MiniMessage without shading component serializers (#1280) 2024-05-03 14:47:25 +02:00
NEZNAMY f8487f07a6 Create array of component decorations to take values from to avoid instantiation each time 2024-05-02 21:06:15 +02:00
NEZNAMY e38721fa69 Speed up function 2024-05-02 19:30:22 +02:00
NEZNAMY 18e52f2f10 Add 1.20.6 to known versions 2024-04-29 18:06:16 +02:00
NEZNAMY f1300410b8 Update Fabric to 1.20.6 2024-04-29 18:03:52 +02:00
NEZNAMY e17059f1f7 Update library to fix error (1276) 2024-04-29 12:26:29 +02:00
NEZNAMY f3e9f7cf06 [BungeeCord] Remove console warn regarding ViaVersion on BungeeCord as it no longer breaks anything 2024-04-28 18:08:50 +02:00
NEZNAMY 63ed24d136 [Bukkit] Use Spigot internals over Paper API as a primary TPS getter (uses 10x less CPU and 100x less memory allocations) 2024-04-28 14:01:33 +02:00
NEZNAMY 66008cd767 [Velocity] Fix "Received packet for unknown scoreboard objective: TAB-BelowName" on server switch 2024-04-27 19:03:46 +02:00
NEZNAMY 1852c1b5c4 Create array to iterate over to prevent creating new instance every time 2024-04-26 20:12:45 +02:00
NEZNAMY 9a4fc2fc73 Fix field type 2024-04-26 11:52:55 +02:00
NEZNAMY 6846a78833 Require plugin list in a bug report 2024-04-26 11:52:15 +02:00
NEZNAMY d3d9e85a7f Merge branch '4.1.5'
# Conflicts:
#	build.gradle.kts
2024-04-26 11:47:03 +02:00
NEZNAMY f867b6b2ea 4.1.5-SNAPSHOT 2024-04-23 21:09:31 +02:00
NEZNAMY 6b38954694 4.1.4 2024-04-23 20:57:31 +02:00
NEZNAMY 9eb350df30 Fix startup on 1.19.3 - 1.20.4 2024-04-23 20:57:12 +02:00
NEZNAMY 639aa5e251 Fix startup on 1.19.3 - 1.20.4 2024-04-23 20:56:06 +02:00
NEZNAMY f089e26998 Added 1.20.5 support to Bukkit 2024-04-23 19:30:14 +02:00
NEZNAMY 9390a402ff Update Fabric to 1.20.5 2024-04-23 17:35:38 +02:00
NEZNAMY 7fda2c5f88 Add 1.20.5 ProtocolVersion constant 2024-04-23 17:35:15 +02:00
NEZNAMY 7f0b2e2b13 Initialize feature data structured on declaration to make it cleaner & fix error 2024-04-23 16:16:51 +02:00
NEZNAMY bdc44d46c8 Add Exception throwing when attempting to use API class instances that got discarded on reload. 2024-04-23 16:10:24 +02:00
NEZNAMY 8d8480469a Update Fabric to 1.20.5-rc3 2024-04-23 15:28:30 +02:00
NEZNAMY a91fe6db36 Remove unused SneakyThrows 2024-04-23 12:51:41 +02:00
NEZNAMY 20d734aa80 Add missing action mapping 2024-04-23 12:43:20 +02:00
NEZNAMY f053bbd544 Access Team packet data directly on 1.17+ Fabric instead of using reflection 2024-04-23 12:40:14 +02:00
NEZNAMY 7eba26cab1 Rename class to match version number 2024-04-23 12:15:47 +02:00
NEZNAMY 36006045e2 Add "listed" TabList flag support into internal API 2024-04-22 16:54:39 +02:00
NEZNAMY d5d025c2c9 Store information about player meeting a feature's disable condition inside player class to improve performance by avoiding Map lookup each time 2024-04-22 14:41:34 +02:00
NEZNAMY 5b7053d500 Store feature data of players inside TabPlayer class to improve performance by avoiding Map lookup 2024-04-22 14:06:37 +02:00
NEZNAMY ce2e60c268 4.1.4-SNAPSHOT 2024-04-21 21:43:25 +02:00
112 changed files with 2414 additions and 1213 deletions

No files matched your search

+8
View File
@@ -18,6 +18,14 @@ body:
validations:
required: true
- type: textarea
attributes:
label: Plugin list
description: Full list of plugins (output of /plugins)
placeholder: ...
validations:
required: true
- type: textarea
attributes:
label: Describe the bug
@@ -205,7 +205,7 @@ public interface BossBar {
* Player to check
* @return {@code true} if player is allowed to see this Boss Bar, {@code false} if not.
*/
boolean containsPlayer(TabPlayer player);
boolean containsPlayer(@NonNull TabPlayer player);
/**
* Returns {@code true} if this boss bar is configured as announcement-only bar,
+3 -2
View File
@@ -4,7 +4,7 @@ plugins {
allprojects {
group = "me.neznamy"
version = "4.1.3"
version = "4.1.5"
description = "An all-in-one solution that works"
ext.set("id", "tab")
@@ -19,7 +19,8 @@ val platforms = setOf(
projects.sponge7,
projects.sponge8,
projects.fabric,
projects.fabric.v1144
projects.fabric.v1144,
projects.fabric.v1182
).map { it.dependencyProject }
val special = setOf(
@@ -53,7 +53,7 @@ public class DataWatcher implements EntityData {
loadSerializers();
}
if (BukkitReflection.is1_19_3Plus()) {
Class<?> dataWatcher$Item = BukkitReflection.getClass("network.syncher.SynchedEntityData$DataValue", "network.syncher.DataWatcher$c");
Class<?> dataWatcher$Item = BukkitReflection.getClass("network.syncher.SynchedEntityData$DataValue", "network.syncher.DataWatcher$c", "network.syncher.DataWatcher$b");
Class<?> dataWatcherSerializer = BukkitReflection.getClass("network.syncher.EntityDataSerializer",
"network.syncher.DataWatcherSerializer", "DataWatcherSerializer");
newDataWatcher$Item = dataWatcher$Item.getConstructor(int.class, dataWatcherSerializer, Object.class);
@@ -110,7 +110,7 @@ public class DataWatcher implements EntityData {
}
}
}
/**
* Sets value into data values
*
@@ -270,4 +270,4 @@ public class DataWatcher implements EntityData {
}
}
}
}
}
@@ -20,10 +20,6 @@ import java.util.*;
@Getter
public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpansion {
/** Map holding all values for all players for easy and high-performance access */
@NotNull
private final WeakHashMap<TabPlayer, Map<String, String>> values = new WeakHashMap<>();
/** List of all placeholders offered by the plugin for command suggestions */
@NotNull
private final List<String> placeholders = Collections.unmodifiableList(Arrays.asList(
@@ -102,12 +98,12 @@ public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpan
if (player == null) return "<Player cannot be null>";
TabPlayer p = TAB.getInstance().getPlayer(player.getUniqueId());
if (p == null || !p.isLoaded()) return "<Player is not loaded>";
return values.get(p).get(identifier);
return p.expansionValues.values.get(identifier);
}
@Override
public void setValue(@NotNull TabPlayer player, @NotNull String key, @NotNull String value) {
values.computeIfAbsent(player, p -> new HashMap<>()).put(key, value);
player.expansionValues.values.put(key, value);
}
@Override
@@ -41,7 +41,7 @@ public class ComponentConverter {
// 1.16+
private Method ChatHexColor_fromRGB;
private Constructor<?> newMinecraftKey;
private Method ResourceLocation_tryParse;
/**
* Constructs new instance and loads all NMS classes, constructors and methods.
@@ -67,13 +67,13 @@ public class ComponentConverter {
}
if (BukkitReflection.getMinorVersion() >= 16) {
Class<?> chatHexColor = BukkitReflection.getClass("network.chat.ChatHexColor", "ChatHexColor");
Class<?> MinecraftKey = BukkitReflection.getClass("resources.MinecraftKey", "MinecraftKey");
Class<?> ResourceLocation = BukkitReflection.getClass("resources.MinecraftKey", "MinecraftKey");
Class<?> chatClickable = BukkitReflection.getClass("network.chat.ChatClickable", "ChatClickable");
Class<?> chatHoverable = BukkitReflection.getClass("network.chat.ChatHoverable", "ChatHoverable");
newMinecraftKey = MinecraftKey.getConstructor(String.class);
ResourceLocation_tryParse = ReflectionUtils.getMethod(ResourceLocation, new String[]{"tryParse", "m_135820_", "a"}, String.class);
ChatHexColor_fromRGB = ReflectionUtils.getOnlyMethod(chatHexColor, chatHexColor, int.class);
newChatModifier = ReflectionUtils.setAccessible(ChatModifier.getDeclaredConstructor(chatHexColor, Boolean.class, Boolean.class, Boolean.class,
Boolean.class, Boolean.class, chatClickable, chatHoverable, String.class, MinecraftKey));
Boolean.class, Boolean.class, chatClickable, chatHoverable, String.class, ResourceLocation));
convertModifier = this::createModifierModern;
} else {
newChatModifier = ChatModifier.getConstructor();
@@ -124,7 +124,7 @@ public class ComponentConverter {
null,
null,
null,
modifier.getFont() == null ? null : newMinecraftKey.newInstance(modifier.getFont())
modifier.getFont() == null ? null : ResourceLocation_tryParse.invoke(null, modifier.getFont())
);
}
@@ -24,7 +24,7 @@ public class PingRetriever {
*/
public static void tryLoad() {
try {
if (BukkitReflection.getMinorVersion() >= 17) {
if (ReflectionUtils.methodExists(Player.class, "getPing")) {
getPing = Player::getPing;
} else {
Method getHandle = BukkitReflection.getBukkitClass("entity.CraftPlayer").getMethod("getHandle");
@@ -1,7 +1,6 @@
package me.neznamy.tab.platforms.bukkit.platform;
import lombok.Getter;
import lombok.SneakyThrows;
import me.clip.placeholderapi.PlaceholderAPI;
import me.neznamy.tab.platforms.bukkit.*;
import me.neznamy.tab.platforms.bukkit.entity.PacketEntityView;
@@ -50,7 +49,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.lang.reflect.Field;
/**
* Implementation of Platform interface for Bukkit platform
@@ -69,13 +67,8 @@ public class BukkitPlatform implements BackendPlatform {
/** Variables checking presence of other plugins to hook into */
private final boolean placeholderAPI = ReflectionUtils.classExists("me.clip.placeholderapi.PlaceholderAPI");
/** NMS server to get TPS from on spigot */
@Nullable
private Object server;
/** TPS field */
@Nullable
private Field spigotTps;
/** Spigot field for tracking TPS, the array is final and only being modified instead of re-instantiated */
private double[] recentTps;
/** Detection for presence of Paper's TPS getter */
private final boolean paperTps = ReflectionUtils.methodExists(Bukkit.class, "getTPS");
@@ -93,8 +86,8 @@ public class BukkitPlatform implements BackendPlatform {
this.plugin = plugin;
long time = System.currentTimeMillis();
try {
server = Bukkit.getServer().getClass().getMethod("getServer").invoke(Bukkit.getServer());
spigotTps = server.getClass().getField("recentTps");
Object server = Bukkit.getServer().getClass().getMethod("getServer").invoke(Bukkit.getServer());
recentTps = ((double[]) server.getClass().getField("recentTps").get(server));
} catch (ReflectiveOperationException ignored) {
//not spigot
}
@@ -298,12 +291,11 @@ public class BukkitPlatform implements BackendPlatform {
}
@Override
@SneakyThrows
public double getTPS() {
if (paperTps) {
if (recentTps != null) {
return recentTps[0];
} else if (paperTps) {
return Bukkit.getTPS()[0];
} else if (spigotTps != null) {
return ((double[]) spigotTps.get(server))[0];
} else {
return -1;
}
@@ -315,18 +307,6 @@ public class BukkitPlatform implements BackendPlatform {
return -1;
}
/**
* Runs task in the main thread for given entity.
*
* @param entity
* Entity's main thread
* @param task
* Task to run
*/
public void runSync(@NotNull Entity entity, @NotNull Runnable task) {
Bukkit.getScheduler().runTask(plugin, task);
}
/**
* Runs an entity task that may or may not need to use its main thread.
*
@@ -80,7 +80,6 @@ public class FoliaPlatform extends BukkitPlatform {
* @param task
* Task to run
*/
@Override
@SneakyThrows
@SuppressWarnings("JavaReflectionMemberAccess")
public void runSync(@NotNull Entity entity, @NotNull Runnable task) {
@@ -29,7 +29,6 @@ public class TeamPacketData {
/** First version with static constructor-like methods */
private final int STATIC_CONSTRUCTOR_VERSION = 17;
private Class<?> Component;
private final Object emptyScoreboard;
@Getter private final Class<?> TeamPacketClass;
private Constructor<?> newTeamPacket;
@@ -85,12 +84,10 @@ public class TeamPacketData {
new String[]{"func_98300_b", "setCanSeeFriendlyInvisibles", "b", "m_83362_", "setSeeFriendlyInvisibles"}, // {Thermos, 1.5.1+, 1.5 & 1.18+, Mohist 1.18.2, 1.20.2+}
boolean.class
);
if (minorVersion >= 7) {
Component = BukkitReflection.getClass("network.chat.Component", "network.chat.IChatBaseComponent", "IChatBaseComponent");
}
if (minorVersion >= 8) loadVisibility(scoreboardTeam);
if (minorVersion >= 9) loadCollision(scoreboardTeam);
if (minorVersion >= MODERN_TEAM_DATA_VERSION) {
Class<?> Component = BukkitReflection.getClass("network.chat.Component", "network.chat.IChatBaseComponent", "IChatBaseComponent");
ScoreboardTeam_setColor = ReflectionUtils.getOnlyMethod(scoreboardTeam, void.class, enumChatFormatClass);
ScoreboardTeam_setPrefix = ReflectionUtils.getMethod(
scoreboardTeam,
@@ -48,7 +48,12 @@ public class BukkitTabList extends TabListBase<String> {
}
@Override
public void addEntry0(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, int latency, int gameMode, @Nullable String displayName) {
public void updateListed(@NonNull UUID entry, boolean listed) {
// Shrug
}
@Override
public void addEntry0(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, boolean listed, int latency, int gameMode, @Nullable String displayName) {
// Shrug
}
@@ -51,6 +51,8 @@ public class PacketTabList1193 extends PacketTabList18 {
* If something goes wrong
*/
public static void loadNew() throws ReflectiveOperationException {
Class<?> IChatBaseComponent = BukkitReflection.getClass("network.chat.Component", "network.chat.IChatBaseComponent", "IChatBaseComponent");
Class<?> RemoteChatSession$Data = BukkitReflection.getClass("network.chat.RemoteChatSession$Data", "network.chat.RemoteChatSession$a");
Class<Enum> EnumGamemodeClass = (Class<Enum>) BukkitReflection.getClass("world.level.GameType", "world.level.EnumGamemode");
ActionClass = (Class<Enum>) BukkitReflection.getClass(
"network.protocol.game.ClientboundPlayerInfoUpdatePacket$Action", // Mojang
@@ -67,9 +69,10 @@ public class PacketTabList1193 extends PacketTabList18 {
loadSharedContent(playerInfoDataClass, EnumGamemodeClass);
newPlayerInfoData = playerInfoDataClass.getConstructor(UUID.class, GameProfile.class, boolean.class, int.class, EnumGamemodeClass, IChatBaseComponent, RemoteChatSession$Data);
PlayerInfoData_Listed = ReflectionUtils.getOnlyField(playerInfoDataClass, boolean.class);
PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(playerInfoDataClass, EnumGamemodeClass);
Class<?> RemoteChatSession$Data = BukkitReflection.getClass("network.chat.RemoteChatSession$Data", "network.chat.RemoteChatSession$a");
PlayerInfoData_RemoteChatSession = ReflectionUtils.getOnlyField(playerInfoDataClass, RemoteChatSession$Data);
PlayerInfoData_UUID = ReflectionUtils.getOnlyField(playerInfoDataClass, UUID.class);
newRemovePacket = BukkitReflection.getClass("network.protocol.game.ClientboundPlayerInfoRemovePacket").getConstructor(List.class);
@@ -77,12 +80,12 @@ public class PacketTabList1193 extends PacketTabList18 {
actionAddPlayer = Enum.valueOf(ActionClass, Action.ADD_PLAYER.name());
actionUpdateDisplayName = Enum.valueOf(ActionClass, Action.UPDATE_DISPLAY_NAME.name());
actionUpdateLatency = Enum.valueOf(ActionClass, Action.UPDATE_LATENCY.name());
Enum actionUpdateGameMode = Enum.valueOf(ActionClass, Action.UPDATE_GAME_MODE.name());
actionToEnumSet.put(Action.ADD_PLAYER, EnumSet.allOf(ActionClass));
actionToEnumSet.put(Action.UPDATE_GAME_MODE, EnumSet.of(actionUpdateGameMode));
actionToEnumSet.put(Action.UPDATE_GAME_MODE, EnumSet.of(Enum.valueOf(ActionClass, Action.UPDATE_GAME_MODE.name())));
actionToEnumSet.put(Action.UPDATE_DISPLAY_NAME, EnumSet.of(actionUpdateDisplayName));
actionToEnumSet.put(Action.UPDATE_LATENCY, EnumSet.of(actionUpdateLatency));
actionToEnumSet.put(Action.UPDATE_LISTED, EnumSet.of(Enum.valueOf(ActionClass, Action.UPDATE_LISTED.name())));
}
@Override
@@ -91,16 +94,22 @@ public class PacketTabList1193 extends PacketTabList18 {
packetSender.sendPacket(player.getPlayer(), newRemovePacket.newInstance(Collections.singletonList(entry)));
}
@Override
public void updateListed(@NonNull UUID entry, boolean listed) {
packetSender.sendPacket(player.getPlayer(),
createPacket(Action.UPDATE_LISTED, entry, "", null, listed, 0, 0, null));
}
@SneakyThrows
@NonNull
@Override
public Object createPacket(@NonNull Action action, @NonNull UUID id, @NonNull String name, @Nullable Skin skin,
int latency, int gameMode, @Nullable Object displayName) {
boolean listed, int latency, int gameMode, @Nullable Object displayName) {
Object packet = newPlayerInfo.newInstance(actionToEnumSet.get(action), Collections.emptyList());
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData.newInstance(
id,
action == Action.ADD_PLAYER ? createProfile(id, name, skin) : null,
true,
listed,
latency,
gameModes[gameMode],
displayName,
@@ -85,7 +85,7 @@ public class PacketTabList17 extends TabListBase<String> {
public void updateDisplayName0(@NonNull UUID entry, @Nullable String displayName) {
if (!displayNames.containsKey(entry)) return; // Entry not tracked by TAB
packetSender.sendPacket(player.getPlayer(), newPacket.apply(displayNames.get(entry), false, 0));
addEntry0(entry, userNames.get(entry), null, 0, 0, displayName);
addEntry0(entry, userNames.get(entry), null, false, 0, 0, displayName);
}
@Override
@@ -100,9 +100,14 @@ public class PacketTabList17 extends TabListBase<String> {
// Added in 1.8
}
@Override
public void updateListed(@NonNull UUID entry, boolean listed) {
// Added in 1.19.3
}
@Override
@SneakyThrows
public void addEntry0(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, int latency, int gameMode, @Nullable String displayName) {
public void addEntry0(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, boolean listed, int latency, int gameMode, @Nullable String displayName) {
String display = displayName == null ? name : displayName;
packetSender.sendPacket(player.getPlayer(), newPacket.apply(display, true, latency));
userNames.put(id, name);
@@ -82,11 +82,12 @@ public class PacketTabList18 extends TabListBase<Object> {
ACTION = ReflectionUtils.getOnlyField(PlayerInfoClass, ActionClass);
loadSharedContent(playerInfoDataClass, EnumGamemodeClass);
newPlayerInfoData = playerInfoDataClass.getConstructors()[0]; // #1105, a specific 1.8.8 fork has 2 constructors
}
protected static void loadSharedContent(Class<?> infoData, Class<Enum> gameMode) throws ReflectiveOperationException {
Class<?> IChatBaseComponent = BukkitReflection.getClass("network.chat.Component", "network.chat.IChatBaseComponent", "IChatBaseComponent");
newPlayerInfoData = infoData.getConstructors()[0]; // #1105, a specific 1.8.8 fork has 2 constructors
PLAYERS = ReflectionUtils.getOnlyField(PlayerInfoClass, List.class);
PlayerInfoData_Profile = ReflectionUtils.getOnlyField(infoData, GameProfile.class);
PlayerInfoData_Latency = ReflectionUtils.getOnlyField(infoData, int.class);
@@ -110,31 +111,36 @@ public class PacketTabList18 extends TabListBase<Object> {
@Override
public void removeEntry(@NonNull UUID entry) {
packetSender.sendPacket(player.getPlayer(),
createPacket(Action.REMOVE_PLAYER, entry, "", null, 0, 0, null));
createPacket(Action.REMOVE_PLAYER, entry, "", null, false, 0, 0, null));
}
@Override
public void updateDisplayName0(@NonNull UUID entry, @Nullable Object displayName) {
packetSender.sendPacket(player.getPlayer(),
createPacket(Action.UPDATE_DISPLAY_NAME, entry, "", null, 0, 0, displayName));
createPacket(Action.UPDATE_DISPLAY_NAME, entry, "", null, false, 0, 0, displayName));
}
@Override
public void updateLatency(@NonNull UUID entry, int latency) {
packetSender.sendPacket(player.getPlayer(),
createPacket(Action.UPDATE_LATENCY, entry, "", null, latency, 0, null));
createPacket(Action.UPDATE_LATENCY, entry, "", null, false, latency, 0, null));
}
@Override
public void updateGameMode(@NonNull UUID entry, int gameMode) {
packetSender.sendPacket(player.getPlayer(),
createPacket(Action.UPDATE_GAME_MODE, entry, "", null, 0, gameMode, null));
createPacket(Action.UPDATE_GAME_MODE, entry, "", null, false, 0, gameMode, null));
}
@Override
public void addEntry0(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, int latency, int gameMode, @Nullable Object displayName) {
public void updateListed(@NonNull UUID entry, boolean listed) {
// Added in 1.19.3
}
@Override
public void addEntry0(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, boolean listed, int latency, int gameMode, @Nullable Object displayName) {
packetSender.sendPacket(player.getPlayer(),
createPacket(Action.ADD_PLAYER, id, name, skin, latency, gameMode, displayName));
createPacket(Action.ADD_PLAYER, id, name, skin, listed, latency, gameMode, displayName));
}
/**
@@ -148,6 +154,8 @@ public class PacketTabList18 extends TabListBase<Object> {
* Entry name
* @param skin
* Entry skin
* @param listed
* Whether entry should be listed or not
* @param latency
* Entry latency
* @param gameMode
@@ -159,7 +167,7 @@ public class PacketTabList18 extends TabListBase<Object> {
@SneakyThrows
@NonNull
public Object createPacket(@NonNull Action action, @NonNull UUID id, @NonNull String name, @Nullable Skin skin,
int latency, int gameMode, @Nullable Object displayName) {
boolean listed, int latency, int gameMode, @Nullable Object displayName) {
Object packet = newPlayerInfo.newInstance(Enum.valueOf(ActionClass, action.name()), Collections.emptyList());
List<Object> parameters = new ArrayList<>();
if (newPlayerInfoData.getParameterTypes()[0] == PlayerInfoClass) {
@@ -29,10 +29,12 @@ public class BungeeBossBar implements BossBar {
@NotNull
private final Map<UUID, Integer> styles = new HashMap<>();
/** Flag tracking whether boss bars should be frozen or not */
private boolean frozen;
@Override
public void create(@NotNull UUID id, @NotNull String title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
if (player.getVersion().getMinorVersion() < 9) return;
if (frozen) return; // Server switch
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 0);
packet.setHealth(progress);
packet.setTitle(TabComponent.optimized(title).convert(player.getVersion()));
@@ -46,8 +48,7 @@ public class BungeeBossBar implements BossBar {
@Override
public void update(@NotNull UUID id, @NotNull String title) {
if (player.getVersion().getMinorVersion() < 9) return;
if (frozen) return; // Server switch
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 3);
packet.setTitle(TabComponent.optimized(title).convert(player.getVersion()));
player.sendPacket(packet);
@@ -55,8 +56,7 @@ public class BungeeBossBar implements BossBar {
@Override
public void update(@NotNull UUID id, float progress) {
if (player.getVersion().getMinorVersion() < 9) return;
if (frozen) return; // Server switch
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 2);
packet.setHealth(progress);
player.sendPacket(packet);
@@ -64,8 +64,7 @@ public class BungeeBossBar implements BossBar {
@Override
public void update(@NotNull UUID id, @NotNull BarStyle style) {
if (player.getVersion().getMinorVersion() < 9) return;
if (frozen) return; // Server switch
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 4);
packet.setDivision(style.ordinal());
packet.setColor(colors.get(id));
@@ -76,8 +75,7 @@ public class BungeeBossBar implements BossBar {
@Override
public void update(@NotNull UUID id, @NotNull BarColor color) {
if (player.getVersion().getMinorVersion() < 9) return;
if (frozen) return; // Server switch
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 4);
packet.setDivision(styles.get(id));
packet.setColor(color.ordinal());
@@ -88,11 +86,20 @@ public class BungeeBossBar implements BossBar {
@Override
public void remove(@NotNull UUID id) {
if (player.getVersion().getMinorVersion() < 9) return;
if (frozen) return; // Server switch
player.sendPacket(new net.md_5.bungee.protocol.packet.BossBar(id, 1));
colors.remove(id);
styles.remove(id);
}
}
@Override
public void freeze() {
frozen = true;
}
@Override
public void unfreeze() {
frozen = false;
}
}
@@ -42,10 +42,12 @@ public class BungeeEventListener implements EventListener<ProxiedPlayer>, Listen
TAB tab = TAB.getInstance();
if (tab.isPluginDisabled()) return;
// Avoid 1.20.3 client crash on scoreboard packets, do it sync to prevent packet being sent after event, but before processing
// Avoid 1.20.3+ client crash on scoreboard packets, do it sync to prevent packet being sent after event, but before processing
// Avoid 1.20.5+ client disconnect with "Network Protocol Error"
TabPlayer p = tab.getPlayer(e.getPlayer().getUniqueId());
if (p != null && p.getVersion().getNetworkId() >= ProtocolVersion.V1_20_3.getNetworkId()) {
if (p != null && p.getVersion().getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) {
p.getScoreboard().freeze();
p.getBossBar().freeze();
}
tab.getCPUManager().runTask(() -> {
@@ -9,7 +9,6 @@ import me.neznamy.tab.shared.chat.*;
import me.neznamy.tab.shared.features.injection.PipelineInjector;
import me.neznamy.tab.shared.features.redis.RedisSupport;
import me.neznamy.tab.shared.hook.PremiumVanishHook;
import me.neznamy.tab.shared.hook.ViaVersionHook;
import me.neznamy.tab.shared.proxy.ProxyPlatform;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.md_5.bungee.api.ChatColor;
@@ -51,7 +50,6 @@ public class BungeePlatform extends ProxyPlatform {
@SuppressWarnings("deprecation")
@Override
public void loadPlayers() {
ViaVersionHook.getInstance().printProxyWarn();
try {
ProxyConfig config = ProxyServer.getInstance().getConfig();
if ((boolean) config.getClass().getMethod("isDisableTabListRewrite").invoke(config)) {
@@ -56,7 +56,7 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
* BungeeCord player
*/
public BungeeTabPlayer(@NotNull BungeePlatform platform, @NotNull ProxiedPlayer p) {
super(platform, p, p.getUniqueId(), p.getName(), p.getServer() != null ? p.getServer().getInfo().getName() : "-", -1);
super(platform, p, p.getUniqueId(), p.getName(), p.getServer() != null ? p.getServer().getInfo().getName() : "-", p.getPendingConnection().getVersion());
}
@Override
@@ -112,19 +112,6 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
}
}
/**
* If ViaVersion is installed on BungeeCord, it changes protocol to match version
* of server to which player is connected to. For that reason, we need to retrieve
* the field more often than just on join.
*
* @return Player's current protocol version
*/
@Override
@NotNull
public ProtocolVersion getVersion() {
return ProtocolVersion.fromNetworkId(getPlayer().getPendingConnection().getVersion());
}
@Override
public boolean isVanished() {
if (PremiumVanishHook.getInstance() != null && PremiumVanishHook.getInstance().isVanished(this)) return true;
@@ -69,6 +69,8 @@ public abstract class BungeeTabList extends TabList<BungeeTabPlayer, BaseCompone
* Entry name
* @param skin
* Entry skin
* @param listed
* Whether entry should be listed or not
* @param latency
* Entry latency
* @param gameMode
@@ -78,12 +80,12 @@ public abstract class BungeeTabList extends TabList<BungeeTabPlayer, BaseCompone
* @return Converted item from parameters
*/
@NonNull
public Item entryToItem(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, int latency, int gameMode, @Nullable BaseComponent displayName) {
public Item entryToItem(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, boolean listed, int latency, int gameMode, @Nullable BaseComponent displayName) {
Item item = item(id);
item.setUsername(name);
item.setDisplayName(displayName);
item.setGamemode(gameMode);
item.setListed(true);
item.setListed(listed);
item.setPing(latency);
if (skin != null) {
item.setProperties(new Property[]{new Property(TEXTURES_PROPERTY, skin.getValue(), skin.getSignature())});
@@ -24,6 +24,7 @@ public class BungeeTabList1193 extends BungeeTabList {
actions.put(Action.UPDATE_GAME_MODE, EnumSet.of(PlayerListItemUpdate.Action.UPDATE_GAMEMODE));
actions.put(Action.UPDATE_DISPLAY_NAME, EnumSet.of(PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME));
actions.put(Action.UPDATE_LATENCY, EnumSet.of(PlayerListItemUpdate.Action.UPDATE_LATENCY));
actions.put(Action.UPDATE_LISTED, EnumSet.of(PlayerListItemUpdate.Action.UPDATE_LISTED));
}
/**
@@ -66,9 +67,16 @@ public class BungeeTabList1193 extends BungeeTabList {
}
@Override
public void addEntry0(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, int latency, int gameMode, @Nullable BaseComponent displayName) {
public void updateListed(@NonNull UUID entry, boolean listed) {
Item item = item(entry);
item.setListed(listed);
sendPacket(Action.UPDATE_LISTED, item);
}
@Override
public void addEntry0(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, boolean listed, int latency, int gameMode, @Nullable BaseComponent displayName) {
addUuid(id);
sendPacket(Action.ADD_PLAYER, entryToItem(id, name, skin, latency, gameMode, displayName));
sendPacket(Action.ADD_PLAYER, entryToItem(id, name, skin, listed, latency, gameMode, displayName));
}
private void sendPacket(@NonNull Action action, @NonNull Item item) {
@@ -56,7 +56,7 @@ public class BungeeTabList17 extends BungeeTabList {
public void updateDisplayName0(@NonNull UUID entry, @Nullable BaseComponent displayName) {
if (!displayNames.containsKey(entry)) return; // Entry not tracked by TAB
update(PlayerListItem.Action.REMOVE_PLAYER, createItem(null, displayNames.get(entry), 0));
addEntry0(entry, userNames.get(entry), null, 0, 0, displayName);
addEntry0(entry, userNames.get(entry), null, false, 0, 0, displayName);
}
@Override
@@ -66,10 +66,17 @@ public class BungeeTabList17 extends BungeeTabList {
}
@Override
public void updateGameMode(@NonNull UUID entry, int gameMode) {/*Added in 1.8*/}
public void updateGameMode(@NonNull UUID entry, int gameMode) {
// Added in 1.8
}
@Override
public void addEntry0(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, int latency, int gameMode, @Nullable BaseComponent displayName) {
public void updateListed(@NonNull UUID entry, boolean listed) {
// Added in 1.19.3
}
@Override
public void addEntry0(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, boolean listed, int latency, int gameMode, @Nullable BaseComponent displayName) {
addUuid(id);
update(PlayerListItem.Action.ADD_PLAYER, createItem(name, displayName == null ? new TextComponent(name) : displayName, latency));
@@ -53,9 +53,14 @@ public class BungeeTabList18 extends BungeeTabList {
}
@Override
public void addEntry0(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, int latency, int gameMode, @Nullable BaseComponent displayName) {
public void updateListed(@NonNull UUID entry, boolean listed) {
// Added in 1.19.3
}
@Override
public void addEntry0(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, boolean listed, int latency, int gameMode, @Nullable BaseComponent displayName) {
addUuid(id);
sendPacket(PlayerListItem.Action.ADD_PLAYER, entryToItem(id, name, skin, latency, gameMode, displayName));
sendPacket(PlayerListItem.Action.ADD_PLAYER, entryToItem(id, name, skin, listed, latency, gameMode, displayName));
}
private void sendPacket(@NonNull PlayerListItem.Action action, @NonNull Item item) {
+2 -2
View File
@@ -12,11 +12,11 @@ repositories {
dependencies {
api(projects.shared)
minecraft("com.mojang:minecraft:1.20.5")
minecraft("com.mojang:minecraft:24w19a")
mappings(loom.officialMojangMappings())
modImplementation("me.lucko:fabric-permissions-api:0.2-SNAPSHOT")
modImplementation("net.fabricmc:fabric-loader:0.15.9")
val version = "0.96.14+1.20.5"
val version = "0.97.8+1.20.6"
modImplementation(fabricApi.module("fabric-api-base", version))
modImplementation(fabricApi.module("fabric-lifecycle-events-v1", version))
modImplementation(fabricApi.module("fabric-networking-api-v1", version))
@@ -3,6 +3,7 @@ package me.neznamy.tab.platforms.fabric;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.platform.EventListener;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents;
import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
@@ -21,7 +22,7 @@ public class FabricEventListener implements EventListener<ServerPlayer> {
ServerPlayConnectionEvents.DISCONNECT.register((connection, $) -> quit(connection.player.getUUID()));
ServerPlayConnectionEvents.JOIN.register((connection, $, $$) -> join(connection.player));
//TODO command preprocess
if (FabricTAB.supportsEntityEvents()) {
if (ReflectionUtils.classExists("net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents")) {
// Added in 1.16
ServerPlayerEvents.AFTER_RESPAWN.register(
(oldPlayer, newPlayer, alive) -> {
@@ -34,226 +34,538 @@ import java.util.UUID;
*/
public class FabricMultiVersion {
public static final ProtocolVersion serverVersion = ProtocolVersion.fromFriendlyName(FabricTAB.minecraftVersion);
public static final Loader loaderNew = new Loader_1_20_5();
public static final Loader loaderOld = createOldLoader();
/** Server version */
private static final ProtocolVersion serverVersion = ProtocolVersion.fromFriendlyName(FabricTAB.minecraftVersion);
/** Method loader using latest supported MC version */
private static final Loader loaderNew = new Loader_1_20_5();
/** Method loader using 1.18.2 */
private static final Loader loader1_18_2 = createLoader("1_18_2");
/** Method loader using 1.14.4 */
private static final Loader loader1_14_4 = createLoader("1_14_4");
@SneakyThrows
private static Loader createOldLoader() {
return (Loader) Class.forName("me.neznamy.tab.platforms.fabric.loader.Loader_1_14_4")
private static Loader createLoader(@NotNull String version) {
return (Loader) Class.forName("me.neznamy.tab.platforms.fabric.loader.Loader_" + version)
.getConstructor(ProtocolVersion.class).newInstance(serverVersion);
}
/**
* Returns {@code true} if player is sneaking, {@code false} if not.
*
* @param player
* Player to check sneak status of
* @return {@code true} if player is sneaking, {@code false} if not
*/
public static boolean isSneaking(@NotNull ServerPlayer player) {
if (serverVersion.getMinorVersion() >= 15) return loaderNew.isSneaking(player);
return loaderOld.isSneaking(player);
return loader1_14_4.isSneaking(player);
}
/**
* Returns name of specified world.
*
* @param level
* World to get name of
* @return Name of the world
*/
@NotNull
public static String getLevelName(@NotNull Level level) {
if (serverVersion.getMinorVersion() >= 16) return loaderNew.getLevelName(level);
return loaderOld.getLevelName(level);
return loader1_14_4.getLevelName(level);
}
/**
* Adds sibling to a component.
*
* @param parent
* Parent to add sibling to
* @param child
* Sibling to add
*/
public static void addSibling(@NotNull Component parent, @NotNull Component child) {
if (serverVersion.getMinorVersion() >= 16) loaderNew.addSibling(parent, child);
else loaderOld.addSibling(parent, child);
else loader1_14_4.addSibling(parent, child);
}
/**
* Converts TAB's ChatModifier class to Minecraft Style class.
*
* @param modifier
* Modifier to convert
* @param modern
* Whether RGB should be supported or not
* @return Converted style
*/
@NotNull
public static Style convertModifier(@NotNull ChatModifier modifier, boolean modern) {
if (serverVersion.getMinorVersion() >= 16) return loaderNew.convertModifier(modifier, modern);
return loaderOld.convertModifier(modifier, modern);
return loader1_14_4.convertModifier(modifier, modern);
}
/**
* Creates new Header/Footer packet with given parameters.
*
* @param header
* Header to use
* @param footer
* Footer to use
* @return Packet with given parameters
*/
@NotNull
public static Packet<?> newHeaderFooter(@NotNull Component header, @NotNull Component footer) {
if (serverVersion.getMinorVersion() >= 17) return loaderNew.newHeaderFooter(header, footer);
return loaderOld.newHeaderFooter(header, footer);
return loader1_14_4.newHeaderFooter(header, footer);
}
public static boolean isTeamPacket(@NotNull Packet<?> packet) {
if (serverVersion.getMinorVersion() >= 17) return loaderNew.isTeamPacket(packet);
return loaderOld.isTeamPacket(packet);
/**
* Checks outgoing team packet in pipeline to potentially remove players from it.
*
* @param packet
* Packet to check
* @param scoreboard
* Scoreboard of player who received the packet
*/
public static void checkTeamPacket(@NotNull Packet<?> packet, @NotNull FabricScoreboard scoreboard) {
if (serverVersion.getMinorVersion() >= 17) loaderNew.checkTeamPacket(packet, scoreboard);
else loader1_14_4.checkTeamPacket(packet, scoreboard);
}
/**
* Creates team register packet using given team.
*
* @param team
* Team to register
* @return Team register packet using given team
*/
@NotNull
public static Packet<?> registerTeam(@NotNull PlayerTeam team) {
if (serverVersion.getMinorVersion() >= 17) return loaderNew.registerTeam(team);
return loaderOld.registerTeam(team);
return loader1_14_4.registerTeam(team);
}
/**
* Creates team unregister packet using given team.
*
* @param team
* Team to unregister
* @return Team unregister packet using given team
*/
@NotNull
public static Packet<?> unregisterTeam(@NotNull PlayerTeam team) {
if (serverVersion.getMinorVersion() >= 17) return loaderNew.unregisterTeam(team);
return loaderOld.unregisterTeam(team);
return loader1_14_4.unregisterTeam(team);
}
/**
* Creates team update packet using given team.
*
* @param team
* Team to update
* @return Team update packet using given team
*/
@NotNull
public static Packet<?> updateTeam(@NotNull PlayerTeam team) {
if (serverVersion.getMinorVersion() >= 17) return loaderNew.updateTeam(team);
return loaderOld.updateTeam(team);
return loader1_14_4.updateTeam(team);
}
@SneakyThrows
public static int[] getDestroyedEntities(Packet<?> destroyPacket) {
/**
* Returns destroyed entities from destroy entity packet.
*
* @param destroyPacket
* Entity destroy packet
* @return Destroyed entities
*/
public static int[] getDestroyedEntities(@NotNull Packet<?> destroyPacket) {
if (serverVersion.getMinorVersion() >= 17) return loaderNew.getDestroyedEntities(destroyPacket);
return loaderOld.getDestroyedEntities(destroyPacket);
return loader1_14_4.getDestroyedEntities(destroyPacket);
}
/**
* Logs console message as info.
*
* @param message
* Message to log
*/
public static void logInfo(@NotNull TabComponent message) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_18_2.getNetworkId()) loaderNew.logInfo(message);
else loaderOld.logInfo(message);
else loader1_14_4.logInfo(message);
}
/**
* Logs console message as warn.
*
* @param message
* Message to log
*/
public static void logWarn(@NotNull TabComponent message) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_18_2.getNetworkId()) loaderNew.logWarn(message);
else loaderOld.logWarn(message);
else loader1_14_4.logWarn(message);
}
/**
* Creates new text component using given text.
*
* @param text
* Component text
* @return Text component with given text
*/
@NotNull
public static Component newTextComponent(@NotNull String text) {
if (serverVersion.getMinorVersion() >= 19) return loaderNew.newTextComponent(text);
return loaderOld.newTextComponent(text);
return loader1_14_4.newTextComponent(text);
}
/**
* Sends message to command source.
*
* @param source
* Command source to send message to
* @param message
* Message to send
*/
public static void sendMessage(@NotNull CommandSourceStack source, @NotNull Component message) {
if (serverVersion.getMinorVersion() >= 19) loaderNew.sendMessage(source, message);
else loaderOld.sendMessage(source, message);
else loader1_14_4.sendMessage(source, message);
}
/**
* Creates spawn entity packet with given parameters.
*
* @param level
* World to fill for dummy entity
* @param id
* Entity ID
* @param uuid
* Entity UUID
* @param type
* Entity type
* @param location
* Spawn location
* @return Spawn entity packet with given parameters
*/
@NotNull
public static Packet<ClientGamePacketListener> spawnEntity(Level level, int id, UUID uuid, Object type, Location location) {
public static Packet<ClientGamePacketListener> spawnEntity(@NotNull Level level, int id, @NotNull UUID uuid, @NotNull Object type, @NotNull Location location) {
if (serverVersion.getMinorVersion() >= 19) return loaderNew.spawnEntity(level, id, uuid, type, location);
return loaderOld.spawnEntity(level, id, uuid, type, location);
return loader1_14_4.spawnEntity(level, id, uuid, type, location);
}
/**
* Sets style in a component to specified style.
*
* @param component
* Component to change style of
* @param style
* Style to use
*/
@SneakyThrows
public static void setStyle(@NotNull Component component, @NotNull Style style) {
if (serverVersion.getMinorVersion() >= 19) loaderNew.setStyle(component, style);
else if (serverVersion.getMinorVersion() >= 16) component.getClass().getMethod("method_10862", Style.class).invoke(component, style);
else loaderOld.setStyle(component, style);
else if (serverVersion.getMinorVersion() >= 16) loader1_18_2.setStyle(component, style);
else loader1_14_4.setStyle(component, style);
}
/**
* Sends message to player.
*
* @param player
* Player to send message to
* @param message
* Message to send
*/
@SneakyThrows
public static void sendMessage(@NotNull ServerPlayer player, @NotNull Component message) {
if (serverVersion.getMinorVersion() >= 19) loaderNew.sendMessage(player, message);
else if (serverVersion.getMinorVersion() >= 16)
player.getClass().getMethod("method_9203", Component.class, UUID.class).invoke(player, message, new UUID(0, 0));
else loaderOld.sendMessage(player, message);
else if (serverVersion.getMinorVersion() >= 16) loader1_18_2.sendMessage(player, message);
else loader1_14_4.sendMessage(player, message);
}
/**
* Creates entity metadata packet with given metadata.
*
* @param entityId
* Entity ID to change metadata of
* @param data
* Metadata to change
* @return Entity metadata packet with given parameters
*/
@NotNull
public static Packet<ClientGamePacketListener> newEntityMetadata(int entityId, @NotNull EntityData data) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) return loaderNew.newEntityMetadata(entityId, data);
return loaderOld.newEntityMetadata(entityId, data);
return loader1_14_4.newEntityMetadata(entityId, data);
}
/**
* Creates entity data with given parameters.
*
* @param viewer
* Viewer of custom name
* @param flags
* Entity flags
* @param displayName
* Custom name
* @param nameVisible
* Custom name visibility
* @return Entity data with given parameters
*/
@NotNull
public static EntityData createDataWatcher(@NotNull TabPlayer viewer, byte flags, @NotNull String displayName, boolean nameVisible) {
int position = EntityData.getArmorStandFlagsPosition(serverVersion.getMinorVersion());
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) return loaderNew.createDataWatcher(viewer, flags, displayName, nameVisible, position);
return loaderOld.createDataWatcher(viewer, flags, displayName, nameVisible, position);
return loader1_14_4.createDataWatcher(viewer, flags, displayName, nameVisible, position);
}
/**
* Returns {@code true} if packet is player info packet, {@code false} if not.
*
* @param packet
* Packet to check
* @return {@code true} if packet is player info packet, {@code false} if not
*/
public static boolean isPlayerInfo(@NotNull Packet<?> packet) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) return loaderNew.isPlayerInfo(packet);
return loaderOld.isPlayerInfo(packet);
return loader1_14_4.isPlayerInfo(packet);
}
/**
* Processed player info packet for anti-override and similar.
*
* @param receiver
* Player who received the packet
* @param packet
* Received packet
*/
public static void onPlayerInfo(@NotNull TabPlayer receiver, @NotNull Object packet) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) loaderNew.onPlayerInfo(receiver, packet);
else loaderOld.onPlayerInfo(receiver, packet);
else if (serverVersion.getMinorVersion() >= 17) loader1_18_2.onPlayerInfo(receiver, packet);
else loader1_14_4.onPlayerInfo(receiver, packet);
}
/**
* Creates tablist entry packet using given parameters.
*
* @param action
* Tablist action
* @param builder
* Entry data
* @return Tablist entry packet with given parameters
*/
@NotNull
public static Packet<?> buildTabListPacket(@NotNull TabList.Action action, @NotNull FabricTabList.Builder builder) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) return loaderNew.buildTabListPacket(action, builder);
return loaderOld.buildTabListPacket(action, builder);
else if (serverVersion.getMinorVersion() >= 17) return loader1_18_2.buildTabListPacket(action, builder);
return loader1_14_4.buildTabListPacket(action, builder);
}
/**
* Returns {@code true} if packet is bundle packet, {@code false} if not.
*
* @param packet
* Packet to check
* @return {@code true} if packet is bundle packet, {@code false} if not
*/
public static boolean isBundlePacket(@NotNull Packet<?> packet) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_19_4.getNetworkId()) return loaderNew.isBundlePacket(packet);
return loaderOld.isBundlePacket(packet);
return loader1_14_4.isBundlePacket(packet);
}
/**
* Returns packets bundled in given bundle packet.
*
* @param bundlePacket
* Bundle packet
* @return Bundled packets
*/
@NotNull
public static Iterable<Object> getBundledPackets(@NotNull Packet<?> bundlePacket) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_19_4.getNetworkId()) return loaderNew.getBundledPackets(bundlePacket);
return loaderOld.getBundledPackets(bundlePacket);
return loader1_14_4.getBundledPackets(bundlePacket);
}
/**
* Sends packets to player as a bundle.
*
* @param player
* Player to send packets to
* @param packets
* Packets to send
*/
public static void sendPackets(@NotNull ServerPlayer player, @NotNull Iterable<Packet<ClientGamePacketListener>> packets) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_19_4.getNetworkId()) loaderNew.sendPackets(player, packets);
else loaderOld.sendPackets(player, packets);
else loader1_14_4.sendPackets(player, packets);
}
/**
* Returns player's world
*
* @param player
* Player to get world of
* @return Player's world
*/
@NotNull
public static Level getLevel(@NotNull ServerPlayer player) {
if (serverVersion.getMinorVersion() >= 20) return loaderNew.getLevel(player);
return loaderOld.getLevel(player);
return loader1_14_4.getLevel(player);
}
/**
* Converts minecraft property class into TAB skin class.
*
* @param property
* Property to convert
* @return Converted skin
*/
@NotNull
public static TabList.Skin propertyToSkin(@NotNull Property property) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) return loaderNew.propertyToSkin(property);
return loaderOld.propertyToSkin(property);
return loader1_14_4.propertyToSkin(property);
}
/**
* Returns {@code true} if packet is player spawn packet, {@code false} if not.
*
* @param packet
* Packet to check
* @return {@code true} if packet is player spawn packet, {@code false} if not
*/
public static boolean isSpawnPlayerPacket(@NotNull Packet<?> packet) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) return loaderNew.isSpawnPlayerPacket(packet);
return loaderOld.isSpawnPlayerPacket(packet);
return loader1_14_4.isSpawnPlayerPacket(packet);
}
/**
* Returns player ID of given player spawn packet.
*
* @param packet
* Player spawn packet
* @return Player ID
*/
public static int getSpawnedPlayerId(@NotNull Packet<?> packet) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) return loaderNew.getSpawnedPlayerId(packet);
return loaderOld.getSpawnedPlayerId(packet);
return loader1_14_4.getSpawnedPlayerId(packet);
}
/**
* Returns player's ping.
*
* @param player
* Player to get ping of
* @return Player's ping
*/
public static int getPing(@NotNull ServerPlayer player) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) return loaderNew.getPing(player);
return loaderOld.getPing(player);
return loader1_14_4.getPing(player);
}
/**
* Returns display slot of given display objective packet.
*
* @param packet
* Display objective packet
* @return Display slot of packet
*/
public static int getDisplaySlot(@NotNull ClientboundSetDisplayObjectivePacket packet) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) return loaderNew.getDisplaySlot(packet);
return loaderOld.getDisplaySlot(packet);
return loader1_14_4.getDisplaySlot(packet);
}
/**
* Creates display objective packet with given parameters.
*
* @param slot
* Display slot
* @param objective
* Objective to display
* @return Display objective packet with given parameters
*/
@NotNull
public static Packet<?> setDisplaySlot(int slot, @NotNull Objective objective) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) return loaderNew.setDisplaySlot(slot, objective);
return loaderOld.setDisplaySlot(slot, objective);
return loader1_14_4.setDisplaySlot(slot, objective);
}
/**
* Returns player's network channel.
*
* @param player
* Player to get channel of
* @return Player's channel
*/
@NotNull
public static Channel getChannel(@NotNull ServerPlayer player) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) return loaderNew.getChannel(player);
return loaderOld.getChannel(player);
return loader1_14_4.getChannel(player);
}
/**
* Returns server's current milliseconds per tick.
*
* @param server
* Server to get MSPT value from
* @return Server's milliseconds per tick
*/
public static float getMSPT(@NotNull MinecraftServer server) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_20_3.getNetworkId()) return loaderNew.getMSPT(server);
return loaderOld.getMSPT(server);
return loader1_14_4.getMSPT(server);
}
/**
* Creates new remove score packet with given parameters.
*
* @param objective
* Objective to remove score from
* @param holder
* Score holder to remove
* @return Remove score packet with given parameters
*/
@NotNull
public static Packet<?> removeScore(@NotNull String objective, @NotNull String holder) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_20_3.getNetworkId()) return loaderNew.removeScore(objective, holder);
return loaderOld.removeScore(objective, holder);
return loader1_14_4.removeScore(objective, holder);
}
/**
* Creates new objective with given parameters.
*
* @param name
* Objective name
* @param displayName
* Objective display name
* @param renderType
* Score render type
* @param numberFormat
* Score number format (1.20.3+)
* @return New objective with given parameters
*/
@NotNull
public static Objective newObjective(@NotNull String name, @NotNull Component displayName,
@NotNull RenderType renderType, @Nullable Component numberFormat) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_20_3.getNetworkId()) return loaderNew.newObjective(name, displayName, renderType, numberFormat);
return loaderOld.newObjective(name, displayName, renderType, numberFormat);
return loader1_14_4.newObjective(name, displayName, renderType, numberFormat);
}
/**
* Creates a new set score packet with given parameters.
*
* @param objective
* Objective to set score in
* @param holder
* Score holder
* @param score
* Score value
* @param displayName
* Display name of score holder (1.20.3+)
* @param numberFormat
* Number format of score value (1.20.3+)
* @return New set score packet with given parameters
*/
@NotNull
public static Packet<?> setScore(@NotNull String objective, @NotNull String holder, int score,
@Nullable Component displayName, @Nullable Component numberFormat) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_20_3.getNetworkId()) return loaderNew.setScore(objective, holder, score, displayName, numberFormat);
return loaderOld.setScore(objective, holder, score, displayName, numberFormat);
return loader1_14_4.setScore(objective, holder, score, displayName, numberFormat);
}
}
@@ -1,12 +1,10 @@
package me.neznamy.tab.platforms.fabric;
import lombok.NonNull;
import lombok.SneakyThrows;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
@@ -18,7 +16,6 @@ import net.minecraft.world.scores.Team;
import net.minecraft.world.scores.criteria.ObjectiveCriteria.RenderType;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@@ -121,9 +118,7 @@ public class FabricScoreboard extends Scoreboard<FabricTabPlayer, Component> {
player.sendPacket(FabricMultiVersion.removeScore(objective, scoreHolder));
}
@SuppressWarnings("unchecked")
@Override
@SneakyThrows
public void onPacketSend(@NonNull Object packet) {
if (packet instanceof ClientboundSetDisplayObjectivePacket display) {
TAB.getInstance().getFeatureManager().onDisplayObjective(player, FabricMultiVersion.getDisplaySlot(display), display.objectiveName);
@@ -131,13 +126,8 @@ public class FabricScoreboard extends Scoreboard<FabricTabPlayer, Component> {
if (packet instanceof ClientboundSetObjectivePacket objective) {
TAB.getInstance().getFeatureManager().onObjective(player, objective.method, objective.objectiveName);
}
if (isAntiOverrideTeams() && FabricMultiVersion.isTeamPacket((Packet<?>) packet)) {
int action = ReflectionUtils.getInstanceFields(packet.getClass(), int.class).get(0).getInt(packet);
if (action == TeamAction.REMOVE || action == TeamAction.UPDATE) return;
Field playersField = ReflectionUtils.getFields(packet.getClass(), Collection.class).get(0);
Collection<String> players = (Collection<String>) playersField.get(packet);
String teamName = String.valueOf(ReflectionUtils.getFields(packet.getClass(), String.class).get(0).get(packet));
playersField.set(packet, onTeamPacket(action, teamName, players));
if (isAntiOverrideTeams()) {
FabricMultiVersion.checkTeamPacket((Packet<?>) packet, this);
}
}
@@ -1,8 +1,8 @@
package me.neznamy.tab.platforms.fabric;
import lombok.SneakyThrows;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.fabricmc.api.DedicatedServerModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.minecraft.SharedConstants;
@@ -17,7 +17,7 @@ public class FabricTAB implements DedicatedServerModInitializer {
@Override
public void onInitializeServer() {
if (ProtocolVersion.fromFriendlyName(minecraftVersion).getMinorVersion() >= 19) {
if (ReflectionUtils.classExists("net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback")) {
net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback.EVENT.register((dispatcher, $, $$) -> new FabricTabCommand().onRegisterCommands(dispatcher));
} else {
net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback.EVENT.register((dispatcher, $) -> new FabricTabCommand().onRegisterCommands(dispatcher));
@@ -38,13 +38,4 @@ public class FabricTAB implements DedicatedServerModInitializer {
return (String) gameVersion.getClass().getMethod("getName").invoke(gameVersion);
}
}
/**
* Returns {@code true} if fabric api contains entity events, {@code false} if not.
*
* @return {@code true} if supports entity events, {@code false} if not
*/
public static boolean supportsEntityEvents() {
return ProtocolVersion.fromFriendlyName(minecraftVersion).getMinorVersion() >= 16;
}
}
@@ -3,6 +3,7 @@ package me.neznamy.tab.platforms.fabric;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import lombok.*;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.platform.TabList;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
@@ -29,31 +30,39 @@ public class FabricTabList extends TabList<FabricTabPlayer, Component> {
@Override
public void removeEntry(@NonNull UUID entry) {
player.sendPacket(FabricMultiVersion.buildTabListPacket(Action.REMOVE_PLAYER,
new Builder(entry, "", null, 0, 0, null)));
new Builder(entry, "", null, false, 0, 0, null)));
}
@Override
public void updateDisplayName0(@NonNull UUID entry, @Nullable Component displayName) {
player.sendPacket(FabricMultiVersion.buildTabListPacket(Action.UPDATE_DISPLAY_NAME,
new Builder(entry, "", null, 0, 0, displayName)));
new Builder(entry, "", null, false, 0, 0, displayName)));
}
@Override
public void updateLatency(@NonNull UUID entry, int latency) {
player.sendPacket(FabricMultiVersion.buildTabListPacket(Action.UPDATE_LATENCY,
new Builder(entry, "", null, latency, 0, null)));
new Builder(entry, "", null, false, latency, 0, null)));
}
@Override
public void updateGameMode(@NonNull UUID entry, int gameMode) {
player.sendPacket(FabricMultiVersion.buildTabListPacket(Action.UPDATE_GAME_MODE,
new Builder(entry, "", null, 0, gameMode, null)));
new Builder(entry, "", null, false, 0, gameMode, null)));
}
@Override
public void addEntry0(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, int latency, int gameMode, @Nullable Component displayName) {
public void updateListed(@NonNull UUID entry, boolean listed) {
if (player.getPlatform().getServerVersion().getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) {
player.sendPacket(FabricMultiVersion.buildTabListPacket(Action.UPDATE_LISTED,
new Builder(entry, "", null, listed, 0, 0, null)));
}
}
@Override
public void addEntry0(@NonNull UUID id, @NonNull String name, @Nullable Skin skin, boolean listed, int latency, int gameMode, @Nullable Component displayName) {
player.sendPacket(FabricMultiVersion.buildTabListPacket(Action.ADD_PLAYER,
new Builder(id, name, skin, latency, gameMode, displayName)));
new Builder(id, name, skin, listed, latency, gameMode, displayName)));
}
@Override
@@ -83,6 +92,7 @@ public class FabricTabList extends TabList<FabricTabPlayer, Component> {
@NonNull private final UUID id;
@NonNull private String name;
@Nullable private Skin skin;
private boolean listed;
private int latency;
private int gameMode;
@Nullable private Component displayName;
@@ -1,11 +1,11 @@
package me.neznamy.tab.platforms.fabric.features;
import me.neznamy.tab.platforms.fabric.FabricMultiVersion;
import me.neznamy.tab.platforms.fabric.FabricTAB;
import me.neznamy.tab.platforms.fabric.FabricTabPlayer;
import me.neznamy.tab.shared.backend.EntityData;
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendNameTagX;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
@@ -28,7 +28,7 @@ public class FabricNameTagX extends BackendNameTagX {
* Constructs new instance and registers event listener.
*/
public FabricNameTagX() {
if (FabricTAB.supportsEntityEvents()) {
if (ReflectionUtils.classExists("net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents")) {
// Added in 1.16
ServerPlayerEvents.AFTER_RESPAWN.register((oldPlayer, newPlayer, alive) -> {
if (enabled) respawn(oldPlayer.getUUID());
@@ -2,6 +2,7 @@ package me.neznamy.tab.platforms.fabric.loader;
import com.mojang.authlib.properties.Property;
import io.netty.channel.Channel;
import me.neznamy.tab.platforms.fabric.FabricScoreboard;
import me.neznamy.tab.platforms.fabric.FabricTabList;
import me.neznamy.tab.shared.backend.EntityData;
import me.neznamy.tab.shared.backend.Location;
@@ -27,100 +28,413 @@ import org.jetbrains.annotations.Nullable;
import java.util.UUID;
/**
* Interface for executing methods that have changed over the versions.
* Each implementation of this interface is compiled for a different version of Minecraft.
* Then, internal logic is used to decide which implementation to use for each method
* based on server version.
*/
public interface Loader {
/** Dummy scoreboard for objectives */
Scoreboard dummyScoreboard = new Scoreboard();
/**
* Returns name of specified world.
*
* @param level
* World to get name of
* @return Name of the world
*/
@NotNull
String getLevelName(@NotNull Level level);
/**
* Converts minecraft property class into TAB skin class.
*
* @param property
* Property to convert
* @return Converted skin
*/
@NotNull
TabList.Skin propertyToSkin(@NotNull Property property);
/**
* Creates new text component using given text.
*
* @param text
* Component text
* @return Text component with given text
*/
@NotNull
Component newTextComponent(@NotNull String text);
/**
* Converts TAB's ChatModifier class to Minecraft Style class.
*
* @param modifier
* Modifier to convert
* @param modern
* Whether RGB should be supported or not
* @return Converted style
*/
@NotNull
Style convertModifier(@NotNull ChatModifier modifier, boolean modern);
/**
* Adds sibling to a component.
*
* @param parent
* Parent to add sibling to
* @param child
* Sibling to add
*/
void addSibling(@NotNull Component parent, @NotNull Component child);
/**
* Creates team register packet using given team.
*
* @param team
* Team to register
* @return Team register packet using given team
*/
@NotNull
Packet<?> registerTeam(@NotNull PlayerTeam team);
/**
* Creates team unregister packet using given team.
*
* @param team
* Team to unregister
* @return Team unregister packet using given team
*/
@NotNull
Packet<?> unregisterTeam(@NotNull PlayerTeam team);
/**
* Creates team update packet using given team.
*
* @param team
* Team to update
* @return Team update packet using given team
*/
@NotNull
Packet<?> updateTeam(@NotNull PlayerTeam team);
/**
* Returns {@code true} if player is sneaking, {@code false} if not.
*
* @param player
* Player to check sneak status of
* @return {@code true} if player is sneaking, {@code false} if not
*/
boolean isSneaking(@NotNull ServerPlayer player);
/**
* Sends message to player.
*
* @param player
* Player to send message to
* @param message
* Message to send
*/
void sendMessage(@NotNull ServerPlayer player, @NotNull Component message);
/**
* Sends message to command source.
*
* @param source
* Command source to send message to
* @param message
* Message to send
*/
void sendMessage(@NotNull CommandSourceStack source, @NotNull Component message);
/**
* Creates new Header/Footer packet with given parameters.
*
* @param header
* Header to use
* @param footer
* Footer to use
* @return Packet with given parameters
*/
@NotNull
Packet<?> newHeaderFooter(@NotNull Component header, @NotNull Component footer);
boolean isTeamPacket(@NotNull Packet<?> packet);
/**
* Checks outgoing team packet in pipeline to potentially remove players from it.
*
* @param packet
* Packet to check
* @param scoreboard
* Scoreboard of player who received the packet
*/
void checkTeamPacket(@NotNull Packet<?> packet, @NotNull FabricScoreboard scoreboard);
/**
* Creates spawn entity packet with given parameters.
*
* @param level
* World to fill for dummy entity
* @param id
* Entity ID
* @param uuid
* Entity UUID
* @param type
* Entity type
* @param location
* Spawn location
* @return Spawn entity packet with given parameters
*/
@NotNull
Packet<ClientGamePacketListener> spawnEntity(@NotNull Level level, int id, @NotNull UUID uuid, @NotNull Object type, @NotNull Location location);
/**
* Creates entity metadata packet with given metadata.
*
* @param entityId
* Entity ID to change metadata of
* @param data
* Metadata to change
* @return Entity metadata packet with given parameters
*/
@NotNull
Packet<ClientGamePacketListener> newEntityMetadata(int entityId, @NotNull EntityData data);
/**
* Creates entity data with given parameters.
*
* @param viewer
* Viewer of custom name
* @param flags
* Entity flags
* @param displayName
* Custom name
* @param nameVisible
* Custom name visibility
* @param markerPosition
* Armor stand marker flag position
* @return Entity data with given parameters
*/
@NotNull
EntityData createDataWatcher(@NotNull TabPlayer viewer, byte flags, @NotNull String displayName, boolean nameVisible, int markerPosition);
/**
* Returns {@code true} if packet is player info packet, {@code false} if not.
*
* @param packet
* Packet to check
* @return {@code true} if packet is player info packet, {@code false} if not
*/
boolean isPlayerInfo(@NotNull Packet<?> packet);
/**
* Processed player info packet for anti-override and similar.
*
* @param receiver
* Player who received the packet
* @param packet
* Received packet
*/
void onPlayerInfo(@NotNull TabPlayer receiver, @NotNull Object packet);
/**
* Creates tablist entry packet using given parameters.
*
* @param action
* Tablist action
* @param builder
* Entry data
* @return Tablist entry packet with given parameters
*/
@NotNull
Packet<?> buildTabListPacket(@NotNull TabList.Action action, @NotNull FabricTabList.Builder builder);
/**
* Returns {@code true} if packet is bundle packet, {@code false} if not.
*
* @param packet
* Packet to check
* @return {@code true} if packet is bundle packet, {@code false} if not
*/
boolean isBundlePacket(@NotNull Packet<?> packet);
/**
* Returns packets bundled in given bundle packet.
*
* @param bundlePacket
* Bundle packet
* @return Bundled packets
*/
@NotNull
Iterable<Object> getBundledPackets(@NotNull Packet<?> bundlePacket);
/**
* Sends packets to player as a bundle.
*
* @param player
* Player to send packets to
* @param packets
* Packets to send
*/
void sendPackets(@NotNull ServerPlayer player, @NotNull Iterable<Packet<ClientGamePacketListener>> packets);
/**
* Returns player's world
*
* @param player
* Player to get world of
* @return Player's world
*/
@NotNull
Level getLevel(@NotNull ServerPlayer player);
/**
* Returns {@code true} if packet is player spawn packet, {@code false} if not.
*
* @param packet
* Packet to check
* @return {@code true} if packet is player spawn packet, {@code false} if not
*/
boolean isSpawnPlayerPacket(@NotNull Packet<?> packet);
/**
* Returns player ID of given player spawn packet.
*
* @param packet
* Player spawn packet
* @return Player ID
*/
int getSpawnedPlayerId(@NotNull Packet<?> packet);
/**
* Returns player's ping.
*
* @param player
* Player to get ping of
* @return Player's ping
*/
int getPing(@NotNull ServerPlayer player);
/**
* Returns display slot of given display objective packet.
*
* @param packet
* Display objective packet
* @return Display slot of packet
*/
int getDisplaySlot(@NotNull ClientboundSetDisplayObjectivePacket packet);
/**
* Creates display objective packet with given parameters.
*
* @param slot
* Display slot
* @param objective
* Objective to display
* @return Display objective packet with given parameters
*/
@NotNull
Packet<?> setDisplaySlot(int slot, @NotNull Objective objective);
/**
* Returns player's network channel.
*
* @param player
* Player to get channel of
* @return Player's channel
*/
@NotNull
Channel getChannel(@NotNull ServerPlayer player);
/**
* Returns server's current milliseconds per tick.
*
* @param server
* Server to get MSPT value from
* @return Server's milliseconds per tick
*/
float getMSPT(@NotNull MinecraftServer server);
/**
* Creates new remove score packet with given parameters.
*
* @param objective
* Objective to remove score from
* @param holder
* Score holder to remove
* @return Remove score packet with given parameters
*/
@NotNull
Packet<?> removeScore(@NotNull String objective, @NotNull String holder);
int[] getDestroyedEntities(Packet<?> destroyPacket);
/**
* Returns destroyed entities from destroy entity packet.
*
* @param destroyPacket
* Entity destroy packet
* @return Destroyed entities
*/
int[] getDestroyedEntities(@NotNull Packet<?> destroyPacket);
/**
* Creates new objective with given parameters.
*
* @param name
* Objective name
* @param displayName
* Objective display name
* @param renderType
* Score render type
* @param numberFormat
* Score number format (1.20.3+)
* @return New objective with given parameters
*/
@NotNull
Objective newObjective(@NotNull String name, @NotNull Component displayName, @NotNull RenderType renderType, @Nullable Component numberFormat);
/**
* Creates a new set score packet with given parameters.
*
* @param objective
* Objective to set score in
* @param holder
* Score holder
* @param score
* Score value
* @param displayName
* Display name of score holder (1.20.3+)
* @param numberFormat
* Number format of score value (1.20.3+)
* @return New set score packet with given parameters
*/
@NotNull
Packet<?> setScore(@NotNull String objective, @NotNull String holder, int score, @Nullable Component displayName, @Nullable Component numberFormat);
/**
* Sets style in a component to specified style.
*
* @param component
* Component to change style of
* @param style
* Style to use
*/
void setStyle(@NotNull Component component, @NotNull Style style);
/**
* Logs console message as info.
*
* @param message
* Message to log
*/
void logInfo(@NotNull TabComponent message);
/**
* Logs console message as warn.
*
* @param message
* Message to log
*/
void logWarn(@NotNull TabComponent message);
}
@@ -4,6 +4,7 @@ import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import io.netty.channel.Channel;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.fabric.FabricScoreboard;
import me.neznamy.tab.platforms.fabric.FabricTabList;
import me.neznamy.tab.platforms.fabric.FabricTabPlayer;
import me.neznamy.tab.shared.TAB;
@@ -11,6 +12,7 @@ import me.neznamy.tab.shared.backend.EntityData;
import me.neznamy.tab.shared.backend.Location;
import me.neznamy.tab.shared.chat.ChatModifier;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabPlayer;
import net.minecraft.commands.CommandSourceStack;
@@ -143,8 +145,11 @@ public class Loader_1_20_5 implements Loader {
}
@Override
public boolean isTeamPacket(@NotNull Packet<?> packet) {
return packet instanceof ClientboundSetPlayerTeamPacket; // Fabric-mapped name changed
public void checkTeamPacket(@NotNull Packet<?> packet, @NotNull FabricScoreboard scoreboard) {
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
if (team.method == Scoreboard.TeamAction.REMOVE || team.method == Scoreboard.TeamAction.UPDATE) return;
team.players = scoreboard.onTeamPacket(team.method, team.getName(), team.players);
}
}
@Override
@@ -211,7 +216,7 @@ public class Loader_1_20_5 implements Loader {
packet.entries = Collections.singletonList(new ClientboundPlayerInfoUpdatePacket.Entry(
entry.getId(),
action == TabList.Action.ADD_PLAYER ? entry.createProfile() : null,
true,
entry.isListed(),
entry.getLatency(),
GameType.byId(entry.getGameMode()),
entry.getDisplayName(),
@@ -291,7 +296,7 @@ public class Loader_1_20_5 implements Loader {
}
@Override
public int[] getDestroyedEntities(Packet<?> destroyPacket) {
public int[] getDestroyedEntities(@NotNull Packet<?> destroyPacket) {
return ((ClientboundRemoveEntitiesPacket) destroyPacket).getEntityIds().toIntArray();
}
@@ -354,7 +359,7 @@ public class Loader_1_20_5 implements Loader {
private static class Register1_19_3 {
public static final Map<TabList.Action, EnumSet<ClientboundPlayerInfoUpdatePacket.Action>> actionMap = createActionMap();
static final Map<TabList.Action, EnumSet<ClientboundPlayerInfoUpdatePacket.Action>> actionMap = createActionMap();
public static EnumSet<ClientboundPlayerInfoUpdatePacket.Action> convertAction(TabList.Action action) {
return EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.valueOf(action.name()));
@@ -366,6 +371,7 @@ public class Loader_1_20_5 implements Loader {
actions.put(TabList.Action.UPDATE_GAME_MODE, EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE));
actions.put(TabList.Action.UPDATE_DISPLAY_NAME, EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME));
actions.put(TabList.Action.UPDATE_LATENCY, EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY));
actions.put(TabList.Action.UPDATE_LISTED, EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED));
return actions;
}
}
+2 -1
View File
@@ -19,7 +19,8 @@
"1.17.1",
"1.18", "1.18.1", "1.18.2",
"1.19", "1.19.1", "1.19.2", "1.19.3", "1.19.4",
"1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4", "1.20.5"
"1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4", "1.20.5", "1.20.6",
"1.21-alpha.24.19.a"
]
},
"recommends": {
@@ -15,6 +15,11 @@ accessible field net/minecraft/network/protocol/game/ClientboundSetDisplayObject
# Style constructor (1.16+)
accessible method net/minecraft/network/chat/Style <init> (Lnet/minecraft/network/chat/TextColor;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Lnet/minecraft/network/chat/ClickEvent;Lnet/minecraft/network/chat/HoverEvent;Ljava/lang/String;Lnet/minecraft/resources/ResourceLocation;)V
# Team packet (1.17+)
accessible field net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket method I
accessible field net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket players Ljava/util/Collection;
mutable field net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket players Ljava/util/Collection;
# Logger (1.18.2+)
accessible field net/minecraft/server/MinecraftServer LOGGER Lorg/slf4j/Logger;
@@ -5,6 +5,7 @@ import com.mojang.authlib.properties.Property;
import io.netty.channel.Channel;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.fabric.FabricScoreboard;
import me.neznamy.tab.platforms.fabric.FabricTabList;
import me.neznamy.tab.platforms.fabric.FabricTabPlayer;
import me.neznamy.tab.shared.ProtocolVersion;
@@ -13,6 +14,7 @@ import me.neznamy.tab.shared.backend.EntityData;
import me.neznamy.tab.shared.backend.Location;
import me.neznamy.tab.shared.chat.ChatModifier;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.util.ReflectionUtils;
@@ -42,7 +44,6 @@ import net.minecraft.world.scores.criteria.ObjectiveCriteria.RenderType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.*;
@@ -51,29 +52,13 @@ import java.util.*;
*/
@SuppressWarnings({
"unchecked", // Java generic types
"unused", // Actually used, just via reflection
"rawtypes" // raw enums
"unused" // Actually used, just via reflection
})
@RequiredArgsConstructor
public class Loader_1_14_4 implements Loader {
private final ProtocolVersion serverVersion;
private ArmorStand dummyEntity;
private Class<Enum> tablistActionClass;
@SneakyThrows
private ClientboundPlayerInfoPacket createInfoPacket(@NotNull ProtocolVersion serverVersion, @NotNull TabList.Action action) {
if (tablistActionClass == null) {
if (serverVersion.getMinorVersion() >= 17) {
tablistActionClass = (Class<Enum>) Class.forName("net.minecraft.class_2703$class_5893");
} else {
tablistActionClass = (Class<Enum>) Class.forName("net.minecraft.class_2703$class_2704");
}
}
Class<?> classType = serverVersion.getMinorVersion() >= 17 ? Collection.class : Iterable.class;
return ClientboundPlayerInfoPacket.class.getConstructor(tablistActionClass, classType)
.newInstance(Enum.valueOf(tablistActionClass, action.name()), Collections.emptyList());
}
@Override
@NotNull
@@ -158,8 +143,16 @@ public class Loader_1_14_4 implements Loader {
}
@Override
public boolean isTeamPacket(@NotNull Packet<?> packet) {
return packet instanceof ClientboundSetPlayerTeamPacket;
@SneakyThrows
public void checkTeamPacket(@NotNull Packet<?> packet, @NotNull FabricScoreboard scoreboard) {
if (packet instanceof ClientboundSetPlayerTeamPacket) {
int action = ReflectionUtils.getInstanceFields(packet.getClass(), int.class).get(0).getInt(packet);
if (action == Scoreboard.TeamAction.REMOVE || action == Scoreboard.TeamAction.UPDATE) return;
Field playersField = ReflectionUtils.getFields(packet.getClass(), Collection.class).get(0);
Collection<String> players = (Collection<String>) playersField.get(packet);
String teamName = String.valueOf(ReflectionUtils.getFields(packet.getClass(), String.class).get(0).get(packet));
playersField.set(packet, scoreboard.onTeamPacket(action, teamName, players));
}
}
@Override
@@ -198,7 +191,7 @@ public class Loader_1_14_4 implements Loader {
@Override
@SneakyThrows
public void onPlayerInfo(@NotNull TabPlayer receiver, @NotNull Object packet) {
Enum action = (Enum) ReflectionUtils.getFields(packet.getClass(), tablistActionClass).get(0).get(packet);
ClientboundPlayerInfoPacket.Action action = (ClientboundPlayerInfoPacket.Action) ReflectionUtils.getFields(packet.getClass(), ClientboundPlayerInfoPacket.Action.class).get(0).get(packet);
List<PlayerUpdate> players = (List<PlayerUpdate>) ReflectionUtils.getFields(packet.getClass(), List.class).get(0).get(packet);
for (PlayerUpdate nmsData : players) {
GameProfile profile = nmsData.getProfile();
@@ -221,30 +214,12 @@ public class Loader_1_14_4 implements Loader {
@NotNull
@SneakyThrows
public Packet<?> buildTabListPacket(TabList.@NotNull Action action, @NotNull FabricTabList.Builder entry) {
ClientboundPlayerInfoPacket packet = createInfoPacket(serverVersion, action);
ReflectionUtils.getFields(ClientboundPlayerInfoPacket.class, List.class).get(0).set(packet, Collections.singletonList(createUpdate(serverVersion, entry)));
ClientboundPlayerInfoPacket packet = new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.valueOf(action.name()));
ReflectionUtils.getFields(ClientboundPlayerInfoPacket.class, List.class).get(0).set(packet, Collections.singletonList(
packet.new PlayerUpdate(entry.createProfile(), entry.getLatency(), GameType.byId(entry.getGameMode()), entry.getDisplayName())));
return packet;
}
@SneakyThrows
@SuppressWarnings("unchecked")
private PlayerUpdate createUpdate(@NotNull ProtocolVersion serverVersion, @NotNull FabricTabList.Builder entry) {
Constructor<PlayerUpdate> constructor = (Constructor<PlayerUpdate>) PlayerUpdate.class.getConstructors()[0];
if (serverVersion.getMinorVersion() >= 19) {
// 1.19 - 1.19.2
return constructor.newInstance(
entry.createProfile(), entry.getLatency(), GameType.byId(entry.getGameMode()), entry.getDisplayName(), null);
} else if (serverVersion.getMinorVersion() >= 17) {
// 1.17 - 1.18.2
return constructor.newInstance(
entry.createProfile(), entry.getLatency(), GameType.byId(entry.getGameMode()), entry.getDisplayName());
} else {
// 1.14 - 1.16.5
return constructor.newInstance(new ClientboundPlayerInfoPacket(null, Collections.emptyList()),
entry.createProfile(), entry.getLatency(), GameType.byId(entry.getGameMode()), entry.getDisplayName());
}
}
@Override
public boolean isBundlePacket(@NotNull Packet<?> packet) {
return false;
@@ -318,7 +293,7 @@ public class Loader_1_14_4 implements Loader {
@Override
@SneakyThrows
public int[] getDestroyedEntities(Packet<?> destroyPacket) {
public int[] getDestroyedEntities(@NotNull Packet<?> destroyPacket) {
return (int[]) ReflectionUtils.getOnlyField(destroyPacket.getClass()).get(destroyPacket);
}
+24
View File
@@ -0,0 +1,24 @@
plugins {
id("fabric-loom")
}
repositories {
// Gradle doesn't support combining settings and project repositories, so we have to re-declare all the settings repos we need
maven("https://jitpack.io") // YamlAssist
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://repo.viaversion.com/")
maven("https://oss.sonatype.org/content/repositories/snapshots")
}
dependencies {
implementation(projects.fabric)
minecraft("com.mojang:minecraft:1.18.2")
mappings(loom.officialMojangMappings())
modImplementation("net.fabricmc:fabric-loader:0.15.9") // Not required, but causes warn if not present
}
tasks {
compileJava {
options.release.set(17)
}
}
@@ -0,0 +1,285 @@
package me.neznamy.tab.platforms.fabric.loader;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import io.netty.channel.Channel;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.fabric.FabricScoreboard;
import me.neznamy.tab.platforms.fabric.FabricTabList;
import me.neznamy.tab.platforms.fabric.FabricTabPlayer;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.backend.EntityData;
import me.neznamy.tab.shared.backend.Location;
import me.neznamy.tab.shared.chat.ChatModifier;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.BaseComponent;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket.PlayerUpdate;
import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.Level;
import net.minecraft.world.scores.Objective;
import net.minecraft.world.scores.PlayerTeam;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
/**
* Method loader compiled using Minecraft containing method implementations from 1.17 - 1.18.2.
*/
@SuppressWarnings("unused") // Actually used, just via reflection
@RequiredArgsConstructor
public class Loader_1_18_2 implements Loader {
private final ProtocolVersion serverVersion;
@Override
public void sendMessage(@NotNull ServerPlayer player, @NotNull Component message) {
player.sendMessage(message, new UUID(0, 0));
}
@Override
public void setStyle(@NotNull Component component, @NotNull Style style) {
((BaseComponent)component).setStyle(style);
}
@Override
@NotNull
@SneakyThrows
public Packet<?> buildTabListPacket(@NotNull TabList.Action action, @NotNull FabricTabList.Builder entry) {
PlayerUpdate update;
if (serverVersion.getMinorVersion() >= 19) {
// 1.19 - 1.19.2
update = (PlayerUpdate) PlayerUpdate.class.getConstructors()[0].newInstance(
entry.createProfile(), entry.getLatency(), GameType.byId(entry.getGameMode()), entry.getDisplayName(), null);
} else {
update = new PlayerUpdate(entry.createProfile(), entry.getLatency(), GameType.byId(entry.getGameMode()), entry.getDisplayName());
}
ClientboundPlayerInfoPacket packet = new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.valueOf(action.name()));
ReflectionUtils.getFields(ClientboundPlayerInfoPacket.class, List.class).get(0).set(packet, Collections.singletonList(update));
return packet;
}
@Override
@SneakyThrows
@SuppressWarnings("unchecked")
public void onPlayerInfo(@NotNull TabPlayer receiver, @NotNull Object packet) {
ClientboundPlayerInfoPacket.Action action = (ClientboundPlayerInfoPacket.Action) ReflectionUtils.getFields(packet.getClass(), ClientboundPlayerInfoPacket.Action.class).get(0).get(packet);
List<PlayerUpdate> players = (List<PlayerUpdate>) ReflectionUtils.getFields(packet.getClass(), List.class).get(0).get(packet);
for (PlayerUpdate nmsData : players) {
GameProfile profile = nmsData.getProfile();
Field displayNameField = ReflectionUtils.getFields(PlayerUpdate.class, Component.class).get(0);
Field latencyField = ReflectionUtils.getFields(PlayerUpdate.class, int.class).get(0);
if (action.name().equals(TabList.Action.UPDATE_DISPLAY_NAME.name()) || action.name().equals(TabList.Action.ADD_PLAYER.name())) {
Object expectedName = ((FabricTabPlayer)receiver).getTabList().getExpectedDisplayName(profile.getId());
if (expectedName != null) displayNameField.set(nmsData, expectedName);
}
if (action.name().equals(TabList.Action.UPDATE_LATENCY.name()) || action.name().equals(TabList.Action.ADD_PLAYER.name())) {
latencyField.set(nmsData, TAB.getInstance().getFeatureManager().onLatencyChange(receiver, profile.getId(), latencyField.getInt(nmsData)));
}
if (action.name().equals(TabList.Action.ADD_PLAYER.name())) {
TAB.getInstance().getFeatureManager().onEntryAdd(receiver, profile.getId(), profile.getName());
}
}
}
@Override
@NotNull
public String getLevelName(@NotNull Level level) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@NotNull
@Override
public TabList.Skin propertyToSkin(@NotNull Property property) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Component newTextComponent(@NotNull String text) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Style convertModifier(@NotNull ChatModifier modifier, boolean modern) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public void addSibling(@NotNull Component parent, @NotNull Component child) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Packet<?> registerTeam(@NotNull PlayerTeam team) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Packet<?> unregisterTeam(@NotNull PlayerTeam team) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Packet<?> updateTeam(@NotNull PlayerTeam team) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public boolean isSneaking(@NotNull ServerPlayer player) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public void sendMessage(@NotNull CommandSourceStack source, @NotNull Component message) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Packet<?> newHeaderFooter(@NotNull Component header, @NotNull Component footer) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public void checkTeamPacket(@NotNull Packet<?> packet, @NotNull FabricScoreboard scoreboard) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Packet<ClientGamePacketListener> spawnEntity(@NotNull Level level, int id, @NotNull UUID uuid, @NotNull Object type, @NotNull Location location) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Packet<ClientGamePacketListener> newEntityMetadata(int entityId, @NotNull EntityData data) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public EntityData createDataWatcher(@NotNull TabPlayer viewer, byte flags, @NotNull String displayName, boolean nameVisible, int markerPosition) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public boolean isPlayerInfo(@NotNull Packet<?> packet) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public boolean isBundlePacket(@NotNull Packet<?> packet) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Iterable<Object> getBundledPackets(@NotNull Packet<?> bundlePacket) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public void sendPackets(@NotNull ServerPlayer player, @NotNull Iterable<Packet<ClientGamePacketListener>> packets) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Level getLevel(@NotNull ServerPlayer player) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public boolean isSpawnPlayerPacket(@NotNull Packet<?> packet) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public int getSpawnedPlayerId(@NotNull Packet<?> packet) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public int getPing(@NotNull ServerPlayer player) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public int getDisplaySlot(@NotNull ClientboundSetDisplayObjectivePacket packet) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Packet<?> setDisplaySlot(int slot, @NotNull Objective objective) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Channel getChannel(@NotNull ServerPlayer player) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public float getMSPT(@NotNull MinecraftServer server) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Packet<?> removeScore(@NotNull String objective, @NotNull String holder) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public int[] getDestroyedEntities(@NotNull Packet<?> destroyPacket) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Objective newObjective(@NotNull String name, @NotNull Component displayName, ObjectiveCriteria.@NotNull RenderType renderType, @Nullable Component numberFormat) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
@NotNull
public Packet<?> setScore(@NotNull String objective, @NotNull String holder, int score, @Nullable Component displayName, @Nullable Component numberFormat) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public void logInfo(@NotNull TabComponent message) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
@Override
public void logWarn(@NotNull TabComponent message) {
throw new UnsupportedOperationException("Not implemented in this submodule");
}
}
+2 -1
View File
@@ -14,7 +14,8 @@ val platforms = setOf(
val fabrics = setOf(
rootProject.projects.fabric,
rootProject.projects.fabric.v1144
rootProject.projects.fabric.v1144,
rootProject.projects.fabric.v1182
).map { it.dependencyProject }
tasks {
+1
View File
@@ -35,4 +35,5 @@ include(":sponge7")
include(":sponge8")
include(":fabric")
include(":fabric:v1_14_4")
include(":fabric:v1_18_2")
include(":jar")
+1 -1
View File
@@ -5,7 +5,7 @@ plugins {
dependencies {
api(projects.api)
api("org.yaml:snakeyaml:2.0")
api("com.github.NEZNAMY:yamlassist:1.0.5")
api("com.github.NEZNAMY:yamlassist:1.0.8")
api("com.googlecode.json-simple:json-simple:1.1.1") {
exclude("junit", "junit")
}
@@ -227,20 +227,6 @@ public class ErrorManager {
t, true, errorLog);
}
/**
* Prints error message when attempting to send plugin message to player
* who is not connected to any server.
*
* @param player
* Player message was attempted to be sent to
* @param message
* Message that failed to deliver
*/
public void noServerPluginMessage(@NotNull TabPlayer player, byte[] message) {
printError("Skipped plugin message send to " + player.getName() + ", because player is not " +
"connected to any server (message=" + new String(message) + ")", Collections.emptyList(), false, errorLog);
}
/**
* Prints error message when RedidSupport received message with unknown action.
*
@@ -292,18 +278,6 @@ public class ErrorManager {
t, true, errorLog);
}
/**
* Prints warn if player is not in plugin's scoreboard.
*
* @param player
* Player who was in the wrong scoreboard
*/
public void playerInWrongScoreboard(@NotNull TabPlayer player) {
printError("Player " + player.getName() + " was in a different scoreboard " +
"than expected. This means another plugin changed player's scoreboard.",
Collections.emptyList(), false, errorLog);
}
/**
* Prints error message if armor stand manager of player is unexpectedly {@code null}.
*
@@ -75,6 +75,9 @@ public class FeatureManager {
((UnLoadable) f).unload();
TAB.getInstance().debug("Feature " + f.getClass().getSimpleName() + " processed unload in " + (System.currentTimeMillis()-time) + "ms");
}
for (TabFeature f : values) {
f.deactivate();
}
TAB.getInstance().getPlaceholderManager().getTabExpansion().unregisterExpansion();
if (TAB.getInstance().getPlatform() instanceof ProxyPlatform) {
for (TabPlayer player : TAB.getInstance().getOnlinePlayers()) {
@@ -11,6 +11,7 @@ import org.jetbrains.annotations.NotNull;
public enum ProtocolVersion {
UNKNOWN,
V1_20_6 (766),
V1_20_5 (766),
V1_20_4 (765),
V1_20_3 (765),
@@ -76,8 +77,11 @@ public enum ProtocolVersion {
V1_4_7 (51),
V1_4_6 (51);
/** Array to iterate over to prevent new array creation each time */
private static final ProtocolVersion[] VALUES = values();
/** Newest MC version this plugin jar knows */
public static final ProtocolVersion LATEST_KNOWN_VERSION = V1_20_5;
public static final ProtocolVersion LATEST_KNOWN_VERSION = V1_20_6;
/** Version's network id found at <a href="https://wiki.vg/Protocol_version_numbers">wiki.vg</a> */
private final int networkId;
@@ -146,7 +150,7 @@ public enum ProtocolVersion {
* @return version from given network id
*/
public static @NotNull ProtocolVersion fromNetworkId(int networkId) {
for (ProtocolVersion v : values()) {
for (ProtocolVersion v : VALUES) {
if (networkId == v.networkId) return v;
}
return UNKNOWN;
@@ -133,7 +133,7 @@ public class ArmorStand {
if (manager.isArmorStandsAlwaysVisible()) return true;
if (owner.isDisguised() || manager.isOnBoat(owner)) return false;
return owner.getGamemode() != 3 && !manager.hasHiddenNameTag(owner) && !property.get().isEmpty() &&
!manager.getUnlimitedDisableChecker().isDisabledPlayer(owner);
!owner.disabledUnlimitedNametags.get();
}
/**
@@ -45,9 +45,8 @@ public abstract class BackendNameTagX extends NameTagX implements GameModeListen
});
}
@Override
public BackendArmorStandManager getArmorStandManager(@NotNull TabPlayer player) {
return (BackendArmorStandManager) armorStandManagerMap.get(player);
return (BackendArmorStandManager) player.unlimitedNametagData.armorStandManager;
}
@Override
@@ -80,7 +79,7 @@ public abstract class BackendNameTagX extends NameTagX implements GameModeListen
@Override
public boolean isOnBoat(@NotNull TabPlayer player) {
return vehicleManager != null && vehicleManager.isOnBoat(player);
return player.unlimitedNametagData.onBoat;
}
/**
@@ -107,8 +106,7 @@ public abstract class BackendNameTagX extends NameTagX implements GameModeListen
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
getArmorStandManager(all).unregisterPlayer((BackendTabPlayer) disconnectedPlayer);
}
armorStandManagerMap.get(disconnectedPlayer).destroy();
armorStandManagerMap.remove(disconnectedPlayer); // WeakHashMap doesn't clear this due to value referencing the key
disconnectedPlayer.unlimitedNametagData.armorStandManager.destroy();
}
@Override
@@ -144,7 +142,7 @@ public abstract class BackendNameTagX extends NameTagX implements GameModeListen
public void onWorldChange(@NotNull TabPlayer p, @NotNull String from, @NotNull String to) {
super.onWorldChange(p, from, to);
BackendArmorStandManager asm = getArmorStandManager(p);
if (isPreviewingNameTag(p)) {
if (p.unlimitedNametagData.previewing) {
asm.spawn((BackendTabPlayer) p);
}
//for some reason this is needed for some users
@@ -167,7 +165,7 @@ public abstract class BackendNameTagX extends NameTagX implements GameModeListen
@Override
public void onPacketSend(@NotNull TabPlayer receiver, @NotNull Object packet) {
if (receiver.getVersion().getMinorVersion() < 8) return;
if (!receiver.isLoaded() || getDisableChecker().isDisabledPlayer(receiver) || getUnlimitedDisableChecker().isDisabledPlayer(receiver)) return;
if (!receiver.isLoaded() || receiver.disabledNametags.get() || receiver.disabledUnlimitedNametags.get()) return;
packetListener.onPacketSend((BackendTabPlayer) receiver, packet);
}
@@ -34,9 +34,6 @@ public class VehicleRefresher extends TabFeature implements JoinListener, QuitLi
@Getter
private final Map<Integer, List<Integer>> vehicles = new ConcurrentHashMap<>();
/** set of players currently on boats */
private final Set<TabPlayer> playersOnBoats = Collections.newSetFromMap(new WeakHashMap<>());
/** Reference to the main feature */
private final BackendNameTagX feature;
@@ -48,7 +45,7 @@ public class VehicleRefresher extends TabFeature implements JoinListener, QuitLi
feature.getArmorStandManager(inVehicle).teleport();
}
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (feature.isPreviewingNameTag(p)) {
if (p.unlimitedNametagData.previewing) {
feature.getArmorStandManager(p).teleport((BackendTabPlayer) p);
}
}
@@ -65,7 +62,7 @@ public class VehicleRefresher extends TabFeature implements JoinListener, QuitLi
updateVehicle(vehicle);
addToVehicle(p, vehicle);
if (feature.isDisableOnBoats() && feature.getEntityType(vehicle).contains("boat")) {
playersOnBoats.add(p);
p.unlimitedNametagData.onBoat = true;
}
}
}
@@ -95,8 +92,8 @@ public class VehicleRefresher extends TabFeature implements JoinListener, QuitLi
vehicles.remove(feature.getEntityId(playersInVehicle.remove(p)));
feature.getArmorStandManager(p).teleport();
playersInVehicleArray = playersInVehicle.keySet().toArray(new TabPlayer[0]);
if (feature.isDisableOnBoats() && playersOnBoats.contains(p)) {
playersOnBoats.remove(p);
if (feature.isDisableOnBoats() && p.unlimitedNametagData.onBoat) {
p.unlimitedNametagData.onBoat = false;
feature.updateTeamData(p);
}
feature.getArmorStandManager(p).updateVisibility(true);
@@ -107,7 +104,7 @@ public class VehicleRefresher extends TabFeature implements JoinListener, QuitLi
feature.getArmorStandManager(p).respawn(); //making teleport instant instead of showing teleport animation
addToVehicle(p, vehicle);
if (feature.isDisableOnBoats() && feature.getEntityType(vehicle).contains("boat")) {
playersOnBoats.add(p);
p.unlimitedNametagData.onBoat = true;
feature.updateTeamData(p);
}
feature.getArmorStandManager(p).updateVisibility(true);
@@ -125,17 +122,6 @@ public class VehicleRefresher extends TabFeature implements JoinListener, QuitLi
playersInVehicleArray = playersInVehicle.keySet().toArray(new TabPlayer[0]);
}
/**
* Returns {@code true} if the player is in a boat, {@code false} if not
*
* @param p
* Player to check
* @return {@code true} if in a boat, {@code false} if not
*/
public boolean isOnBoat(@NotNull TabPlayer p) {
return playersOnBoats.contains(p);
}
private void updateVehicle(Object vehicle) {
feature.runInEntityScheduler(vehicle, () -> vehicles.put(feature.getEntityId(vehicle), feature.getPassengers(vehicle)));
}
@@ -11,18 +11,18 @@ public class ChatModifier {
@Nullable private TextColor color;
private boolean bold;
private boolean italic;
private boolean underlined;
private boolean strikethrough;
private boolean obfuscated;
private boolean strikethrough;
private boolean underlined;
@Nullable private String font;
public ChatModifier(@NotNull ChatModifier modifier) {
color = modifier.color;
bold = modifier.bold;
italic = modifier.italic;
underlined = modifier.underlined;
strikethrough = modifier.strikethrough;
obfuscated = modifier.obfuscated;
strikethrough = modifier.strikethrough;
underlined = modifier.underlined;
font = modifier.font;
}
@@ -37,18 +37,24 @@ public class ChatModifier {
StringBuilder builder = new StringBuilder();
if (bold) builder.append(EnumChatFormat.BOLD);
if (italic) builder.append(EnumChatFormat.ITALIC);
if (underlined) builder.append(EnumChatFormat.UNDERLINE);
if (strikethrough) builder.append(EnumChatFormat.STRIKETHROUGH);
if (obfuscated) builder.append(EnumChatFormat.OBFUSCATED);
if (strikethrough) builder.append(EnumChatFormat.STRIKETHROUGH);
if (underlined) builder.append(EnumChatFormat.UNDERLINE);
return builder.toString();
}
/**
* Returns {@code true} if this modifier has any magic codes, {@code false} if not.
* Returns bitmask of magic codes.
*
* @return {@code true} if this modifier has any magic codes, {@code false} if not
* @return Bitmask of magic codes
*/
public boolean hasMagicCodes() {
return bold || italic || underlined || strikethrough || obfuscated;
public int getMagicCodeBitMask() {
int mask = 0;
if (bold) mask += 1;
if (italic) mask += 2;
if (obfuscated) mask += 4;
if (strikethrough) mask += 8;
if (underlined) mask += 16;
return mask;
}
}
@@ -213,8 +213,8 @@ public abstract class TabComponent {
* @return {@code true} if valid, {@code false} if not
*/
private static boolean isHexCode(@NotNull String string) {
for (char c : string.toCharArray()) {
if ("0123456789AaBbCcDdEeFf".indexOf(c) == -1) return false;
for (int i=0; i<string.length(); i++) {
if ("0123456789AaBbCcDdEeFf".indexOf(string.charAt(i)) == -1) return false;
}
return true;
}
@@ -39,7 +39,8 @@ public class RGBUtils {
*/
public RGBUtils() {
List<RGBFormatter> list = new ArrayList<>();
if (ReflectionUtils.classExists("net.kyori.adventure.text.minimessage.MiniMessage")) {
if (ReflectionUtils.classExists("net.kyori.adventure.text.minimessage.MiniMessage") &&
ReflectionUtils.classExists("net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer")) {
list.add(new MiniMessageFormat());
}
list.add(new BukkitFormat());
@@ -12,7 +12,6 @@ import me.neznamy.tab.shared.config.file.ConfigurationFile;
import me.neznamy.tab.shared.Property;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.features.PlayerList;
import me.neznamy.tab.shared.features.sorting.Sorting;
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
import org.jetbrains.annotations.NotNull;
@@ -83,20 +82,17 @@ public class DebugCommand extends SubCommand {
sendMessage(sender, getTeamName(analyzed));
sendMessage(sender, getTeamNameNote(analyzed));
if (tab.getFeatureManager().isFeatureEnabled(TabConstants.Feature.PLAYER_LIST)) {
PlayerList playerlist = tab.getFeatureManager().getFeature(TabConstants.Feature.PLAYER_LIST);
boolean disabledPlayerlist = playerlist.getDisableChecker().isDisabledPlayer(analyzed);
showProperty(sender, analyzed, TabConstants.Property.TABPREFIX, disabledPlayerlist);
showProperty(sender, analyzed, TabConstants.Property.TABSUFFIX, disabledPlayerlist);
showProperty(sender, analyzed, TabConstants.Property.CUSTOMTABNAME, disabledPlayerlist);
showProperty(sender, analyzed, TabConstants.Property.TABPREFIX, analyzed.disabledPlayerList.get());
showProperty(sender, analyzed, TabConstants.Property.TABSUFFIX, analyzed.disabledPlayerList.get());
showProperty(sender, analyzed, TabConstants.Property.CUSTOMTABNAME, analyzed.disabledPlayerList.get());
} else {
sendMessage(sender, "&atabprefix: &cDisabled");
sendMessage(sender, "&atabsuffix: &cDisabled");
sendMessage(sender, "&acustomtabname: &cDisabled");
}
if (tab.getNameTagManager() != null) {
boolean disabledNametags = tab.getNameTagManager().getDisableChecker().isDisabledPlayer(analyzed);
showProperty(sender, analyzed, TabConstants.Property.TAGPREFIX, disabledNametags);
showProperty(sender, analyzed, TabConstants.Property.TAGSUFFIX, disabledNametags);
showProperty(sender, analyzed, TabConstants.Property.TAGPREFIX, analyzed.disabledNametags.get());
showProperty(sender, analyzed, TabConstants.Property.TAGSUFFIX, analyzed.disabledNametags.get());
NameTagX nameTagX = TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS);
if (nameTagX != null) {
boolean disabledUnlimited = nameTagX.isPlayerDisabled(analyzed);
@@ -162,12 +158,11 @@ public class DebugCommand extends SubCommand {
private @NotNull String getTeamName(@NotNull TabPlayer analyzed) {
Sorting sorting = TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (sorting == null) return "";
if (TAB.getInstance().getNameTagManager() != null &&
TAB.getInstance().getNameTagManager().getDisableChecker().isDisabledPlayer(analyzed)) {
if (TAB.getInstance().getNameTagManager() != null && analyzed.disabledNametags.get()) {
return "&eTeam name: &cSorting is disabled in player's world/server";
}
return "&eTeam name: &a" + (TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.LAYOUT)
? sorting.getFullTeamName(analyzed) : sorting.getShortTeamName(analyzed));
? analyzed.sortingData.fullTeamName : analyzed.sortingData.getShortTeamName());
}
/**
@@ -178,13 +173,11 @@ public class DebugCommand extends SubCommand {
* @return team name note of specified player
*/
private @NotNull String getTeamNameNote(@NotNull TabPlayer analyzed) {
Sorting sorting = TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (sorting == null) return "";
if (TAB.getInstance().getNameTagManager() != null &&
TAB.getInstance().getNameTagManager().getDisableChecker().isDisabledPlayer(analyzed)) {
if (analyzed.sortingData == null) return "";
if (TAB.getInstance().getNameTagManager() != null && analyzed.disabledNametags.get()) {
return "";
}
return "&eSorting note: &r" + sorting.getTeamNameNote(analyzed);
return "&eSorting note: &r" + analyzed.sortingData.teamNameNote;
}
/**
@@ -47,7 +47,7 @@ public class ScoreboardOffCommand extends SubCommand {
return;
}
boolean silent = args.length == 2 && args[1].equals("-s");
if (scoreboard.getOtherPluginScoreboards().containsKey(target)) return; //not overriding other plugins
if (target.scoreboardData.otherPluginScoreboard != null) return; //not overriding other plugins
scoreboard.setScoreboardVisible(target, false, !silent);
}
@@ -47,7 +47,7 @@ public class ScoreboardOnCommand extends SubCommand {
return;
}
boolean silent = args.length == 2 && args[1].equals("-s");
if (scoreboard.getOtherPluginScoreboards().containsKey(target)) return; //not overriding other plugins
if (target.scoreboardData.otherPluginScoreboard != null) return; //not overriding other plugins
scoreboard.setScoreboardVisible(target, true, !silent);
}
@@ -47,7 +47,7 @@ public class ScoreboardToggleCommand extends SubCommand {
return;
}
boolean silent = args.length == 2 && args[1].equals("-s");
if (scoreboard.getOtherPluginScoreboards().containsKey(target)) return; //not overriding other plugins
if (target.scoreboardData.otherPluginScoreboard != null) return; //not overriding other plugins
scoreboard.toggleScoreboard(target, !silent);
}
@@ -43,7 +43,7 @@ public class CpuManager {
private volatile boolean enabled;
/** Boolean tracking whether CPU usage should be tracked or not */
private boolean trackUsage;
@Getter private boolean trackUsage;
/**
* Enables CPU usage tracking and returns {@code true} if it was not enabled previously.
@@ -120,6 +120,19 @@ public class CpuManager {
placeholderUsageCurrent.computeIfAbsent(placeholder, l -> new AtomicLong()).addAndGet(nanoseconds);
}
/**
* Adds placeholder time from given map.
*
* @param times
* How long each placeholder took
*/
public void addPlaceholderTimes(@NotNull Map<String, Long> times) {
if (!trackUsage) return;
for (Map.Entry<String, Long> entry : times.entrySet()) {
placeholderUsageCurrent.computeIfAbsent(entry.getKey(), l -> new AtomicLong()).addAndGet(entry.getValue());
}
}
public void runMeasuredTask(@NotNull String feature, @NotNull String type, @NotNull Runnable task) {
submit(() -> runAndMeasure(task, feature, type));
}
@@ -44,7 +44,7 @@ public class BelowName extends TabFeature implements JoinListener, Loadable, UnL
*/
public BelowName() {
Condition disableCondition = Condition.getCondition(config().getString("belowname-objective.disable-condition"));
disableChecker = new DisableChecker(getFeatureName(), disableCondition, this::onDisableConditionChange);
disableChecker = new DisableChecker(getFeatureName(), disableCondition, this::onDisableConditionChange, p -> p.disabledBelowname);
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.BELOW_NAME + "-Condition", disableChecker);
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.BELOW_NAME_TEXT, textRefresher);
TAB.getInstance().getConfigHelper().startup().checkBelowNameText(rawText);
@@ -60,7 +60,7 @@ public class BelowName extends TabFeature implements JoinListener, Loadable, UnL
loaded.setProperty(textRefresher, TEXT_PROPERTY, rawText);
loaded.setProperty(textRefresher, DEFAULT_FORMAT_PROPERTY, fancyDisplayDefault);
if (disableChecker.isDisableConditionMet(loaded)) {
disableChecker.addDisabledPlayer(loaded);
loaded.disabledBelowname.set(true);
} else {
register(loaded);
}
@@ -78,7 +78,7 @@ public class BelowName extends TabFeature implements JoinListener, Loadable, UnL
@Override
public void unload() {
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (disableChecker.isDisabledPlayer(p)) continue;
if (p.disabledBelowname.get()) continue;
p.getScoreboard().unregisterObjective(OBJECTIVE_NAME);
}
}
@@ -90,7 +90,7 @@ public class BelowName extends TabFeature implements JoinListener, Loadable, UnL
connectedPlayer.setProperty(textRefresher, TEXT_PROPERTY, rawText);
connectedPlayer.setProperty(textRefresher, DEFAULT_FORMAT_PROPERTY, fancyDisplayDefault);
if (disableChecker.isDisableConditionMet(connectedPlayer)) {
disableChecker.addDisabledPlayer(connectedPlayer);
connectedPlayer.disabledBelowname.set(true);
} else {
register(connectedPlayer);
}
@@ -168,7 +168,7 @@ public class BelowName extends TabFeature implements JoinListener, Loadable, UnL
@Override
public void onLoginPacket(@NotNull TabPlayer player) {
if (disableChecker.isDisabledPlayer(player) || !player.isLoaded()) return;
if (player.disabledBelowname.get() || !player.isLoaded()) return;
register(player);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (!sameServerAndWorld(all, player)) continue;
@@ -199,7 +199,7 @@ public class BelowName extends TabFeature implements JoinListener, Loadable, UnL
* NumberFormat display of the score
*/
public void setScore(@NotNull TabPlayer viewer, @NotNull TabPlayer scoreHolder, int value, @NotNull String fancyDisplay) {
if (disableChecker.isDisabledPlayer(viewer)) return;
if (viewer.disabledBelowname.get()) return;
viewer.getScoreboard().setScore(
OBJECTIVE_NAME,
scoreHolder.getNickname(),
@@ -254,7 +254,7 @@ public class BelowName extends TabFeature implements JoinListener, Loadable, UnL
@Override
public void refresh(@NotNull TabPlayer refreshed, boolean force) {
if (feature.disableChecker.isDisabledPlayer(refreshed)) return;
if (refreshed.disabledBelowname.get()) return;
refreshed.getScoreboard().updateObjective(
OBJECTIVE_NAME,
refreshed.getProperty(feature.TEXT_PROPERTY).updateAndGet(),
@@ -169,7 +169,7 @@ public class GlobalPlayerList extends TabFeature implements JoinListener, QuitLi
@NotNull
public TabList.Entry getAddInfoData(@NotNull TabPlayer p, @NotNull TabPlayer viewer) {
TabComponent format = null;
if (playerlist != null && !playerlist.getDisableChecker().isDisabledPlayer(p)) {
if (playerlist != null && !p.disabledPlayerList.get()) {
format = playerlist.getTabFormat(p, viewer);
}
int gameMode = (othersAsSpectators && !p.getServer().equals(viewer.getServer())) ||
@@ -178,6 +178,7 @@ public class GlobalPlayerList extends TabFeature implements JoinListener, QuitLi
p.getTablistId(),
p.getNickname(),
p.getSkin(),
true,
p.getPing(),
gameMode,
viewer.getVersion().getMinorVersion() >= 8 ? format : null
@@ -29,7 +29,7 @@ public class HeaderFooter extends TabFeature implements HeaderFooterManager, Joi
*/
public HeaderFooter() {
Condition disableCondition = Condition.getCondition(config().getString("header-footer.disable-condition"));
disableChecker = new DisableChecker(getFeatureName(), disableCondition, this::onDisableConditionChange);
disableChecker = new DisableChecker(getFeatureName(), disableCondition, this::onDisableConditionChange, p -> p.disabledHeaderFooter);
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.HEADER_FOOTER + "-Condition", disableChecker);
TAB.getInstance().getConfigHelper().hint().checkHeaderFooterForRedundancy(config().getConfigurationSection("header-footer"));
}
@@ -44,7 +44,7 @@ public class HeaderFooter extends TabFeature implements HeaderFooterManager, Joi
@Override
public void unload() {
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (disableChecker.isDisabledPlayer(p)) continue;
if (p.disabledHeaderFooter.get()) continue;
sendHeaderFooter(p, "","");
}
}
@@ -52,7 +52,7 @@ public class HeaderFooter extends TabFeature implements HeaderFooterManager, Joi
@Override
public void onJoin(@NotNull TabPlayer connectedPlayer) {
if (disableChecker.isDisableConditionMet(connectedPlayer)) {
disableChecker.addDisabledPlayer(connectedPlayer);
connectedPlayer.disabledHeaderFooter.set(true);
}
refresh(connectedPlayer, true);
}
@@ -140,7 +140,7 @@ public class HeaderFooter extends TabFeature implements HeaderFooterManager, Joi
}
private void sendHeaderFooter(TabPlayer player, String header, String footer) {
if (disableChecker.isDisabledPlayer(player)) return;
if (player.disabledHeaderFooter.get()) return;
player.getTabList().setPlayerListHeaderFooter(TabComponent.optimized(header), TabComponent.optimized(footer));
}
@@ -156,6 +156,7 @@ public class HeaderFooter extends TabFeature implements HeaderFooterManager, Joi
@Override
public void setHeader(@NotNull me.neznamy.tab.api.TabPlayer p, @Nullable String header) {
ensureActive();
TabPlayer player = (TabPlayer) p;
player.ensureLoaded();
player.getProperty(TabConstants.Property.HEADER).setTemporaryValue(header);
@@ -165,6 +166,7 @@ public class HeaderFooter extends TabFeature implements HeaderFooterManager, Joi
@Override
public void setFooter(@NotNull me.neznamy.tab.api.TabPlayer p, @Nullable String footer) {
ensureActive();
TabPlayer player = (TabPlayer) p;
player.ensureLoaded();
player.getProperty(TabConstants.Property.FOOTER).setTemporaryValue(footer);
@@ -174,6 +176,7 @@ public class HeaderFooter extends TabFeature implements HeaderFooterManager, Joi
@Override
public void setHeaderAndFooter(@NotNull me.neznamy.tab.api.TabPlayer p, @Nullable String header, @Nullable String footer) {
ensureActive();
TabPlayer player = (TabPlayer) p;
player.ensureLoaded();
player.getProperty(TabConstants.Property.HEADER).setTemporaryValue(header);
@@ -6,7 +6,6 @@ import java.util.UUID;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.features.nametags.NameTag;
import me.neznamy.tab.shared.features.redis.RedisPlayer;
import me.neznamy.tab.shared.features.redis.RedisSupport;
@@ -71,13 +70,13 @@ public class NickCompatibility extends TabFeature implements EntryAddListener {
if (nameTags != null && !nameTags.hasTeamHandlingPaused(player))
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
String prefix = player.getProperty(TabConstants.Property.TAGPREFIX).getFormat(viewer);
viewer.getScoreboard().unregisterTeam(nameTags.getSorting().getShortTeamName(player));
viewer.getScoreboard().unregisterTeam(player.sortingData.getShortTeamName());
viewer.getScoreboard().registerTeam(
nameTags.getSorting().getShortTeamName(player),
player.sortingData.getShortTeamName(),
prefix,
player.getProperty(TabConstants.Property.TAGSUFFIX).getFormat(viewer),
nameTags.getTeamVisibility(player, viewer) ? Scoreboard.NameVisibility.ALWAYS : Scoreboard.NameVisibility.NEVER,
nameTags.getCollisionManager().getCollision(player) ? Scoreboard.CollisionRule.ALWAYS : Scoreboard.CollisionRule.NEVER,
player.teamData.getCollisionRule() ? Scoreboard.CollisionRule.ALWAYS : Scoreboard.CollisionRule.NEVER,
Collections.singletonList(player.getNickname()),
nameTags.getTeamOptions(),
EnumChatFormat.lastColorsOf(prefix)
@@ -100,18 +99,18 @@ public class NickCompatibility extends TabFeature implements EntryAddListener {
private void processNameChange(RedisPlayer player) {
TAB.getInstance().getCPUManager().runMeasuredTask(getFeatureName(), TabConstants.CpuUsageCategory.NICK_PLUGIN_COMPATIBILITY, () -> {
if (redisTeams != null) {
String teamName = redisTeams.getTeamNames().get(player);
String teamName = player.getTeamName();
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.getScoreboard().unregisterTeam(teamName);
viewer.getScoreboard().registerTeam(
teamName,
redisTeams.getPrefixes().get(player),
redisTeams.getSuffixes().get(player),
redisTeams.getNameVisibilities().get(player),
player.getTagPrefix(),
player.getTagSuffix(),
player.getNameVisibility(),
Scoreboard.CollisionRule.ALWAYS,
Collections.singletonList(player.getNickname()),
redisTeams.getNameTags().getTeamOptions(),
EnumChatFormat.lastColorsOf(redisTeams.getPrefixes().get(player))
EnumChatFormat.lastColorsOf(player.getTagPrefix())
);
}
}
@@ -120,9 +119,9 @@ public class NickCompatibility extends TabFeature implements EntryAddListener {
all.getScoreboard().setScore(
BelowName.OBJECTIVE_NAME,
player.getNickname(),
redisBelowName.getValues().get(player),
player.getBelowNameNumber(),
null, // Unused by this objective slot
TabComponent.optimized(redisBelowName.getFancyValues().get(player))
player.getBelowNameFancy()
);
}
}
@@ -131,9 +130,9 @@ public class NickCompatibility extends TabFeature implements EntryAddListener {
all.getScoreboard().setScore(
YellowNumber.OBJECTIVE_NAME,
player.getNickname(),
redisYellowNumber.getValues().get(player),
player.getPlayerlistNumber(),
null, // Unused by this objective slot
TabComponent.optimized(redisYellowNumber.getFancyValues().get(player))
player.getPlayerlistFancy()
);
}
}
@@ -3,7 +3,6 @@ package me.neznamy.tab.shared.features;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.features.layout.LayoutManagerImpl;
import me.neznamy.tab.shared.features.layout.LayoutView;
import me.neznamy.tab.shared.features.layout.ParentGroup;
import me.neznamy.tab.shared.features.layout.PlayerSlot;
import me.neznamy.tab.shared.features.types.*;
@@ -26,9 +25,8 @@ public class PingSpoof extends TabFeature implements JoinListener, LatencyListen
@Override
public int onLatencyChange(@NotNull TabPlayer packetReceiver, @NotNull UUID id, int latency) {
if (layoutManager != null) {
LayoutView layout = layoutManager.getViews().get(packetReceiver);
if (layout != null) {
for (ParentGroup group : layout.getGroups()) {
if (packetReceiver.layoutData.view != null) {
for (ParentGroup group : packetReceiver.layoutData.view.getGroups()) {
PlayerSlot slot = group.getPlayerSlots().get((int) id.getLeastSignificantBits());
if (slot != null && slot.getPlayer() != null) return value;
}
@@ -96,6 +96,7 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
updatePlayerPlaceholders(task.getPlayerPlaceholderResults(), update);
Map<TabPlayer, Set<Refreshable>> forceUpdate = updateRelationalPlaceholders(task.getRelationalPlaceholderResults());
cpu.addTime(getFeatureName(), CpuUsageCategory.PLACEHOLDER_SAVE, System.nanoTime() - time);
cpu.addPlaceholderTimes(task.getUsedTime());
refreshFeatures(forceUpdate, update);
}
@@ -338,28 +339,50 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
return "Other";
}
/**
* Returns {@code true} if placeholder is registered, {@code false} if not.
*
* @param identifier
* Placeholder to check
* @return {@code true} if placeholder is registered, {@code false} if not
*/
public boolean isPlaceholderRegistered(@NotNull String identifier) {
return registeredPlaceholders.containsKey(identifier);
}
@Override
@NotNull
public String getFeatureName() {
return "Refreshing placeholders";
}
// ------------------
// API Implementation
// ------------------
@Override
public @NotNull ServerPlaceholderImpl registerServerPlaceholder(@NonNull String identifier, int refresh, @NonNull Supplier<Object> supplier) {
ensureActive();
return registerPlaceholder(new ServerPlaceholderImpl(identifier, refresh, supplier));
}
@Override
public @NotNull PlayerPlaceholderImpl registerPlayerPlaceholder(@NonNull String identifier, int refresh, @NonNull Function<me.neznamy.tab.api.TabPlayer, Object> function) {
public @NotNull PlayerPlaceholderImpl registerPlayerPlaceholder(@NonNull String identifier, int refresh,
@NonNull Function<me.neznamy.tab.api.TabPlayer, Object> function) {
ensureActive();
return registerPlaceholder(new PlayerPlaceholderImpl(identifier, refresh, function));
}
@Override
public @NotNull RelationalPlaceholderImpl registerRelationalPlaceholder(
@NonNull String identifier, int refresh, @NonNull BiFunction<me.neznamy.tab.api.TabPlayer, me.neznamy.tab.api.TabPlayer, Object> function) {
ensureActive();
return registerPlaceholder(new RelationalPlaceholderImpl(identifier, refresh, function));
}
@Override
public @NotNull TabPlaceholder getPlaceholder(@NonNull String identifier) {
ensureActive();
TabPlaceholder p = (TabPlaceholder) registeredPlaceholders.get(identifier);
if (p == null) {
TabPlaceholderRegisterEvent event = new TabPlaceholderRegisterEvent(identifier);
@@ -385,30 +408,15 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
@Override
public void unregisterPlaceholder(@NonNull Placeholder placeholder) {
ensureActive();
unregisterPlaceholder(placeholder.getIdentifier());
}
@Override
public void unregisterPlaceholder(@NonNull String identifier) {
ensureActive();
registeredPlaceholders.remove(identifier);
placeholderUsage.remove(identifier);
recalculateUsedPlaceholders();
}
/**
* Returns {@code true} if placeholder is registered, {@code false} if not.
*
* @param identifier
* Placeholder to check
* @return {@code true} if placeholder is registered, {@code false} if not
*/
public boolean isPlaceholderRegistered(@NotNull String identifier) {
return registeredPlaceholders.containsKey(identifier);
}
@Override
@NotNull
public String getFeatureName() {
return "Refreshing placeholders";
}
}
@@ -9,7 +9,6 @@ import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.SimpleComponent;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.features.layout.LayoutManagerImpl;
import me.neznamy.tab.shared.features.layout.LayoutView;
import me.neznamy.tab.shared.features.layout.PlayerSlot;
import me.neznamy.tab.shared.features.redis.RedisSupport;
import me.neznamy.tab.shared.features.types.*;
@@ -46,7 +45,7 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
*/
public PlayerList() {
Condition disableCondition = Condition.getCondition(config().getString("tablist-name-formatting.disable-condition"));
disableChecker = new DisableChecker(getFeatureName(), disableCondition, this::onDisableConditionChange);
disableChecker = new DisableChecker(getFeatureName(), disableCondition, this::onDisableConditionChange, p -> p.disabledPlayerList);
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.PLAYER_LIST + "-Condition", disableChecker);
if (antiOverrideTabList) {
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(500, getFeatureName(), TabConstants.CpuUsageCategory.ANTI_OVERRIDE, () -> {
@@ -73,9 +72,8 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
*/
public UUID getTablistUUID(@NotNull TabPlayer p, @NotNull TabPlayer viewer) {
if (layoutManager != null) {
LayoutView layout = layoutManager.getViews().get(viewer);
if (layout != null) {
PlayerSlot slot = layout.getSlot(p);
if (viewer.layoutData.view != null) {
PlayerSlot slot = viewer.layoutData.view.getSlot(p);
if (slot != null) {
return slot.getUniqueId();
}
@@ -111,7 +109,7 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
TabPlayer player = (TabPlayer) p;
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer.getVersion().getMinorVersion() < 8) continue;
if (!viewer.getTabList().containsEntry(player.getTablistId())) continue;
//if (!viewer.getTabList().containsEntry(player.getTablistId())) continue;
UUID tablistId = getTablistUUID(player, viewer);
viewer.getTabList().updateDisplayName(tablistId, format ? getTabFormat(player, viewer) :
tablistId.getMostSignificantBits() == 0 ? new SimpleComponent(player.getName()) : null);
@@ -146,7 +144,7 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
all.getTabList().setAntiOverride(antiOverrideTabList);
updateProperties(all);
if (disableChecker.isDisableConditionMet(all)) {
disableChecker.addDisabledPlayer(all);
all.disabledPlayerList.set(true);
} else {
if (redis != null) redis.updateTabFormat(all, all.getProperty(TabConstants.Property.TABPREFIX).get() + all.getProperty(TabConstants.Property.CUSTOMTABNAME).get() + all.getProperty(TabConstants.Property.TABSUFFIX).get());
}
@@ -154,8 +152,8 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer.getVersion().getMinorVersion() < 8) continue;
for (TabPlayer target : TAB.getInstance().getOnlinePlayers()) {
if (disableChecker.isDisabledPlayer(target)) continue;
if (!viewer.getTabList().containsEntry(target.getTablistId())) continue;
if (target.disabledPlayerList.get()) continue;
//if (!viewer.getTabList().containsEntry(target.getTablistId())) continue;
viewer.getTabList().updateDisplayName(getTablistUUID(target, viewer), getTabFormat(target, viewer));
}
}
@@ -167,8 +165,8 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer.getVersion().getMinorVersion() < 8) continue;
for (TabPlayer target : TAB.getInstance().getOnlinePlayers()) {
if (disableChecker.isDisabledPlayer(target)) continue;
if (!viewer.getTabList().containsEntry(target.getTablistId())) continue;
if (target.disabledPlayerList.get()) continue;
//if (!viewer.getTabList().containsEntry(target.getTablistId())) continue;
viewer.getTabList().updateDisplayName(getTablistUUID(target, target), null);
}
}
@@ -176,15 +174,17 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
@Override
public void onServerChange(@NotNull TabPlayer p, @NotNull String from, @NotNull String to) {
if (updateProperties(p) && !disableChecker.isDisabledPlayer(p)) updatePlayer(p, true);
if (updateProperties(p) && !p.disabledPlayerList.get()) updatePlayer(p, true);
if (TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.PIPELINE_INJECTION)) return;
TAB.getInstance().getCPUManager().runTaskLater(300, getFeatureName(), TabConstants.CpuUsageCategory.PLAYER_JOIN, () -> {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (!disableChecker.isDisabledPlayer(all) && p.getVersion().getMinorVersion() >= 8
&& p.getTabList().containsEntry(all.getTablistId()))
if (!all.disabledPlayerList.get() && p.getVersion().getMinorVersion() >= 8
//&& p.getTabList().containsEntry(all.getTablistId())
)
p.getTabList().updateDisplayName(getTablistUUID(all, p), getTabFormat(all, p));
if (all != p && !disableChecker.isDisabledPlayer(p) && all.getVersion().getMinorVersion() >= 8
&& all.getTabList().containsEntry(p.getTablistId()))
if (all != p && !p.disabledPlayerList.get() && all.getVersion().getMinorVersion() >= 8
//&& all.getTabList().containsEntry(p.getTablistId())
)
all.getTabList().updateDisplayName(getTablistUUID(p, all), getTabFormat(p, all));
}
});
@@ -192,7 +192,7 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
@Override
public void onWorldChange(@NotNull TabPlayer changed, @NotNull String from, @NotNull String to) {
if (updateProperties(changed) && !disableChecker.isDisabledPlayer(changed)) updatePlayer(changed, true);
if (updateProperties(changed) && !changed.disabledPlayerList.get()) updatePlayer(changed, true);
}
/**
@@ -220,7 +220,7 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
boolean suffix = refreshed.getProperty(TabConstants.Property.TABSUFFIX).update();
refresh = prefix || name || suffix;
}
if (disableChecker.isDisabledPlayer(refreshed)) return;
if (refreshed.disabledPlayerList.get()) return;
if (refresh) {
updatePlayer(refreshed, true);
}
@@ -237,7 +237,7 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
connectedPlayer.getTabList().setAntiOverride(antiOverrideTabList);
updateProperties(connectedPlayer);
if (disableChecker.isDisableConditionMet(connectedPlayer)) {
disableChecker.addDisabledPlayer(connectedPlayer);
connectedPlayer.disabledPlayerList.set(true);
return;
}
Runnable r = () -> {
@@ -257,10 +257,10 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
@Override
public void onVanishStatusChange(@NotNull TabPlayer player) {
if (player.isVanished() || disableChecker.isDisabledPlayer(player)) return;
if (player.isVanished() || player.disabledPlayerList.get()) return;
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer.getVersion().getMinorVersion() < 8) continue;
if (!viewer.getTabList().containsEntry(player.getTablistId())) continue;
//if (!viewer.getTabList().containsEntry(player.getTablistId())) continue;
viewer.getTabList().updateDisplayName(player.getTablistId(), getTabFormat(player, viewer));
}
}
@@ -277,6 +277,7 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
@Override
public void setPrefix(@NonNull me.neznamy.tab.api.TabPlayer player, @Nullable String prefix) {
ensureActive();
((TabPlayer)player).ensureLoaded();
((TabPlayer)player).getProperty(TabConstants.Property.TABPREFIX).setTemporaryValue(prefix);
updatePlayer(player, true);
@@ -284,6 +285,7 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
@Override
public void setName(@NonNull me.neznamy.tab.api.TabPlayer player, @Nullable String customName) {
ensureActive();
((TabPlayer)player).ensureLoaded();
((TabPlayer)player).getProperty(TabConstants.Property.CUSTOMTABNAME).setTemporaryValue(customName);
updatePlayer(player, true);
@@ -291,6 +293,7 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
@Override
public void setSuffix(@NonNull me.neznamy.tab.api.TabPlayer player, @Nullable String suffix) {
ensureActive();
((TabPlayer)player).ensureLoaded();
((TabPlayer)player).getProperty(TabConstants.Property.TABSUFFIX).setTemporaryValue(suffix);
updatePlayer(player, true);
@@ -298,36 +301,42 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
@Override
public String getCustomPrefix(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
((TabPlayer)player).ensureLoaded();
return ((TabPlayer)player).getProperty(TabConstants.Property.TABPREFIX).getTemporaryValue();
}
@Override
public String getCustomName(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
((TabPlayer)player).ensureLoaded();
return ((TabPlayer)player).getProperty(TabConstants.Property.CUSTOMTABNAME).getTemporaryValue();
}
@Override
public String getCustomSuffix(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
((TabPlayer)player).ensureLoaded();
return ((TabPlayer)player).getProperty(TabConstants.Property.TABSUFFIX).getTemporaryValue();
}
@Override
public @NotNull String getOriginalPrefix(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
((TabPlayer)player).ensureLoaded();
return ((TabPlayer)player).getProperty(TabConstants.Property.TABPREFIX).getOriginalRawValue();
}
@Override
public @NotNull String getOriginalName(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
((TabPlayer)player).ensureLoaded();
return ((TabPlayer)player).getProperty(TabConstants.Property.CUSTOMTABNAME).getOriginalRawValue();
}
@Override
public @NotNull String getOriginalSuffix(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
((TabPlayer)player).ensureLoaded();
return ((TabPlayer)player).getProperty(TabConstants.Property.TABSUFFIX).getOriginalRawValue();
}
@@ -48,7 +48,7 @@ public class YellowNumber extends TabFeature implements JoinListener, Loadable,
*/
public YellowNumber() {
Condition disableCondition = Condition.getCondition(config().getString("playerlist-objective.disable-condition"));
disableChecker = new DisableChecker(getFeatureName(), disableCondition, this::onDisableConditionChange);
disableChecker = new DisableChecker(getFeatureName(), disableCondition, this::onDisableConditionChange, p -> p.disabledYellowNumber);
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.YELLOW_NUMBER + "-Condition", disableChecker);
}
@@ -86,7 +86,7 @@ public class YellowNumber extends TabFeature implements JoinListener, Loadable,
loaded.setProperty(this, PROPERTY_VALUE, rawValue);
loaded.setProperty(this, PROPERTY_VALUE_FANCY, rawValueFancy);
if (disableChecker.isDisableConditionMet(loaded)) {
disableChecker.addDisabledPlayer(loaded);
loaded.disabledYellowNumber.set(true);
} else {
register(loaded);
}
@@ -102,7 +102,7 @@ public class YellowNumber extends TabFeature implements JoinListener, Loadable,
@Override
public void unload() {
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (disableChecker.isDisabledPlayer(p) || p.isBedrockPlayer()) continue;
if (p.disabledYellowNumber.get() || p.isBedrockPlayer()) continue;
p.getScoreboard().unregisterObjective(OBJECTIVE_NAME);
}
}
@@ -112,7 +112,7 @@ public class YellowNumber extends TabFeature implements JoinListener, Loadable,
connectedPlayer.setProperty(this, PROPERTY_VALUE, rawValue);
connectedPlayer.setProperty(this, PROPERTY_VALUE_FANCY, rawValueFancy);
if (disableChecker.isDisableConditionMet(connectedPlayer)) {
disableChecker.addDisabledPlayer(connectedPlayer);
connectedPlayer.disabledYellowNumber.set(true);
} else {
register(connectedPlayer);
}
@@ -163,7 +163,7 @@ public class YellowNumber extends TabFeature implements JoinListener, Loadable,
@Override
public void onLoginPacket(@NotNull TabPlayer p) {
if (disableChecker.isDisabledPlayer(p) || !p.isLoaded()) return;
if (p.disabledYellowNumber.get() || !p.isLoaded()) return;
register(p);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all.isLoaded()) setScore(p, all, getValueNumber(all), all.getProperty(PROPERTY_VALUE_FANCY).getFormat(p));
@@ -189,7 +189,7 @@ public class YellowNumber extends TabFeature implements JoinListener, Loadable,
* NumberFormat display of the score
*/
public void setScore(@NotNull TabPlayer viewer, @NotNull TabPlayer scoreHolder, int value, @NotNull String fancyValue) {
if (viewer.isBedrockPlayer() || disableChecker.isDisabledPlayer(viewer)) return;
if (viewer.isBedrockPlayer() || viewer.disabledYellowNumber.get()) return;
viewer.getScoreboard().setScore(
OBJECTIVE_NAME,
scoreHolder.getNickname(),
@@ -175,6 +175,36 @@ public class BossBarLine implements BossBar {
}
}
/**
* Resends bossbar to the player.
*
* @param player
* Player to resend bossbar to
*/
public void sendToPlayerRaw(@NotNull TabPlayer player) {
player.getBossBar().create(
uniqueId,
player.getProperty(propertyTitle).updateAndGet(),
parseProgress(player, player.getProperty(propertyProgress).updateAndGet())/100,
parseColor(player.getProperty(propertyColor).updateAndGet()),
parseStyle(player.getProperty(propertyStyle).updateAndGet())
);
}
/**
* Removes player from set of players.
*
* @param player
* Player to remove
*/
public void removePlayerRaw(@NotNull TabPlayer player) {
players.remove(player);
}
// ------------------
// API Implementation
// ------------------
@Override
public void setTitle(@NonNull String title) {
if (this.title.equals(title)) return;
@@ -234,28 +264,12 @@ public class BossBarLine implements BossBar {
public void addPlayer(@NonNull me.neznamy.tab.api.TabPlayer p) {
TabPlayer player = (TabPlayer) p;
if (players.contains(player)) return;
players.add(player);
player.setProperty(textRefresher, propertyTitle, title);
player.setProperty(progressRefresher, propertyProgress, progress);
player.setProperty(colorRefresher, propertyColor, color);
player.setProperty(styleRefresher, propertyStyle, style);
sendToPlayerRaw(player);
}
/**
* Resends bossbar to the player.
*
* @param player
* Player to resend bossbar to
*/
public void sendToPlayerRaw(@NotNull TabPlayer player) {
player.getBossBar().create(
uniqueId,
player.getProperty(propertyTitle).updateAndGet(),
parseProgress(player, player.getProperty(propertyProgress).updateAndGet())/100,
parseColor(player.getProperty(propertyColor).updateAndGet()),
parseStyle(player.getProperty(propertyStyle).updateAndGet())
);
players.add(player);
}
@Override
@@ -266,16 +280,6 @@ public class BossBarLine implements BossBar {
player.getBossBar().remove(uniqueId);
}
/**
* Removes player from set of players.
*
* @param player
* Player to remove
*/
public void removePlayerRaw(@NotNull TabPlayer player) {
players.remove(player);
}
@Override
@NotNull
public List<me.neznamy.tab.api.TabPlayer> getPlayers() {
@@ -283,7 +287,7 @@ public class BossBarLine implements BossBar {
}
@Override
public boolean containsPlayer(me.neznamy.tab.api.TabPlayer player) {
public boolean containsPlayer(@NonNull me.neznamy.tab.api.TabPlayer player) {
return players.contains((TabPlayer) player);
}
@@ -14,6 +14,7 @@ import me.neznamy.tab.shared.features.types.*;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@@ -46,8 +47,6 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager, Jo
//time when BossBar announce ends, used for placeholder
private long announceEndTime;
private final Set<me.neznamy.tab.api.TabPlayer> visiblePlayers = Collections.newSetFromMap(new WeakHashMap<>());
/**
* Constructs new instance and loads configuration
*/
@@ -84,7 +83,11 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager, Jo
@Override
public void load() {
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(TabConstants.Placeholder.COUNTDOWN, 100, () -> (announceEndTime - System.currentTimeMillis()) / 1000);
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(TabConstants.Placeholder.COUNTDOWN, 100, () -> {
long seconds = TimeUnit.MILLISECONDS.toSeconds(announceEndTime - System.currentTimeMillis());
if (seconds < 0) return 0;
return seconds;
});
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
onJoin(p);
}
@@ -166,12 +169,46 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager, Jo
}
@Override
public @NotNull BossBar createBossBar(@NonNull String title, float progress, @NonNull BarColor color, @NonNull BarStyle style) {
public void onLoginPacket(TabPlayer player) {
// Since 1.20.2, Login packet clears BossBars as well
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) {
player.getBossBar().unfreeze();
for (BossBar bar : lineValues) {
if (bar.containsPlayer(player)) {
((BossBarLine)bar).sendToPlayerRaw(player);
}
}
}
}
@Override
public void onQuit(@NotNull TabPlayer disconnectedPlayer) {
for (BossBar line : lineValues) {
((BossBarLine)line).removePlayerRaw(disconnectedPlayer);
}
}
@Override
@NotNull
public String getFeatureName() {
return "BossBar";
}
// ------------------
// API Implementation
// ------------------
@Override
@NotNull
public BossBar createBossBar(@NonNull String title, float progress, @NonNull BarColor color, @NonNull BarStyle style) {
ensureActive();
return createBossBar(title, String.valueOf(progress), color.toString(), style.toString());
}
@Override
public @NotNull BossBar createBossBar(@NonNull String title, @NonNull String progress, @NonNull String color, @NonNull String style) {
@NotNull
public BossBar createBossBar(@NonNull String title, @NonNull String progress, @NonNull String color, @NonNull String style) {
ensureActive();
UUID id = UUID.randomUUID();
BossBar bar = new BossBarLine(this, id.toString(), null, color, style, title, progress, true);
registeredBossBars.put(id.toString(), bar);
@@ -181,25 +218,29 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager, Jo
@Override
public BossBar getBossBar(@NonNull String name) {
ensureActive();
return registeredBossBars.get(name);
}
@Override
public void toggleBossBar(me.neznamy.tab.api.@NonNull TabPlayer player, boolean sendToggleMessage) {
ensureActive();
setBossBarVisible(player, !hasBossBarVisible(player), sendToggleMessage);
}
@Override
public boolean hasBossBarVisible(me.neznamy.tab.api.@NonNull TabPlayer player) {
return visiblePlayers.contains(player);
ensureActive();
return ((TabPlayer)player).bossbarData.visible;
}
@Override
public void setBossBarVisible(me.neznamy.tab.api.@NonNull TabPlayer p, boolean visible, boolean sendToggleMessage) {
ensureActive();
TabPlayer player = (TabPlayer) p;
if (visiblePlayers.contains(player) == visible) return;
if (player.bossbarData.visible == visible) return;
if (visible) {
visiblePlayers.add(player);
player.bossbarData.visible = true;
detectBossBarsAndSend(player);
if (sendToggleMessage) player.sendMessage(toggleOnMessage, true);
if (rememberToggleChoice) {
@@ -215,7 +256,7 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager, Jo
}
}
} else {
visiblePlayers.remove(player);
player.bossbarData.visible = false;
for (BossBar l : lineValues) {
l.removePlayer(player);
}
@@ -244,58 +285,44 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager, Jo
@Override
public void sendBossBarTemporarily(me.neznamy.tab.api.@NonNull TabPlayer player, @NonNull String bossBar, int duration) {
ensureActive();
if (!hasBossBarVisible(player)) return;
BossBar line = registeredBossBars.get(bossBar);
if (line == null) throw new IllegalArgumentException("No registered BossBar found with name " + bossBar);
TAB.getInstance().getCPUManager().runMeasuredTask(getFeatureName(), "Adding temporary BossBar", () -> line.addPlayer(player));
TAB.getInstance().getCPUManager().runTaskLater(duration*1000,
TAB.getInstance().getCPUManager().runTaskLater((int) TimeUnit.SECONDS.toMillis(duration),
getFeatureName(), "Removing temporary BossBar", () -> line.removePlayer(player));
}
@Override
public void announceBossBar(@NonNull String bossBar, int duration) {
ensureActive();
BossBar line = registeredBossBars.get(bossBar);
if (line == null) throw new IllegalArgumentException("No registered BossBar found with name " + bossBar);
List<TabPlayer> players = Arrays.stream(TAB.getInstance().getOnlinePlayers()).filter(
this::hasBossBarVisible).collect(Collectors.toList());
TAB.getInstance().getCPUManager().runMeasuredTask(getFeatureName(), "Adding announced BossBar", () -> {
announcedBossBars.add(line);
announceEndTime = System.currentTimeMillis() + duration* 1000L;
announceEndTime = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(duration);
for (TabPlayer all : players) {
if (((BossBarLine)line).isConditionMet(all)) line.addPlayer(all);
}
});
TAB.getInstance().getCPUManager().runTaskLater(duration*1000,
TAB.getInstance().getCPUManager().runTaskLater((int) TimeUnit.SECONDS.toMillis(duration),
getFeatureName(), "Removing announced BossBar", () -> {
for (TabPlayer all : players) {
line.removePlayer(all);
}
announcedBossBars.remove(line);
});
for (TabPlayer all : players) {
line.removePlayer(all);
}
announcedBossBars.remove(line);
});
}
@Override
public void onLoginPacket(TabPlayer player) {
// Since 1.20.2, Login packet clears BossBars as well
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) {
for (BossBar bar : lineValues) {
if (bar.containsPlayer(player)) {
((BossBarLine)bar).sendToPlayerRaw(player);
}
}
}
}
/**
* Class storing bossbar data for players.
*/
public static class PlayerData {
@Override
public void onQuit(@NotNull TabPlayer disconnectedPlayer) {
for (BossBar line : lineValues) {
((BossBarLine)line).removePlayerRaw(disconnectedPlayer);
}
}
@Override
@NotNull
public String getFeatureName() {
return "BossBar";
/** Whether player wishes to see boss bars or not */
public boolean visible;
}
}
@@ -29,7 +29,7 @@ public class FixedSlot extends TabFeature implements Refreshable {
@Override
public void refresh(@NotNull TabPlayer p, boolean force) {
if (!manager.getViews().containsKey(p) || manager.getViews().get(p).getPattern() != pattern ||
if (p.layoutData.view == null || p.layoutData.view.getPattern() != pattern ||
p.getVersion().getMinorVersion() < 8 || p.isBedrockPlayer()) return;
if (p.getProperty(skinProperty).update()) {
p.getTabList().removeEntry(id);
@@ -52,6 +52,7 @@ public class FixedSlot extends TabFeature implements Refreshable {
id,
manager.getDirection().getEntryName(viewer, slot),
manager.getSkinManager().getSkin(viewer.getProperty(skinProperty).updateAndGet()),
true,
ping,
0,
TabComponent.optimized(viewer.getProperty(propertyName).updateAndGet())
@@ -21,9 +21,8 @@ public class LayoutLatencyRefresher extends TabFeature implements Refreshable {
public void refresh(@NotNull TabPlayer p, boolean force) {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all.getVersion().getMinorVersion() < 8) continue;
LayoutView layout = manager.getViews().get(all);
if (layout == null) continue;
PlayerSlot slot = layout.getSlot(p);
if (all.layoutData.view == null) continue;
PlayerSlot slot = all.layoutData.view.getSlot(p);
if (slot == null) continue;
all.getTabList().updateLatency(slot.getUniqueId(), p.getPing());
}
@@ -17,7 +17,6 @@ import me.neznamy.tab.shared.config.file.ConfigurationFile;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.features.PlayerList;
import me.neznamy.tab.shared.features.layout.skin.SkinManager;
import me.neznamy.tab.shared.features.sorting.Sorting;
import me.neznamy.tab.shared.features.types.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -41,12 +40,8 @@ public class LayoutManagerImpl extends TabFeature implements LayoutManager, Join
}
}};
private final Map<String, LayoutPattern> layouts = loadLayouts();
private final WeakHashMap<TabPlayer, String> teamNames = new WeakHashMap<>();
private final Map<TabPlayer, String> sortedPlayers = Collections.synchronizedMap(new TreeMap<>(Comparator.comparing(teamNames::get)));
private final Sorting sorting = TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
private final Map<TabPlayer, String> sortedPlayers = Collections.synchronizedMap(new TreeMap<>(Comparator.comparing(p -> p.layoutData.sortingString)));
private PlayerList playerList;
private final WeakHashMap<TabPlayer, LayoutView> views = new WeakHashMap<>();
private final WeakHashMap<me.neznamy.tab.api.TabPlayer, LayoutPattern> forcedLayouts = new WeakHashMap<>();
private static boolean teamsEnabled;
@@ -105,15 +100,15 @@ public class LayoutManagerImpl extends TabFeature implements LayoutManager, Join
@Override
public void onJoin(@NotNull TabPlayer p) {
teamNames.put(p, sorting.getFullTeamName(p));
sortedPlayers.put(p, sorting.getFullTeamName(p));
p.layoutData.sortingString = p.sortingData.fullTeamName;
sortedPlayers.put(p, p.sortingData.fullTeamName);
LayoutPattern highest = getHighestLayout(p);
if (highest != null) {
LayoutView view = new LayoutView(this, highest, p);
view.send();
views.put(p, view);
p.layoutData.view = view;
}
views.values().forEach(LayoutView::tick);
tickAllLayouts();
// Unformat original entries for players who can see a layout to avoid spaces due to unparsed placeholders and such
if (highest == null) return;
@@ -125,24 +120,25 @@ public class LayoutManagerImpl extends TabFeature implements LayoutManager, Join
@Override
public void onQuit(@NotNull TabPlayer p) {
sortedPlayers.remove(p);
teamNames.remove(p);
views.remove(p);
views.values().forEach(LayoutView::tick);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all == p) continue;
if (all.layoutData.view != null) all.layoutData.view.tick();
}
}
@Override
public void refresh(@NotNull TabPlayer p, boolean force) {
LayoutPattern highest = getHighestLayout(p);
String highestName = highest == null ? null : highest.getName();
LayoutView current = views.get(p);
LayoutView current = p.layoutData.view;
String currentName = current == null ? null : current.getPattern().getName();
if (!Objects.equals(highestName, currentName)) {
if (current != null) current.destroy();
views.remove(p);
p.layoutData.view = null;
if (highest != null) {
LayoutView view = new LayoutView(this, highest, p);
view.send();
views.put(p, view);
p.layoutData.view = view;
}
}
}
@@ -163,11 +159,11 @@ public class LayoutManagerImpl extends TabFeature implements LayoutManager, Join
@Override
public void onVanishStatusChange(@NotNull TabPlayer p) {
views.values().forEach(LayoutView::tick);
tickAllLayouts();
}
private @Nullable LayoutPattern getHighestLayout(@NotNull TabPlayer p) {
if (forcedLayouts.containsKey(p)) return forcedLayouts.get(p);
if (p.layoutData.forcedLayout != null) return p.layoutData.forcedLayout;
for (LayoutPattern pattern : layouts.values()) {
if (pattern.isConditionMet(p)) return pattern;
}
@@ -180,9 +176,9 @@ public class LayoutManagerImpl extends TabFeature implements LayoutManager, Join
public void updateTeamName(@NotNull TabPlayer p, @NotNull String teamName) {
sortedPlayers.remove(p);
teamNames.put(p, teamName);
p.layoutData.sortingString = teamName;
sortedPlayers.put(p, teamName);
views.values().forEach(LayoutView::tick);
tickAllLayouts();
}
@Override
@@ -193,8 +189,16 @@ public class LayoutManagerImpl extends TabFeature implements LayoutManager, Join
@Override
public void onTabListClear(@NotNull TabPlayer player) {
LayoutView view = views.get(player);
if (view != null) view.send();
if (player.layoutData.view != null) player.layoutData.view.send();
}
/**
* Ticks layouts for all players.
*/
public void tickAllLayouts() {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all.layoutData.view != null) all.layoutData.view.tick();
}
}
// ------------------
@@ -203,21 +207,26 @@ public class LayoutManagerImpl extends TabFeature implements LayoutManager, Join
@Override
public Layout createNewLayout(String name) {
ensureActive();
return new LayoutPattern(this, name, Collections.emptyMap());
}
@Override
public void sendLayout(@NonNull me.neznamy.tab.api.TabPlayer player, @Nullable Layout layout) {
((TabPlayer)player).ensureLoaded();
forcedLayouts.put(player, (LayoutPattern) layout);
refresh((TabPlayer) player, false);
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
p.layoutData.forcedLayout = (LayoutPattern) layout;
refresh(p, false);
}
@Override
public void resetLayout(@NonNull me.neznamy.tab.api.TabPlayer player) {
((TabPlayer)player).ensureLoaded();
forcedLayouts.remove(player);
refresh((TabPlayer) player, false);
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
p.layoutData.forcedLayout = null;
refresh(p, false);
}
@RequiredArgsConstructor
@@ -244,4 +253,21 @@ public class LayoutManagerImpl extends TabFeature implements LayoutManager, Join
}
}
}
/**
* Class storing layout data for players.
*/
public static class PlayerData {
/** Merged string to sort players by */
public String sortingString;
/** Layout the player can currently see */
@Nullable
public LayoutView view;
/** Layout forced via API */
@Nullable
public LayoutPattern forcedLayout;
}
}
@@ -70,7 +70,7 @@ public class LayoutPattern extends TabFeature implements Refreshable, Layout {
@Override
public void refresh(@NotNull TabPlayer refreshed, boolean force) {
manager.getViews().values().forEach(LayoutView::tick);
manager.tickAllLayouts();
}
@Override
@@ -79,35 +79,44 @@ public class LayoutPattern extends TabFeature implements Refreshable, Layout {
return "Updating player groups";
}
@Override
@NotNull
public String getFeatureName() {
return manager.getFeatureName();
}
// ------------------
// API Implementation
// ------------------
@Override
public void addFixedSlot(int slot, @NonNull String text) {
ensureActive();
addFixedSlot(slot, text, manager.getDefaultSkin(slot), manager.getEmptySlotPing());
}
@Override
public void addFixedSlot(int slot, @NonNull String text, @NonNull String skin) {
ensureActive();
addFixedSlot(slot, text, skin, manager.getEmptySlotPing());
}
@Override
public void addFixedSlot(int slot, @NonNull String text, int ping) {
ensureActive();
addFixedSlot(slot, text, manager.getDefaultSkin(slot), ping);
}
@Override
public void addFixedSlot(int slot, @NonNull String text, @NonNull String skin, int ping) {
ensureActive();
fixedSlots.put(slot, new FixedSlot(manager, slot, this, manager.getUUID(slot), text,
"Layout-" + text + "-SLOT-" + slot, skin, "Layout-" + text + "-SLOT-" + slot+ "-skin", ping));
}
@Override
public void addGroup(@Nullable String condition, int[] slots) {
ensureActive();
addGroup(UUID.randomUUID().toString(), Condition.getCondition(condition), slots);
}
@Override
@NotNull
public String getFeatureName() {
return manager.getFeatureName();
}
}
@@ -53,6 +53,7 @@ public class LayoutView {
manager.getUUID(slot),
manager.getDirection().getEntryName(viewer, slot),
manager.getSkinManager().getDefaultSkin(slot),
true,
manager.getEmptySlotPing(),
0,
new SimpleComponent("")
@@ -15,7 +15,7 @@ public class ParentGroup {
@Getter private final int[] slots;
private final TabPlayer viewer;
@Getter private final Map<Integer, PlayerSlot> playerSlots = new HashMap<>();
@Getter final Map<TabPlayer, PlayerSlot> players = new HashMap<>();
@Getter private final Map<TabPlayer, PlayerSlot> players = new HashMap<>();
public ParentGroup(@NotNull LayoutView layout, @NotNull GroupPattern pattern, @NotNull TabPlayer viewer) {
this.layout = layout;
@@ -39,6 +39,7 @@ public class PlayerSlot {
uniqueId,
layout.getManager().getDirection().getEntryName(p, slot),
player.getSkin(),
true,
player.getPing(),
0,
playerList == null ? new SimpleComponent(player.getName()) : playerList.getTabFormat(player, p)
@@ -48,6 +49,7 @@ public class PlayerSlot {
uniqueId,
layout.getManager().getDirection().getEntryName(p, slot),
layout.getManager().getSkinManager().getDefaultSkin(slot),
true,
layout.getManager().getEmptySlotPing(),
0,
new SimpleComponent(text)
@@ -1,19 +1,15 @@
package me.neznamy.tab.shared.features.nametags;
import java.util.Objects;
import java.util.WeakHashMap;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.shared.placeholders.conditions.Condition;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.features.types.JoinListener;
import me.neznamy.tab.shared.features.types.Loadable;
import me.neznamy.tab.shared.features.types.Refreshable;
import me.neznamy.tab.shared.features.types.TabFeature;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.placeholders.conditions.Condition;
import me.neznamy.tab.shared.platform.TabPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Class managing collision rule for players.
@@ -22,33 +18,31 @@ import org.jetbrains.annotations.Nullable;
public class CollisionManager extends TabFeature implements JoinListener, Loadable, Refreshable {
private final NameTag nameTags;
private final String collisionRule = config().getString("scoreboard-teams.enable-collision", "true");
private final Condition refreshCondition = Condition.getCondition(collisionRule);
private final WeakHashMap<TabPlayer, Boolean> collision = new WeakHashMap<>();
private final WeakHashMap<me.neznamy.tab.api.TabPlayer, Boolean> forcedCollision = new WeakHashMap<>();
private final Condition refreshCondition = Condition.getCondition(config().getString("scoreboard-teams.enable-collision", "true"));
@Override
public void load() {
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.COLLISION, 500, p -> {
if (forcedCollision.containsKey(p)) return forcedCollision.get(p);
TabPlayer player = (TabPlayer) p;
if (player.teamData.forcedCollision != null) return player.teamData.forcedCollision;
boolean newCollision = !((TabPlayer)p).isDisguised() && refreshCondition.isMet((TabPlayer) p);
collision.put((TabPlayer) p, newCollision);
player.teamData.collisionRule = newCollision;
return newCollision;
});
addUsedPlaceholder(TabConstants.Placeholder.COLLISION);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
collision.put(all, true);
onJoin(all);
}
}
@Override
public void onJoin(@NotNull TabPlayer connectedPlayer) {
collision.put(connectedPlayer, refreshCondition.isMet(connectedPlayer));
connectedPlayer.teamData.collisionRule = refreshCondition.isMet(connectedPlayer);
}
@Override
public void refresh(@NotNull TabPlayer p, boolean force) {
if (nameTags.getDisableChecker().isDisabledPlayer(p)) return;
if (p.disabledNametags.get()) return;
nameTags.updateTeamData(p);
}
@@ -58,48 +52,6 @@ public class CollisionManager extends TabFeature implements JoinListener, Loadab
return "Updating collision";
}
/**
* Returns current collision rule for player.
*
* @param p
* Player to get collision rule of
* @return Current collision rule for player
*/
public boolean getCollision(@NotNull TabPlayer p) {
return forcedCollision.getOrDefault(p, collision.get(p));
}
/**
* Forces collision rule for player. {@code null} removes forced collision.
*
* @param player
* Player to force collision rule for
* @param collision
* Forced collision rule, {@code null} for resetting.
*/
public void setCollisionRule(@NotNull TabPlayer player, @Nullable Boolean collision) {
if (Objects.equals(forcedCollision.get(player), collision)) return;
if (collision == null) {
forcedCollision.remove(player);
} else {
forcedCollision.put(player, collision);
}
nameTags.updateTeamData(player);
}
/**
* Returns forced collision rule for specified player. If no collision
* rule is forced for the player, returns {@code null}.
*
* @param player
* Player to get forced collision rule of
* @return Forced collision rule of player or {@code null} if not forced
*/
@Nullable
public Boolean getCollisionRule(@NotNull TabPlayer player) {
return forcedCollision.get(player);
}
@Override
@NotNull
public String getFeatureName() {
@@ -11,7 +11,6 @@ import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.redis.RedisSupport;
import me.neznamy.tab.shared.features.sorting.Sorting;
import me.neznamy.tab.shared.features.types.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -25,39 +24,31 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
protected final boolean invisibleNameTags = config().getBoolean("scoreboard-teams.invisible-nametags", false);
private final boolean canSeeFriendlyInvisibles = config().getBoolean("scoreboard-teams.can-see-friendly-invisibles", false);
private final boolean antiOverride = config().getBoolean("scoreboard-teams.anti-override", true);
@Getter private final Sorting sorting = TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
@Getter private final CollisionManager collisionManager = new CollisionManager(this);
@Getter private final int teamOptions = canSeeFriendlyInvisibles ? 2 : 0;
private final Set<me.neznamy.tab.api.TabPlayer> hiddenNameTag = Collections.newSetFromMap(new WeakHashMap<>());
protected final Set<me.neznamy.tab.api.TabPlayer> teamHandlingPaused = Collections.newSetFromMap(new WeakHashMap<>());
protected final WeakHashMap<me.neznamy.tab.api.TabPlayer, Set<me.neznamy.tab.api.TabPlayer>> hiddenNameTagFor = new WeakHashMap<>();
protected final Set<me.neznamy.tab.api.TabPlayer> playersWithInvisibleNameTagView = Collections.newSetFromMap(new WeakHashMap<>());
@Getter private final DisableChecker disableChecker;
private RedisSupport redis;
// Key - Vanished player, Value = List of players who cannot see the player (UUIDs to prevent memory leak)
private final WeakHashMap<TabPlayer, List<UUID>> vanishedPlayers = new WeakHashMap<>();
public NameTag() {
Condition disableCondition = Condition.getCondition(config().getString("scoreboard-teams.disable-condition"));
disableChecker = new DisableChecker(getFeatureName(), disableCondition, this::onDisableConditionChange);
disableChecker = new DisableChecker(getFeatureName(), disableCondition, this::onDisableConditionChange, p -> p.disabledNametags);
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.NAME_TAGS + "-Condition", disableChecker);
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.NAME_TAGS_COLLISION, collisionManager);
if (!antiOverride) TAB.getInstance().getConfigHelper().startup().teamAntiOverrideDisabled();
}
@Override
public void load() {
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.NAME_TAGS_COLLISION, collisionManager);
collisionManager.load();
// RedisSupport is instantiated after NameTags, so must be loaded after
redis = TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.NAME_TAGS_VISIBILITY, new VisibilityRefresher(this));
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
all.getScoreboard().setAntiOverrideTeams(antiOverride);
updateProperties(all);
hiddenNameTagFor.put(all, new HashSet<>());
if (disableChecker.isDisableConditionMet(all)) {
disableChecker.addDisabledPlayer(all);
all.disabledNametags.set(true);
continue;
}
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagVisibility(all, true);
@@ -65,9 +56,9 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
for (TabPlayer target : TAB.getInstance().getOnlinePlayers()) {
if (target.isVanished() && !TAB.getInstance().getPlatform().canSee(viewer, target)) {
vanishedPlayers.computeIfAbsent(target, p -> new ArrayList<>()).add(viewer.getUniqueId());
target.teamData.vanishedFor.add(viewer.getUniqueId());
}
if (!disableChecker.isDisabledPlayer(target)) registerTeam(target, viewer);
if (!target.disabledNametags.get()) registerTeam(target, viewer);
}
}
}
@@ -76,15 +67,15 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
public void unload() {
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
for (TabPlayer target : TAB.getInstance().getOnlinePlayers()) {
if (hasTeamHandlingPaused(target) || disableChecker.isDisabledPlayer(target)) continue;
viewer.getScoreboard().unregisterTeam(sorting.getShortTeamName(target));
if (hasTeamHandlingPaused(target) || target.disabledNametags.get()) continue;
viewer.getScoreboard().unregisterTeam(target.sortingData.getShortTeamName());
}
}
}
@Override
public void refresh(@NotNull TabPlayer refreshed, boolean force) {
if (disableChecker.isDisabledPlayer(refreshed)) return;
if (refreshed.disabledNametags.get()) return;
boolean refresh;
if (force) {
updateProperties(refreshed);
@@ -107,22 +98,21 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
public void onJoin(@NotNull TabPlayer connectedPlayer) {
connectedPlayer.getScoreboard().setAntiOverrideTeams(antiOverride);
updateProperties(connectedPlayer);
hiddenNameTagFor.put(connectedPlayer, new HashSet<>());
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all == connectedPlayer) continue; //avoiding double registration
if (connectedPlayer.isVanished() && !TAB.getInstance().getPlatform().canSee(all, connectedPlayer)) {
vanishedPlayers.computeIfAbsent(connectedPlayer, p -> new ArrayList<>()).add(all.getUniqueId());
connectedPlayer.teamData.vanishedFor.add(all.getUniqueId());
}
if (all.isVanished() && !TAB.getInstance().getPlatform().canSee(connectedPlayer, all)) {
vanishedPlayers.computeIfAbsent(all, p -> new ArrayList<>()).add(connectedPlayer.getUniqueId());
all.teamData.vanishedFor.add(connectedPlayer.getUniqueId());
}
if (!disableChecker.isDisabledPlayer(all)) {
if (!all.disabledNametags.get()) {
registerTeam(all, connectedPlayer);
}
}
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagVisibility(connectedPlayer, true);
if (disableChecker.isDisableConditionMet(connectedPlayer)) {
disableChecker.addDisabledPlayer(connectedPlayer);
connectedPlayer.disabledNametags.set(true);
return;
}
registerTeam(connectedPlayer);
@@ -130,8 +120,8 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
@Override
public void onQuit(@NotNull TabPlayer disconnectedPlayer) {
if (!disableChecker.isDisabledPlayer(disconnectedPlayer) && !hasTeamHandlingPaused(disconnectedPlayer)) {
String teamName = sorting.getShortTeamName(disconnectedPlayer);
if (!disconnectedPlayer.disabledNametags.get() && !hasTeamHandlingPaused(disconnectedPlayer)) {
String teamName = disconnectedPlayer.sortingData.getShortTeamName();
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer == disconnectedPlayer) continue; //player who just disconnected
if (viewer.getScoreboard().containsTeam(teamName)) {
@@ -139,25 +129,21 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
}
}
}
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all == disconnectedPlayer) continue;
hiddenNameTagFor.getOrDefault(all, Collections.emptySet()).remove(disconnectedPlayer); //clearing memory from API method
}
}
@Override
public void onServerChange(@NonNull TabPlayer p, @NonNull String from, @NonNull String to) {
if (updateProperties(p) && !disableChecker.isDisabledPlayer(p)) updateTeamData(p);
if (updateProperties(p) && !p.disabledNametags.get()) updateTeamData(p);
}
@Override
public void onWorldChange(@NotNull TabPlayer changed, @NotNull String from, @NotNull String to) {
if (updateProperties(changed) && !disableChecker.isDisabledPlayer(changed)) updateTeamData(changed);
if (updateProperties(changed) && !changed.disabledNametags.get()) updateTeamData(changed);
}
public void onDisableConditionChange(TabPlayer p, boolean disabledNow) {
if (disabledNow) {
unregisterTeam(p, sorting.getShortTeamName(p));
unregisterTeam(p, p.sortingData.getShortTeamName());
} else {
registerTeam(p);
}
@@ -167,22 +153,22 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
updateTeamData(p, viewer);
}
if (redis != null) redis.updateTeam(p, sorting.getShortTeamName(p),
if (redis != null) redis.updateTeam(p, p.sortingData.getShortTeamName(),
p.getProperty(TabConstants.Property.TAGPREFIX).get(),
p.getProperty(TabConstants.Property.TAGSUFFIX).get(),
getTeamVisibility(p, p) ? NameVisibility.ALWAYS : NameVisibility.NEVER);
}
public void updateTeamData(@NonNull TabPlayer p, @NonNull TabPlayer viewer) {
if (!viewer.getScoreboard().containsTeam(sorting.getShortTeamName(p))) return;
if (!viewer.getScoreboard().containsTeam(p.sortingData.getShortTeamName())) return;
boolean visible = getTeamVisibility(p, viewer);
String prefix = p.getProperty(TabConstants.Property.TAGPREFIX).getFormat(viewer);
viewer.getScoreboard().updateTeam(
sorting.getShortTeamName(p),
p.sortingData.getShortTeamName(),
prefix,
p.getProperty(TabConstants.Property.TAGSUFFIX).getFormat(viewer),
visible ? NameVisibility.ALWAYS : NameVisibility.NEVER,
collisionManager.getCollision(p) ? CollisionRule.ALWAYS : CollisionRule.NEVER,
p.teamData.getCollisionRule() ? CollisionRule.ALWAYS : CollisionRule.NEVER,
teamOptions,
EnumChatFormat.lastColorsOf(prefix)
);
@@ -208,11 +194,11 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
if (!TAB.getInstance().getPlatform().canSee(viewer, p) && p != viewer) return;
String prefix = p.getProperty(TabConstants.Property.TAGPREFIX).getFormat(viewer);
viewer.getScoreboard().registerTeam(
sorting.getShortTeamName(p),
p.sortingData.getShortTeamName(),
prefix,
p.getProperty(TabConstants.Property.TAGSUFFIX).getFormat(viewer),
getTeamVisibility(p, viewer) ? NameVisibility.ALWAYS : NameVisibility.NEVER,
collisionManager.getCollision(p) ? CollisionRule.ALWAYS : CollisionRule.NEVER,
p.teamData.getCollisionRule() ? CollisionRule.ALWAYS : CollisionRule.NEVER,
Collections.singletonList(p.getNickname()),
teamOptions,
EnumChatFormat.lastColorsOf(prefix)
@@ -226,15 +212,15 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
}
public boolean getTeamVisibility(@NonNull TabPlayer p, @NonNull TabPlayer viewer) {
return !hasHiddenNameTag(p) && !hasHiddenNameTag(p, viewer) && !invisibleNameTags
&& !p.hasInvisibilityPotion() && !playersWithInvisibleNameTagView.contains(viewer);
if (viewer.getVersion().getMinorVersion() == 8 && p.hasInvisibilityPotion()) return false;
return !hasHiddenNameTag(p) && !hasHiddenNameTag(p, viewer) && !invisibleNameTags && !viewer.teamData.invisibleNameTagView;
}
@Override
public void onLoginPacket(TabPlayer player) {
if (!player.isLoaded()) return;
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (!disableChecker.isDisabledPlayer(all) && all.isLoaded()) registerTeam(all, player);
if (!all.disabledNametags.get() && all.isLoaded()) registerTeam(all, player);
}
}
@@ -244,16 +230,16 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer == player) continue;
if (!TAB.getInstance().getPlatform().canSee(viewer, player)) {
vanishedPlayers.computeIfAbsent(player, p -> new ArrayList<>()).add(viewer.getUniqueId());
viewer.getScoreboard().unregisterTeam(sorting.getShortTeamName(player));
player.teamData.vanishedFor.add(viewer.getUniqueId());
viewer.getScoreboard().unregisterTeam(player.sortingData.getShortTeamName());
}
}
} else {
for (UUID id : vanishedPlayers.getOrDefault(player, Collections.emptyList())) {
for (UUID id : player.teamData.vanishedFor) {
TabPlayer viewer = TAB.getInstance().getPlayer(id);
if (viewer != null) registerTeam(player, viewer);
}
vanishedPlayers.remove(player);
player.teamData.vanishedFor.clear();
}
}
@@ -269,84 +255,103 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
@Override
public void hideNameTag(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (!hiddenNameTag.add(player)) return;
updateTeamData(p);
if (!p.teamData.hiddenNameTag) {
p.teamData.hiddenNameTag = true;
updateTeamData(p);
}
}
@Override
public void hideNameTag(@NonNull me.neznamy.tab.api.TabPlayer player, @NonNull me.neznamy.tab.api.TabPlayer viewer) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (!hiddenNameTagFor.get(player).add(viewer)) return;
if (!p.teamData.hiddenNameTagFor.add((TabPlayer) viewer)) return;
updateTeamData(p, (TabPlayer) viewer);
}
@Override
public void showNameTag(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (!hiddenNameTag.remove(player)) return;
updateTeamData(p);
if (p.teamData.hiddenNameTag) {
p.teamData.hiddenNameTag = false;
updateTeamData(p);
}
}
@Override
public void showNameTag(@NonNull me.neznamy.tab.api.TabPlayer player, @NonNull me.neznamy.tab.api.TabPlayer viewer) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (!hiddenNameTagFor.get(player).remove(viewer)) return;
if (!p.teamData.hiddenNameTagFor.remove((TabPlayer) viewer)) return;
updateTeamData(p, (TabPlayer) viewer);
}
@Override
public boolean hasHiddenNameTag(@NonNull me.neznamy.tab.api.TabPlayer player) {
return hiddenNameTag.contains(player);
ensureActive();
return ((TabPlayer)player).teamData.hiddenNameTag;
}
@Override
public boolean hasHiddenNameTag(@NonNull me.neznamy.tab.api.TabPlayer player, @NonNull me.neznamy.tab.api.TabPlayer viewer) {
return hiddenNameTagFor.containsKey(player) && hiddenNameTagFor.get(player).contains(viewer);
ensureActive();
return ((TabPlayer)player).teamData.hiddenNameTagFor.contains((TabPlayer) viewer);
}
@Override
public void pauseTeamHandling(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (teamHandlingPaused.contains(player)) return;
if (!disableChecker.isDisabledPlayer(p)) unregisterTeam(p, sorting.getShortTeamName(p));
teamHandlingPaused.add(player); //adding after, so unregisterTeam method runs
if (p.teamData.teamHandlingPaused) return;
if (!p.disabledNametags.get()) unregisterTeam(p, p.sortingData.getShortTeamName());
p.teamData.teamHandlingPaused = true; //setting after, so unregisterTeam method runs
}
@Override
public void resumeTeamHandling(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (!teamHandlingPaused.remove(player)) return; //removing before, so registerTeam method runs
if (!disableChecker.isDisabledPlayer(p)) registerTeam(p);
if (!p.teamData.teamHandlingPaused) return;
p.teamData.teamHandlingPaused = false; //setting before, so registerTeam method runs
if (!p.disabledNametags.get()) registerTeam(p);
}
@Override
public boolean hasTeamHandlingPaused(@NonNull me.neznamy.tab.api.TabPlayer player) {
return teamHandlingPaused.contains(player);
ensureActive();
return ((TabPlayer)player).teamData.teamHandlingPaused;
}
@Override
public void setCollisionRule(@NonNull me.neznamy.tab.api.TabPlayer player, Boolean collision) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
collisionManager.setCollisionRule(p, collision);
if (Objects.equals(p.teamData.forcedCollision, collision)) return;
p.teamData.forcedCollision = collision;
updateTeamData(p);
}
@Override
public Boolean getCollisionRule(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
return collisionManager.getCollisionRule(p);
return p.teamData.forcedCollision;
}
@Override
public void setPrefix(@NonNull me.neznamy.tab.api.TabPlayer player, @Nullable String prefix) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
p.getProperty(TabConstants.Property.TAGPREFIX).setTemporaryValue(prefix);
@@ -355,6 +360,7 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
@Override
public void setSuffix(@NonNull me.neznamy.tab.api.TabPlayer player, @Nullable String suffix) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
p.getProperty(TabConstants.Property.TAGSUFFIX).setTemporaryValue(suffix);
@@ -363,6 +369,7 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
@Override
public String getCustomPrefix(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
return p.getProperty(TabConstants.Property.TAGPREFIX).getTemporaryValue();
@@ -370,6 +377,7 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
@Override
public String getCustomSuffix(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
return p.getProperty(TabConstants.Property.TAGSUFFIX).getTemporaryValue();
@@ -378,6 +386,7 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
@Override
@NonNull
public String getOriginalPrefix(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
return p.getProperty(TabConstants.Property.TAGPREFIX).getOriginalRawValue();
@@ -386,6 +395,7 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
@Override
@NonNull
public String getOriginalSuffix(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
return p.getProperty(TabConstants.Property.TAGSUFFIX).getOriginalRawValue();
@@ -393,15 +403,16 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
@Override
public void toggleNameTagVisibilityView(@NonNull me.neznamy.tab.api.TabPlayer p, boolean sendToggleMessage) {
ensureActive();
TabPlayer player = (TabPlayer) p;
if (playersWithInvisibleNameTagView.contains(player)) {
playersWithInvisibleNameTagView.remove(player);
if (player.teamData.invisibleNameTagView) {
player.teamData.invisibleNameTagView = false;
if (sendToggleMessage) player.sendMessage(TAB.getInstance().getConfiguration().getMessages().getNameTagsShown(), true);
} else {
playersWithInvisibleNameTagView.add(player);
player.teamData.invisibleNameTagView = true;
if (sendToggleMessage) player.sendMessage(TAB.getInstance().getConfiguration().getMessages().getNameTagsHidden(), true);
}
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagVisibility(player, !playersWithInvisibleNameTagView.contains(player));
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagVisibility(player, !player.teamData.invisibleNameTagView);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
updateTeamData(all, player);
}
@@ -409,6 +420,45 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
@Override
public boolean hasHiddenNameTagVisibilityView(@NonNull me.neznamy.tab.api.TabPlayer player) {
return playersWithInvisibleNameTagView.contains(player);
ensureActive();
return ((TabPlayer)player).teamData.invisibleNameTagView;
}
/**
* Class holding team data for players.
*/
public static class PlayerData {
/** Flag tracking whether this player has name tag hidden globally using API or not */
public boolean hiddenNameTag;
/** Players who should not see this player's name tag */
public final Set<TabPlayer> hiddenNameTagFor = Collections.newSetFromMap(new WeakHashMap<>());
/** Flag tracking whether team handling is paused or not */
public boolean teamHandlingPaused;
/** Flag tracking whether this player disabled nametags on all players or not */
public boolean invisibleNameTagView;
/** Players who this player is vanished for */
public final Set<UUID> vanishedFor = new HashSet<>();
/** Currently used collision rule */
public boolean collisionRule;
/** Forced collision rule using API */
@Nullable
public Boolean forcedCollision;
/**
* Returns current collision rule. If forced using API, the forced value is returned.
* Otherwise, value assigned internally based on configuration is returned.
*
* @return Current collision rule to use
*/
public boolean getCollisionRule() {
return forcedCollision != null ? forcedCollision : collisionRule;
}
}
}
@@ -21,7 +21,7 @@ public class VisibilityRefresher extends TabFeature implements Refreshable {
@Override
public void refresh(@NotNull TabPlayer p, boolean force) {
if (nameTags.getDisableChecker().isDisabledPlayer(p)) return;
if (p.disabledNametags.get()) return;
nameTags.updateTeamData(p);
}
@@ -15,34 +15,29 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiFunction;
@Getter
public abstract class NameTagX extends NameTag implements UnlimitedNameTagManager {
//config options
@Getter private final boolean disableOnBoats = config().getBoolean("scoreboard-teams.unlimited-nametag-mode.disable-on-boats", true);
@Getter private final List<String> dynamicLines = new ArrayList<>(config().getStringList("scoreboard-teams.unlimited-nametag-mode.dynamic-lines", Arrays.asList(TabConstants.Property.ABOVENAME, TabConstants.Property.NAMETAG, TabConstants.Property.BELOWNAME, "another")));
@Getter private final Map<String, Object> staticLines = config().getConfigurationSection("scoreboard-teams.unlimited-nametag-mode.static-lines");
@Getter private final boolean armorStandsAlwaysVisible = TAB.getInstance().getConfiguration().getSecretOption("scoreboard-teams.unlimited-nametag-mode.always-visible", false);
private final boolean disableOnBoats = config().getBoolean("scoreboard-teams.unlimited-nametag-mode.disable-on-boats", true);
private final List<String> dynamicLines = new ArrayList<>(config().getStringList("scoreboard-teams.unlimited-nametag-mode.dynamic-lines", Arrays.asList(TabConstants.Property.ABOVENAME, TabConstants.Property.NAMETAG, TabConstants.Property.BELOWNAME, "another")));
private final Map<String, Object> staticLines = config().getConfigurationSection("scoreboard-teams.unlimited-nametag-mode.static-lines");
private final boolean armorStandsAlwaysVisible = TAB.getInstance().getConfiguration().getSecretOption("scoreboard-teams.unlimited-nametag-mode.always-visible", false);
private final Set<me.neznamy.tab.api.TabPlayer> playersDisabledWithAPI = Collections.newSetFromMap(new WeakHashMap<>());
protected final Map<TabPlayer, ArmorStandManager> armorStandManagerMap = new WeakHashMap<>();
private final Set<TabPlayer> playersPreviewingNameTag = Collections.newSetFromMap(new WeakHashMap<>());
private final BiFunction<NameTagX, TabPlayer, ArmorStandManager> armorStandFunction;
@Getter private final DisableChecker unlimitedDisableChecker;
private final DisableChecker unlimitedDisableChecker;
protected NameTagX(@NonNull BiFunction<NameTagX, TabPlayer, ArmorStandManager> armorStandFunction) {
this.armorStandFunction = armorStandFunction;
Collections.reverse(dynamicLines);
Condition disableCondition = Condition.getCondition(config().getString("scoreboard-teams.unlimited-nametag-mode.disable-condition"));
unlimitedDisableChecker = new DisableChecker(getExtraFeatureName(), disableCondition, this::onUnlimitedDisableConditionChange);
unlimitedDisableChecker = new DisableChecker(getExtraFeatureName(), disableCondition, this::onUnlimitedDisableConditionChange, p -> p.disabledUnlimitedNametags);
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS + "-Condition", unlimitedDisableChecker);
}
public ArmorStandManager getArmorStandManager(@NonNull TabPlayer player) {
return armorStandManagerMap.get(player);
}
public boolean isPlayerDisabled(@NonNull TabPlayer p) {
return getDisableChecker().isDisabledPlayer(p) || unlimitedDisableChecker.isDisabledPlayer(p) || hasTeamHandlingPaused(p) || hasDisabledArmorStands(p);
return p.disabledNametags.get() || p.disabledUnlimitedNametags.get() || hasTeamHandlingPaused(p) || hasDisabledArmorStands(p);
}
@Override
@@ -52,7 +47,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNameTagManage
}
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
updateProperties(all);
armorStandManagerMap.put(all, armorStandFunction.apply(this, all));
all.unlimitedNametagData.armorStandManager = armorStandFunction.apply(this, all);
if (unlimitedDisableChecker.isDisableConditionMet(all)) {
addDisabledPlayer(all);
}
@@ -66,26 +61,26 @@ public abstract class NameTagX extends NameTag implements UnlimitedNameTagManage
if (unlimitedDisableChecker.isDisableConditionMet(connectedPlayer))
addDisabledPlayer(connectedPlayer);
super.onJoin(connectedPlayer);
armorStandManagerMap.put(connectedPlayer, armorStandFunction.apply(this, connectedPlayer));
connectedPlayer.unlimitedNametagData.armorStandManager = armorStandFunction.apply(this, connectedPlayer);
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagPreview(connectedPlayer, false);
}
public void addDisabledPlayer(@NotNull TabPlayer player) {
unlimitedDisableChecker.addDisabledPlayer(player);
player.disabledUnlimitedNametags.set(true);
}
@Override
public void refresh(@NotNull TabPlayer refreshed, boolean force) {
super.refresh(refreshed, force);
if (isPlayerDisabled(refreshed)) return;
getArmorStandManager(refreshed).refresh(force);
refreshed.unlimitedNametagData.armorStandManager.refresh(force);
}
@Override
public void unload() {
super.unload();
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
ArmorStandManager asm = getArmorStandManager(p);
ArmorStandManager asm = p.unlimitedNametagData.armorStandManager;
if (asm != null) {
asm.destroy();
} else {
@@ -95,25 +90,21 @@ public abstract class NameTagX extends NameTag implements UnlimitedNameTagManage
}
public void toggleNameTagPreview(TabPlayer player, boolean sendToggleMessage) {
if (playersPreviewingNameTag.contains(player)) {
if (player.unlimitedNametagData.previewing) {
setNameTagPreview(player, false);
if (sendToggleMessage) player.sendMessage(TAB.getInstance().getConfiguration().getMessages().getNametagPreviewOff(), true);
playersPreviewingNameTag.remove(player);
player.unlimitedNametagData.previewing = false;
} else {
setNameTagPreview(player, true);
if (sendToggleMessage) player.sendMessage(TAB.getInstance().getConfiguration().getMessages().getNametagPreviewOn(), true);
playersPreviewingNameTag.add(player);
player.unlimitedNametagData.previewing = true;
}
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagPreview(player, isPreviewingNameTag(player));
}
public boolean isPreviewingNameTag(@NonNull TabPlayer player) {
return playersPreviewingNameTag.contains(player);
TAB.getInstance().getPlaceholderManager().getTabExpansion().setNameTagPreview(player, player.unlimitedNametagData.previewing);
}
public void onUnlimitedDisableConditionChange(TabPlayer p, boolean disabledNow) {
if (!getDisableChecker().isDisabledPlayer(p)) updateTeamData(p);
getArmorStandManager(p).refresh(true);
if (!p.disabledNametags.get()) updateTeamData(p);
p.unlimitedNametagData.armorStandManager.refresh(true);
}
/**
@@ -140,7 +131,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNameTagManage
@Override
public boolean getTeamVisibility(@NonNull TabPlayer p, @NonNull TabPlayer viewer) {
if (p.hasInvisibilityPotion()) return false; //1.8.x client sided bug
if (playersWithInvisibleNameTagView.contains(viewer)) return false;
if (viewer.teamData.invisibleNameTagView) return false;
return isOnBoat(p) || isPlayerDisabled(p);
}
@@ -158,16 +149,18 @@ public abstract class NameTagX extends NameTag implements UnlimitedNameTagManage
@Override
public void hideNameTag(@NonNull me.neznamy.tab.api.TabPlayer player, @NonNull me.neznamy.tab.api.TabPlayer viewer) {
if (hiddenNameTagFor.get(player).contains(viewer)) return;
hiddenNameTagFor.get(player).add(viewer);
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (!p.teamData.hiddenNameTagFor.add((TabPlayer) viewer)) return;
updateTeamData((TabPlayer) player, (TabPlayer) viewer);
pauseArmorStands((TabPlayer) player);
}
@Override
public void showNameTag(@NonNull me.neznamy.tab.api.TabPlayer player, @NonNull me.neznamy.tab.api.TabPlayer viewer) {
if (!hiddenNameTagFor.get(player).contains(viewer)) return;
hiddenNameTagFor.get(player).remove(viewer);
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (!p.teamData.hiddenNameTagFor.remove((TabPlayer) viewer)) return;
updateTeamData((TabPlayer) player, (TabPlayer) viewer);
resumeArmorStands((TabPlayer) player);
}
@@ -186,40 +179,45 @@ public abstract class NameTagX extends NameTag implements UnlimitedNameTagManage
@Override
public void setPrefix(@NonNull me.neznamy.tab.api.TabPlayer player, String prefix) {
ensureActive();
super.setPrefix(player, prefix);
rebuildNameTagLine((TabPlayer) player);
getArmorStandManager((TabPlayer) player).refresh(true);
((TabPlayer) player).unlimitedNametagData.armorStandManager.refresh(true);
}
@Override
public void setSuffix(@NonNull me.neznamy.tab.api.TabPlayer player, String suffix) {
ensureActive();
super.setSuffix(player, suffix);
rebuildNameTagLine((TabPlayer) player);
getArmorStandManager((TabPlayer) player).refresh(true);
((TabPlayer) player).unlimitedNametagData.armorStandManager.refresh(true);
}
@Override
public void pauseTeamHandling(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (teamHandlingPaused.contains(player)) return;
if (!getDisableChecker().isDisabledPlayer(p)) unregisterTeam(p, getSorting().getShortTeamName(p));
teamHandlingPaused.add(player); //adding after, so unregisterTeam method runs
if (p.teamData.teamHandlingPaused) return;
if (!p.disabledNametags.get()) unregisterTeam(p, p.sortingData.getShortTeamName());
p.teamData.teamHandlingPaused = true; //setting after, so unregisterTeam method runs
pauseArmorStands(p);
}
@Override
public void resumeTeamHandling(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (!teamHandlingPaused.contains(player)) return;
teamHandlingPaused.remove(player); //removing before, so registerTeam method runs
if (!getDisableChecker().isDisabledPlayer(p)) registerTeam(p);
if (!p.teamData.teamHandlingPaused) return;
p.teamData.teamHandlingPaused = false; //setting before, so registerTeam method runs
if (!p.disabledNametags.get()) registerTeam(p);
resumeArmorStands(p);
}
@Override
public void toggleNameTagVisibilityView(@NonNull me.neznamy.tab.api.TabPlayer player, boolean sendToggleMessage) {
ensureActive();
super.toggleNameTagVisibilityView(player, sendToggleMessage);
updateNameTagVisibilityView((TabPlayer) player);
}
@@ -230,49 +228,55 @@ public abstract class NameTagX extends NameTag implements UnlimitedNameTagManage
@Override
public void disableArmorStands(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (playersDisabledWithAPI.contains(player)) return;
playersDisabledWithAPI.add(player);
if (p.unlimitedNametagData.disabledWithAPI) return;
p.unlimitedNametagData.disabledWithAPI = true;
pauseArmorStands(p);
updateTeamData(p);
}
@Override
public void enableArmorStands(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (!playersDisabledWithAPI.contains(player)) return;
playersDisabledWithAPI.remove(player);
if (!p.unlimitedNametagData.disabledWithAPI) return;
p.unlimitedNametagData.disabledWithAPI = false;
resumeArmorStands(p);
updateTeamData(p);
}
@Override
public boolean hasDisabledArmorStands(@NonNull me.neznamy.tab.api.TabPlayer player) {
return playersDisabledWithAPI.contains(player);
ensureActive();
return ((TabPlayer)player).unlimitedNametagData.disabledWithAPI;
}
@Override
public void setName(@NonNull me.neznamy.tab.api.TabPlayer player, @Nullable String customName) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
p.getProperty(TabConstants.Property.CUSTOMTAGNAME).setTemporaryValue(customName);
rebuildNameTagLine(p);
getArmorStandManager(p).refresh(true);
p.unlimitedNametagData.armorStandManager.refresh(true);
}
@Override
public void setLine(@NonNull me.neznamy.tab.api.TabPlayer player, @NonNull String line, @Nullable String value) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (!getDefinedLines().contains(line)) throw new IllegalArgumentException("\"" + line + "\" is not a defined line. Defined lines: " + getDefinedLines());
p.getProperty(line).setTemporaryValue(value);
getArmorStandManager(p).refresh(true);
p.unlimitedNametagData.armorStandManager.refresh(true);
}
@Override
public String getCustomName(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
return p.getProperty(TabConstants.Property.CUSTOMTAGNAME).getTemporaryValue();
@@ -280,6 +284,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNameTagManage
@Override
public String getCustomLineValue(@NonNull me.neznamy.tab.api.TabPlayer player, @NonNull String line) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
return p.getProperty(line).getTemporaryValue();
@@ -288,6 +293,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNameTagManage
@Override
@NotNull
public String getOriginalName(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
return p.getProperty(TabConstants.Property.CUSTOMTAGNAME).getOriginalRawValue();
@@ -296,6 +302,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNameTagManage
@Override
@NotNull
public String getOriginalLineValue(@NonNull me.neznamy.tab.api.TabPlayer player, @NonNull String line) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
return p.getProperty(line).getOriginalRawValue();
@@ -304,8 +311,27 @@ public abstract class NameTagX extends NameTag implements UnlimitedNameTagManage
@Override
@NotNull
public List<String> getDefinedLines() {
ensureActive();
List<String> lines = new ArrayList<>(dynamicLines);
lines.addAll(staticLines.keySet());
return lines;
}
/**
* Class storing unlimited nametag data for players.
*/
public static class PlayerData {
/** Armor stand manager */
public ArmorStandManager armorStandManager;
/** Whether player is previewing armor stands or not */
public boolean previewing;
/** Whether armor stands are disabled via API or not */
public boolean disabledWithAPI;
/** Whether player is riding a boat or not */
public boolean onBoat;
}
}
@@ -1,6 +1,10 @@
package me.neznamy.tab.shared.features.redis;
import lombok.*;
import me.neznamy.tab.shared.TabConstants.Permission;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.platform.TabList;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
@@ -10,16 +14,15 @@ import java.util.UUID;
*/
@Getter
@Setter
@AllArgsConstructor
public class RedisPlayer {
/** Tablist UUID of the player */
@NotNull
private UUID uniqueId;
private final UUID uniqueId;
/** Player's real name */
@NotNull
private String name;
private final String name;
/** Player's name as seen in game profile */
@NotNull
@@ -33,5 +36,60 @@ public class RedisPlayer {
private boolean vanished;
/** Whether player is staff or not */
private boolean staff;
private final boolean staff;
/** Belowname number for 1.20.2- */
private int belowNameNumber;
/** Belowname NumberFormat for 1.20.3+ */
private TabComponent belowNameFancy;
/** Player's skin for global playerlist */
private TabList.Skin skin;
/** Tablist display name */
private TabComponent tabFormat;
/** Scoreboard team name */
private String teamName;
/** Nametag prefix */
private String tagPrefix;
/** Nametag suffix */
private String tagSuffix;
/** Nametag visibility rule */
private Scoreboard.NameVisibility nameVisibility;
/** Playerlist objective number for 1.20.2- */
private int playerlistNumber;
/** Playerlist objective NumberFormat for 1.20.3+ */
private TabComponent playerlistFancy;
/**
* Constructs new instance with given parameters.
*
* @param uniqueId
* Player's tablist UUID
* @param name
* Player's real name
* @param nickname
* Player's nickname in game profile
* @param server
* Player's server
* @param vanished
* Whether player is vanished or not
* @param staff
* Whether player has {@link Permission#STAFF} permission or not
*/
public RedisPlayer(@NotNull UUID uniqueId, @NotNull String name, @NotNull String nickname, @NotNull String server, boolean vanished, boolean staff) {
this.uniqueId = uniqueId;
this.name = name;
this.nickname = nickname;
this.server = server;
this.vanished = vanished;
this.staff = staff;
}
}
@@ -14,18 +14,13 @@ import me.neznamy.tab.shared.features.redis.message.RedisMessage;
import me.neznamy.tab.shared.platform.TabPlayer;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.UUID;
import java.util.WeakHashMap;
public class RedisBelowName extends RedisFeature {
private final RedisSupport redisSupport;
@Getter private final BelowName belowName;
@Getter private final Map<RedisPlayer, Integer> values = new WeakHashMap<>();
@Getter private final Map<RedisPlayer, String> fancyValues = new WeakHashMap<>();
public RedisBelowName(@NotNull RedisSupport redisSupport, @NotNull BelowName belowName) {
this.redisSupport = redisSupport;
this.belowName = belowName;
@@ -38,9 +33,9 @@ public class RedisBelowName extends RedisFeature {
player.getScoreboard().setScore(
BelowName.OBJECTIVE_NAME,
redis.getNickname(),
values.get(redis),
redis.getBelowNameNumber(),
null, // Unused by this objective slot
TabComponent.optimized(fancyValues.get(redis))
redis.getBelowNameFancy()
);
}
}
@@ -51,9 +46,9 @@ public class RedisBelowName extends RedisFeature {
viewer.getScoreboard().setScore(
BelowName.OBJECTIVE_NAME,
player.getNickname(),
values.get(player),
player.getBelowNameNumber(),
null, // Unused by this objective slot
TabComponent.optimized(fancyValues.get(player))
player.getBelowNameFancy()
);
}
}
@@ -66,8 +61,8 @@ public class RedisBelowName extends RedisFeature {
@Override
public void read(@NotNull ByteArrayDataInput in, @NotNull RedisPlayer player) {
values.put(player, in.readInt());
fancyValues.put(player, in.readUTF());
player.setBelowNameNumber(in.readInt());
player.setBelowNameFancy(TabComponent.optimized(in.readUTF()));
}
@Override
@@ -101,8 +96,8 @@ public class RedisBelowName extends RedisFeature {
public void process(@NotNull RedisSupport redisSupport) {
RedisPlayer target = redisSupport.getRedisPlayers().get(playerId);
if (target == null) return; // Print warn?
values.put(target, value);
fancyValues.put(target, fancyValue);
target.setBelowNameNumber(value);
target.setBelowNameFancy(TabComponent.optimized(fancyValue));
onJoin(target);
}
}
@@ -5,7 +5,6 @@ import com.google.common.io.ByteArrayDataOutput;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.features.GlobalPlayerList;
import me.neznamy.tab.shared.features.redis.RedisPlayer;
import me.neznamy.tab.shared.features.redis.RedisSupport;
@@ -13,15 +12,11 @@ import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.platform.TabList;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.WeakHashMap;
@RequiredArgsConstructor
public class RedisGlobalPlayerList extends RedisFeature {
private final RedisSupport redisSupport;
private final GlobalPlayerList globalPlayerList;
private final Map<RedisPlayer, TabList.Skin> skins = new WeakHashMap<>();
@Override
public void onJoin(@NotNull TabPlayer player) {
@@ -84,7 +79,7 @@ public class RedisGlobalPlayerList extends RedisFeature {
if (in.readBoolean()) {
signature = in.readUTF();
}
skins.put(player, new TabList.Skin(value, signature));
player.setSkin(new TabList.Skin(value, signature));
}
}
@@ -99,10 +94,9 @@ public class RedisGlobalPlayerList extends RedisFeature {
return globalPlayerList.getServerGroup(viewer.getServer()).equals(globalPlayerList.getServerGroup(target.getServer()));
}
private @NotNull TabList.Entry getEntry(@NotNull RedisPlayer player) {
return new TabList.Entry(player.getUniqueId(), player.getNickname(), skins.get(player), 0, 0,
redisSupport.getRedisPlayerList() == null ? null :
TabComponent.optimized(redisSupport.getRedisPlayerList().getFormat(player)));
@NotNull
private TabList.Entry getEntry(@NotNull RedisPlayer player) {
return new TabList.Entry(player.getUniqueId(), player.getNickname(), player.getSkin(), true, 0, 0, player.getTabFormat());
}
@Override
@@ -21,7 +21,6 @@ public class RedisPlayerList extends RedisFeature {
private final RedisSupport redisSupport;
@Getter private final PlayerList playerList;
private final Map<RedisPlayer, String> values = new WeakHashMap<>();
public RedisPlayerList(@NotNull RedisSupport redisSupport, @NotNull PlayerList playerList) {
this.redisSupport = redisSupport;
@@ -33,7 +32,7 @@ public class RedisPlayerList extends RedisFeature {
public void onJoin(@NotNull TabPlayer player) {
if (player.getVersion().getMinorVersion() < 8) return;
for (RedisPlayer redis : redisSupport.getRedisPlayers().values()) {
player.getTabList().updateDisplayName(redis.getUniqueId(), TabComponent.optimized(values.get(redis)));
player.getTabList().updateDisplayName(redis.getUniqueId(), redis.getTabFormat());
}
}
@@ -41,7 +40,7 @@ public class RedisPlayerList extends RedisFeature {
public void onJoin(@NotNull RedisPlayer player) {
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer.getVersion().getMinorVersion() < 8) continue;
viewer.getTabList().updateDisplayName(player.getUniqueId(), TabComponent.optimized(values.get(player)));
viewer.getTabList().updateDisplayName(player.getUniqueId(), player.getTabFormat());
}
}
@@ -59,11 +58,7 @@ public class RedisPlayerList extends RedisFeature {
@Override
public void read(@NotNull ByteArrayDataInput in, @NotNull RedisPlayer player) {
values.put(player, in.readUTF());
}
public String getFormat(@NotNull RedisPlayer player) {
return values.get(player);
player.setTabFormat(TabComponent.optimized(in.readUTF()));
}
@Override
@@ -71,7 +66,7 @@ public class RedisPlayerList extends RedisFeature {
if (player.isVanished()) return;
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer.getVersion().getMinorVersion() < 8) continue;
viewer.getTabList().updateDisplayName(player.getUniqueId(), TabComponent.optimized(getFormat(player)));
viewer.getTabList().updateDisplayName(player.getUniqueId(), player.getTabFormat());
}
}
@@ -98,7 +93,7 @@ public class RedisPlayerList extends RedisFeature {
public void process(@NotNull RedisSupport redisSupport) {
RedisPlayer target = redisSupport.getRedisPlayers().get(playerId);
if (target == null) return; // Print warn?
values.put(target, format);
target.setTabFormat(TabComponent.optimized(format));
onJoin(target);
}
}
@@ -24,10 +24,6 @@ public class RedisTeams extends RedisFeature {
private final RedisSupport redisSupport;
private final NameTag nameTags;
private final Map<RedisPlayer, String> teamNames = new WeakHashMap<>();
private final Map<RedisPlayer, String> prefixes = new WeakHashMap<>();
private final Map<RedisPlayer, String> suffixes = new WeakHashMap<>();
private final Map<RedisPlayer, NameVisibility> nameVisibilities = new WeakHashMap<>();
public RedisTeams(@NotNull RedisSupport redisSupport, @NotNull NameTag nameTags) {
this.redisSupport = redisSupport;
@@ -38,31 +34,31 @@ public class RedisTeams extends RedisFeature {
@Override
public void onJoin(@NotNull TabPlayer player) {
for (RedisPlayer redis : redisSupport.getRedisPlayers().values()) {
player.getScoreboard().registerTeam(teamNames.get(redis), prefixes.get(redis), suffixes.get(redis),
nameVisibilities.get(redis), CollisionRule.ALWAYS,
Collections.singletonList(redis.getNickname()), 2, EnumChatFormat.lastColorsOf(prefixes.get(redis)));
player.getScoreboard().registerTeam(redis.getTeamName(), redis.getTagPrefix(), redis.getTagSuffix(),
redis.getNameVisibility(), CollisionRule.ALWAYS,
Collections.singletonList(redis.getNickname()), 2, EnumChatFormat.lastColorsOf(redis.getTagPrefix()));
}
}
@Override
public void onJoin(@NotNull RedisPlayer player) {
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.getScoreboard().registerTeam(teamNames.get(player), prefixes.get(player), suffixes.get(player),
nameVisibilities.get(player), CollisionRule.ALWAYS,
Collections.singletonList(player.getNickname()), 2, EnumChatFormat.lastColorsOf(prefixes.get(player)));
viewer.getScoreboard().registerTeam(player.getTeamName(), player.getTagPrefix(), player.getTagSuffix(),
player.getNameVisibility(), CollisionRule.ALWAYS,
Collections.singletonList(player.getNickname()), 2, EnumChatFormat.lastColorsOf(player.getTagPrefix()));
}
}
@Override
public void onQuit(@NotNull RedisPlayer player) {
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.getScoreboard().unregisterTeam(teamNames.get(player));
viewer.getScoreboard().unregisterTeam(player.getTeamName());
}
}
@Override
public void write(@NotNull ByteArrayDataOutput out, @NotNull TabPlayer player) {
out.writeUTF(nameTags.getSorting().getShortTeamName(player));
out.writeUTF(player.sortingData.getShortTeamName());
out.writeUTF(player.getProperty(TabConstants.Property.TAGPREFIX).get());
out.writeUTF(player.getProperty(TabConstants.Property.TAGSUFFIX).get());
out.writeUTF((nameTags.getTeamVisibility(player, player) ? NameVisibility.ALWAYS : NameVisibility.NEVER).toString());
@@ -72,10 +68,10 @@ public class RedisTeams extends RedisFeature {
public void read(@NotNull ByteArrayDataInput in, @NotNull RedisPlayer player) {
String teamName = in.readUTF();
teamName = checkTeamName(player, teamName.substring(0, teamName.length()-1), 65);
teamNames.put(player, teamName);
prefixes.put(player, in.readUTF());
suffixes.put(player, in.readUTF());
nameVisibilities.put(player, NameVisibility.getByName(in.readUTF()));
player.setTeamName(teamName);
player.setTagPrefix(in.readUTF());
player.setTagSuffix(in.readUTF());
player.setNameVisibility(NameVisibility.getByName(in.readUTF()));
}
@Override
@@ -86,13 +82,13 @@ public class RedisTeams extends RedisFeature {
private @NotNull String checkTeamName(@NotNull RedisPlayer player, @NotNull String currentName15, int id) {
String potentialTeamName = currentName15 + (char)id;
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (nameTags.getSorting().getShortTeamName(all).equals(potentialTeamName)) {
if (all.sortingData.getShortTeamName().equals(potentialTeamName)) {
return checkTeamName(player, currentName15, id+1);
}
}
for (RedisPlayer all : redisSupport.getRedisPlayers().values()) {
if (all == player) continue;
if (teamNames.containsKey(all) && teamNames.get(all).equals(potentialTeamName)) {
if (potentialTeamName.equals(all.getTeamName())) {
return checkTeamName(player, currentName15, id+1);
}
}
@@ -131,11 +127,11 @@ public class RedisTeams extends RedisFeature {
public void process(@NotNull RedisSupport redisSupport) {
RedisPlayer target = redisSupport.getRedisPlayers().get(playerId);
if (target == null) return; // Print warn?
String oldTeamName = teamNames.get(target);
String oldTeamName = target.getTeamName();
String newTeamName = checkTeamName(target, teamName.substring(0, teamName.length()-1), 65);
teamNames.put(target, newTeamName);
prefixes.put(target, prefix);
suffixes.put(target, suffix);
target.setTeamName(newTeamName);
target.setTagPrefix(prefix);
target.setTagSuffix(suffix);
if (!oldTeamName.equals(newTeamName)) {
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.getScoreboard().unregisterTeam(oldTeamName);
@@ -14,18 +14,13 @@ import me.neznamy.tab.shared.features.redis.message.RedisMessage;
import me.neznamy.tab.shared.platform.TabPlayer;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.UUID;
import java.util.WeakHashMap;
public class RedisYellowNumber extends RedisFeature {
private final RedisSupport redisSupport;
@Getter private final YellowNumber yellowNumber;
@Getter private final Map<RedisPlayer, Integer> values = new WeakHashMap<>();
@Getter private final Map<RedisPlayer, String> fancyValues = new WeakHashMap<>();
public RedisYellowNumber(@NotNull RedisSupport redisSupport, @NotNull YellowNumber yellowNumber) {
this.redisSupport = redisSupport;
this.yellowNumber = yellowNumber;
@@ -38,9 +33,9 @@ public class RedisYellowNumber extends RedisFeature {
player.getScoreboard().setScore(
YellowNumber.OBJECTIVE_NAME,
redis.getNickname(),
values.get(redis),
redis.getPlayerlistNumber(),
null, // Unused by this objective slot
TabComponent.optimized(fancyValues.get(redis))
redis.getPlayerlistFancy()
);
}
}
@@ -51,9 +46,9 @@ public class RedisYellowNumber extends RedisFeature {
viewer.getScoreboard().setScore(
YellowNumber.OBJECTIVE_NAME,
player.getNickname(),
values.get(player),
player.getPlayerlistNumber(),
null, // Unused by this objective slot
TabComponent.optimized(fancyValues.get(player))
player.getPlayerlistFancy()
);
}
}
@@ -66,8 +61,8 @@ public class RedisYellowNumber extends RedisFeature {
@Override
public void read(@NotNull ByteArrayDataInput in, @NotNull RedisPlayer player) {
values.put(player, in.readInt());
fancyValues.put(player, in.readUTF());
player.setPlayerlistNumber(in.readInt());
player.setPlayerlistFancy(TabComponent.optimized(in.readUTF()));
}
@Override
@@ -101,8 +96,8 @@ public class RedisYellowNumber extends RedisFeature {
public void process(@NotNull RedisSupport redisSupport) {
RedisPlayer target = redisSupport.getRedisPlayers().get(playerId);
if (target == null) return; // Print warn?
values.put(target, value);
fancyValues.put(target, fancyValue);
target.setPlayerlistNumber(value);
target.setPlayerlistFancy(TabComponent.optimized(fancyValue));
onJoin(target);
}
}
@@ -150,19 +150,11 @@ public class ScoreboardImpl extends TabFeature implements me.neznamy.tab.api.sco
((ScoreboardLine)s).register(p);
}
players.add(p);
manager.getActiveScoreboards().put(p, this);
p.scoreboardData.activeScoreboard = this;
recalculateScores(p);
TAB.getInstance().getPlaceholderManager().getTabExpansion().setScoreboardName(p, name);
}
@Override
public void unregister() {
for (TabPlayer all : players.toArray(new TabPlayer[0])) {
removePlayer(all);
}
players.clear();
}
/**
* Unregisters player from this scoreboard.
*
@@ -176,7 +168,7 @@ public class ScoreboardImpl extends TabFeature implements me.neznamy.tab.api.sco
if (((ScoreboardLine)line).isShownTo(p))
p.getScoreboard().unregisterTeam(((ScoreboardLine)line).getTeamName());
}
manager.getActiveScoreboards().remove(p);
p.scoreboardData.activeScoreboard = null;
TAB.getInstance().getPlaceholderManager().getTabExpansion().setScoreboardName(p, "");
}
@@ -197,38 +189,6 @@ public class ScoreboardImpl extends TabFeature implements me.neznamy.tab.api.sco
return "Updating Scoreboard title";
}
@Override
public void setTitle(@NonNull String title) {
this.title = title;
for (TabPlayer p : players) {
p.setProperty(this, titleProperty, title);
refresh(p, false);
}
}
@Override
public void addLine(@NonNull String text) {
StableDynamicLine line = new StableDynamicLine(this, lines.size()+1, text);
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.scoreboardLine(name, lines.size()), line);
lines.add(line);
for (TabPlayer p : players) {
line.register(p);
recalculateScores(p);
}
}
@Override
public void removeLine(int index) {
if (index < 0 || index >= lines.size()) throw new IndexOutOfBoundsException("Index " + index + " is out of range (0 - " + (lines.size()-1) + ")");
ScoreboardLine line = (ScoreboardLine) lines.get(index);
lines.remove(line);
for (TabPlayer p : players) {
line.unregister(p);
recalculateScores(p);
}
TAB.getInstance().getFeatureManager().unregisterFeature(TabConstants.Feature.scoreboardLine(name, index));
}
/**
* Recalculate scores for each line if using numbers. This takes into
* consideration lines that are not visible.
@@ -271,4 +231,52 @@ public class ScoreboardImpl extends TabFeature implements me.neznamy.tab.api.sco
public String getFeatureName() {
return manager.getFeatureName();
}
// ------------------
// API Implementation
// ------------------
@Override
public void setTitle(@NonNull String title) {
ensureActive();
this.title = title;
for (TabPlayer p : players) {
p.setProperty(this, titleProperty, title);
refresh(p, false);
}
}
@Override
public void addLine(@NonNull String text) {
ensureActive();
StableDynamicLine line = new StableDynamicLine(this, lines.size()+1, text);
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.scoreboardLine(name, lines.size()), line);
lines.add(line);
for (TabPlayer p : players) {
line.register(p);
recalculateScores(p);
}
}
@Override
public void removeLine(int index) {
ensureActive();
if (index < 0 || index >= lines.size()) throw new IndexOutOfBoundsException("Index " + index + " is out of range (0 - " + (lines.size()-1) + ")");
ScoreboardLine line = (ScoreboardLine) lines.get(index);
lines.remove(line);
for (TabPlayer p : players) {
line.unregister(p);
recalculateScores(p);
}
TAB.getInstance().getFeatureManager().unregisterFeature(TabConstants.Feature.scoreboardLine(name, index));
}
@Override
public void unregister() {
ensureActive();
for (TabPlayer all : players.toArray(new TabPlayer[0])) {
removePlayer(all);
}
players.clear();
}
}
@@ -45,12 +45,6 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
@Nullable
private me.neznamy.tab.api.scoreboard.Scoreboard announcement;
private final Set<TabPlayer> joinDelayed = Collections.newSetFromMap(new WeakHashMap<>());
private final WeakHashMap<me.neznamy.tab.api.TabPlayer, ScoreboardImpl> forcedScoreboard = new WeakHashMap<>();
@Getter private final WeakHashMap<me.neznamy.tab.api.TabPlayer, ScoreboardImpl> activeScoreboards = new WeakHashMap<>();
private final Set<me.neznamy.tab.api.TabPlayer> visiblePlayers = Collections.newSetFromMap(new WeakHashMap<>());
@Getter private final WeakHashMap<TabPlayer, String> otherPluginScoreboards = new WeakHashMap<>();
@Override
public void load() {
Map<String, Map<String, Object>> map = config().getConfigurationSection("scoreboard.scoreboards");
@@ -85,8 +79,8 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
@Override
public void refresh(@NotNull TabPlayer p, boolean force) {
if (forcedScoreboard.containsKey(p) || !hasScoreboardVisible(p) ||
announcement != null || otherPluginScoreboards.containsKey(p) || joinDelayed.contains(p)) return;
if (p.scoreboardData.forcedScoreboard != null || !hasScoreboardVisible(p) ||
announcement != null || p.scoreboardData.otherPluginScoreboard != null || p.scoreboardData.joinDelayed) return;
sendHighestScoreboard(p);
}
@@ -108,10 +102,11 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
TAB.getInstance().getPlaceholderManager().getTabExpansion().setScoreboardName(connectedPlayer, "");
TAB.getInstance().getPlaceholderManager().getTabExpansion().setScoreboardVisible(connectedPlayer, false);
if (joinDelay > 0) {
joinDelayed.add(connectedPlayer);
connectedPlayer.scoreboardData.joinDelayed = true;
TAB.getInstance().getCPUManager().runTaskLater(joinDelay, getFeatureName(), TabConstants.CpuUsageCategory.PLAYER_JOIN, () -> {
if (!otherPluginScoreboards.containsKey(connectedPlayer)) setScoreboardVisible(connectedPlayer, hiddenByDefault == sbOffPlayers.contains(connectedPlayer.getName()), false);
joinDelayed.remove(connectedPlayer);
if (connectedPlayer.scoreboardData.otherPluginScoreboard == null)
setScoreboardVisible(connectedPlayer, hiddenByDefault == sbOffPlayers.contains(connectedPlayer.getName()), false);
connectedPlayer.scoreboardData.joinDelayed = false;
});
} else {
setScoreboardVisible(connectedPlayer, hiddenByDefault == sbOffPlayers.contains(connectedPlayer.getName()), false);
@@ -127,7 +122,7 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
public void sendHighestScoreboard(@NonNull TabPlayer p) {
if (!hasScoreboardVisible(p)) return;
ScoreboardImpl scoreboard = (ScoreboardImpl) detectHighestScoreboard(p);
ScoreboardImpl current = activeScoreboards.get(p);
ScoreboardImpl current = p.scoreboardData.activeScoreboard;
if (scoreboard != current) {
if (current != null) {
current.removePlayer(p);
@@ -145,9 +140,9 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
* player to unregister scoreboard to
*/
public void unregisterScoreboard(@NonNull TabPlayer p) {
if (activeScoreboards.containsKey(p)) {
activeScoreboards.get(p).removePlayer(p);
activeScoreboards.remove(p);
if (p.scoreboardData.activeScoreboard != null) {
p.scoreboardData.activeScoreboard.removePlayer(p);
p.scoreboardData.activeScoreboard = null;
}
}
@@ -159,7 +154,7 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
* @return highest scoreboard player should see
*/
public @Nullable me.neznamy.tab.api.scoreboard.Scoreboard detectHighestScoreboard(@NonNull TabPlayer p) {
if (forcedScoreboard.containsKey(p)) return forcedScoreboard.get(p);
if (p.scoreboardData.forcedScoreboard != null) return p.scoreboardData.forcedScoreboard;
for (me.neznamy.tab.api.scoreboard.Scoreboard sb : definedScoreboards) {
if (((ScoreboardImpl)sb).isConditionMet(p)) return sb;
}
@@ -179,8 +174,8 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
public void onDisplayObjective(@NotNull TabPlayer receiver, int slot, @NotNull String objective) {
if (respectOtherPlugins && slot == Scoreboard.DisplaySlot.SIDEBAR && !objective.equals(OBJECTIVE_NAME)) {
TAB.getInstance().debug("Player " + receiver.getName() + " received scoreboard called " + objective + ", hiding TAB one.");
otherPluginScoreboards.put(receiver, objective);
ScoreboardImpl sb = activeScoreboards.get(receiver);
receiver.scoreboardData.otherPluginScoreboard = objective;
ScoreboardImpl sb = receiver.scoreboardData.activeScoreboard;
if (sb != null) {
TAB.getInstance().getCPUManager().runMeasuredTask(getFeatureName(), TabConstants.CpuUsageCategory.SCOREBOARD_PACKET_CHECK, () -> sb.removePlayer(receiver));
}
@@ -189,32 +184,112 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
@Override
public void onObjective(@NotNull TabPlayer receiver, int action, @NotNull String objective) {
if (respectOtherPlugins && action == 1 && otherPluginScoreboards.containsKey(receiver) && otherPluginScoreboards.get(receiver).equals(objective)) {
if (respectOtherPlugins && action == Scoreboard.ObjectiveAction.UNREGISTER && objective.equals(receiver.scoreboardData.otherPluginScoreboard)) {
TAB.getInstance().debug("Player " + receiver.getName() + " no longer has another scoreboard, sending TAB one.");
otherPluginScoreboards.remove(receiver);
receiver.scoreboardData.otherPluginScoreboard = null;
TAB.getInstance().getCPUManager().runMeasuredTask(getFeatureName(), TabConstants.CpuUsageCategory.SCOREBOARD_PACKET_CHECK, () -> sendHighestScoreboard(receiver));
}
}
private void savePlayers() {
synchronized (sbOffPlayers) {
TAB.getInstance().getConfiguration().getPlayerDataFile().set("scoreboard-off", new ArrayList<>(sbOffPlayers));
}
}
@Override
public @NotNull me.neznamy.tab.api.scoreboard.Scoreboard createScoreboard(@NonNull String name, @NonNull String title, @NonNull List<String> lines) {
public void onQuit(@NotNull TabPlayer disconnectedPlayer) {
ScoreboardImpl sb = disconnectedPlayer.scoreboardData.activeScoreboard;
if (sb != null) {
sb.removePlayerFromSet(disconnectedPlayer);
}
}
@Override
public void onLoginPacket(TabPlayer player) {
if (!player.isLoaded()) return;
player.scoreboardData.otherPluginScoreboard = null;
ScoreboardImpl scoreboard = player.scoreboardData.activeScoreboard;
if (scoreboard != null) {
scoreboard.removePlayerFromSet(player);
scoreboard.addPlayer(player);
}
}
@Override
@NotNull
public String getFeatureName() {
return "Scoreboard";
}
// ------------------
// API Implementation
// ------------------
@Override
@NotNull
public me.neznamy.tab.api.scoreboard.Scoreboard createScoreboard(@NonNull String name, @NonNull String title, @NonNull List<String> lines) {
ensureActive();
me.neznamy.tab.api.scoreboard.Scoreboard sb = new ScoreboardImpl(this, name, title, lines, true);
registeredScoreboards.put(name, sb);
definedScoreboards = registeredScoreboards.values().toArray(new me.neznamy.tab.api.scoreboard.Scoreboard[0]);
return sb;
}
@Override
public void showScoreboard(@NonNull me.neznamy.tab.api.TabPlayer player, @NonNull me.neznamy.tab.api.scoreboard.Scoreboard scoreboard) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (p.scoreboardData.forcedScoreboard != null) {
p.scoreboardData.forcedScoreboard.removePlayer(p);
}
p.scoreboardData.forcedScoreboard = (ScoreboardImpl) scoreboard;
if (p.scoreboardData.activeScoreboard != null) {
p.scoreboardData.activeScoreboard.removePlayer(p);
p.scoreboardData.activeScoreboard = null;
}
if (hasScoreboardVisible(player)) ((ScoreboardImpl) scoreboard).addPlayer(p);
}
@Override
public boolean hasCustomScoreboard(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
return ((TabPlayer)player).scoreboardData.forcedScoreboard != null;
}
@Override
public void resetScoreboard(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (p.scoreboardData.forcedScoreboard != null) {
p.scoreboardData.forcedScoreboard.removePlayer(p);
p.scoreboardData.forcedScoreboard = null;
me.neznamy.tab.api.scoreboard.Scoreboard sb = detectHighestScoreboard(p);
if (sb == null) return; //no scoreboard available
p.scoreboardData.activeScoreboard = (ScoreboardImpl) sb;
((ScoreboardImpl) sb).addPlayer(p);
}
}
@Override
public boolean hasScoreboardVisible(@NonNull me.neznamy.tab.api.TabPlayer player) {
return visiblePlayers.contains(player);
ensureActive();
return ((TabPlayer)player).scoreboardData.visible;
}
@Override
public void setScoreboardVisible(@NonNull me.neznamy.tab.api.TabPlayer p, boolean visible, boolean sendToggleMessage) {
ensureActive();
TabPlayer player = (TabPlayer) p;
if (visiblePlayers.contains(player) == visible) return;
if (player.scoreboardData.visible == visible) return;
if (visible) {
visiblePlayers.add(player);
player.scoreboardData.visible = true;
sendHighestScoreboard(player);
if (sendToggleMessage) {
player.sendMessage(TAB.getInstance().getConfiguration().getMessages().getScoreboardOn(), true);
@@ -232,7 +307,7 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
}
}
} else {
visiblePlayers.remove(player);
player.scoreboardData.visible = false;
unregisterScoreboard(player);
if (sendToggleMessage) {
player.sendMessage(TAB.getInstance().getConfiguration().getMessages().getScoreboardOff(), true);
@@ -253,14 +328,15 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
TAB.getInstance().getPlaceholderManager().getTabExpansion().setScoreboardVisible(player, visible);
}
private void savePlayers() {
synchronized (sbOffPlayers) {
TAB.getInstance().getConfiguration().getPlayerDataFile().set("scoreboard-off", new ArrayList<>(sbOffPlayers));
}
@Override
public void toggleScoreboard(@NonNull me.neznamy.tab.api.TabPlayer player, boolean sendToggleMessage) {
ensureActive();
setScoreboardVisible(player, !((TabPlayer)player).scoreboardData.visible, sendToggleMessage);
}
@Override
public void announceScoreboard(@NonNull String scoreboard, int duration) {
ensureActive();
if (duration < 0) throw new IllegalArgumentException("Duration cannot be negative");
ScoreboardImpl sb = (ScoreboardImpl) registeredScoreboards.get(scoreboard);
if (sb == null) throw new IllegalArgumentException("No registered scoreboard found with name " + scoreboard);
@@ -269,93 +345,50 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
announcement = sb;
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (!hasScoreboardVisible(all)) continue;
previous.put(all, activeScoreboards.get(all));
if (activeScoreboards.containsKey(all)) activeScoreboards.get(all).removePlayer(all);
previous.put(all, all.scoreboardData.activeScoreboard);
if (all.scoreboardData.activeScoreboard != null) all.scoreboardData.activeScoreboard.removePlayer(all);
sb.addPlayer(all);
}
});
TAB.getInstance().getCPUManager().runTaskLater(duration*1000,
getFeatureName(), "Removing announced Scoreboard", () -> {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (!hasScoreboardVisible(all)) continue;
sb.removePlayer(all);
if (previous.get(all) != null) previous.get(all).addPlayer(all);
}
announcement = null;
});
}
@Override
public void onQuit(@NotNull TabPlayer disconnectedPlayer) {
ScoreboardImpl sb = activeScoreboards.get(disconnectedPlayer);
if (sb != null) {
sb.removePlayerFromSet(disconnectedPlayer);
}
}
@Override
public void onLoginPacket(TabPlayer player) {
if (!player.isLoaded()) return;
otherPluginScoreboards.remove(player);
ScoreboardImpl scoreboard = activeScoreboards.get(player);
if (scoreboard != null) {
scoreboard.removePlayerFromSet(player);
scoreboard.addPlayer(player);
}
}
@Override
@NotNull
public String getFeatureName() {
return "Scoreboard";
}
// ------------------
// API Implementation
// ------------------
@Override
public void showScoreboard(@NonNull me.neznamy.tab.api.TabPlayer player, @NonNull me.neznamy.tab.api.scoreboard.Scoreboard scoreboard) {
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
ScoreboardImpl forced = forcedScoreboard.put(player, (ScoreboardImpl) scoreboard);
if (forced != null) forced.removePlayer(p);
ScoreboardImpl active = activeScoreboards.remove(player);
if (active != null) active.removePlayer(p);
if (hasScoreboardVisible(player)) ((ScoreboardImpl) scoreboard).addPlayer(p);
}
@Override
public void resetScoreboard(@NonNull me.neznamy.tab.api.TabPlayer player) {
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
ScoreboardImpl forced = forcedScoreboard.remove(player);
if (forced == null) return;
forced.removePlayer(p);
me.neznamy.tab.api.scoreboard.Scoreboard sb = detectHighestScoreboard(p);
if (sb == null) return; //no scoreboard available
activeScoreboards.put(player, (ScoreboardImpl) sb);
((ScoreboardImpl) sb).addPlayer(p);
}
@Override
public boolean hasCustomScoreboard(@NonNull me.neznamy.tab.api.TabPlayer player) {
return forcedScoreboard.containsKey(player);
}
@Override
public void toggleScoreboard(@NonNull me.neznamy.tab.api.TabPlayer player, boolean sendToggleMessage) {
setScoreboardVisible(player, !visiblePlayers.contains(player), sendToggleMessage);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (!hasScoreboardVisible(all)) continue;
sb.removePlayer(all);
if (previous.get(all) != null) previous.get(all).addPlayer(all);
}
announcement = null;
});
}
@Override
@Nullable
public ScoreboardImpl getActiveScoreboard(@NonNull me.neznamy.tab.api.TabPlayer player) {
return activeScoreboards.get(player);
ensureActive();
return ((TabPlayer)player).scoreboardData.activeScoreboard;
}
/**
* Class storing scoreboard data of players.
*/
public static class PlayerData {
/** Flag tracking whether this player is under join delay or not */
public boolean joinDelayed;
/** Flag tracking whether player wishes to have scoreboard visible or not */
public boolean visible;
/** Scoreboard currently displayed to player */
@Nullable
public ScoreboardImpl activeScoreboard;
/** Forced scoreboard using API */
@Nullable
public ScoreboardImpl forcedScoreboard;
/** Scoreboard sent by another plugin (objective name) */
@Nullable
public String otherPluginScoreboard;
}
}
@@ -80,6 +80,7 @@ public class LongLine extends ScoreboardLine {
@Override
public void setText(@NonNull String text) {
ensureActive();
initializeText(text);
for (TabPlayer p : parent.getPlayers()) {
p.setProperty(this, textProperty, text);
@@ -129,6 +129,7 @@ public class StableDynamicLine extends ScoreboardLine {
@Override
public void setText(@NonNull String text) {
ensureActive();
initializeText(text);
for (TabPlayer p : parent.getPlayers()) {
p.setProperty(this, textProperty, text);
@@ -29,6 +29,7 @@ import me.neznamy.tab.shared.features.sorting.types.PlaceholderLowToHigh;
import me.neznamy.tab.shared.features.sorting.types.PlaceholderZtoA;
import me.neznamy.tab.shared.features.sorting.types.SortingType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Class for handling player sorting rules
@@ -47,12 +48,6 @@ public class Sorting extends TabFeature implements SortingManager, JoinListener,
//active sorting types
private final SortingType[] usedSortingTypes;
//team names and notes
private final WeakHashMap<TabPlayer, String> shortTeamNames = new WeakHashMap<>();
private final WeakHashMap<TabPlayer, String> fullTeamNames = new WeakHashMap<>();
private final WeakHashMap<TabPlayer, String> teamNameNotes = new WeakHashMap<>();
private final WeakHashMap<me.neznamy.tab.api.TabPlayer, String> forcedTeamName = new WeakHashMap<>();
/**
* Constructs new instance and loads config options
@@ -70,16 +65,16 @@ public class Sorting extends TabFeature implements SortingManager, JoinListener,
@Override
public void refresh(@NotNull TabPlayer p, boolean force) {
String previousShortName = shortTeamNames.get(p);
String previousShortName = p.sortingData.shortTeamName;
constructTeamNames(p);
if (!shortTeamNames.get(p).equals(previousShortName)) {
if (nameTags != null && getForcedTeamName(p) == null && !nameTags.hasTeamHandlingPaused(p) && !nameTags.getDisableChecker().isDisabledPlayer(p)) {
if (!p.sortingData.shortTeamName.equals(previousShortName)) {
if (nameTags != null && p.sortingData.forcedTeamName == null && !nameTags.hasTeamHandlingPaused(p) && !p.disabledNametags.get()) {
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.getScoreboard().unregisterTeam(previousShortName);
}
nameTags.registerTeam(p);
}
if (layout != null) layout.updateTeamName(p, fullTeamNames.get(p));
if (layout != null) layout.updateTeamName(p, p.sortingData.fullTeamName);
}
}
@@ -96,7 +91,7 @@ public class Sorting extends TabFeature implements SortingManager, JoinListener,
layout = TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.LAYOUT);
redis = TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
constructTeamNames(all);
onJoin(all);
}
}
@@ -131,7 +126,7 @@ public class Sorting extends TabFeature implements SortingManager, JoinListener,
* player to build team name for
*/
public void constructTeamNames(@NotNull TabPlayer p) {
teamNameNotes.put(p, "");
p.sortingData.teamNameNote = "";
StringBuilder shortName = new StringBuilder();
for (SortingType type : usedSortingTypes) {
shortName.append(type.getChars(p));
@@ -146,12 +141,12 @@ public class Sorting extends TabFeature implements SortingManager, JoinListener,
shortName.setLength(Limitations.TEAM_NAME_LENGTH-1);
}
String finalShortName = checkTeamName(p, shortName, 'A');
shortTeamNames.put(p, finalShortName);
fullTeamNames.put(p, fullName.append(finalShortName.charAt(finalShortName.length() - 1)).toString());
p.sortingData.shortTeamName = finalShortName;
p.sortingData.fullTeamName = fullName.append(finalShortName.charAt(finalShortName.length() - 1)).toString();
// Do not randomly override note
if (forcedTeamName.get(p) != null) {
teamNameNotes.put(p, "Set using API");
if (p.sortingData.forcedTeamName != null) {
p.sortingData.teamNameNote = "Set using API";
}
}
@@ -170,13 +165,13 @@ public class Sorting extends TabFeature implements SortingManager, JoinListener,
String potentialTeamName = currentName.toString() + (char)id;
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all == p) continue;
if (shortTeamNames.get(all) != null && shortTeamNames.get(all).equals(potentialTeamName)) {
if (potentialTeamName.equals(all.sortingData.shortTeamName)) {
return checkTeamName(p, currentName, id+1);
}
}
if (redis != null && redis.getRedisTeams() != null) {
for (RedisPlayer all : redis.getRedisPlayers().values()) {
if (redis.getRedisTeams().getTeamNames().get(all).equals(potentialTeamName)) {
if (all.getTeamName().equals(potentialTeamName)) {
return checkTeamName(p, currentName, id+1);
}
}
@@ -193,56 +188,6 @@ public class Sorting extends TabFeature implements SortingManager, JoinListener,
return Arrays.stream(usedSortingTypes).map(SortingType::getDisplayName).collect(Collectors.joining(" -> "));
}
/**
* Returns short team name of specified player.
*
* @param p
* Player to get short team name of
* @return short team name of specified player
*/
@NotNull
public String getShortTeamName(@NotNull TabPlayer p) {
String forced = getForcedTeamName(p);
if (forced != null) return forced;
return shortTeamNames.get(p);
}
/**
* Returns full team name of specified player.
*
* @param p
* Player to get full team name of
* @return full team name of specified player
*/
@NotNull
public String getFullTeamName(@NotNull TabPlayer p) {
return fullTeamNames.get(p);
}
/**
* Returns team name note for player.
*
* @param p
* Player to get team name note of
* @return team name note for player
*/
@NotNull
public String getTeamNameNote(@NotNull TabPlayer p) {
return teamNameNotes.get(p);
}
/**
* Sets team note for player.
*
* @param p
* Player to set team name note of
* @param note
* Team name note
*/
public void setTeamNameNote(@NotNull TabPlayer p, @NotNull String note) {
teamNameNotes.put(p, note);
}
@Override
@NotNull
public String getFeatureName() {
@@ -255,29 +200,64 @@ public class Sorting extends TabFeature implements SortingManager, JoinListener,
@Override
public void forceTeamName(@NonNull me.neznamy.tab.api.TabPlayer player, String name) {
((TabPlayer)player).ensureLoaded();
if (Objects.equals(forcedTeamName.get(player), name)) return;
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
if (Objects.equals(p.sortingData.forcedTeamName, name)) return;
if (name != null && name.length() > Limitations.TEAM_NAME_LENGTH) throw new IllegalArgumentException("Team name cannot be more than 16 characters long.");
if (name != null) setTeamNameNote((TabPlayer) player, "Set using API");
if (name != null) p.sortingData.teamNameNote = "Set using API";
NameTag nametag = TAB.getInstance().getNameTagManager();
if (nametag != null) nametag.unregisterTeam((TabPlayer) player, getShortTeamName((TabPlayer) player));
forcedTeamName.put(player, name);
if (nametag != null) nametag.registerTeam((TabPlayer) player);
if (layout != null) layout.updateTeamName((TabPlayer) player, fullTeamNames.get(player));
if (redis != null && nametag != null) redis.updateTeam((TabPlayer) player, getShortTeamName((TabPlayer) player),
((TabPlayer) player).getProperty(TabConstants.Property.TAGPREFIX).get(),
((TabPlayer) player).getProperty(TabConstants.Property.TAGSUFFIX).get(),
(nametag.getTeamVisibility((TabPlayer) player, (TabPlayer) player) ? Scoreboard.NameVisibility.ALWAYS : Scoreboard.NameVisibility.NEVER));
if (nametag != null) nametag.unregisterTeam(p, p.sortingData.getShortTeamName());
p.sortingData.forcedTeamName = name;
if (nametag != null) nametag.registerTeam(p);
if (layout != null) layout.updateTeamName(p, p.sortingData.fullTeamName);
if (redis != null && nametag != null) redis.updateTeam(p, p.sortingData.getShortTeamName(),
(p).getProperty(TabConstants.Property.TAGPREFIX).get(),
(p).getProperty(TabConstants.Property.TAGSUFFIX).get(),
(nametag.getTeamVisibility(p, p) ? Scoreboard.NameVisibility.ALWAYS : Scoreboard.NameVisibility.NEVER));
}
@Override
@Nullable
public String getForcedTeamName(@NonNull me.neznamy.tab.api.TabPlayer player) {
return forcedTeamName.get(player);
ensureActive();
return ((TabPlayer)player).sortingData.forcedTeamName;
}
@Override
public @NotNull String getOriginalTeamName(@NonNull me.neznamy.tab.api.TabPlayer player) {
@NotNull
public String getOriginalTeamName(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
((TabPlayer)player).ensureLoaded();
return shortTeamNames.get((TabPlayer) player);
return ((TabPlayer)player).sortingData.shortTeamName;
}
/**
* Class storing sorting data for players.
*/
public static class PlayerData {
/** Short team name (16 chars), used for teams */
public String shortTeamName;
/** Full sorting string, used for sorting in Layout (and maybe for 1.18+ in the future) */
public String fullTeamName;
/** Note explaining player's current team name */
public String teamNameNote;
/** Forced team name using API */
@Nullable
public String forcedTeamName;
/**
* Returns short team name. If forced using API, that value is returned.
*
* @return short team name to use
*/
@NotNull
public String getShortTeamName() {
return forcedTeamName != null ? forcedTeamName : shortTeamName;
}
}
}
@@ -36,10 +36,10 @@ public class Groups extends SortingType {
if (!sortedGroups.containsKey(group)) {
TAB.getInstance().getConfigHelper().runtime().groupNotInSortingList(sortedGroups.keySet(), group, p);
position = sortedGroups.size() + 1;
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> &cPrimary group (&e" + p.getGroup() + "&c) is not in sorting list. &r");
p.sortingData.teamNameNote += "\n-> &cPrimary group (&e" + p.getGroup() + "&c) is not in sorting list. &r";
} else {
position = sortedGroups.get(group);
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> Primary group (&e" + p.getGroup() + "&r) is &a#" + position + "&r in sorting list.");
p.sortingData.teamNameNote += "\n-> Primary group (&e" + p.getGroup() + "&r) is &a#" + position + "&r in sorting list.";
}
return String.valueOf((char) (position + 47));
}
@@ -46,9 +46,9 @@ public class Permissions extends SortingType {
for (String permission : sortedGroups.keySet()) {
if (p.hasPermission(permission)) {
position = sortedGroups.get(permission.toLowerCase());
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> Highest sorting permission: &e" + permission + " &a(#" + position + " in list). &r");
p.sortingData.teamNameNote += "\n-> Highest sorting permission: &e" + permission + " &a(#" + position + " in list). &r";
if (p.hasPermission(TabConstants.Permission.TEST_PERMISSION)) {
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "&cThis user appears to have all permissions. Are they OP? &r");
p.sortingData.teamNameNote += "&cThis user appears to have all permissions. Are they OP? &r";
}
break;
}
@@ -56,7 +56,7 @@ public class Permissions extends SortingType {
if (position == 0) {
TAB.getInstance().getConfigHelper().runtime().noPermissionFromSortingList(sortedGroups.keySet(), p);
position = sortedGroups.size()+1;
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> &cPlayer does not have any of the defined permissions. &r");
p.sortingData.teamNameNote += "\n-> &cPlayer does not have any of the defined permissions. &r";
}
return String.valueOf((char) (position + 47));
}
@@ -61,15 +61,15 @@ public class Placeholder extends SortingType {
@Override
public String getChars(@NotNull TabPlayer p) {
String output = EnumChatFormat.color(setPlaceholders(p));
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> " + sortingPlaceholder.getIdentifier() + " returned \"&e" + output + "&r\"");
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder.getIdentifier() + " returned \"&e" + output + "&r\"";
int position;
if (!sortingMap.containsKey(output.toLowerCase(Locale.US))) {
TAB.getInstance().getConfigHelper().runtime().valueNotInPredefinedValues(sortingPlaceholder.getIdentifier(), sortingMap.keySet(), output, p);
position = sortingMap.size()+1;
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "&c (not in list)&r. ");
p.sortingData.teamNameNote += "&c (not in list)&r. ";
} else {
position = sortingMap.get(output.toLowerCase(Locale.US));
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "&r &a(#" + position + " in list). &r");
p.sortingData.teamNameNote += "&r &a(#" + position + " in list). &r";
}
return String.valueOf((char) (position + 47));
}
@@ -24,8 +24,7 @@ public class PlaceholderAtoZ extends SortingType {
@Override
public String getChars(@NotNull TabPlayer p) {
String output = setPlaceholders(p);
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> " + sortingPlaceholder.getIdentifier() +
" returned \"&e" + output + "&r\". &r");
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder.getIdentifier() + " returned \"&e" + output + "&r\". &r";
return sorting.isCaseSensitiveSorting() ? output : output.toLowerCase();
}
}
@@ -24,8 +24,7 @@ public class PlaceholderHighToLow extends SortingType {
@Override
public String getChars(@NotNull TabPlayer p) {
String output = setPlaceholders(p);
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> " + sortingPlaceholder.getIdentifier() +
" returned \"&e" + output + "&r\". &r");
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder.getIdentifier() + " returned \"&e" + output + "&r\". &r";
return compressNumber(DEFAULT_NUMBER - parseDouble(sortingPlaceholder.getIdentifier(), output, 0, p));
}
}
@@ -24,8 +24,7 @@ public class PlaceholderLowToHigh extends SortingType {
@Override
public String getChars(@NotNull TabPlayer p) {
String output = setPlaceholders(p);
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> " + sortingPlaceholder.getIdentifier() +
" returned \"&e" + output + "&r\". &r");
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder.getIdentifier() + " returned \"&e" + output + "&r\". &r";
return compressNumber(DEFAULT_NUMBER + parseDouble(sortingPlaceholder.getIdentifier(), output, 0, p));
}
}
@@ -24,8 +24,7 @@ public class PlaceholderZtoA extends SortingType {
@Override
public String getChars(@NotNull TabPlayer p) {
char[] chars = setPlaceholders(p).toCharArray();
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> " + sortingPlaceholder.getIdentifier() +
" returned \"&e" + new String(chars) + "&r\". &r");
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder.getIdentifier() + " returned \"&e" + new String(chars) + "&r\". &r";
for (int i=0; i<chars.length; i++) {
char c = chars[i];
if (c >= 65 && c <= 90) {
@@ -80,27 +80,27 @@ public abstract class SortingType {
/**
* Compresses a number to ### format, where # is a character symbol representing
* a number in a base of 65536. The first two represent the whole part, the third one decimal part.
* a number in a base of 65534. The first two represent the whole part, the third one decimal part.
* The maximum number it will work properly with is {@link Integer#MAX_VALUE}.
*
* @param number
* Number to convert
* @return 3 characters long String of converted number with a base of 65536.
* @return 3 characters long String of converted number with a base of 65534.
*/
public String compressNumber(double number) {
int wholePart = (int) number;
char decimalChar = (char) ((number - wholePart) * (Character.MAX_VALUE - 1));
int base = Character.MAX_VALUE - 1;
char decimalChar = (char) ((number - wholePart) * base);
// The \ symbol breaks json syntax, skip it (and reduce range) (why is it not being escaped by json writer?)
if (decimalChar >= '\\') decimalChar++;
StringBuilder sb = new StringBuilder();
while (wholePart > 0) {
char digit = (char) (wholePart % (Character.MAX_VALUE - 1));
char digit = (char) (wholePart % base);
if (digit >= '\\') digit++;
sb.append(digit);
wholePart /= Character.MAX_VALUE;
sb.insert(0, digit);
wholePart /= base;
}
sb.reverse();
if (sb.length() == 1) sb.insert(0, (char) 0); // Avoid a single # if number is < 65535
if (sb.length() == 1) sb.insert(0, (char) 0); // Avoid a single # if number is < base
sb.append(decimalChar);
return sb.toString();
}
@@ -7,10 +7,9 @@ import me.neznamy.tab.shared.platform.TabPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.Function;
/**
* Class checking if disable-condition of each feature is met or not.
@@ -29,9 +28,8 @@ public class DisableChecker extends TabFeature implements Refreshable {
@NotNull
private final BiConsumer<TabPlayer, Boolean> action;
/** Players with the feature currently disabled */
@NotNull
private final Set<TabPlayer> disabledPlayers = Collections.newSetFromMap(new WeakHashMap<>());
private final Function<TabPlayer, AtomicBoolean> field;
/**
* Constructs new instance with given parameters.
@@ -42,11 +40,15 @@ public class DisableChecker extends TabFeature implements Refreshable {
* Configured condition for disabling the feature
* @param action
* Action to take on condition change
* @param field
* Function that returns field storing disable status
*/
public DisableChecker(@NotNull String featureName, @Nullable Condition disableCondition, @NotNull BiConsumer<TabPlayer, Boolean> action) {
public DisableChecker(@NotNull String featureName, @Nullable Condition disableCondition,
@NotNull BiConsumer<TabPlayer, Boolean> action, @NotNull Function<TabPlayer, AtomicBoolean> field) {
this.featureName = featureName;
this.disableCondition = disableCondition;
this.action = action;
this.field = field;
if (disableCondition != null) addUsedPlaceholder(TabConstants.Placeholder.condition(disableCondition.getName()));
}
@@ -54,37 +56,12 @@ public class DisableChecker extends TabFeature implements Refreshable {
public void refresh(@NotNull TabPlayer refreshed, boolean force) {
if (disableCondition == null) return;
boolean disabledNow = disableCondition.isMet(refreshed);
if (disabledNow == disabledPlayers.contains(refreshed)) return; // Condition result did not change, only placeholders inside
if (disabledNow) {
disabledPlayers.add(refreshed);
} else {
disabledPlayers.remove(refreshed);
}
AtomicBoolean value = field.apply(refreshed);
if (disabledNow == value.get()) return; // Condition result did not change, only placeholders inside
value.set(disabledNow);
action.accept(refreshed, disabledNow);
}
/**
* Returns {@code true} if the feature is disabled for specified player, {@code false} if not
*
* @param p
* Player to check
* @return {@code true} if player is disabled,
* {@code false} if not
*/
public boolean isDisabledPlayer(@NotNull TabPlayer p) {
return disabledPlayers.contains(p);
}
/**
* Adds specified player into set of disabled players
*
* @param p
* Player to add
*/
public void addDisabledPlayer(@NotNull TabPlayer p) {
disabledPlayers.add(p);
}
/**
* Returns {@code true} if disable condition is {@code null} or met,
* {@code false} if not met.
@@ -9,6 +9,23 @@ import org.jetbrains.annotations.NotNull;
*/
public abstract class TabFeature {
/** Flag tracking whether this instance is active or not */
private boolean active = true;
/**
* Marks this instance as no longer active.
*/
public void deactivate() {
active = false;
}
/**
* Throws {@link IllegalStateException} if this instance is no longer active.
*/
public void ensureActive() {
if (!active) throw new IllegalStateException("This instance got discarded because plugin was reloaded. Obtain a new instance.");
}
/**
* Returns name of this feature displayed in /tab cpu
*
@@ -24,6 +24,29 @@ public class AdventureHook {
private static final ComponentCache<Component, String> componentToString = new ComponentCache<>(1000,
(component, version) -> GsonComponentSerializer.gson().serialize(component));
/** Array of all 32 possible decoration combinations for fast access */
private static final EnumSet<TextDecoration>[] decorations = loadDecorations();
/**
* Loads decoration array.
*
* @return Decoration array with all possible options
*/
@SuppressWarnings("unchecked")
private static EnumSet<TextDecoration>[] loadDecorations() {
EnumSet<TextDecoration>[] decorations = new EnumSet[32];
for (int i=0; i<32; i++) {
EnumSet<TextDecoration> set = EnumSet.noneOf(TextDecoration.class);
if ((i & 1) > 0) set.add(TextDecoration.BOLD);
if ((i & 2) > 0) set.add(TextDecoration.ITALIC);
if ((i & 4) > 0) set.add(TextDecoration.OBFUSCATED);
if ((i & 8) > 0) set.add(TextDecoration.STRIKETHROUGH);
if ((i & 16) > 0) set.add(TextDecoration.UNDERLINED);
decorations[i] = set;
}
return decorations;
}
/**
* Converts component to adventure component
*
@@ -42,7 +65,7 @@ public class AdventureHook {
Component adventureComponent = Component.text(
iComponent.getText(),
convertColor(modifier.getColor(), modern),
getDecorations(modifier)
decorations[modifier.getMagicCodeBitMask()]
);
if (modifier.getFont() != null) {
@@ -88,22 +111,4 @@ public class AdventureHook {
return TextColor.color(color.getLegacyColor().getRgb());
}
}
/**
* Gets decorations from modifier.
*
* @param modifier
* Modifier to get decorations from
* @return Decorations from modifier
*/
private static Set<TextDecoration> getDecorations(@NotNull ChatModifier modifier) {
if (!modifier.hasMagicCodes()) return Collections.emptySet();
Set<TextDecoration> decorations = EnumSet.noneOf(TextDecoration.class);
if (modifier.isBold()) decorations.add(TextDecoration.BOLD);
if (modifier.isItalic()) decorations.add(TextDecoration.ITALIC);
if (modifier.isObfuscated()) decorations.add(TextDecoration.OBFUSCATED);
if (modifier.isStrikethrough()) decorations.add(TextDecoration.STRIKETHROUGH);
if (modifier.isUnderlined()) decorations.add(TextDecoration.UNDERLINED);
return decorations;
}
}
@@ -3,7 +3,6 @@ package me.neznamy.tab.shared.hook;
import com.viaversion.viaversion.api.Via;
import lombok.Getter;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.SimpleComponent;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.jetbrains.annotations.NotNull;
@@ -50,22 +49,4 @@ public class ViaVersionHook {
TAB.getInstance().debug("ViaVersion returned protocol version " + version + " for " + playerName);
return version;
}
/**
* Prints warn into console when ViaVersion is installed on BungeeCord.
* Surprisingly, the issue with wrong protocol version is not present on Velocity.
*/
public void printProxyWarn() {
if (!installed) return;
TAB.getInstance().getPlatform().logWarn(new SimpleComponent("Detected plugin ViaVersion, which when installed on BungeeCord acts like a" +
" client-sided protocol hack, making it impossible for TAB to properly detect player version and causing issues " +
"if client and server versions don't match, which include, but are not limited to:"));
TAB.getInstance().getPlatform().logWarn(new SimpleComponent("#1 - NameTag prefix/suffix being cut to 16 characters even for 1.13+ players"));
TAB.getInstance().getPlatform().logWarn(new SimpleComponent("#2 - Scoreboard lines being cut to 26 characters even for 1.13+ players"));
TAB.getInstance().getPlatform().logWarn(new SimpleComponent("#3 - Scoreboard lines being cut to 14 characters for <1.13"));
TAB.getInstance().getPlatform().logWarn(new SimpleComponent("#4 - Scoreboard lines might be out of order when using all 0s"));
TAB.getInstance().getPlatform().logWarn(new SimpleComponent("#5 - RGB colors will display as legacy colors even for 1.16+ players"));
TAB.getInstance().getPlatform().logWarn(new SimpleComponent("#6 - Layout entries will be in random order for 1.19.3+ players"));
TAB.getInstance().getPlatform().logWarn(new SimpleComponent("Please install ViaVersion on all backend servers instead."));
}
}
@@ -35,6 +35,9 @@ public class PlaceholderRefreshTask implements Runnable {
@Nullable
private Map<RelationalPlaceholderImpl, Map<TabPlayer, Map<TabPlayer, Object>>> relationalPlaceholderResults;
/** Time it took placeholders to retrieve value (in nanoseconds) */
private final Map<String, Long> usedTime = new HashMap<>();
@Override
public void run() {
TabPlayer[] players = TAB.getInstance().getOnlinePlayers();
@@ -74,7 +77,7 @@ public class PlaceholderRefreshTask implements Runnable {
if (relationalPlaceholderResults == null) relationalPlaceholderResults = new HashMap<>();
relationalPlaceholderResults.put(relationalPlaceholder, viewerMap);
}
TAB.getInstance().getCPUManager().addPlaceholderTime(placeholder.getIdentifier(), nanoTime);
usedTime.put(placeholder.getIdentifier(), nanoTime);
}
}
}
@@ -0,0 +1,13 @@
package me.neznamy.tab.shared.placeholders.expansion;
import java.util.HashMap;
import java.util.Map;
/**
* Class storing placeholders for plugin's PlaceholderAPI expansion.
*/
public class PlayerExpansionValues {
/** Map of placeholder values */
public final Map<String, String> values = new HashMap<>();
}
@@ -74,4 +74,19 @@ public interface BossBar {
* Unique identifier to match the bossbar with other functions
*/
void remove(@NotNull UUID id);
/**
* Freezes the class, not letting any packets through.
*/
default void freeze() {
// Empty by default
}
/**
* Unfreezes the class, enabling it back.
*/
default void unfreeze() {
// Empty by default
}
}
Loaded 100 of 112 files, more files were not shown because too many files have changed in this diff. Show more