Compare commits

...
68 Commits
Author SHA1 Message Date
NEZNAMY cf065ba27d 4.1.6 2024-06-13 19:19:54 +02:00
NEZNAMY f96e263bd7 Add 1.21 support 2024-06-13 19:19:41 +02:00
NEZNAMY a22f8eccaf Update message about unlimited nametags 2024-06-13 15:28:44 +02:00
NEZNAMY 718ee89211 [BungeeCord] Fix another issue leading to "Network Protocol Error" disconnect on 1.20.5+ 2024-06-13 11:33:04 +02:00
NEZNAMY 54b44ce6cd [Unlimited nametags] Print console warn if the feature is enabled 2024-06-13 11:30:26 +02:00
NEZNAMY 29da28e955 [BungeeCord] Fix error in pipeline injector (#1310) 2024-06-12 17:44:24 +02:00
NEZNAMY fa195389ab [Sorting] Fix sorting by pre-defined values space trimming inconsistency (#1308) 2024-06-11 15:39:39 +02:00
NEZNAMY 9e3864be53 [Bukkit] Add missing support for mojang mapped jars 2024-06-06 15:16:17 +02:00
NEZNAMY 29b89b3bb7 [Commands] Fix error when setting property value to ' or " 2024-06-01 16:50:02 +02:00
NEZNAMY e741608261 [Placeholders] Fix server placeholders not returning proper value on request sometimes 2024-05-30 16:10:38 +02:00
NEZNAMY 8e8a4cc9e8 [Teams] [Bukkit] Fix team action not being taken correctly on 1.8 - 1.12, causing issues with previous commit 2024-05-29 13:34:35 +02:00
NEZNAMY 3bb6e0c3f2 [Teams] Fix another instance of "Network Protocol Error" disconnect on 1.20.5+ 2024-05-28 14:40:17 +02:00
NEZNAMY 4c3b13a81c 4.1.6-SNAPSHOT 2024-05-28 14:40:04 +02:00
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 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
117 changed files with 2507 additions and 1253 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.4"
version = "4.1.6"
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(
@@ -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(
@@ -101,13 +97,13 @@ 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);
if (p == null) return "<Player is not loaded>";
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
@@ -29,8 +29,8 @@ public class ComponentConverter {
private final FunctionWithException<String, Object> newTextComponent;
private final BiFunction<ChatModifier, Boolean, Object> convertModifier;
private final Class<?> ChatModifier = BukkitReflection.getClass("network.chat.ChatModifier", "ChatModifier");
private final Class<Enum> EnumChatFormat = (Class<Enum>) BukkitReflection.getClass("EnumChatFormat");
private final Class<?> ChatModifier = BukkitReflection.getClass("network.chat.Style", "network.chat.ChatModifier", "ChatModifier");
private final Class<Enum> EnumChatFormat = (Class<Enum>) BukkitReflection.getClass("ChatFormatting", "EnumChatFormat");
private final Constructor<?> newChatModifier;
private final Method ChatBaseComponent_addSibling;
private final Field Component_modifier;
@@ -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.
@@ -50,30 +50,30 @@ public class ComponentConverter {
* If something failed
*/
private ComponentConverter() throws ReflectiveOperationException {
Class<?> IChatBaseComponent = BukkitReflection.getClass("network.chat.IChatBaseComponent", "IChatBaseComponent");
Class<?> IChatBaseComponent = BukkitReflection.getClass("network.chat.Component", "network.chat.IChatBaseComponent", "IChatBaseComponent");
if (BukkitReflection.getMinorVersion() >= 19) {
Method IChatBaseComponent_b = ReflectionUtils.getMethod(IChatBaseComponent, new String[] {"b", "literal"}, String.class);
newTextComponent = text -> IChatBaseComponent_b.invoke(null, text);
Class<?> IChatMutableComponent = BukkitReflection.getClass("network.chat.IChatMutableComponent", "IChatMutableComponent");
Class<?> IChatMutableComponent = BukkitReflection.getClass("network.chat.MutableComponent", "network.chat.IChatMutableComponent", "IChatMutableComponent");
Component_modifier = ReflectionUtils.getOnlyField(IChatMutableComponent, ChatModifier);
ChatBaseComponent_addSibling = ReflectionUtils.getOnlyMethod(IChatMutableComponent, IChatMutableComponent, IChatBaseComponent);
} else {
Class<?> ChatComponentText = BukkitReflection.getClass("network.chat.ChatComponentText", "ChatComponentText");
Class<?> ChatComponentText = BukkitReflection.getClass("network.chat.TextComponent", "network.chat.ChatComponentText", "ChatComponentText");
Constructor<?> newChatComponentText = ChatComponentText.getConstructor(String.class);
newTextComponent = newChatComponentText::newInstance;
Class<?> ChatBaseComponent = BukkitReflection.getClass("network.chat.ChatBaseComponent", "ChatBaseComponent");
Class<?> ChatBaseComponent = BukkitReflection.getClass("network.chat.BaseComponent", "network.chat.ChatBaseComponent", "ChatBaseComponent");
Component_modifier = ReflectionUtils.getOnlyField(ChatBaseComponent, ChatModifier);
ChatBaseComponent_addSibling = ReflectionUtils.getOnlyMethod(ChatComponentText, IChatBaseComponent, IChatBaseComponent);
}
if (BukkitReflection.getMinorVersion() >= 16) {
Class<?> chatHexColor = BukkitReflection.getClass("network.chat.ChatHexColor", "ChatHexColor");
Class<?> MinecraftKey = 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);
Class<?> chatHexColor = BukkitReflection.getClass("network.chat.TextColor", "network.chat.ChatHexColor", "ChatHexColor");
Class<?> ResourceLocation = BukkitReflection.getClass("resources.ResourceLocation", "resources.MinecraftKey", "MinecraftKey");
Class<?> chatClickable = BukkitReflection.getClass("network.chat.ClickEvent", "network.chat.ChatClickable", "ChatClickable");
Class<?> chatHoverable = BukkitReflection.getClass("network.chat.HoverEvent", "network.chat.ChatHoverable", "ChatHoverable");
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) {
@@ -16,6 +16,7 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
/**
* Class storing all team related fields and methods.
@@ -29,7 +30,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;
@@ -71,7 +71,12 @@ public class TeamPacketData {
emptyScoreboard = Scoreboard.getConstructor().newInstance();
newScoreboardTeam = scoreboardTeam.getConstructor(Scoreboard, String.class);
TeamPacket_NAME = ReflectionUtils.getFields(TeamPacketClass, String.class).get(0);
TeamPacket_ACTION = ReflectionUtils.getInstanceFields(TeamPacketClass, int.class).get(0);
List<Field> intFields = ReflectionUtils.getInstanceFields(TeamPacketClass, int.class);
if (minorVersion >= 8 && minorVersion <= 12) {
TeamPacket_ACTION = intFields.get(1);
} else {
TeamPacket_ACTION = intFields.get(0);
}
TeamPacket_PLAYERS = ReflectionUtils.getOnlyField(TeamPacketClass, Collection.class);
ScoreboardTeam_getPlayerNameSet = ReflectionUtils.getOnlyMethod(scoreboardTeam, Collection.class);
chatFormats = (Enum<?>[]) enumChatFormatClass.getMethod("values").invoke(null);
@@ -85,12 +90,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,
@@ -305,7 +308,7 @@ public class TeamPacketData {
public void onPacketSend(@NonNull TabPlayer player, @NonNull Object packet) {
if (!TeamPacketClass.isInstance(packet)) return;
int action = TeamPacket_ACTION.getInt(packet);
if (action == TeamAction.REMOVE || action == TeamAction.UPDATE) return;
if (action == TeamAction.UPDATE) return;
TeamPacket_PLAYERS.set(packet, player.getScoreboard().onTeamPacket(
action, (String) TeamPacket_NAME.get(packet), (Collection<String>) TeamPacket_PLAYERS.get(packet)));
}
@@ -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) {
+1 -1
View File
@@ -1,7 +1,7 @@
dependencies {
implementation(projects.shared)
implementation("org.bstats:bstats-bungeecord:3.0.1")
compileOnly("net.md-5:bungeecord-proxy:1.20-R0.2-SNAPSHOT")
compileOnly("net.md-5:bungeecord-proxy:1.20-R0.3-SNAPSHOT")
compileOnly("com.github.limework.redisbungee:RedisBungee-Bungee:0.11.0")
compileOnly("com.github.LeonMangler:PremiumVanishAPI:2.8.8")
}
@@ -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(() -> {
@@ -57,6 +59,7 @@ public class BungeeEventListener implements EventListener<ProxiedPlayer>, Listen
// Sending these packets before login packet will also crash the client on 1.20.3
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) {
player.getScoreboard().freeze();
player.getBossBar().freeze();
}
tab.getFeatureManager().onJoin(player);
@@ -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)) {
@@ -14,6 +14,8 @@ import net.md_5.bungee.protocol.packet.*;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Scoreboard handler for BungeeCord. Because it does not offer
@@ -158,8 +160,9 @@ public class BungeeScoreboard extends Scoreboard<BungeeTabPlayer, BaseComponent>
}
if (isAntiOverrideTeams() && packet instanceof Team) {
Team team = (Team) packet;
if (team.getMode() == TeamAction.REMOVE || team.getMode() == TeamAction.UPDATE) return;
team.setPlayers(onTeamPacket(team.getMode(), team.getName(), Lists.newArrayList(team.getPlayers())).toArray(new String[0]));
if (team.getMode() == TeamAction.UPDATE) return;
List<String> players = team.getPlayers() == null ? Collections.emptyList() : Lists.newArrayList(team.getPlayers());
team.setPlayers(onTeamPacket(team.getMode(), team.getName(), players).toArray(new String[0]));
}
}
}
@@ -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:1.21")
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.100.1+1.21"
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) -> {
@@ -4,7 +4,7 @@ import com.mojang.authlib.properties.Property;
import io.netty.channel.Channel;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.fabric.loader.Loader;
import me.neznamy.tab.platforms.fabric.loader.Loader_1_20_5;
import me.neznamy.tab.platforms.fabric.loader.Loader_1_21;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.backend.EntityData;
import me.neznamy.tab.shared.backend.Location;
@@ -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_21();
/** 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;
@@ -40,14 +42,14 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* Method loader compiled using Minecraft 1.20.5.
* Method loader compiled using Minecraft 1.21.
*/
@SuppressWarnings({
"unchecked", // Java generic types
"DataFlowIssue", // Profile is not null on add action
"unused" // Actually used, just via reflection
})
public class Loader_1_20_5 implements Loader {
public class Loader_1_21 implements Loader {
@Override
@NotNull
@@ -94,7 +96,7 @@ public class Loader_1_20_5 implements Loader {
null,
null,
null,
modifier.getFont() == null ? null : new ResourceLocation(modifier.getFont())
modifier.getFont() == null ? null : ResourceLocation.tryParse(modifier.getFont())
);
}
@@ -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.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"
]
},
"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.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}.
*
@@ -3,6 +3,7 @@ package me.neznamy.tab.shared;
import java.util.*;
import me.neznamy.tab.api.placeholder.PlayerPlaceholder;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.config.Configs;
import me.neznamy.tab.shared.config.mysql.MySQLUserConfiguration;
import me.neznamy.tab.shared.features.*;
@@ -75,6 +76,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()) {
@@ -484,6 +488,18 @@ public class FeatureManager {
NameTag unlimited = TAB.getInstance().getPlatform().getUnlimitedNameTags();
if (unlimited instanceof NameTagX) {
featureManager.registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, unlimited);
for (String message : new String[]{
"---------------------------------------------------------------------",
"You have unlimited nametag mode feature enabled.",
"This feature is scheduled for removal in a future TAB release.",
"Please considering the alternative solutions available to achieve your desired result.",
"No bug reports with this feature will be accepted anymore.",
"No support with this feature will be provided anymore.",
"See https://gist.github.com/NEZNAMY/f4cabf2fd9251a836b5eb877720dee5c/ for more info.",
"---------------------------------------------------------------------"
}) {
TAB.getInstance().getPlatform().logWarn(TabComponent.fromColoredText(message));
}
} else {
featureManager.registerFeature(TabConstants.Feature.NAME_TAGS, unlimited);
}
@@ -11,6 +11,8 @@ import org.jetbrains.annotations.NotNull;
public enum ProtocolVersion {
UNKNOWN,
V1_21 (767),
V1_20_6 (766),
V1_20_5 (766),
V1_20_4 (765),
V1_20_3 (765),
@@ -76,8 +78,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 = VALUES[1];
/** Version's network id found at <a href="https://wiki.vg/Protocol_version_numbers">wiki.vg</a> */
private final int networkId;
@@ -146,7 +151,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;
}
/**
@@ -42,7 +42,7 @@ public abstract class PropertyCommand extends SubCommand {
server = args[args.length-1];
value = value.startsWith("-s") ? "" : value.substring(0, value.length()-server.length()-4);
}
if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) {
if (((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) && value.length() > 1) {
value = value.substring(1, value.length()-1);
}
String property = args[1].toLowerCase();
@@ -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,16 @@ 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);
String cleanOutput = output.trim().toLowerCase(Locale.US);
if (!sortingMap.containsKey(cleanOutput)) {
TAB.getInstance().getConfigHelper().runtime().valueNotInPredefinedValues(sortingPlaceholder.getIdentifier(), sortingMap.keySet(), cleanOutput, 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");
position = sortingMap.get(cleanOutput);
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."));
}
}
Loaded 100 of 117 files, more files were not shown because too many files have changed in this diff. Show more