Compare commits

...
34 Commits
Author SHA1 Message Date
NEZNAMY d5c5502abe 4.0.7 2023-10-16 20:45:35 +02:00
NEZNAMY 194e8ffbd9 Code style 2023-10-15 20:39:06 +02:00
NEZNAMY ef39458abd With all the problems on 1.20.2 BungeeCord it turns out that Velocity isn't that bad. 2023-10-15 17:39:21 +02:00
NEZNAMY 6a6907fb0a Process login packet with a delay to avoid problems 2023-10-15 17:33:06 +02:00
NEZNAMY 8bb7c4a4a6 Adapt redis support subfeatures to recent changes 2023-10-15 17:00:55 +02:00
NEZNAMY 91700e7ed0 Fix global playerlist not readding players for 1.20.2 clients on server switch 2023-10-15 17:00:16 +02:00
NEZNAMY 661c2e6fc3 Simplify 2023-10-15 15:37:38 +02:00
NEZNAMY 3d053a6149 Implement backup solutions if header/footer fields fail to initialize 2023-10-15 15:35:25 +02:00
NEZNAMY 34179e4f62 Fix startup on old versions due to wrong enum names 2023-10-15 14:56:40 +02:00
NEZNAMY a23e5d9f50 Remove toComponent method from Platform interface and only keep it in platforms that actually use it 2023-10-15 14:26:09 +02:00
NEZNAMY 1cbcbf70d9 Fix aligned playerlist attempting to send packets to players who just left 2023-10-15 14:08:35 +02:00
NEZNAMY 0a7371aaec Micro optimizations with enum conversions 2023-10-14 13:55:52 +02:00
NEZNAMY da13e2d861 Fix Bridge still injecting placeholders that are not used on proxy anymore 2023-10-13 12:27:12 +02:00
NEZNAMY 5b18fa8c25 Fix PingSpoof spoofing everything instead of just online players (or their respective layout entries) 2023-10-13 11:34:38 +02:00
NEZNAMY 347231850a Fix error 2023-10-13 09:51:40 +02:00
NEZNAMY b1f9bdc26c Resend BossBar on server switch for 1.20.2 players as Login packet now clears it 2023-10-12 20:51:50 +02:00
NEZNAMY 03c9b95041 Listen to login packet instead of server switch delay for 1.20.2 players for better consistency (also fix layout not being resent on server switch on 1.20.2) 2023-10-12 19:20:22 +02:00
NEZNAMY 6e7e1d783b Fix rare placeholder formatting error 2023-10-12 16:33:58 +02:00
NEZNAMY cfb9b0a029 Fix compatibility with some random 1.8.8 overpriced fork (#1105) 2023-10-12 14:32:21 +02:00
NEZNAMY b7ea6e5af6 Fix wrong exception being caught causing the try/catch to not work (#1092) 2023-10-12 09:33:50 +02:00
NEZNAMY 44104df712 Change error message about invalid team/objective actions to a debug message. They don't cause any issues as they are properly caught and debugging is hard, especially if reporter has no interest in cooperating (#1083) 2023-10-11 19:35:57 +02:00
NEZNAMY 5de20d1c58 Add try/catch for BungeeCord sendPacket method to avoid a NPE in BungeeCord code (#1090, #1092) 2023-10-11 16:24:43 +02:00
NEZNAMY d156303740 Remove warn when placeholders use too much CPU. Since they have been moved to another thread, they will no longer prevent the plugin from working. While the warning can be useful for more advanced users, this is a public plugin used by many and most of them don't care about performance at all. This has resulted in having to answer people who would have never contacted me otherwise. 2023-10-10 15:04:00 +02:00
NEZNAMY 6e36eb3554 Add back fabric class for multi-version code for easier backport 2023-10-09 21:49:21 +02:00
NEZNAMY f7f3891817 Avoid attempts to send plugin message to offline players on bungeecord (#1090? #1092?) that may? cause errors on 1.20.2 2023-10-08 12:41:12 +02:00
NEZNAMY 1ec28856ff Avoid attempts to send packets to offline players on bungeecord (#1090? #1092?) that may? cause errors on 1.20.2 2023-10-06 11:41:32 +02:00
NEZNAMY 9658e0b74d Add debug message showing how long bridge took to respond to player join plugin message 2023-10-06 11:31:09 +02:00
NEZNAMY 86cd8d4151 Make feature loading more readable 2023-10-05 17:39:27 +02:00
NEZNAMY 2418eb206e Improve unknown version handling code & error messages 2023-10-05 13:57:56 +02:00
NEZNAMY 08fa715d26 Fix runtime warns false triggering on proxy when placeholders are not loaded due to bridge not having a chance to respond yet (#1088) 2023-10-04 14:16:08 +02:00
NEZNAMY 52426de283 Remove double method call 2023-10-04 14:04:57 +02:00
NEZNAMY 64b3da14c4 It's called disable condition, not display condition 2023-10-02 20:38:21 +02:00
NEZNAMY bf7341c52c Change way getProfile method is retrieved (#1089) 2023-10-02 19:09:53 +02:00
NEZNAMY e3490564a2 4.0.7-SNAPSHOT 2023-10-02 19:07:05 +02:00
89 changed files with 1196 additions and 581 deletions

No files matched your search

@@ -5,5 +5,12 @@ package me.neznamy.tab.api.bossbar;
* Calling ordinal() will return color's network ID.
*/
public enum BarColor {
PINK, BLUE, RED, GREEN, YELLOW, PURPLE, WHITE
PINK,
BLUE,
RED,
GREEN,
YELLOW,
PURPLE,
WHITE
}
@@ -1,22 +1,14 @@
package me.neznamy.tab.api.bossbar;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* An enum representing all available BossBar styles.
* Calling ordinal() will return style's network ID.
* Style names are equal to NMS names.
*/
@AllArgsConstructor
public enum BarStyle {
PROGRESS("SOLID"),
NOTCHED_6("SEGMENTED_6"),
NOTCHED_10("SEGMENTED_10"),
NOTCHED_12("SEGMENTED_12"),
NOTCHED_20("SEGMENTED_20");
/** Style's name used in Bukkit API */
@Getter private final String bukkitName;
PROGRESS,
NOTCHED_6,
NOTCHED_10,
NOTCHED_12,
NOTCHED_20
}
+1 -1
View File
@@ -4,7 +4,7 @@ plugins {
allprojects {
group = "me.neznamy"
version = "4.0.6"
version = "4.0.7"
description = "An all-in-one solution that works"
ext.set("id", "tab")
@@ -111,7 +111,7 @@ public class BukkitPipelineInjector extends NettyPipelineInjector {
if (actions.contains(TabList.Action.UPDATE_DISPLAY_NAME.name()) || actions.contains(TabList.Action.ADD_PLAYER.name())) {
displayName = BukkitTabList.PlayerInfoData_DisplayName.get(nmsData);
IChatBaseComponent newDisplayName = TAB.getInstance().getFeatureManager().onDisplayNameChange(receiver, id);
if (newDisplayName != null) displayName = TAB.getInstance().getPlatform().toComponent(newDisplayName, receiver.getVersion());
if (newDisplayName != null) displayName = ((BukkitTabPlayer)receiver).getPlatform().toComponent(newDisplayName, receiver.getVersion());
if (!BukkitReflection.is1_19_3Plus()) BukkitTabList.PlayerInfoData_DisplayName.set(nmsData, displayName);
}
if (actions.contains(TabList.Action.UPDATE_LATENCY.name()) || actions.contains(TabList.Action.ADD_PLAYER.name())) {
@@ -139,6 +139,11 @@ public class BukkitPipelineInjector extends NettyPipelineInjector {
}
}
@Override
public boolean isLogin(@NotNull Object packet) {
return false;
}
@SuppressWarnings("unchecked")
@Override
@SneakyThrows
@@ -44,12 +44,13 @@ public class BukkitTAB extends JavaPlugin {
return true;
} catch (Exception ex) {
if (ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0]) == ProtocolVersion.UNKNOWN_SERVER_VERSION) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is not compatible. " +
"This plugin version was made for " + ProtocolVersion.values()[ProtocolVersion.values().length-1].getFriendlyName() +
" - " + ProtocolVersion.values()[3].getFriendlyName() + ". Disabling."));
Bukkit.getConsoleSender().sendMessage(String.format(
"%s[TAB] Your server version is not compatible. This plugin version was made for %s - %s. Disabling.",
EnumChatFormat.RED.getFormat(), ProtocolVersion.V1_5, ProtocolVersion.LATEST_KNOWN_VERSION
));
} else {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] A compatibility issue " +
"with your server was found. Unless you are running some really weird server software, this is a bug."));
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.RED.getFormat() + "[TAB] A compatibility issue " +
"with your server was found. Unless you are running some really weird server software, this is a bug.");
}
return false;
}
@@ -5,6 +5,7 @@ import com.mojang.authlib.properties.Property;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.header.HeaderFooter;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.TabList;
@@ -33,10 +34,6 @@ import java.util.*;
public class BukkitTabList implements TabList {
// NMS Fields
private static Class<?> HeaderFooterClass;
private static Constructor<?> newHeaderFooter;
private static Field HEADER;
private static Field FOOTER;
public static Class<?> PlayerInfoClass;
private static Constructor<?> newPlayerInfo;
@@ -76,7 +73,6 @@ public class BukkitTabList implements TabList {
IChatBaseComponent = Class.forName("net.minecraft.network.chat.Component");
EntityPlayer = Class.forName("net.minecraft.server.level.ServerPlayer");
EnumGamemodeClass = (Class<Enum>) Class.forName("net.minecraft.world.level.GameType");
HeaderFooterClass = Class.forName("net.minecraft.network.protocol.game.ClientboundTabListPacket");
if (BukkitReflection.is1_19_3Plus()) {
ClientboundPlayerInfoRemovePacket = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket");
PlayerInfoClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
@@ -92,7 +88,6 @@ public class BukkitTabList implements TabList {
IChatBaseComponent = Class.forName("net.minecraft.network.chat.IChatBaseComponent");
EntityPlayer = Class.forName("net.minecraft.server.level.EntityPlayer");
EnumGamemodeClass = (Class<Enum>) Class.forName("net.minecraft.world.level.EnumGamemode");
HeaderFooterClass = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerListHeaderFooter");
if (BukkitReflection.is1_19_3Plus()) {
ClientboundPlayerInfoRemovePacket = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket");
PlayerInfoClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
@@ -107,25 +102,14 @@ public class BukkitTabList implements TabList {
} else {
IChatBaseComponent = BukkitReflection.getLegacyClass("IChatBaseComponent");
EntityPlayer = BukkitReflection.getLegacyClass("EntityPlayer");
HeaderFooterClass = BukkitReflection.getLegacyClass("PacketPlayOutPlayerListHeaderFooter");
PlayerInfoClass = BukkitReflection.getLegacyClass("PacketPlayOutPlayerInfo");
ActionClass = (Class<Enum>) BukkitReflection.getLegacyClass("PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "EnumPlayerInfoAction");
playerInfoDataClass = BukkitReflection.getLegacyClass("PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
EnumGamemodeClass = (Class<Enum>) BukkitReflection.getLegacyClass("EnumGamemode", "WorldSettings$EnumGamemode");
}
// Header & Footer
if (BukkitReflection.getMinorVersion() >= 17) {
newHeaderFooter = HeaderFooterClass.getConstructor(IChatBaseComponent, IChatBaseComponent);
} else {
newHeaderFooter = HeaderFooterClass.getConstructor();
HEADER = ReflectionUtils.getFields(HeaderFooterClass, IChatBaseComponent).get(0);
FOOTER = ReflectionUtils.getFields(HeaderFooterClass, IChatBaseComponent).get(1);
}
// Info packet
PLAYERS = ReflectionUtils.getOnlyField(PlayerInfoClass, List.class);
newPlayerInfoData = ReflectionUtils.getOnlyConstructor(playerInfoDataClass);
newPlayerInfoData = playerInfoDataClass.getConstructors()[0]; // #1105, a specific 1.8.8 fork has 2 constructors
PlayerInfoData_Profile = ReflectionUtils.getOnlyField(playerInfoDataClass, GameProfile.class);
PlayerInfoData_Latency = ReflectionUtils.getOnlyField(playerInfoDataClass, int.class);
PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(playerInfoDataClass, EnumGamemodeClass);
@@ -190,15 +174,7 @@ public class BukkitTabList implements TabList {
@Override
@SneakyThrows
public void setPlayerListHeaderFooter(@NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
if (HeaderFooterClass == null) return;
if (BukkitReflection.getMinorVersion() >= 17) {
player.sendPacket(newHeaderFooter.newInstance(toComponent(header), toComponent(footer)));
} else {
Object packet = newHeaderFooter.newInstance();
HEADER.set(packet, toComponent(header));
FOOTER.set(packet, toComponent(footer));
player.sendPacket(packet);
}
if (HeaderFooter.getInstance() != null) HeaderFooter.getInstance().set(player, header, footer);
}
@SneakyThrows
@@ -21,6 +21,26 @@ import java.util.UUID;
@RequiredArgsConstructor
public class BukkitBossBar implements BossBar {
/** Color array for fast access */
private static final org.bukkit.boss.BarColor[] colors = new org.bukkit.boss.BarColor[] {
org.bukkit.boss.BarColor.PINK,
org.bukkit.boss.BarColor.BLUE,
org.bukkit.boss.BarColor.RED,
org.bukkit.boss.BarColor.GREEN,
org.bukkit.boss.BarColor.YELLOW,
org.bukkit.boss.BarColor.PURPLE,
org.bukkit.boss.BarColor.WHITE
};
/** Style array for fast access */
private static final org.bukkit.boss.BarStyle[] styles = new org.bukkit.boss.BarStyle[] {
org.bukkit.boss.BarStyle.SOLID,
org.bukkit.boss.BarStyle.SEGMENTED_6,
org.bukkit.boss.BarStyle.SEGMENTED_10,
org.bukkit.boss.BarStyle.SEGMENTED_12,
org.bukkit.boss.BarStyle.SEGMENTED_20
};
/** Player this handler belongs to */
@NotNull
private final BukkitTabPlayer player;
@@ -34,8 +54,9 @@ public class BukkitBossBar implements BossBar {
if (bossBars.containsKey(id)) return;
org.bukkit.boss.BossBar bar = Bukkit.createBossBar(
BukkitUtils.toBukkitFormat(IChatBaseComponent.optimizedComponent(title), player.getVersion().getMinorVersion() >= 16),
org.bukkit.boss.BarColor.valueOf(color.name()),
org.bukkit.boss.BarStyle.valueOf(style.getBukkitName()));
colors[color.ordinal()],
styles[style.ordinal()]
);
bar.setProgress(progress);
bar.addPlayer(player.getPlayer());
bossBars.put(id, bar);
@@ -53,12 +74,12 @@ public class BukkitBossBar implements BossBar {
@Override
public void update(@NotNull UUID id, @NotNull BarStyle style) {
bossBars.get(id).setStyle(org.bukkit.boss.BarStyle.valueOf(style.getBukkitName()));
bossBars.get(id).setStyle(styles[style.ordinal()]);
}
@Override
public void update(@NotNull UUID id, @NotNull BarColor color) {
bossBars.get(id).setColor(org.bukkit.boss.BarColor.valueOf(color.name()));
bossBars.get(id).setColor(colors[color.ordinal()]);
}
@Override
@@ -21,6 +21,26 @@ import java.util.UUID;
@RequiredArgsConstructor
public class ViaBossBar implements BossBar {
/** Color array for fast access */
private static final BossColor[] colors = new BossColor[] {
BossColor.PINK,
BossColor.BLUE,
BossColor.RED,
BossColor.GREEN,
BossColor.YELLOW,
BossColor.PURPLE,
BossColor.WHITE
};
/** Style array for fast access */
private static final BossStyle[] styles = new BossStyle[] {
BossStyle.SOLID,
BossStyle.SEGMENTED_6,
BossStyle.SEGMENTED_10,
BossStyle.SEGMENTED_12,
BossStyle.SEGMENTED_20
};
/** Player this handler belongs to */
@NotNull
private final BukkitTabPlayer player;
@@ -35,8 +55,9 @@ public class ViaBossBar implements BossBar {
com.viaversion.viaversion.api.legacy.bossbar.BossBar bar = Via.getAPI().legacyAPI().createLegacyBossBar(
IChatBaseComponent.optimizedComponent(title).toString(player.getVersion()),
progress,
BossColor.valueOf(color.name()),
BossStyle.valueOf(style.getBukkitName()));
colors[color.ordinal()],
styles[style.ordinal()]
);
viaBossBars.put(id, bar);
bar.addPlayer(player.getPlayer().getUniqueId());
}
@@ -53,12 +74,12 @@ public class ViaBossBar implements BossBar {
@Override
public void update(@NotNull UUID id, @NotNull BarStyle style) {
viaBossBars.get(id).setStyle(BossStyle.valueOf(style.getBukkitName()));
viaBossBars.get(id).setStyle(styles[style.ordinal()]);
}
@Override
public void update(@NotNull UUID id, @NotNull BarColor color) {
viaBossBars.get(id).setColor(BossColor.valueOf(color.name()));
viaBossBars.get(id).setColor(colors[color.ordinal()]);
}
@Override
@@ -0,0 +1,33 @@
package me.neznamy.tab.platforms.bukkit.header;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.BukkitUtils;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Header/footer sender using Bukkit API. Despite this feature getting
* added into the game in 1.8, Bukkit team took 4 entire years to add
* it to the API, which eventually made it there in 1.13. <p>
* The input is just a String, meaning Bukkit will have to deserialize it
* into components, which is a massive performance loss. It also does not
* seem to support fonts.
*/
public class BukkitHeaderFooter extends HeaderFooter {
/** Flag tracking whether this class can be used on current server */
@Getter
private static final boolean available = ReflectionUtils.methodExists(Player.class, "setPlayerListHeaderFooter", String.class, String.class);
@Override
@SuppressWarnings("deprecation") // Marked as deprecated by Paper to make us use their methods instead
public void set(@NotNull BukkitTabPlayer player, @NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
player.getPlayer().setPlayerListHeaderFooter(
BukkitUtils.toBukkitFormat(header, player.getVersion().getMinorVersion() >= 16),
BukkitUtils.toBukkitFormat(footer, player.getVersion().getMinorVersion() >= 16)
);
}
}
@@ -0,0 +1,57 @@
package me.neznamy.tab.platforms.bukkit.header;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A class responsible for sending header/footer to players on request.
*/
public abstract class HeaderFooter {
/** Instance of the class, null if no implementation is available */
@Getter
@Nullable
private static HeaderFooter instance;
/**
* Finds the best available instance for current server software.
*/
public static void findInstance() {
if (PacketHeaderFooter.isAvailable()) {
instance = new PacketHeaderFooter();
} else if (PaperHeaderFooter.isAvailable()) {
instance = new PaperHeaderFooter();
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.RED.getFormat() + "[TAB] Failed to initialize NMS fields for " +
"sending Header/Footer due to a compatibility error. Using fallback solution using Paper API.");
} else if (BukkitHeaderFooter.isAvailable()) {
instance = new BukkitHeaderFooter();
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.RED.getFormat() + "[TAB] Failed to initialize NMS fields for " +
"sending Header/Footer due to a compatibility error. Using fallback solution using Bukkit API. " +
"This will drastically drop performance, as well as miss support for fonts (added in 1.16). " +
"Please update the plugin with to version proper support for your server version.");
} else if (BukkitReflection.getMinorVersion() >= 8){
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.RED.getFormat() + "[TAB] Failed to initialize NMS fields for " +
"sending Header/Footer due to a compatibility error. No fallback solution was found either. This will " +
"result in the feature not working. " +
"Please update the plugin with to version proper support for your server version.");
}
}
/**
* Sends header/footer to player.
*
* @param player
* Player to send header/footer to.
* @param header
* Header to use.
* @param footer
* Footer to use.
*/
public abstract void set(@NotNull BukkitTabPlayer player, @NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer);
}
@@ -0,0 +1,70 @@
package me.neznamy.tab.platforms.bukkit.header;
import lombok.Getter;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
* Header/footer sender that uses NMS to send packets. Available
* on all versions since 1.8 when the feature was added into the game.
*/
public class PacketHeaderFooter extends HeaderFooter {
private static Constructor<?> newHeaderFooter;
private static Field HEADER;
private static Field FOOTER;
@Getter
private static boolean available;
static {
try {
if (BukkitReflection.getMinorVersion() >= 8) {
Class<?> IChatBaseComponent;
Class<?> HeaderFooterClass;
if (BukkitReflection.isMojangMapped()) {
IChatBaseComponent = Class.forName("net.minecraft.network.chat.Component");
HeaderFooterClass = Class.forName("net.minecraft.network.protocol.game.ClientboundTabListPacket");
} else if (BukkitReflection.getMinorVersion() >= 17) {
IChatBaseComponent = Class.forName("net.minecraft.network.chat.IChatBaseComponent");
HeaderFooterClass = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerListHeaderFooter");
} else {
IChatBaseComponent = BukkitReflection.getLegacyClass("IChatBaseComponent");
HeaderFooterClass = BukkitReflection.getLegacyClass("PacketPlayOutPlayerListHeaderFooter");
}
if (BukkitReflection.getMinorVersion() >= 17) {
newHeaderFooter = HeaderFooterClass.getConstructor(IChatBaseComponent, IChatBaseComponent);
} else {
newHeaderFooter = HeaderFooterClass.getConstructor();
HEADER = ReflectionUtils.getFields(HeaderFooterClass, IChatBaseComponent).get(0);
FOOTER = ReflectionUtils.getFields(HeaderFooterClass, IChatBaseComponent).get(1);
}
available = true;
}
} catch (Exception ignored) {
// Print exception to find out what went wrong
}
}
@SneakyThrows
@Override
public void set(@NotNull BukkitTabPlayer player, @NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
Object headerComponent = player.getPlatform().toComponent(header, player.getVersion());
Object footerComponent = player.getPlatform().toComponent(footer, player.getVersion());
if (BukkitReflection.getMinorVersion() >= 17) {
player.sendPacket(newHeaderFooter.newInstance(headerComponent, footerComponent));
} else {
Object packet = newHeaderFooter.newInstance();
HEADER.set(packet, headerComponent);
FOOTER.set(packet, footerComponent);
player.sendPacket(packet);
}
}
}
@@ -0,0 +1,33 @@
package me.neznamy.tab.platforms.bukkit.header;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.hook.AdventureHook;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Header/footer sender using Paper's API added in 1.16.5.
* Due to lack of component cache for adventure components in TAB,
* there is a performance loss compared to using NMS. However, we do not
* need to serialize/deserialize anything ourselves thanks to direct component
* conversion, so in the end it is roughly the same.
*/
public class PaperHeaderFooter extends HeaderFooter {
/** Flag tracking whether this class can be used on current server */
@Getter
private static final boolean available = ReflectionUtils.classExists("net.kyori.adventure.text.Component") &&
ReflectionUtils.methodExists(Player.class, "sendPlayerListHeaderAndFooter", Component.class, Component.class);
@Override
public void set(@NotNull BukkitTabPlayer player, @NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
player.getPlayer().sendPlayerListHeaderAndFooter(
AdventureHook.toAdventureComponent(header, player.getVersion()),
AdventureHook.toAdventureComponent(footer, player.getVersion())
);
}
}
@@ -1,6 +1,7 @@
package me.neznamy.tab.platforms.bukkit.nms;
import lombok.*;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.backend.EntityData;
@@ -73,17 +74,21 @@ public class DataWatcher implements EntityData {
*/
public static void load() throws ReflectiveOperationException {
int minorVersion = BukkitReflection.getMinorVersion();
Class<?> Entity;
if (BukkitReflection.isMojangMapped()) {
Entity = Class.forName("net.minecraft.world.entity.Entity");
DataWatcher = Class.forName("net.minecraft.network.syncher.SynchedEntityData");
DataWatcherObject = Class.forName("net.minecraft.network.syncher.EntityDataAccessor");
DataWatcherRegistry = Class.forName("net.minecraft.network.syncher.EntityDataSerializers");
DataWatcherSerializer = Class.forName("net.minecraft.network.syncher.EntityDataSerializer");
} else if (minorVersion >= 17) {
Entity = Class.forName("net.minecraft.world.entity.Entity");
DataWatcher = Class.forName("net.minecraft.network.syncher.DataWatcher");
DataWatcherObject = Class.forName("net.minecraft.network.syncher.DataWatcherObject");
DataWatcherRegistry = Class.forName("net.minecraft.network.syncher.DataWatcherRegistry");
DataWatcherSerializer = Class.forName("net.minecraft.network.syncher.DataWatcherSerializer");
} else {
Entity = BukkitReflection.getLegacyClass("Entity");
DataWatcher = BukkitReflection.getLegacyClass("DataWatcher");
if (minorVersion >= 9) {
DataWatcherObject = BukkitReflection.getLegacyClass("DataWatcherObject");
@@ -91,7 +96,11 @@ public class DataWatcher implements EntityData {
DataWatcherSerializer = BukkitReflection.getLegacyClass("DataWatcherSerializer");
}
}
newDataWatcher = ReflectionUtils.getOnlyConstructor(DataWatcher);
if (minorVersion >= 7) {
newDataWatcher = DataWatcher.getConstructor(Entity);
} else {
newDataWatcher = DataWatcher.getConstructor();
}
if (minorVersion >= 9) {
DataWatcher_register = ReflectionUtils.getMethod(
DataWatcher,
@@ -167,7 +176,7 @@ public class DataWatcher implements EntityData {
public void setCustomName(@NotNull String customName, @NotNull ProtocolVersion clientVersion) {
if (BukkitReflection.getMinorVersion() >= 13) {
setValue(2, DataWatcherSerializer_OPTIONAL_COMPONENT,
Optional.of(TAB.getInstance().getPlatform().toComponent(IChatBaseComponent.optimizedComponent(customName), clientVersion)));
Optional.of(((BukkitPlatform)TAB.getInstance().getPlatform()).toComponent(IChatBaseComponent.optimizedComponent(customName), clientVersion)));
} else if (BukkitReflection.getMinorVersion() >= 8) {
setValue(2, DataWatcherSerializer_STRING, customName);
} else {
@@ -6,6 +6,7 @@ import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabList;
import me.neznamy.tab.platforms.bukkit.header.HeaderFooter;
import me.neznamy.tab.platforms.bukkit.scoreboard.PacketScoreboard;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
@@ -74,11 +75,12 @@ public class NMSStorage {
if (minorVersion >= 8) {
ChatSerializer_DESERIALIZE = ReflectionUtils.getMethods(ChatSerializer, Object.class, String.class).get(0);
CHANNEL = ReflectionUtils.getOnlyField(NetworkManager, Channel.class);
try {
getProfile = ReflectionUtils.getOnlyMethod(EntityHuman, GameProfile.class);
} catch (IllegalStateException catServer) {
getProfile = ReflectionUtils.getMethod(EntityHuman, new String[] {"getProfile"});
}
// There is only supposed to be one, however there are exceptions:
// #1 - CatServer adds another method
// #2 - Random mods may perform deep hack into the server and add another one (see #1089)
// Get first and hope for the best, alternatively players may not have correct skins in layout, but who cares
getProfile = ReflectionUtils.getMethods(EntityHuman, GameProfile.class).get(0);
}
PLAYER_CONNECTION = ReflectionUtils.getOnlyField(EntityPlayer, PlayerConnection);
getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".entity.CraftPlayer").getMethod("getHandle");
@@ -86,6 +88,7 @@ public class NMSStorage {
DataWatcher.load();
PacketEntityView.load();
PacketScoreboard.load();
HeaderFooter.findInstance();
if (minorVersion < 17) {
PING = ReflectionUtils.getField(EntityPlayer, "ping", "field_71138_i"); // 1.5.2 - 1.16.5, 1.7.10 Thermos
}
@@ -47,7 +47,7 @@ import java.lang.reflect.Field;
* Implementation of Platform interface for Bukkit platform
*/
@Getter
public class BukkitPlatform implements BackendPlatform<Object> {
public class BukkitPlatform implements BackendPlatform {
/** Plugin instance for registering tasks and events */
@NotNull
@@ -233,7 +233,15 @@ public class BukkitPlatform implements BackendPlatform<Object> {
return plugin.getDataFolder();
}
@Override
/**
* Converts internal component class to platform's component class
*
* @param component
* Component to convert
* @param version
* Game version to convert component for
* @return Converted component
*/
public Object toComponent(@NotNull IChatBaseComponent component, @NotNull ProtocolVersion version) {
return NMSStorage.getInstance().componentCache.get(component, version);
}
@@ -37,7 +37,6 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
private static Constructor<?> newDisplayObjective;
public static Field DisplayObjective_POSITION;
public static Field DisplayObjective_OBJECTIVE_NAME;
public static Enum[] DisplaySlot_values;
// PacketPlayOutScoreboardScore
private static Class<Enum> EnumScoreboardAction;
@@ -67,7 +66,6 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
public static Field TeamPacket_PLAYERS;
private static Class<Enum> EnumNameTagVisibility;
private static Class<Enum> EnumTeamPush;
private static Class<Enum> EnumChatFormatClass;
private static Constructor<?> newScoreboardTeam;
private static Method ScoreboardTeam_getPlayerNameSet;
private static Method ScoreboardTeam_setNameTagVisibility;
@@ -78,6 +76,13 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
private static Method ScoreboardTeam_setAllowFriendlyFire;
private static Method ScoreboardTeam_setCanSeeFriendlyInvisibles;
private static Enum[] scoreboardActions;
private static Enum[] displaySlots;
private static Enum[] nameVisibilities;
private static Enum[] collisionRules;
private static Enum[] healthDisplays;
private static Enum[] chatFormats;
public static void load() throws ReflectiveOperationException {
int minorVersion = BukkitReflection.getMinorVersion();
Class<?> scoreboardTeam;
@@ -87,9 +92,10 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
Class<?> scoreboard;
Class<?> scorePacketClass;
Class<?> IChatBaseComponent = null;
Class<Enum> enumChatFormatClass;
if (BukkitReflection.isMojangMapped()) {
IChatBaseComponent = Class.forName("net.minecraft.network.chat.Component");
EnumChatFormatClass = (Class<Enum>) Class.forName("net.minecraft.ChatFormatting");
enumChatFormatClass = (Class<Enum>) Class.forName("net.minecraft.ChatFormatting");
DisplayObjectiveClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket");
ObjectivePacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetObjectivePacket");
scoreboard = Class.forName("net.minecraft.world.scores.Scoreboard");
@@ -105,7 +111,7 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
EnumTeamPush = (Class<Enum>) Class.forName("net.minecraft.world.scores.Team$CollisionRule");
} else if (minorVersion >= 17) {
IChatBaseComponent = Class.forName("net.minecraft.network.chat.IChatBaseComponent");
EnumChatFormatClass = (Class<Enum>) Class.forName("net.minecraft.EnumChatFormat");
enumChatFormatClass = (Class<Enum>) Class.forName("net.minecraft.EnumChatFormat");
DisplayObjectiveClass = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardDisplayObjective");
ObjectivePacketClass = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective");
scoreboard = Class.forName("net.minecraft.world.scores.Scoreboard");
@@ -120,7 +126,7 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
EnumNameTagVisibility = (Class<Enum>) Class.forName("net.minecraft.world.scores.ScoreboardTeamBase$EnumNameTagVisibility");
EnumTeamPush = (Class<Enum>) Class.forName("net.minecraft.world.scores.ScoreboardTeamBase$EnumTeamPush");
} else {
EnumChatFormatClass = (Class<Enum>) getLegacyClass("EnumChatFormat");
enumChatFormatClass = (Class<Enum>) getLegacyClass("EnumChatFormat");
DisplayObjectiveClass = getLegacyClass("PacketPlayOutScoreboardDisplayObjective", "Packet208SetScoreboardDisplayObjective");
ObjectivePacketClass = getLegacyClass("PacketPlayOutScoreboardObjective", "Packet206SetScoreboardObjective");
TeamPacketClass = getLegacyClass("PacketPlayOutScoreboardTeam", "Packet209SetScoreboardTeam");
@@ -148,7 +154,7 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
Class<?> DisplaySlot = Class.forName("net.minecraft.world.scores.DisplaySlot");
DisplayObjective_POSITION = ReflectionUtils.getOnlyField(DisplayObjectiveClass, DisplaySlot);
newDisplayObjective = DisplayObjectiveClass.getConstructor(DisplaySlot, scoreboardObjective);
DisplaySlot_values = (Enum[]) DisplaySlot.getDeclaredMethod("values").invoke(null);
displaySlots = (Enum[]) DisplaySlot.getDeclaredMethod("values").invoke(null);
} else {
DisplayObjective_POSITION = ReflectionUtils.getOnlyField(DisplayObjectiveClass, int.class);
newDisplayObjective = DisplayObjectiveClass.getConstructor(int.class, scoreboardObjective);
@@ -168,7 +174,12 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
newScorePacket_1_13 = scorePacketClass.getConstructor(EnumScoreboardAction, String.class, String.class, int.class);
newObjectivePacket = ObjectivePacketClass.getConstructor(scoreboardObjective, int.class);
Objective_DISPLAY_NAME = ReflectionUtils.getOnlyField(ObjectivePacketClass, IChatBaseComponent);
ScoreboardTeam_setColor = ReflectionUtils.getOnlyMethod(scoreboardTeam, void.class, EnumChatFormatClass);
ScoreboardTeam_setColor = ReflectionUtils.getOnlyMethod(scoreboardTeam, void.class, enumChatFormatClass);
scoreboardActions = new Enum[] {
Enum.valueOf(EnumScoreboardAction, "CHANGE"),
Enum.valueOf(EnumScoreboardAction, "REMOVE")
};
chatFormats = (Enum[]) enumChatFormatClass.getMethod("values").invoke(null);
} else {
newScorePacket_String = scorePacketClass.getConstructor(String.class);
newObjectivePacket = ObjectivePacketClass.getConstructor();
@@ -180,8 +191,21 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
newScorePacket = scorePacketClass.getConstructor(scoreboardScoreClass, int.class);
}
}
if (minorVersion >= 8) {
nameVisibilities = new Enum[] {
Enum.valueOf(EnumNameTagVisibility, "ALWAYS"),
Enum.valueOf(EnumNameTagVisibility, "NEVER"),
Enum.valueOf(EnumNameTagVisibility, "HIDE_FOR_OTHER_TEAMS"),
Enum.valueOf(EnumNameTagVisibility, "HIDE_FOR_OWN_TEAM")
};
healthDisplays = new Enum[] {
Enum.valueOf(EnumScoreboardHealthDisplay, "INTEGER"),
Enum.valueOf(EnumScoreboardHealthDisplay, "HEARTS")
};
}
if (minorVersion >= 9) {
ScoreboardTeam_setCollisionRule = ReflectionUtils.getOnlyMethod(scoreboardTeam, void.class, EnumTeamPush);
collisionRules = (Enum[]) EnumTeamPush.getMethod("values").invoke(null); // <1.13 has wrong enum constant names
}
if (minorVersion >= 17) {
TeamPacketConstructor_of = ReflectionUtils.getOnlyMethod(TeamPacketClass, TeamPacketClass, scoreboardTeam);
@@ -246,7 +270,7 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
public void setDisplaySlot(@NotNull DisplaySlot slot, @NotNull String objective) {
Object displaySlot;
if (BukkitReflection.is1_20_2Plus()) {
displaySlot = DisplaySlot_values[slot.ordinal()];
displaySlot = displaySlots[slot.ordinal()];
} else {
displaySlot = slot.ordinal();
}
@@ -273,9 +297,13 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
private Object buildObjective(int action, String objectiveName, String title, @NotNull HealthDisplay display) {
if (BukkitReflection.getMinorVersion() >= 13) {
return newObjectivePacket.newInstance(
newScoreboardObjective.newInstance(null, objectiveName, null,
toComponent(IChatBaseComponent.optimizedComponent(title)),
Enum.valueOf(EnumScoreboardHealthDisplay, display.name())),
newScoreboardObjective.newInstance(
null,
objectiveName,
null,
toComponent(title),
healthDisplays[display.ordinal()]
),
action
);
}
@@ -283,7 +311,7 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
Objective_OBJECTIVE_NAME.set(nmsPacket, objectiveName);
Objective_DISPLAY_NAME.set(nmsPacket, title);
if (BukkitReflection.getMinorVersion() >= 8) {
Objective_RENDER_TYPE.set(nmsPacket, Enum.valueOf(EnumScoreboardHealthDisplay, display.name()));
Objective_RENDER_TYPE.set(nmsPacket, healthDisplays[display.ordinal()]);
}
Objective_METHOD.set(nmsPacket, action);
return nmsPacket;
@@ -333,15 +361,17 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
ScoreboardTeam_setAllowFriendlyFire.invoke(team, (options & 0x1) > 0);
ScoreboardTeam_setCanSeeFriendlyInvisibles.invoke(team, (options & 0x2) > 0);
if (BukkitReflection.getMinorVersion() >= 13) {
ScoreboardTeam_setPrefix.invoke(team, toComponent(IChatBaseComponent.optimizedComponent(prefix)));
ScoreboardTeam_setSuffix.invoke(team, toComponent(IChatBaseComponent.optimizedComponent(suffix)));
ScoreboardTeam_setColor.invoke(team, Enum.valueOf(EnumChatFormatClass, EnumChatFormat.lastColorsOf(prefix).toString()));
ScoreboardTeam_setPrefix.invoke(team, toComponent(prefix));
ScoreboardTeam_setSuffix.invoke(team, toComponent(suffix));
ScoreboardTeam_setColor.invoke(team, chatFormats[EnumChatFormat.lastColorsOf(prefix).ordinal()]);
} else {
ScoreboardTeam_setPrefix.invoke(team, prefix);
ScoreboardTeam_setSuffix.invoke(team, suffix);
}
if (BukkitReflection.getMinorVersion() >= 8) ScoreboardTeam_setNameTagVisibility.invoke(team, Enum.valueOf(EnumNameTagVisibility, visibility.name()));
if (BukkitReflection.getMinorVersion() >= 9) ScoreboardTeam_setCollisionRule.invoke(team, Enum.valueOf(EnumTeamPush, collision.name()));
if (BukkitReflection.getMinorVersion() >= 8)
ScoreboardTeam_setNameTagVisibility.invoke(team, nameVisibilities[visibility.ordinal()]);
if (BukkitReflection.getMinorVersion() >= 9)
ScoreboardTeam_setCollisionRule.invoke(team, collisionRules[collision.ordinal()]);
return team;
}
@@ -349,7 +379,7 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
@SneakyThrows
public void setScore0(@NotNull String objective, @NotNull String playerName, int score) {
if (BukkitReflection.getMinorVersion() >= 13) {
player.sendPacket(newScorePacket_1_13.newInstance(Enum.valueOf(EnumScoreboardAction, "CHANGE"), objective, playerName, score));
player.sendPacket(newScorePacket_1_13.newInstance(scoreboardActions[0], objective, playerName, score));
} else {
Object scoreboardScore = newScoreboardScore.newInstance(emptyScoreboard, newScoreboardObjective(objective), playerName);
ScoreboardScore_setScore.invoke(scoreboardScore, score);
@@ -365,7 +395,7 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
@SneakyThrows
public void removeScore0(@NotNull String objective, @NotNull String playerName) {
if (BukkitReflection.getMinorVersion() >= 13) {
player.sendPacket(newScorePacket_1_13.newInstance(Enum.valueOf(EnumScoreboardAction, "REMOVE"), objective, playerName, 0));
player.sendPacket(newScorePacket_1_13.newInstance(scoreboardActions[1], objective, playerName, 0));
} else {
player.sendPacket(newScorePacket_String.newInstance(playerName));
}
@@ -385,7 +415,7 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
null,
objectiveName,
null,
toComponent(new IChatBaseComponent("")),
toComponent(""),
null
);
}
@@ -396,7 +426,7 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
);
}
private Object toComponent(IChatBaseComponent component) {
return player.getPlatform().toComponent(component, player.getVersion());
private Object toComponent(String text) {
return player.getPlatform().toComponent(IChatBaseComponent.optimizedComponent(text), player.getVersion());
}
}
@@ -1,6 +1,5 @@
package me.neznamy.tab.platforms.bungeecord;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.platform.EventListener;
@@ -26,13 +25,7 @@ public class BungeeEventListener extends EventListener<ProxiedPlayer> implements
@EventHandler
public void onSwitch(ServerSwitchEvent e) {
Runnable task = () -> serverChange(e.getPlayer(), e.getPlayer().getUniqueId(), e.getPlayer().getServer().getInfo().getName());
// Temporary workaround until https://github.com/SpigotMC/BungeeCord/issues/3542 is resolved
if (e.getPlayer().getPendingConnection().getVersion() >= ProtocolVersion.V1_20_2.getNetworkId()) {
TAB.getInstance().getCPUManager().runTaskLater(500, "", "", task);
} else {
task.run();
}
serverChange(e.getPlayer(), e.getPlayer().getUniqueId(), e.getPlayer().getServer().getInfo().getName(), false);
}
@EventHandler
@@ -189,6 +189,11 @@ public class BungeePipelineInjector extends NettyPipelineInjector {
}
}
@Override
public boolean isLogin(@NotNull Object packet) {
return packet instanceof Login;
}
/**
* Constructs new instance of the feature
*/
@@ -3,22 +3,16 @@ package me.neznamy.tab.platforms.bungeecord;
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
import lombok.AllArgsConstructor;
import me.neznamy.tab.platforms.bungeecord.features.BungeeRedisSupport;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.ChatModifier;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.features.injection.PipelineInjector;
import me.neznamy.tab.shared.features.redis.RedisSupport;
import me.neznamy.tab.shared.hook.ViaVersionHook;
import me.neznamy.tab.shared.proxy.ProxyPlatform;
import me.neznamy.tab.shared.util.ComponentCache;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import org.bstats.bungeecord.Metrics;
import org.bstats.charts.SimplePie;
@@ -26,22 +20,16 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.stream.Collectors;
/**
* BungeeCord implementation of Platform
*/
@AllArgsConstructor
public class BungeePlatform extends ProxyPlatform<BaseComponent> {
public class BungeePlatform extends ProxyPlatform {
@NotNull
private final BungeeTAB plugin;
/** Component cache for BungeeCord components */
@NotNull
private final ComponentCache<IChatBaseComponent, BaseComponent> bungeeCache =
new ComponentCache<>(1000, this::toComponent0);
@Override
public void loadPlayers() {
ViaVersionHook.getInstance().printProxyWarn();
@@ -98,27 +86,6 @@ public class BungeePlatform extends ProxyPlatform<BaseComponent> {
return plugin.getDataFolder();
}
@Override
public BaseComponent toComponent(@NotNull IChatBaseComponent component, @NotNull ProtocolVersion version) {
return bungeeCache.get(component, version);
}
private BaseComponent toComponent0(IChatBaseComponent component, ProtocolVersion version) {
ChatModifier modifier = component.getModifier();
TextComponent textComponent = new TextComponent(component.getText());
if (modifier.getColor() != null) textComponent.setColor(ChatColor.of(
modifier.getColor().toString(version.getMinorVersion() >= 16)));
if (modifier.isBold()) textComponent.setBold(true);
if (modifier.isItalic()) textComponent.setItalic(true);
if (modifier.isObfuscated()) textComponent.setObfuscated(true);
if (modifier.isStrikethrough()) textComponent.setStrikethrough(true);
if (modifier.isUnderlined()) textComponent.setUnderlined(true);
if (modifier.getFont() != null) textComponent.setFont(modifier.getFont());
if (!component.getExtra().isEmpty()) textComponent.setExtra(
component.getExtra().stream().map(c -> toComponent0(c, version)).collect(Collectors.toList()));
return textComponent;
}
@Override
@Nullable
public PipelineInjector createPipelineInjector() {
@@ -7,6 +7,7 @@ import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.platforms.bungeecord.tablist.BungeeTabList1193;
import me.neznamy.tab.platforms.bungeecord.tablist.BungeeTabList17;
import me.neznamy.tab.platforms.bungeecord.tablist.BungeeTabList18;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.Scoreboard;
@@ -17,6 +18,7 @@ import me.neznamy.tab.shared.util.ReflectionUtils;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.chat.ComponentSerializer;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.connection.LoginResult;
import net.md_5.bungee.protocol.DefinedPacket;
@@ -24,6 +26,8 @@ import net.md_5.bungee.protocol.Property;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
/**
* TabPlayer implementation for BungeeCord
*/
@@ -78,7 +82,7 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
@Override
public void sendMessage(@NotNull IChatBaseComponent message) {
getPlayer().sendMessage(getPlatform().toComponent(message, getVersion()));
getPlayer().sendMessage(ComponentSerializer.parse(message.toString(getVersion())));
}
@Override
@@ -109,11 +113,18 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
@Override
public void sendPluginMessage(byte[] message) {
if (getPlayer().getServer() == null) {
errorNoServer(message);
return;
try {
getPlayer().getServer().sendData(TabConstants.PLUGIN_MESSAGE_CHANNEL_NAME, message);
} catch (NullPointerException BungeeCordBug) {
// java.lang.NullPointerException: Cannot invoke "net.md_5.bungee.protocol.MinecraftEncoder.getProtocol()" because the return value of "io.netty.channel.ChannelPipeline.get(java.lang.Class)" is null
// at net.md_5.bungee.netty.ChannelWrapper.getEncodeProtocol(ChannelWrapper.java:51)
// at net.md_5.bungee.ServerConnection.sendPacketQueued(ServerConnection.java:48)
// at net.md_5.bungee.ServerConnection.sendData(ServerConnection.java:70)
if (TAB.getInstance().getConfiguration().isDebugMode()) {
TAB.getInstance().getErrorManager().printError("Failed to deliver plugin message to player " + getName() +
" (online = " + getPlayer().isConnected() + ")", BungeeCordBug);
}
}
getPlayer().getServer().sendData(TabConstants.PLUGIN_MESSAGE_CHANNEL_NAME, message);
}
/**
@@ -159,7 +170,17 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
@SneakyThrows
public void sendPacket(@NotNull DefinedPacket packet) {
if (packetQueue) {
UserConnection.class.getDeclaredMethod("sendPacketQueued", DefinedPacket.class).invoke(player, packet);
try {
UserConnection.class.getDeclaredMethod("sendPacketQueued", DefinedPacket.class).invoke(player, packet);
} catch (InvocationTargetException BungeeCordBug) { // Since we use reflection we must catch this one
// java.lang.NullPointerException: Cannot invoke "net.md_5.bungee.protocol.MinecraftEncoder.getProtocol()" because the return value of "io.netty.channel.ChannelPipeline.get(java.lang.Class)" is null
// at net.md_5.bungee.netty.ChannelWrapper.getEncodeProtocol(ChannelWrapper.java:51)
// at net.md_5.bungee.UserConnection.sendPacketQueued(UserConnection.java:194)
if (TAB.getInstance().getConfiguration().isDebugMode()) {
TAB.getInstance().getErrorManager().printError("Failed to deliver packet to player " + getName() +
" (online = " + getPlayer().isConnected() + "): " + packet, BungeeCordBug);
}
}
} else {
getPlayer().unsafe().sendPacket(packet);
}
@@ -7,6 +7,7 @@ import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.protocol.Property;
import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter;
import net.md_5.bungee.protocol.packet.PlayerListItem.Item;
import net.md_5.bungee.tab.ServerUnique;
import org.jetbrains.annotations.NotNull;
@@ -32,10 +33,10 @@ public abstract class BungeeTabList implements TabList {
@Override
public void setPlayerListHeaderFooter(@NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
player.getPlayer().setTabHeader(
player.getPlatform().toComponent(header, player.getVersion()),
player.getPlatform().toComponent(footer, player.getVersion())
);
player.sendPacket(new PlayerListHeaderFooter(
header.toString(player.getVersion()),
footer.toString(player.getVersion())
));
}
@NotNull
+4 -7
View File
@@ -1,6 +1,5 @@
val minecraftVersion = "1.20.2"
val v114 = "0.28.5+1.14"
val v115 = "0.28.5+1.15"
val v116 = "0.42.0+1.16"
@@ -27,11 +26,7 @@ val fabricApiVersions = mapOf(
"1.15.2" to v115,
"1.15.1" to v115,
"1.15" to v115,
"1.14.4" to v114,
"1.14.3" to v114,
"1.14.2" to v114,
"1.14.1" to v114,
"1.14" to v114,
"1.14.4" to "0.28.5+1.14",
)
plugins {
@@ -57,8 +52,10 @@ dependencies {
modImplementation(fabricApi.module("fabric-api-base", fabricApiVersions[minecraftVersion]))
modImplementation(fabricApi.module("fabric-lifecycle-events-v1", fabricApiVersions[minecraftVersion]))
modImplementation(fabricApi.module("fabric-networking-api-v1", fabricApiVersions[minecraftVersion]))
modImplementation(fabricApi.module("fabric-entity-events-v1", fabricApiVersions[minecraftVersion]))
modImplementation(fabricApi.module("fabric-command-api-v${if (minecraftVersion.split(".")[1].toInt() >= 19) "2" else "1"}", fabricApiVersions[minecraftVersion]))
if (minecraftVersion.split(".")[1].toInt() >= 16) {
modImplementation(fabricApi.module("fabric-entity-events-v1", fabricApiVersions[minecraftVersion]))
}
}
loom {
@@ -9,12 +9,33 @@ import me.neznamy.tab.api.bossbar.BarStyle;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.bossbar.BossBar;
import net.minecraft.server.level.ServerBossEvent;
import net.minecraft.world.BossEvent;
import net.minecraft.world.BossEvent.BossBarColor;
import net.minecraft.world.BossEvent.BossBarOverlay;
import org.jetbrains.annotations.NotNull;
@RequiredArgsConstructor
public class FabricBossBar implements BossBar {
/** Color array for fast access */
private static final BossBarColor[] colors = new BossBarColor[] {
BossBarColor.PINK,
BossBarColor.BLUE,
BossBarColor.RED,
BossBarColor.GREEN,
BossBarColor.YELLOW,
BossBarColor.PURPLE,
BossBarColor.WHITE
};
/** Overlay array for fast access */
private static final BossBarOverlay[] overlays = new BossBarOverlay[] {
BossBarOverlay.PROGRESS,
BossBarOverlay.NOTCHED_6,
BossBarOverlay.NOTCHED_10,
BossBarOverlay.NOTCHED_12,
BossBarOverlay.NOTCHED_20
};
@NotNull
private final FabricTabPlayer player;
@@ -25,10 +46,10 @@ public class FabricBossBar implements BossBar {
public void create(@NotNull UUID id, @NotNull String title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
ServerBossEvent bar = new ServerBossEvent(
player.getPlatform().toComponent(IChatBaseComponent.optimizedComponent(title), player.getVersion()),
BossEvent.BossBarColor.valueOf(color.name()),
BossEvent.BossBarOverlay.valueOf(style.name())
colors[color.ordinal()],
overlays[style.ordinal()]
);
bar.setProgress(progress);
FabricMultiVersion.setProgress(bar, progress);
bars.put(id, bar);
bar.addPlayer(player.getPlayer());
}
@@ -40,17 +61,17 @@ public class FabricBossBar implements BossBar {
@Override
public void update(@NotNull UUID id, float progress) {
bars.get(id).setProgress(progress);
FabricMultiVersion.setProgress(bars.get(id), progress);
}
@Override
public void update(@NotNull UUID id, @NotNull BarStyle style) {
bars.get(id).setOverlay(BossEvent.BossBarOverlay.valueOf(style.name()));
bars.get(id).setOverlay(overlays[style.ordinal()]);
}
@Override
public void update(@NotNull UUID id, @NotNull BarColor color) {
bars.get(id).setColor(BossEvent.BossBarColor.valueOf(color.name()));
bars.get(id).setColor(colors[color.ordinal()]);
}
@Override
@@ -4,14 +4,10 @@ import me.neznamy.tab.shared.backend.EntityData;
import me.neznamy.tab.shared.backend.Location;
import me.neznamy.tab.shared.backend.entityview.EntityView;
import net.minecraft.network.protocol.game.*;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.UUID;
public record FabricEntityView(FabricTabPlayer player) implements EntityView {
@@ -22,16 +18,13 @@ public record FabricEntityView(FabricTabPlayer player) implements EntityView {
@Override
public void spawnEntity(int entityId, @NotNull UUID id, @NotNull Object entityType, @NotNull Location location,
@NotNull EntityData data) {
player.sendPacket(new ClientboundAddEntityPacket(entityId, id,
location.getX(), location.getY(), location.getZ(), 0, 0,
(EntityType<?>) entityType, 0, Vec3.ZERO, 0));
player.sendPacket(FabricMultiVersion.addEntity(entityId, id, entityType, location, data));
updateEntityMetadata(entityId, data);
}
@Override
@SuppressWarnings("unchecked")
public void updateEntityMetadata(int entityId, @NotNull EntityData data) {
player.sendPacket(new ClientboundSetEntityDataPacket(entityId, (List<SynchedEntityData.DataValue<?>>) data.build()));
player.sendPacket(FabricMultiVersion.newEntityMetadata(entityId, data));
}
@@ -44,12 +37,12 @@ public record FabricEntityView(FabricTabPlayer player) implements EntityView {
@Override
public void destroyEntities(int... entities) {
player.sendPacket(new ClientboundRemoveEntitiesPacket(entities));
FabricMultiVersion.destroyEntities(player, entities);
}
@Override
public boolean isDestroyPacket(@NotNull Object packet) {
return packet instanceof ClientboundRemoveEntitiesPacket;
return FabricMultiVersion.isEntityDestroyPacket(packet);
}
@Override
@@ -89,6 +82,6 @@ public record FabricEntityView(FabricTabPlayer player) implements EntityView {
@Override
public int[] getDestroyedEntities(@NotNull Object destroyPacket) {
return ((ClientboundRemoveEntitiesPacket) destroyPacket).getEntityIds().toIntArray();
return FabricMultiVersion.getDestroyedEntities(destroyPacket);
}
}
@@ -3,8 +3,6 @@ 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 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;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.NotNull;
@@ -14,10 +12,8 @@ public class FabricEventListener extends EventListener<ServerPlayer> {
public void register() {
ServerPlayConnectionEvents.DISCONNECT.register((connection, $) -> quit(connection.player.getUUID()));
ServerPlayConnectionEvents.JOIN.register((connection, $, $$) -> join(connection.player));
ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register((player, origin, destination) ->
worldChange(player.getUUID(), destination.dimension().location().toString()));
//TODO command
ServerPlayerEvents.AFTER_RESPAWN.register((oldPlayer, newPlayer, alive) -> replacePlayer(oldPlayer.getUUID(), newPlayer));
FabricMultiVersion.registerEntityEvents(this::replacePlayer);
//TODO command preprocess
}
@Override
@@ -0,0 +1,293 @@
package me.neznamy.tab.platforms.fabric;
import com.mojang.authlib.properties.Property;
import me.neznamy.tab.platforms.fabric.features.FabricNameTagX;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.backend.EntityData;
import me.neznamy.tab.shared.backend.Location;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabPlayer;
import net.minecraft.SharedConstants;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.*;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerBossEvent;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.GameType;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.scores.Objective;
import net.minecraft.world.scores.PlayerTeam;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.BiConsumer;
/**
* Class that contains (almost) all code that needs to be changed
* to set compatibility with any MC version that fabric supports (1.14.4+).
*/
public class FabricMultiVersion {
private static final int armorStandFlagsPosition = getArmorStandFlagsPosition();
private static int getArmorStandFlagsPosition() {
int minorVersion = ProtocolVersion.fromFriendlyName(SharedConstants.getCurrentVersion().getName()).getMinorVersion();
if (minorVersion >= 17) {
//1.17.x, 1.18.x, 1.19.x, 1.20.x
return 15;
} else if (minorVersion >= 15) {
//1.15.x, 1.16.x
return 14;
} else {
//1.14.x
return 13;
}
}
public static void sendSystemMessage(CommandSourceStack source, Component message) {
// 1.19.4-
// source.sendSuccess(message, false);
// 1.19.1+
source.sendSystemMessage(message);
}
public static boolean isSneaking(ServerPlayer player) {
// 1.14.4-
// return player.isSneaking();
// 1.15+
return player.isCrouching();
}
public static void registerNameTagXEvents(FabricNameTagX nameTagX) {
// 1.16+
net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents.AFTER_RESPAWN.register((oldPlayer, newPlayer, alive) -> {
if (nameTagX.enabled) nameTagX.respawn(oldPlayer.getUUID());
});
// TODO sneaking
}
public static void registerEntityEvents(BiConsumer<UUID, ServerPlayer> action) {
// 1.16+
net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents.AFTER_RESPAWN.register((oldPlayer, newPlayer, alive) ->
action.accept(oldPlayer.getUUID(), newPlayer));
// TODO world switch with proper world name
}
public static ClientboundTabListPacket setHeaderFooter(Component header, Component footer) {
// 1.16.5-
/*ClientboundTabListPacket packet = new ClientboundTabListPacket();
packet.header = header;
packet.footer = footer;
return packet;*/
// 1.17+
return new ClientboundTabListPacket(header, footer);
}
public static ClientboundSetPlayerTeamPacket createTeam(PlayerTeam team) {
// 1.16.5-
// return new ClientboundSetPlayerTeamPacket(team, 0);
// 1.17+
return ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, true);
}
public static ClientboundSetPlayerTeamPacket removeTeam(PlayerTeam team) {
// 1.16.5-
// return new ClientboundSetPlayerTeamPacket(team, 1);
// 1.17+
return ClientboundSetPlayerTeamPacket.createRemovePacket(team);
}
public static ClientboundSetPlayerTeamPacket updateTeam(PlayerTeam team) {
// 1.16.5-
// return new ClientboundSetPlayerTeamPacket(team, 2);
// 1.17+
return ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, false);
}
public static void setProgress(ServerBossEvent bar, float progress) {
// 1.16.5-
// bar.setPercent(progress);
// 1.17+
bar.setProgress(progress);
}
public static int[] getDestroyedEntities(Object destroyPacket) {
// 1.16.5-
// return ((ClientboundRemoveEntitiesPacket) destroyPacket).getEntityIds();
// 1.17
// return new int[]{((ClientboundRemoveEntityPacket)destroyPacket).getEntityId()};
// 1.17.1+
return ((ClientboundRemoveEntitiesPacket) destroyPacket).getEntityIds().toIntArray();
}
public static void destroyEntities(FabricTabPlayer player, int... entities) {
// 1.17
/*for (int i : entities) {
player.sendPacket(new ClientboundRemoveEntityPacket(i));
}*/
// every other version
player.sendPacket(new ClientboundRemoveEntitiesPacket(entities));
}
public static boolean isEntityDestroyPacket(Object packet) {
// 1.17
// return packet instanceof ClientboundRemoveEntityPacket;
// every other version
return packet instanceof ClientboundRemoveEntitiesPacket;
}
public static void registerCommand() {
// 1.18.2-
// net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback.EVENT.register((dispatcher, $) -> new FabricTabCommand().onRegisterCommands(dispatcher));
// 1.19+
net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback.EVENT.register((dispatcher, $, $$) -> new FabricTabCommand().onRegisterCommands(dispatcher));
}
public static void sendMessage(FabricTabPlayer player, Component message) {
// 1.15.2-
// player.getPlayer().sendMessage(message);
// 1.16 - 1.18.2
// player.getPlayer().sendMessage(message, new UUID(0, 0));
// 1.19+
player.getPlayer().sendSystemMessage(message);
}
public static ClientboundAddEntityPacket addEntity(int entityId, @NotNull UUID id, @NotNull Object entityType,
@NotNull Location location, @NotNull EntityData data) {
// 1.18.2-
// return new ClientboundAddEntityPacket(entityId, id, location.getX(), location.getY(), location.getZ(), 0, 0, (EntityType<?>) entityType, 0, Vec3.ZERO);
// 1.19+
return new ClientboundAddEntityPacket(entityId, id, location.getX(), location.getY(), location.getZ(), 0, 0, (EntityType<?>) entityType, 0, Vec3.ZERO, 0);
}
public static boolean isPlayerInfo(Object packet) {
// 1.19.2-
// return packet instanceof ClientboundPlayerInfoPacket;
// 1.19.3+
return packet instanceof ClientboundPlayerInfoUpdatePacket;
}
public static Packet<?> build(FabricTabList.Action action, FabricTabList.Builder entry) {
// 1.16.5-
/*ClientboundPlayerInfoPacket packet = new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.valueOf(action.name()), Collections.emptyList());
packet.entries = Collections.singletonList(new ClientboundPlayerInfoPacket().new PlayerUpdate(
entry.createProfile(), entry.getLatency(), GameType.byId(entry.getGameMode()), entry.getDisplayName()
));
return packet;*/
// 1.17 - 1.18.2
/*ClientboundPlayerInfoPacket packet = new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.valueOf(action.name()), Collections.emptyList());
packet.entries = Collections.singletonList(new ClientboundPlayerInfoPacket.PlayerUpdate(
entry.createProfile(), entry.getLatency(), GameType.byId(entry.getGameMode()), entry.getDisplayName()
));
return packet;*/
// 1.19 - 1.19.2
/*ClientboundPlayerInfoPacket packet = new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.valueOf(action.name()), Collections.emptyList());
packet.entries = Collections.singletonList(new ClientboundPlayerInfoPacket.PlayerUpdate(
entry.createProfile(), entry.getLatency(), GameType.byId(entry.getGameMode()), entry.getDisplayName(), null
));
return packet;*/
// 1.19.3+
if (action == FabricTabList.Action.REMOVE_PLAYER) {
return new ClientboundPlayerInfoRemovePacket(Collections.singletonList(entry.getId()));
}
EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = action == FabricTabList.Action.ADD_PLAYER ?
EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class) :
EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.valueOf(action.name()));
ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(actions, Collections.emptyList());
packet.entries = Collections.singletonList(new ClientboundPlayerInfoUpdatePacket.Entry(
entry.getId(),
entry.createProfile(),
true,
entry.getLatency(),
GameType.byId(entry.getGameMode()),
entry.getDisplayName(),
null
));
return packet;
}
public static EntityData createDataWatcher(@NotNull TabPlayer viewer, byte flags, @NotNull String displayName, boolean nameVisible) {
// 1.19.2-
/*SynchedEntityData data = new SynchedEntityData(null);
data.define(new EntityDataAccessor<>(0, EntityDataSerializers.BYTE), flags);
data.define(new EntityDataAccessor<>(2, EntityDataSerializers.OPTIONAL_COMPONENT),
Optional.of(((FabricTabPlayer)viewer).getPlatform().toComponent(IChatBaseComponent.optimizedComponent(displayName), viewer.getVersion())));
data.define(new EntityDataAccessor<>(3, EntityDataSerializers.BOOLEAN), nameVisible);
data.define(new EntityDataAccessor<>(15, EntityDataSerializers.BYTE), (byte)16);
return () -> data;*/
// 1.19.3+
return () -> java.util.Arrays.asList(
new SynchedEntityData.DataValue<>(0, EntityDataSerializers.BYTE, flags),
new SynchedEntityData.DataValue<>(2, EntityDataSerializers.OPTIONAL_COMPONENT,
Optional.of(((FabricTabPlayer)viewer).getPlatform().toComponent(IChatBaseComponent.optimizedComponent(displayName), viewer.getVersion()))),
new SynchedEntityData.DataValue<>(3, EntityDataSerializers.BOOLEAN, nameVisible),
new SynchedEntityData.DataValue<>(armorStandFlagsPosition, EntityDataSerializers.BYTE, (byte)16)
);
}
@SuppressWarnings("unchecked")
public static ClientboundSetEntityDataPacket newEntityMetadata(int entityId, EntityData data) {
// 1.19.2-
// return new ClientboundSetEntityDataPacket(entityId, (SynchedEntityData) data.build(), true);
// 1.19.3+
return new ClientboundSetEntityDataPacket(entityId, (List<SynchedEntityData.DataValue<?>>) data.build());
}
public static ClientboundSetDisplayObjectivePacket newDisplayObjective(int slot, Objective objective) {
// 1.20.1-
// return new ClientboundSetDisplayObjectivePacket(slot, objective);
// 1.20.2+
return new ClientboundSetDisplayObjectivePacket(net.minecraft.world.scores.DisplaySlot.values()[slot], objective);
}
public static int getSlot(ClientboundSetDisplayObjectivePacket packet) {
// 1.20.1-
// return packet.getSlot();
// 1.20.2+
return packet.getSlot().ordinal();
}
public static int getPing(FabricTabPlayer player) {
// 1.20.1-
// return player.getPlayer().latency;
// 1.20.2+
return player.getPlayer().connection.latency();
}
public static TabList.Skin propertyToSkin(Property property) {
// 1.20.1-
// return new TabList.Skin(property.getValue(), property.getSignature());
// 1.20.2+
return new TabList.Skin(property.value(), property.signature());
}
}
@@ -11,8 +11,6 @@ import me.neznamy.tab.shared.features.sorting.Sorting;
import me.neznamy.tab.shared.platform.TabPlayer;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket.Action;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket.Entry;
import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket;
import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket;
import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket;
@@ -39,7 +37,7 @@ public class FabricPipelineInjector extends NettyPipelineInjector {
@Override
public void onDisplayObjective(@NotNull TabPlayer player, @NotNull Object packet) {
TAB.getInstance().getFeatureManager().onDisplayObjective(player,
((ClientboundSetDisplayObjectivePacket)packet).getSlot().ordinal(),
FabricMultiVersion.getSlot((ClientboundSetDisplayObjectivePacket) packet),
String.valueOf(((ClientboundSetDisplayObjectivePacket)packet).getObjectiveName()));
}
@@ -67,33 +65,41 @@ public class FabricPipelineInjector extends NettyPipelineInjector {
@Override
public boolean isPlayerInfo(@NotNull Object packet) {
return packet instanceof ClientboundPlayerInfoUpdatePacket;
return FabricMultiVersion.isPlayerInfo(packet);
}
@Override
public void onPlayerInfo(@NotNull TabPlayer receiver, @NotNull Object packet0) {
// Comment this entire method out when compiling with 1.19.2-, adding compatibility would be tough and not worth,
// as nobody needs these features on Fabric anyway
ClientboundPlayerInfoUpdatePacket packet = (ClientboundPlayerInfoUpdatePacket) packet0;
EnumSet<Action> actions = packet.actions();
List<Entry> updatedList = new ArrayList<>();
for (Entry nmsData : packet.entries()) {
EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = packet.actions();
List<ClientboundPlayerInfoUpdatePacket.Entry> updatedList = new ArrayList<>();
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : packet.entries()) {
GameProfile profile = nmsData.profile();
Component displayName = nmsData.displayName();
int latency = nmsData.latency();
if (actions.contains(Action.UPDATE_DISPLAY_NAME)) {
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME)) {
IChatBaseComponent newDisplayName = TAB.getInstance().getFeatureManager().onDisplayNameChange(receiver, nmsData.profileId());
if (newDisplayName != null) displayName = ((FabricTabPlayer)receiver).getPlatform().toComponent(newDisplayName, receiver.getVersion());
}
if (actions.contains(Action.UPDATE_LATENCY)) {
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY)) {
latency = TAB.getInstance().getFeatureManager().onLatencyChange(receiver, nmsData.profileId(), latency);
}
if (actions.contains(Action.ADD_PLAYER)) {
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
TAB.getInstance().getFeatureManager().onEntryAdd(receiver, nmsData.profileId(), profile.getName());
}
updatedList.add(new Entry(nmsData.profileId(), profile, nmsData.listed(), latency, nmsData.gameMode(), displayName, nmsData.chatSession()));
updatedList.add(new ClientboundPlayerInfoUpdatePacket.Entry(nmsData.profileId(), profile, nmsData.listed(), latency, nmsData.gameMode(), displayName, nmsData.chatSession()));
}
packet.entries = updatedList;
}
@Override
public boolean isLogin(@NotNull Object packet) {
return false;
}
@Override
public void modifyPlayers(@NotNull Object teamPacket) {
if (TAB.getInstance().getNameTagManager() == null) return;
@@ -13,26 +13,19 @@ import me.neznamy.tab.shared.placeholders.expansion.EmptyTabExpansion;
import me.neznamy.tab.shared.placeholders.expansion.TabExpansion;
import net.fabricmc.fabric.api.networking.v1.PlayerLookup;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.ChatFormatting;
import net.minecraft.SharedConstants;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.contents.LiteralContents;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* Platform implementation for Fabric
*/
public record FabricPlatform(MinecraftServer server) implements BackendPlatform<Component> {
public record FabricPlatform(MinecraftServer server) implements BackendPlatform {
@Override
public void registerUnknownPlaceholder(@NotNull String identifier) {
@@ -113,34 +106,17 @@ public record FabricPlatform(MinecraftServer server) implements BackendPlatform<
return FabricLoader.getInstance().getConfigDir().resolve(TabConstants.PLUGIN_ID).toFile();
}
@Override
/**
* Converts internal component class to platform's component class
*
* @param component
* Component to convert
* @param version
* Game version to convert component for
* @return Converted component
*/
public Component toComponent(@NotNull IChatBaseComponent component, @NotNull ProtocolVersion version) {
// Text
MutableComponent comp = MutableComponent.create(new LiteralContents(component.getText()));
// Color and style
List<ChatFormatting> formats = new ArrayList<>();
if (component.getModifier().isUnderlined()) formats.add(ChatFormatting.UNDERLINE);
if (component.getModifier().isObfuscated()) formats.add(ChatFormatting.OBFUSCATED);
if (component.getModifier().isStrikethrough()) formats.add(ChatFormatting.STRIKETHROUGH);
if (component.getModifier().isItalic()) formats.add(ChatFormatting.ITALIC);
if (component.getModifier().isBold()) formats.add(ChatFormatting.BOLD);
Style style = comp.getStyle().applyFormats(formats.toArray(new ChatFormatting[0]));
if (component.getModifier().getColor() != null) {
if (version.getMinorVersion() >= 16) {
style = style.withColor(component.getModifier().getColor().getRgb());
} else {
style = style.withColor(ChatFormatting.valueOf(component.getModifier().getColor().getLegacyColor().name()));
}
}
if (component.getModifier().getFont() != null)
style = style.withFont(new ResourceLocation(component.getModifier().getFont()));
comp.withStyle(style);
// Extra
comp.getSiblings().addAll(component.getExtra().stream().map(c -> toComponent(c, version)).toList());
return comp;
return Component.Serializer.fromJson(component.toString(version));
}
@Override
@@ -1,15 +1,14 @@
package me.neznamy.tab.platforms.fabric;
import java.util.Collection;
import java.util.Objects;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.Scoreboard;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket;
import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket;
import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket;
import net.minecraft.network.protocol.game.ClientboundSetScorePacket;
import net.minecraft.server.ServerScoreboard;
import net.minecraft.world.scores.Objective;
@@ -23,6 +22,9 @@ public class FabricScoreboard extends Scoreboard<FabricTabPlayer> {
@NotNull
private static final net.minecraft.world.scores.Scoreboard dummyScoreboard = new net.minecraft.world.scores.Scoreboard();
@NotNull
private static final Component EMPTY_COMPONENT = Objects.requireNonNull(Component.Serializer.fromJson(IChatBaseComponent.EMPTY_COMPONENT));
public FabricScoreboard(FabricTabPlayer player) {
super(player);
}
@@ -30,13 +32,13 @@ public class FabricScoreboard extends Scoreboard<FabricTabPlayer> {
@Override
public void setDisplaySlot(@NotNull DisplaySlot slot, @NotNull String objective) {
player.sendPacket(
new ClientboundSetDisplayObjectivePacket(
net.minecraft.world.scores.DisplaySlot.values()[slot.ordinal()],
FabricMultiVersion.newDisplayObjective(
slot.ordinal(),
new Objective(
dummyScoreboard,
objective,
ObjectiveCriteria.DUMMY,
Component.empty(),
EMPTY_COMPONENT,
ObjectiveCriteria.RenderType.INTEGER
)
)
@@ -67,7 +69,7 @@ public class FabricScoreboard extends Scoreboard<FabricTabPlayer> {
dummyScoreboard,
objectiveName,
ObjectiveCriteria.DUMMY,
Component.empty(),
EMPTY_COMPONENT,
ObjectiveCriteria.RenderType.INTEGER
),
1
@@ -104,12 +106,12 @@ public class FabricScoreboard extends Scoreboard<FabricTabPlayer> {
team.setPlayerPrefix(player.getPlatform().toComponent(IChatBaseComponent.optimizedComponent(prefix), player.getVersion()));
team.setPlayerSuffix(player.getPlatform().toComponent(IChatBaseComponent.optimizedComponent(suffix), player.getVersion()));
team.getPlayers().addAll(players);
player.sendPacket(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, true));
player.sendPacket(FabricMultiVersion.createTeam(team));
}
@Override
public void unregisterTeam0(@NotNull String name) {
player.sendPacket(ClientboundSetPlayerTeamPacket.createRemovePacket(new PlayerTeam(dummyScoreboard, name)));
player.sendPacket(FabricMultiVersion.removeTeam(new PlayerTeam(dummyScoreboard, name)));
}
@Override
@@ -123,7 +125,7 @@ public class FabricScoreboard extends Scoreboard<FabricTabPlayer> {
team.setNameTagVisibility(Team.Visibility.valueOf(visibility.name()));
team.setPlayerPrefix(player.getPlatform().toComponent(IChatBaseComponent.optimizedComponent(prefix), player.getVersion()));
team.setPlayerSuffix(player.getPlatform().toComponent(IChatBaseComponent.optimizedComponent(suffix), player.getVersion()));
player.sendPacket(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, false));
player.sendPacket(FabricMultiVersion.updateTeam(team));
}
@Override
@@ -3,7 +3,6 @@ package me.neznamy.tab.platforms.fabric;
import me.lucko.fabric.api.permissions.v0.Permissions;
import me.neznamy.tab.shared.TAB;
import net.fabricmc.api.DedicatedServerModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.commands.CommandSourceStack;
@@ -15,7 +14,7 @@ public class FabricTAB implements DedicatedServerModInitializer {
@Override
public void onInitializeServer() {
CommandRegistrationCallback.EVENT.register((dispatcher, $, $$) -> new FabricTabCommand().onRegisterCommands(dispatcher));
FabricMultiVersion.registerCommand();
ServerLifecycleEvents.SERVER_STARTING.register(server -> TAB.create(new FabricPlatform(server)));
ServerLifecycleEvents.SERVER_STOPPING.register($ -> TAB.getInstance().unload());
}
@@ -51,7 +51,8 @@ public class FabricTabCommand {
boolean hasReloadPermission = FabricTAB.hasPermission(source, TabConstants.Permission.COMMAND_RELOAD);
boolean hasAdminPermission = FabricTAB.hasPermission(source, TabConstants.Permission.COMMAND_ALL);
for (String message : TAB.getInstance().getDisabledCommand().execute(args, hasReloadPermission, hasAdminPermission)) {
source.sendSystemMessage(((FabricPlatform)TAB.getInstance().getPlatform()).toComponent(IChatBaseComponent.optimizedComponent(message), TAB.getInstance().getServerVersion()));
FabricMultiVersion.sendSystemMessage(source, ((FabricPlatform)TAB.getInstance().getPlatform()).toComponent(
IChatBaseComponent.optimizedComponent(message), TAB.getInstance().getServerVersion()));
}
} else {
if (source.getEntity() == null) {
@@ -7,11 +7,6 @@ import lombok.RequiredArgsConstructor;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.TabList;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundTabListPacket;
import net.minecraft.world.level.GameType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -21,71 +16,58 @@ public record FabricTabList(@NotNull FabricTabPlayer player) implements TabList
@Override
public void removeEntry(@NotNull UUID entry) {
player.sendPacket(new ClientboundPlayerInfoRemovePacket(Collections.singletonList(entry)));
player.sendPacket(FabricMultiVersion.build(Action.REMOVE_PLAYER, new Builder(entry)));
}
@Override
public void updateDisplayName(@NotNull UUID entry, @Nullable IChatBaseComponent displayName) {
player.sendPacket(build(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME),
new Builder(entry).setDisplayName(displayName == null ? null : player.getPlatform().toComponent(displayName, player.getVersion()))));
player.sendPacket(FabricMultiVersion.build(Action.UPDATE_DISPLAY_NAME,
new Builder(entry).setDisplayName(displayName == null ? null : player.getPlatform().toComponent(displayName, player.getVersion()))
));
}
@Override
public void updateLatency(@NotNull UUID entry, int latency) {
player.sendPacket(build(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY), new Builder(entry).setLatency(latency)));
player.sendPacket(FabricMultiVersion.build(Action.UPDATE_LATENCY, new Builder(entry).setLatency(latency)));
}
@Override
public void updateGameMode(@NotNull UUID entry, int gameMode) {
player.sendPacket(build(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE), new Builder(entry).setGameMode(gameMode)));
player.sendPacket(FabricMultiVersion.build(Action.UPDATE_GAME_MODE, new Builder(entry).setGameMode(gameMode)));
}
@Override
public void addEntry(@NotNull Entry entry) {
player.sendPacket(build(EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class), new Builder(entry.getUniqueId())
player.sendPacket(FabricMultiVersion.build(Action.ADD_PLAYER, new Builder(entry.getUniqueId())
.setName(entry.getName())
.setSkin(entry.getSkin())
.setGameMode(entry.getGameMode())
.setLatency(entry.getLatency())
.setDisplayName(entry.getDisplayName() == null ? null : player.getPlatform().toComponent(entry.getDisplayName(), player.getVersion()))));
.setDisplayName(entry.getDisplayName() == null ? null : player.getPlatform().toComponent(entry.getDisplayName(), player.getVersion()))
));
}
@Override
public void setPlayerListHeaderFooter(@NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
player.getPlayer().connection.send(new ClientboundTabListPacket(
player.getPlayer().connection.send(FabricMultiVersion.setHeaderFooter(
player.getPlatform().toComponent(header, player.getVersion()),
player.getPlatform().toComponent(footer, player.getVersion()))
);
}
@NotNull
private Packet<?> build(@NotNull EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions, @NotNull FabricTabList.Builder entry) {
ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(actions, Collections.emptyList());
packet.entries = Collections.singletonList(new ClientboundPlayerInfoUpdatePacket.Entry(
entry.getId(),
entry.createProfile(),
true,
entry.getLatency(),
GameType.byId(entry.getGameMode()),
entry.getDisplayName(),
null
));
return packet;
}
@RequiredArgsConstructor
@Getter
public static class Builder {
@NotNull private final UUID id;
@Nullable private String name;
@NotNull private String name = ""; // Avoid nullability issues as things are changing over versions
@Nullable private Skin skin;
private int latency;
private int gameMode;
@Nullable private Component displayName;
@NotNull
public Builder setName(@Nullable String name) {
public Builder setName(@NotNull String name) {
this.name = name;
return this;
}
@@ -114,9 +96,8 @@ public record FabricTabList(@NotNull FabricTabPlayer player) implements TabList
return this;
}
@Nullable
@NotNull
public GameProfile createProfile() {
if (name == null) return null;
GameProfile profile = new GameProfile(id, name);
if (skin != null) {
profile.getProperties().put(TabList.TEXTURES_PROPERTY,
@@ -31,7 +31,7 @@ public class FabricTabPlayer extends BackendTabPlayer {
private final EntityView entityView = new FabricEntityView(this);
public FabricTabPlayer(@NotNull FabricPlatform platform, @NotNull ServerPlayer player) {
super(platform, player, player.getUUID(), player.getGameProfile().getName(), player.level().dimension().location().toString());
super(platform, player, player.getUUID(), player.getGameProfile().getName(), "N/A");
}
@Override
@@ -41,12 +41,12 @@ public class FabricTabPlayer extends BackendTabPlayer {
@Override
public int getPing() {
return getPlayer().connection.latency();
return FabricMultiVersion.getPing(this);
}
@Override
public void sendMessage(@NotNull IChatBaseComponent message) {
getPlayer().sendSystemMessage(getPlatform().toComponent(message, getVersion()));
FabricMultiVersion.sendMessage(this, getPlatform().toComponent(message, getVersion()));
}
@Override
@@ -65,7 +65,7 @@ public class FabricTabPlayer extends BackendTabPlayer {
Collection<Property> properties = getPlayer().getGameProfile().getProperties().get(TabList.TEXTURES_PROPERTY);
if (properties.isEmpty()) return null; // Offline mode
Property skinProperty = properties.iterator().next();
return new TabList.Skin(skinProperty.value(), skinProperty.signature());
return FabricMultiVersion.propertyToSkin(skinProperty);
}
@Override
@@ -1,33 +1,25 @@
package me.neznamy.tab.platforms.fabric.features;
import me.neznamy.tab.platforms.fabric.FabricMultiVersion;
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.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.TabPlayer;
import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class FabricNameTagX extends BackendNameTagX {
private boolean enabled = true;
public boolean enabled = true;
public FabricNameTagX() {
ServerPlayerEvents.AFTER_RESPAWN.register((oldPlayer, newPlayer, alive) -> {
if (enabled) respawn(oldPlayer.getUUID());
});
// no sneaking for fabric //TODO
FabricMultiVersion.registerNameTagXEvents(this);
}
@Override
@@ -39,7 +31,7 @@ public class FabricNameTagX extends BackendNameTagX {
@Override
public boolean areInSameWorld(@NotNull TabPlayer player1, @NotNull TabPlayer player2) {
return ((FabricTabPlayer)player1).getPlayer().level() == ((FabricTabPlayer)player2).getPlayer().level();
return ((FabricTabPlayer)player1).getPlayer().level == ((FabricTabPlayer)player2).getPlayer().level;
}
@Override
@@ -77,7 +69,7 @@ public class FabricNameTagX extends BackendNameTagX {
@Override
public boolean isSneaking(@NotNull TabPlayer player) {
return ((FabricTabPlayer)player).getPlayer().isCrouching();
return FabricMultiVersion.isSneaking(((FabricTabPlayer)player).getPlayer());
}
@Override
@@ -119,13 +111,7 @@ public class FabricNameTagX extends BackendNameTagX {
@Override
@NotNull
public EntityData createDataWatcher(@NotNull TabPlayer viewer, byte flags, @NotNull String displayName, boolean nameVisible) {
return () -> Arrays.asList(
new SynchedEntityData.DataValue<>(0, EntityDataSerializers.BYTE, flags),
new SynchedEntityData.DataValue<>(2, EntityDataSerializers.OPTIONAL_COMPONENT,
Optional.of(((FabricTabPlayer)viewer).getPlatform().toComponent(IChatBaseComponent.optimizedComponent(displayName), viewer.getVersion()))),
new SynchedEntityData.DataValue<>(3, EntityDataSerializers.BOOLEAN, nameVisible),
new SynchedEntityData.DataValue<>(15, EntityDataSerializers.BYTE, (byte)16)
);
return FabricMultiVersion.createDataWatcher(viewer, flags, displayName, nameVisible);
}
@Override
@@ -8,6 +8,9 @@ accessible field net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPac
mutable field net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket players Ljava/util/Collection;
accessible field net/minecraft/server/network/ServerCommonPacketListenerImpl connection Lnet/minecraft/network/Connection;
# 1.20+
accessible field net/minecraft/world/entity/Entity level Lnet/minecraft/world/level/Level;
# new
accessible field net/minecraft/server/MinecraftServer LOGGER Lorg/slf4j/Logger;
mutable field net/minecraft/server/MinecraftServer LOGGER Lorg/slf4j/Logger;
@@ -159,12 +159,11 @@ public class FeatureManager {
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder(TabConstants.Placeholder.WORLD)).updateValue(changed, to);
}
public void onServerChange(@NotNull UUID playerUUID, @NotNull String to) {
public void onServerChange(@NotNull UUID playerUUID, @NotNull String to, boolean callOnLoginPacket) {
TabPlayer changed = TAB.getInstance().getPlayer(playerUUID);
if (changed == null) return;
String from = changed.getServer();
changed.setServer(to);
changed.getScoreboard().clearRegisteredObjectives();
((ProxyTabPlayer)changed).sendJoinPluginMessage();
for (TabFeature f : values) {
if (!(f instanceof ServerSwitchListener)) continue;
@@ -173,6 +172,7 @@ public class FeatureManager {
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.SERVER_SWITCH, System.nanoTime()-time);
}
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder(TabConstants.Placeholder.SERVER)).updateValue(changed, to);
if (callOnLoginPacket) onLoginPacket(changed);
}
public boolean onCommand(@Nullable TabPlayer sender, @NotNull String command) {
@@ -271,6 +271,16 @@ public class FeatureManager {
return newLatency;
}
public void onLoginPacket(TabPlayer packetReceiver) {
packetReceiver.getScoreboard().clearRegisteredObjectives();
for (TabFeature f : values) {
if (!(f instanceof LoginPacketListener)) continue;
long time = System.nanoTime();
((LoginPacketListener)f).onLoginPacket(packetReceiver);
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.PACKET_LOGIN, System.nanoTime() - time);
}
}
public void registerFeature(@NotNull String featureName, @NotNull TabFeature featureHandler) {
features.put(featureName, featureHandler);
values = features.values().toArray(new TabFeature[0]);
@@ -304,51 +314,50 @@ public class FeatureManager {
FeatureManager featureManager = TAB.getInstance().getFeatureManager();
int minorVersion = TAB.getInstance().getServerVersion().getMinorVersion();
if (configuration.getConfig().getBoolean("ping-spoof.enabled", false))
featureManager.registerFeature(TabConstants.Feature.PING_SPOOF, new PingSpoof());
boolean pingSpoof = configuration.getConfig().getBoolean("ping-spoof.enabled", false);
boolean bossbar = configuration.getConfig().getBoolean("bossbar.enabled", false);
boolean headerFooter = configuration.getConfig().getBoolean("header-footer.enabled", true);
boolean spectatorFix = configuration.getConfig().getBoolean("prevent-spectator-effect.enabled", false);
boolean scoreboard = configuration.getConfig().getBoolean("scoreboard.enabled", false);
boolean perWorldPlayerList = configuration.getConfig().getBoolean("per-world-playerlist.enabled", false);
boolean layout = configuration.getConfig().getBoolean("layout.enabled", false);
boolean yellowNumber = configuration.getConfig().getBoolean("yellow-number-in-tablist.enabled", true);
boolean belowName = configuration.getConfig().getBoolean("belowname-objective.enabled", false);
boolean teams = configuration.getConfig().getBoolean("scoreboard-teams.enabled", true);
boolean unlimitedTags = configuration.getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false);
boolean globalPlayerList = configuration.getConfig().getBoolean("global-playerlist.enabled", false);
boolean tablistFormatting = configuration.getConfig().getBoolean("tablist-name-formatting.enabled", true);
boolean alignedSuffix = configuration.getConfig().getBoolean("tablist-name-formatting.align-tabsuffix-on-the-right", false);
if (configuration.getConfig().getBoolean("bossbar.enabled", false)) {
featureManager.registerFeature(TabConstants.Feature.BOSS_BAR,
minorVersion >= 9 ? new BossBarManagerImpl() : TAB.getInstance().getPlatform().getLegacyBossBar());
}
if (configuration.getConfig().getBoolean("header-footer.enabled", true))
featureManager.registerFeature(TabConstants.Feature.HEADER_FOOTER, new HeaderFooter());
if (configuration.getConfig().getBoolean("prevent-spectator-effect.enabled", false))
featureManager.registerFeature(TabConstants.Feature.SPECTATOR_FIX, new SpectatorFix());
if (perWorldPlayerList && layout) TAB.getInstance().getMisconfigurationHelper().bothPerWorldPlayerListAndLayoutEnabled();
if (yellowNumber && layout) TAB.getInstance().getMisconfigurationHelper().layoutBreaksYellowNumber();
if (spectatorFix && layout) TAB.getInstance().getMisconfigurationHelper().layoutIncludesPreventSpectatorEffect();
if (globalPlayerList && layout) TAB.getInstance().getMisconfigurationHelper().bothGlobalPlayerListAndLayoutEnabled();
if (configuration.isPipelineInjection()) {
PipelineInjector inj = TAB.getInstance().getPlatform().createPipelineInjector();
if (inj != null) featureManager.registerFeature(TabConstants.Feature.PIPELINE_INJECTION, inj);
}
if (configuration.getConfig().getBoolean("scoreboard.enabled", false))
featureManager.registerFeature(TabConstants.Feature.SCOREBOARD, new ScoreboardManagerImpl());
if (configuration.getConfig().getBoolean("per-world-playerlist.enabled", false)) {
if (perWorldPlayerList) {
TabFeature pwp = TAB.getInstance().getPlatform().getPerWorldPlayerList();
if (pwp != null) featureManager.registerFeature(TabConstants.Feature.PER_WORLD_PLAYER_LIST, pwp);
if (configuration.getConfig().getBoolean("layout.enabled", false)) {
TAB.getInstance().getMisconfigurationHelper().bothPerWorldPlayerListAndLayoutEnabled();
}
}
if (configuration.getConfig().getBoolean("yellow-number-in-tablist.enabled", true))
featureManager.registerFeature(TabConstants.Feature.YELLOW_NUMBER, new YellowNumber());
if (configuration.getConfig().getBoolean("belowname-objective.enabled", true))
featureManager.registerFeature(TabConstants.Feature.BELOW_NAME, new BelowName());
// No requirements, but due to chicken vs egg, the feature uses NameTags, Layout and RedisBungee
if (configuration.getConfig().getBoolean("scoreboard-teams.enabled", true) ||
configuration.getConfig().getBoolean("layout.enabled", false)) {
featureManager.registerFeature(TabConstants.Feature.SORTING, new Sorting());
if (bossbar) {
featureManager.registerFeature(TabConstants.Feature.BOSS_BAR,
minorVersion >= 9 ? new BossBarManagerImpl() : TAB.getInstance().getPlatform().getLegacyBossBar());
}
if (pingSpoof) featureManager.registerFeature(TabConstants.Feature.PING_SPOOF, new PingSpoof());
if (headerFooter) featureManager.registerFeature(TabConstants.Feature.HEADER_FOOTER, new HeaderFooter());
if (spectatorFix) featureManager.registerFeature(TabConstants.Feature.SPECTATOR_FIX, new SpectatorFix());
if (scoreboard) featureManager.registerFeature(TabConstants.Feature.SCOREBOARD, new ScoreboardManagerImpl());
if (yellowNumber) featureManager.registerFeature(TabConstants.Feature.YELLOW_NUMBER, new YellowNumber());
if (belowName) featureManager.registerFeature(TabConstants.Feature.BELOW_NAME, new BelowName());
if (teams || layout) featureManager.registerFeature(TabConstants.Feature.SORTING, new Sorting());
// Must be loaded after: Sorting
if (configuration.getConfig().getBoolean("scoreboard-teams.enabled", true)) {
if (configuration.getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false) && minorVersion >= 8) {
if (teams) {
if (unlimitedTags && minorVersion >= 8) {
NameTag unlimited = TAB.getInstance().getPlatform().getUnlimitedNameTags();
if (unlimited instanceof NameTagX) {
featureManager.registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, unlimited);
@@ -361,19 +370,11 @@ public class FeatureManager {
}
// Must be loaded after: Sorting
if (minorVersion >= 8 && configuration.getConfig().getBoolean("layout.enabled", false)) {
featureManager.registerFeature(TabConstants.Feature.LAYOUT, new LayoutManagerImpl());
if (configuration.getConfig().getBoolean("yellow-number-in-tablist.enabled", true)) {
TAB.getInstance().getMisconfigurationHelper().layoutBreaksYellowNumber();
}
if (configuration.getConfig().getBoolean("prevent-spectator-effect.enabled", false)) {
TAB.getInstance().getMisconfigurationHelper().layoutIncludesPreventSpectatorEffect();
}
}
if (layout) featureManager.registerFeature(TabConstants.Feature.LAYOUT, new LayoutManagerImpl());
// Must be loaded after: Layout
if (minorVersion >= 8 && configuration.getConfig().getBoolean("tablist-name-formatting.enabled", true)) {
if (configuration.getConfig().getBoolean("tablist-name-formatting.align-tabsuffix-on-the-right", false)) {
if (tablistFormatting) {
if (alignedSuffix) {
featureManager.registerFeature(TabConstants.Feature.PLAYER_LIST, new AlignedPlayerList());
} else {
featureManager.registerFeature(TabConstants.Feature.PLAYER_LIST, new PlayerList());
@@ -381,12 +382,8 @@ public class FeatureManager {
}
// Must be loaded after: PlayerList
if (configuration.getConfig().getBoolean("global-playerlist.enabled", false) &&
TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY) {
if (globalPlayerList && TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY) {
featureManager.registerFeature(TabConstants.Feature.GLOBAL_PLAYER_LIST, new GlobalPlayerList());
if (configuration.getConfig().getBoolean("layout.enabled", false)) {
TAB.getInstance().getMisconfigurationHelper().bothGlobalPlayerListAndLayoutEnabled();
}
}
// Must be loaded after: Global PlayerList, PlayerList, NameTags, YellowNumber, BelowName
@@ -9,6 +9,7 @@ import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.features.layout.LayoutManagerImpl;
import me.neznamy.tab.shared.features.sorting.types.SortingType;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -361,7 +362,10 @@ public class MisconfigurationHelper {
// Runtime Errors
// ------------------------
public void invalidNumberForBossBarProgress(@NotNull String bossBar, @NotNull String input, @NotNull String configuredValue) {
public void invalidNumberForBossBarProgress(@NotNull String bossBar, @NotNull String input, @NotNull String configuredValue, TabPlayer player) {
// Placeholders are not initialized, because bridge did not respond yet (typically on join)
if (player instanceof ProxyTabPlayer && !((ProxyTabPlayer)player).isBridgeConnected()) return;
if (configuredValue.contains("%")) {
TAB.getInstance().getPlatform().logWarn(new IChatBaseComponent("Placeholder \"" + configuredValue +
"\" used in BossBar progress of \"" + bossBar + "\" returned value, which cannot be evaluated to a number between 0 and 100 (\"" + input + "\")"));
@@ -373,6 +377,9 @@ public class MisconfigurationHelper {
}
public void invalidInputForNumericSorting(SortingType type, String placeholder, String output, TabPlayer player) {
// Placeholders are not initialized, because bridge did not respond yet (typically on join)
if (player instanceof ProxyTabPlayer && !((ProxyTabPlayer)player).isBridgeConnected()) return;
TAB.getInstance().getPlatform().logWarn(new IChatBaseComponent("Placeholder " + placeholder + " used in sorting type " +
type + " returned \"" + output + "\" for player " + player.getName() + ", which is not a valid number."));
}
@@ -103,7 +103,7 @@ public class Property {
}
String rawFormattedValue0 = value;
for (String placeholder : placeholders0) {
rawFormattedValue0 = rawFormattedValue0.replace(placeholder, "%s");
rawFormattedValue0 = replaceFirst(rawFormattedValue0, placeholder, "%s");
}
if (placeholders0.size() > 0 && rawFormattedValue0.contains("%")) {
int index = rawFormattedValue0.lastIndexOf('%');
@@ -129,6 +129,15 @@ public class Property {
}
}
private String replaceFirst(String original, String searchString, String replacement) {
int index = original.indexOf(searchString);
if (index != -1) {
return original.substring(0, index) + replacement + original.substring(index + searchString.length());
} else {
return original;
}
}
/**
* Changes raw value to new provided value and performs all
* operations related to it. Changes source as well.
@@ -74,6 +74,9 @@ public enum ProtocolVersion {
V1_5_1 (60),
V1_5 (60);
/** Newest MC version this plugin jar knows */
public static final ProtocolVersion LATEST_KNOWN_VERSION = ProtocolVersion.V1_20_2;
/** Version's network id found at https://wiki.vg/Protocol_version_numbers */
private final int networkId;
@@ -55,7 +55,7 @@ public class TAB extends TabAPI {
@Getter private final DisabledCommand disabledCommand = new DisabledCommand();
/** Implementation of platform the plugin is installed on for platform-specific calls */
@Getter private final Platform<?> platform;
@Getter private final Platform platform;
/**
* CPU manager for thread and task management as well as
@@ -107,7 +107,7 @@ public class TAB extends TabAPI {
* @param platform
* Platform interface
*/
public static void create(@NotNull Platform<?> platform) {
public static void create(@NotNull Platform platform) {
instance = new TAB(platform);
instance.load();
}
@@ -119,7 +119,7 @@ public class TAB extends TabAPI {
* @param platform
* Platform interface
*/
private TAB(@NotNull Platform<?> platform) {
private TAB(@NotNull Platform platform) {
this.platform = platform;
this.serverVersion = platform.getServerVersion();
this.dataFolder = platform.getDataFolder();
@@ -134,7 +134,7 @@ public class TAB extends TabAPI {
platform.registerCommand();
platform.startMetrics();
if (platform instanceof ProxyPlatform) {
((ProxyPlatform<?>) platform).registerChannel();
((ProxyPlatform) platform).registerChannel();
}
}
@@ -52,6 +52,7 @@ public class TabConstants {
public static final String PACKET_ENTITY_SPAWN = "PacketPlayOutNamedEntitySpawn";
public static final String PACKET_ENTITY_DESTROY = "PacketPlayOutEntityDestroy";
public static final String RAW_PACKET_OUT = "Packet reading (out)";
public static final String PACKET_LOGIN = "Login packet";
public static final String PLACEHOLDER_REFRESHING = "Refreshing placeholders";
@@ -10,7 +10,7 @@ import me.neznamy.tab.shared.platform.Platform;
import me.neznamy.tab.shared.features.redis.RedisSupport;
import org.jetbrains.annotations.NotNull;
public interface BackendPlatform<T> extends Platform<T> {
public interface BackendPlatform extends Platform {
@Override
@NotNull default GroupManager detectPermissionPlugin() {
@@ -26,7 +26,7 @@ public abstract class BackendTabPlayer extends TabPlayer {
* @param world
* Player's world
*/
protected BackendTabPlayer(@NotNull BackendPlatform<?> platform, @NotNull Object player, @NotNull UUID uniqueId,
protected BackendTabPlayer(@NotNull BackendPlatform platform, @NotNull Object player, @NotNull UUID uniqueId,
@NotNull String name, @NotNull String world) {
super(platform, player, uniqueId, name, TAB.getInstance().getConfiguration().getServerName(),
world, ViaVersionHook.getInstance().getPlayerVersion(uniqueId, name), true);
@@ -12,14 +12,11 @@ public class MiniMessageFormat implements RGBFormatter {
@Override
public @NotNull String reformat(@NotNull String text) {
if (
!text.contains("<") // No MiniMessage syntax is used
|| text.contains(EnumChatFormat.COLOR_STRING) // MiniMessage throws errors when using legacy color code
) return text;
try {
return LegacyComponentSerializer.legacySection().serialize(MiniMessage.miniMessage().deserialize(text));
} catch (Throwable ignored) {
return text;
if (text.contains("<") && !text.contains(EnumChatFormat.COLOR_STRING)) {
try {
return LegacyComponentSerializer.legacySection().serialize(MiniMessage.miniMessage().deserialize(text));
} catch (Throwable ignored) {}
}
return text;
}
}
@@ -6,10 +6,8 @@ import java.util.concurrent.*;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.Getter;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.features.types.TabFeature;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -52,11 +50,6 @@ public class CpuManager {
lastReport = new CpuReport(UPDATE_RATE_SECONDS, featureUsageCurrent, placeholderUsageCurrent);
featureUsageCurrent = new ConcurrentHashMap<>();
placeholderUsageCurrent = new ConcurrentHashMap<>();
if (lastReport.getPlaceholderUsageTotal() > 50) {
TAB.getInstance().getPlatform().logWarn(new IChatBaseComponent("CPU usage of placeholders is " + (int) lastReport.getPlaceholderUsageTotal() +
"%. See /" + (TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY ? TabConstants.COMMAND_PROXY : TabConstants.COMMAND_BACKEND) +
" cpu for more info. Try increasing refresh intervals."));
}
});
}
@@ -18,7 +18,7 @@ import java.util.Map;
* Feature handler for BelowName feature
*/
public class BelowName extends TabFeature implements JoinListener, Loadable, UnLoadable,
ServerSwitchListener, Refreshable {
Refreshable, LoginPacketListener {
public static final String OBJECTIVE_NAME = "TAB-BelowName";
@@ -98,20 +98,6 @@ public class BelowName extends TabFeature implements JoinListener, Loadable, UnL
if (redis != null) redis.updateBelowName(connectedPlayer, number);
}
@Override
public void onServerChange(@NotNull TabPlayer player, @NotNull String from, @NotNull String to) {
if (disableChecker.isDisabledPlayer(player)) return;
player.getScoreboard().registerObjective(
OBJECTIVE_NAME,
player.getProperty(TabConstants.Property.BELOWNAME_TEXT).updateAndGet(),
Scoreboard.HealthDisplay.INTEGER
);
player.getScoreboard().setDisplaySlot(Scoreboard.DisplaySlot.BELOW_NAME, OBJECTIVE_NAME);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
player.getScoreboard().setScore(OBJECTIVE_NAME, all.getNickname(), getValue(all));
}
}
public void onDisableConditionChange(TabPlayer p, boolean disabledNow) {
if (disabledNow) {
p.getScoreboard().unregisterObjective(OBJECTIVE_NAME);
@@ -134,6 +120,16 @@ public class BelowName extends TabFeature implements JoinListener, Loadable, UnL
if (redis != null) redis.updateBelowName(refreshed, number);
}
@Override
public void onLoginPacket(TabPlayer player) {
if (disableChecker.isDisabledPlayer(player) || !player.isLoaded()) return;
player.getScoreboard().registerObjective(OBJECTIVE_NAME, player.getProperty(TabConstants.Property.BELOWNAME_TEXT).updateAndGet(), Scoreboard.HealthDisplay.INTEGER);
player.getScoreboard().setDisplaySlot(Scoreboard.DisplaySlot.BELOW_NAME, OBJECTIVE_NAME);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all.isLoaded()) player.getScoreboard().setScore(OBJECTIVE_NAME, all.getNickname(), getValue(all));
}
}
@RequiredArgsConstructor
public static class TextRefresher extends TabFeature implements Refreshable {
@@ -2,6 +2,11 @@ package me.neznamy.tab.shared.features;
import lombok.Getter;
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.*;
import me.neznamy.tab.shared.platform.TabPlayer;
import org.jetbrains.annotations.NotNull;
@@ -20,13 +25,26 @@ public class PingSpoof extends TabFeature implements JoinListener, LatencyListen
/** Value to display as ping instead of real ping */
private final int value = TAB.getInstance().getConfig().getInt("ping-spoof.value", 0);
private LayoutManagerImpl layoutManager;
@Override
public int onLatencyChange(TabPlayer packetReceiver, UUID id, int latency) {
return value;
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()) {
PlayerSlot slot = group.getPlayerSlots().get((int) id.getLeastSignificantBits());
if (slot != null && slot.getPlayer() != null) return value;
}
}
}
if (TAB.getInstance().getPlayer(id) != null) return value;
return latency;
}
@Override
public void load() {
layoutManager = TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.LAYOUT);
updateAll(false);
}
@@ -295,4 +295,8 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
placeholderUsage.remove(identifier);
recalculateUsedPlaceholders();
}
public boolean isPlaceholderRegistered(@NotNull String identifier) {
return registeredPlaceholders.containsKey(identifier);
}
}
@@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull;
* PLAYER_LIST display slot (in tablist).
*/
public class YellowNumber extends TabFeature implements JoinListener, Loadable, UnLoadable,
ServerSwitchListener, Refreshable {
Refreshable, LoginPacketListener {
@Getter private final String featureName = "Yellow Number";
@Getter private final String refreshDisplayName = "Updating value";
@@ -107,16 +107,6 @@ public class YellowNumber extends TabFeature implements JoinListener, Loadable,
if (redis != null) redis.updateYellowNumber(connectedPlayer, value);
}
@Override
public void onServerChange(@NotNull TabPlayer p, @NotNull String from, @NotNull String to) {
if (disableChecker.isDisabledPlayer(p) || p.isBedrockPlayer()) return;
p.getScoreboard().registerObjective(OBJECTIVE_NAME, TITLE, displayType);
p.getScoreboard().setDisplaySlot(Scoreboard.DisplaySlot.PLAYER_LIST, OBJECTIVE_NAME);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
p.getScoreboard().setScore(OBJECTIVE_NAME, all.getNickname(), getValue(all));
}
}
public void onDisableConditionChange(TabPlayer p, boolean disabledNow) {
if (disabledNow) {
p.getScoreboard().unregisterObjective(OBJECTIVE_NAME);
@@ -134,4 +124,14 @@ public class YellowNumber extends TabFeature implements JoinListener, Loadable,
}
if (redis != null) redis.updateYellowNumber(refreshed, value);
}
@Override
public void onLoginPacket(TabPlayer p) {
if (disableChecker.isDisabledPlayer(p) || p.isBedrockPlayer() || !p.isLoaded()) return;
p.getScoreboard().registerObjective(OBJECTIVE_NAME, TITLE, displayType);
p.getScoreboard().setDisplaySlot(Scoreboard.DisplaySlot.PLAYER_LIST, OBJECTIVE_NAME);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all.isLoaded()) p.getScoreboard().setScore(OBJECTIVE_NAME, all.getNickname(), getValue(all));
}
}
}
@@ -102,8 +102,8 @@ public class AlignedPlayerList extends PlayerList implements QuitListener {
@Override
public void onQuit(@NotNull TabPlayer p) {
playerViews.values().forEach(v -> v.processPlayerQuit(p));
playerViews.remove(p);
playerViews.values().forEach(v -> v.processPlayerQuit(p));
}
@Override
@@ -161,7 +161,12 @@ public class BossBarLine implements BossBar {
if (value > 100) value = 100;
return value;
} catch (NumberFormatException e) {
TAB.getInstance().getMisconfigurationHelper().invalidNumberForBossBarProgress(name, progress, player.getProperty(propertyProgress).getCurrentRawValue());
TAB.getInstance().getMisconfigurationHelper().invalidNumberForBossBarProgress(
name,
progress,
player.getProperty(propertyProgress).getCurrentRawValue(),
player
);
return 100;
}
}
@@ -230,6 +235,10 @@ public class BossBarLine implements BossBar {
player.setProperty(progressRefresher, propertyProgress, progress);
player.setProperty(colorRefresher, propertyColor, color);
player.setProperty(styleRefresher, propertyStyle, style);
sendToPlayerRaw(player);
}
public void sendToPlayerRaw(TabPlayer player) {
player.getBossBar().create(
uniqueId,
player.getProperty(propertyTitle).updateAndGet(),
@@ -2,6 +2,7 @@ package me.neznamy.tab.shared.features.bossbar;
import lombok.Getter;
import lombok.NonNull;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.api.bossbar.BarColor;
import me.neznamy.tab.api.bossbar.BarStyle;
@@ -19,7 +20,7 @@ import java.util.stream.Collectors;
* Class for handling BossBar feature
*/
public class BossBarManagerImpl extends TabFeature implements BossBarManager, JoinListener, CommandListener, Loadable,
UnLoadable, Refreshable {
UnLoadable, Refreshable, LoginPacketListener {
//default BossBars
private final List<String> defaultBars = new ArrayList<>();
@@ -255,4 +256,16 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager, Jo
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);
}
}
}
}
}
@@ -16,7 +16,7 @@ import org.jetbrains.annotations.NotNull;
* Feature handler for global PlayerList feature
*/
public class GlobalPlayerList extends TabFeature implements JoinListener, QuitListener, VanishListener, GameModeListener,
Loadable, UnLoadable, ServerSwitchListener {
Loadable, UnLoadable, ServerSwitchListener, LoginPacketListener {
// config options
private final List<String> spyServers = TAB.getInstance().getConfiguration().getConfig().getStringList("global-playerlist.spy-servers", Collections.singletonList("spyserver1"));
@@ -102,14 +102,6 @@ public class GlobalPlayerList extends TabFeature implements JoinListener, QuitLi
@Override
public void onServerChange(@NotNull TabPlayer changed, @NotNull String from, @NotNull String to) {
// Event is fired after all entries are removed from switched player's tablist, ready to re-add immediately
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
// Ignore players on the same server, since the server already sends add packet
if (!all.getServer().equals(changed.getServer()) && shouldSee(changed, all)) {
changed.getTabList().addEntry(getAddInfoData(all, changed));
}
}
// Player who switched server is removed from tablist of other players in ~70-110ms (depending on online count), re-add with a delay
TAB.getInstance().getCPUManager().runTaskLater(200, featureName, TabConstants.CpuUsageCategory.SERVER_SWITCH, () -> {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
@@ -125,6 +117,17 @@ public class GlobalPlayerList extends TabFeature implements JoinListener, QuitLi
});
}
@Override
public void onLoginPacket(TabPlayer player) {
// Login packet itself does not clear this, but BungeeCord does when the packet is received
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
// Ignore players on the same server, since the server already sends add packet
if (!all.getServer().equals(player.getServer()) && shouldSee(player, all)) {
player.getTabList().addEntry(getAddInfoData(all, player));
}
}
}
public @NotNull TabList.Entry getAddInfoData(@NotNull TabPlayer p, @NotNull TabPlayer viewer) {
IChatBaseComponent format = null;
if (playerlist != null && !playerlist.getDisableChecker().isDisabledPlayer(p)) {
@@ -84,6 +84,8 @@ public abstract class NettyPipelineInjector extends PipelineInjector {
public abstract void onPlayerInfo(@NotNull TabPlayer receiver, @NotNull Object packet);
public abstract boolean isLogin(@NotNull Object packet);
/**
* Removes all real players from team if packet does not come from TAB and reports this to override log
*
@@ -110,6 +112,13 @@ public abstract class NettyPipelineInjector extends PipelineInjector {
modifyPlayers(packet);
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
}
if (isLogin(packet)) {
// Logic must be processed after packet is actually sent
super.write(context, packet, channelPromise);
TAB.getInstance().getCPUManager().runTaskLater(200, getFeatureName(), TabConstants.CpuUsageCategory.PACKET_LOGIN,
() -> TAB.getInstance().getFeatureManager().onLoginPacket(player));
return;
}
TAB.getInstance().getFeatureManager().onPacketSend(player, packet);
} catch (Throwable e) {
TAB.getInstance().getErrorManager().printError("An error occurred when reading packets", e);
@@ -24,7 +24,7 @@ import org.jetbrains.annotations.Nullable;
@Getter
public class LayoutManagerImpl extends TabFeature implements LayoutManager, JoinListener, QuitListener, VanishListener, Loadable,
UnLoadable, Refreshable, ServerSwitchListener {
UnLoadable, Refreshable, LoginPacketListener {
/** Config options */
private final Direction direction = parseDirection(TAB.getInstance().getConfig().getString("layout.direction", "COLUMNS"));
@@ -181,12 +181,6 @@ public class LayoutManagerImpl extends TabFeature implements LayoutManager, Join
views.values().forEach(LayoutView::tick);
}
@Override
public void onServerChange(@NotNull TabPlayer changed, @NotNull String from, @NotNull String to) {
LayoutView view = views.get(changed);
if (view != null) view.send();
}
@Override
public Layout createNewLayout(String name) {
return new LayoutPattern(this, name, Collections.emptyMap());
@@ -204,6 +198,12 @@ public class LayoutManagerImpl extends TabFeature implements LayoutManager, Join
refresh((TabPlayer) player, false);
}
@Override
public void onLoginPacket(TabPlayer player) {
LayoutView view = views.get(player);
if (view != null) view.send();
}
@RequiredArgsConstructor
public enum Direction {
@@ -21,7 +21,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
public class NameTag extends TabFeature implements NameTagManager, JoinListener, QuitListener,
Loadable, UnLoadable, WorldSwitchListener, ServerSwitchListener, Refreshable {
Loadable, UnLoadable, WorldSwitchListener, ServerSwitchListener, Refreshable, LoginPacketListener {
@Getter private final String featureName = "NameTags";
@Getter private final String refreshDisplayName = "Updating prefix/suffix";
@@ -100,7 +100,6 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
@Override
public void onJoin(@NotNull TabPlayer connectedPlayer) {
sorting.constructTeamNames(connectedPlayer);
updateProperties(connectedPlayer);
hiddenNameTagFor.put(connectedPlayer, new ArrayList<>());
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
@@ -135,9 +134,6 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
@Override
public void onServerChange(@NonNull TabPlayer p, @NonNull String from, @NonNull String to) {
if (updateProperties(p) && !disableChecker.isDisabledPlayer(p)) updateTeamData(p);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (!disableChecker.isDisabledPlayer(all)) registerTeam(all, p);
}
}
@Override
@@ -341,4 +337,12 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
public boolean hasHiddenNameTagVisibilityView(@NonNull me.neznamy.tab.api.TabPlayer player) {
return playersWithInvisibleNameTagView.contains(player);
}
@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);
}
}
}
@@ -29,7 +29,7 @@ import java.util.function.Supplier;
@SuppressWarnings("UnstableApiUsage")
@Getter
public abstract class RedisSupport extends TabFeature implements JoinListener, QuitListener,
DisplayNameListener, Loadable, UnLoadable, ServerSwitchListener {
DisplayNameListener, Loadable, UnLoadable, ServerSwitchListener, LoginPacketListener {
@NotNull private final String featureName = "RedisSupport";
@@ -207,4 +207,9 @@ public abstract class RedisSupport extends TabFeature implements JoinListener, Q
messages.put(name, supplier);
classStringMap.put(clazz, name);
}
@Override
public void onLoginPacket(TabPlayer player) {
features.forEach(f -> f.onLoginPacket(player));
}
}
@@ -42,21 +42,6 @@ public class RedisBelowName extends RedisFeature {
}
}
@Override
public void onServerSwitch(@NotNull TabPlayer player) {
onJoin(player);
}
@Override
public void onServerSwitch(@NotNull RedisPlayer player) {
// No action is needed
}
@Override
public void onQuit(@NotNull RedisPlayer player) {
// No action is needed
}
@Override
public void write(@NotNull ByteArrayDataOutput out, @NotNull TabPlayer player) {
out.writeInt(TAB.getInstance().getErrorManager().parseInteger(
@@ -68,6 +53,11 @@ public class RedisBelowName extends RedisFeature {
values.put(player, in.readInt());
}
@Override
public void onLoginPacket(@NotNull TabPlayer player) {
onJoin(player);
}
@NoArgsConstructor
@AllArgsConstructor
public class Update extends RedisMessage {
@@ -12,13 +12,15 @@ public abstract class RedisFeature {
public abstract void onJoin(@NotNull RedisPlayer player);
public abstract void onServerSwitch(@NotNull TabPlayer player);
public void onServerSwitch(@NotNull TabPlayer player) {/* Do nothing by default */}
public abstract void onServerSwitch(@NotNull RedisPlayer player);
public void onServerSwitch(@NotNull RedisPlayer player) {/* Do nothing by default */}
public abstract void onQuit(@NotNull RedisPlayer player);
public void onQuit(@NotNull RedisPlayer player) {/* Do nothing by default */}
public abstract void write(@NotNull ByteArrayDataOutput out, @NotNull TabPlayer player);
public abstract void read(@NotNull ByteArrayDataInput in, @NotNull RedisPlayer player);
public void onLoginPacket(@NotNull TabPlayer player) {/* Do nothing by default */}
}
@@ -41,11 +41,6 @@ public class RedisGlobalPlayerList extends RedisFeature {
}
}
@Override
public void onServerSwitch(@NotNull TabPlayer player) {
onJoin(player);
}
@Override
public void onServerSwitch(@NotNull RedisPlayer player) {
TAB.getInstance().getCPUManager().runTaskLater(200, redisSupport.getFeatureName(), TabConstants.CpuUsageCategory.SERVER_SWITCH, () -> {
@@ -93,6 +88,11 @@ public class RedisGlobalPlayerList extends RedisFeature {
}
}
@Override
public void onLoginPacket(@NotNull TabPlayer player) {
onJoin(player);
}
private boolean shouldSee(@NotNull TabPlayer viewer, @NotNull RedisPlayer target) {
if (target.isVanished() && !viewer.hasPermission(TabConstants.Permission.SEE_VANISHED)) return false;
if (globalPlayerList.isSpyServer(viewer.getServer())) return true;
@@ -50,16 +50,6 @@ public class RedisPlayerList extends RedisFeature {
onJoin(player);
}
@Override
public void onServerSwitch(@NotNull RedisPlayer player) {
// No action is needed
}
@Override
public void onQuit(@NotNull RedisPlayer player) {
// No action is needed
}
@Override
public void write(@NotNull ByteArrayDataOutput out, @NotNull TabPlayer player) {
out.writeUTF(player.getProperty(TabConstants.Property.TABPREFIX).get() +
@@ -52,16 +52,6 @@ public class RedisTeams extends RedisFeature {
}
}
@Override
public void onServerSwitch(@NotNull TabPlayer player) {
onJoin(player);
}
@Override
public void onServerSwitch(@NotNull RedisPlayer player) {
// No action is needed
}
@Override
public void onQuit(@NotNull RedisPlayer player) {
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
@@ -87,6 +77,11 @@ public class RedisTeams extends RedisFeature {
nameVisibilities.put(player, NameVisibility.getByName(in.readUTF()));
}
@Override
public void onLoginPacket(@NotNull TabPlayer player) {
onJoin(player);
}
private @NotNull String checkTeamName(@NotNull RedisPlayer player, @NotNull String currentName15, int id) {
String potentialTeamName = currentName15 + (char)id;
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
@@ -42,21 +42,6 @@ public class RedisYellowNumber extends RedisFeature {
}
}
@Override
public void onServerSwitch(@NotNull TabPlayer player) {
onJoin(player);
}
@Override
public void onServerSwitch(@NotNull RedisPlayer player) {
// No action is needed
}
@Override
public void onQuit(@NotNull RedisPlayer player) {
// No action is needed
}
@Override
public void write(@NotNull ByteArrayDataOutput out, @NotNull TabPlayer player) {
out.writeInt(TAB.getInstance().getErrorManager().parseInteger(
@@ -68,6 +53,11 @@ public class RedisYellowNumber extends RedisFeature {
values.put(player, in.readInt());
}
@Override
public void onLoginPacket(@NotNull TabPlayer player) {
onJoin(player);
}
@NoArgsConstructor
@AllArgsConstructor
public class Update extends RedisMessage {
@@ -19,7 +19,7 @@ import java.util.Map.Entry;
*/
public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManager, JoinListener,
CommandListener, DisplayObjectiveListener, ObjectiveListener, Loadable, UnLoadable, Refreshable,
ServerSwitchListener, QuitListener {
QuitListener, LoginPacketListener {
public static final String OBJECTIVE_NAME = "TAB-Scoreboard";
@@ -305,16 +305,6 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
return activeScoreboards.get(player);
}
@Override
public void onServerChange(@NotNull TabPlayer changed, @NotNull String from, @NotNull String to) {
otherPluginScoreboards.remove(changed);
ScoreboardImpl scoreboard = activeScoreboards.get(changed);
if (scoreboard != null) {
scoreboard.removePlayerFromSet(changed);
scoreboard.addPlayer(changed);
}
}
@Override
public void onQuit(@NotNull TabPlayer disconnectedPlayer) {
ScoreboardImpl sb = activeScoreboards.get(disconnectedPlayer);
@@ -322,4 +312,15 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
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);
}
}
}
@@ -15,7 +15,7 @@ import java.util.function.BiConsumer;
public class DisableChecker extends TabFeature implements Refreshable {
@Getter private final String featureName;
@Getter private final String refreshDisplayName = "Updating display condition";
@Getter private final String refreshDisplayName = "Refreshing disable condition";
private final @Nullable Condition disableCondition;
private final BiConsumer<TabPlayer, Boolean> action;
@@ -0,0 +1,18 @@
package me.neznamy.tab.shared.features.types;
import me.neznamy.tab.shared.platform.TabPlayer;
/**
* Interface for features that resend content on Login packet.
*/
public interface LoginPacketListener {
/**
* Called after Login packet is received by the player to resend all
* scoreboard content as the packet has cleared it.
*
* @param player
* Player who received the packet
*/
void onLoginPacket(TabPlayer player);
}
@@ -5,6 +5,7 @@ import me.neznamy.tab.shared.chat.ChatModifier;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import org.jetbrains.annotations.NotNull;
@@ -29,9 +30,9 @@ public class AdventureHook {
net.kyori.adventure.text.format.TextColor color = null;
if (modifier.getColor() != null) {
if (clientVersion.getMinorVersion() >= 16) {
color = net.kyori.adventure.text.format.TextColor.color(modifier.getColor().getRgb());
color = TextColor.color(modifier.getColor().getRgb());
} else {
color = net.kyori.adventure.text.format.TextColor.color(modifier.getColor().getLegacyColor().getHexCode());
color = TextColor.color(modifier.getColor().getLegacyColor().getHexCode());
}
}
Set<TextDecoration> decorations = new HashSet<>();
@@ -40,8 +41,9 @@ public class AdventureHook {
if (modifier.isObfuscated()) decorations.add(TextDecoration.OBFUSCATED);
if (modifier.isStrikethrough()) decorations.add(TextDecoration.STRIKETHROUGH);
if (modifier.isUnderlined()) decorations.add(TextDecoration.UNDERLINED);
Component advComponent = Component.text(component.getText(), color, decorations);
if (modifier.getFont() != null) advComponent = advComponent.font(Key.key(modifier.getFont()));
return advComponent.children(component.getExtra().stream().map(c -> toAdventureComponent(c, clientVersion)).collect(Collectors.toList()));
Key font = modifier.getFont() == null ? null : Key.key(modifier.getFont());
return Component.text(component.getText(), color, decorations)
.font(font)
.children(component.getExtra().stream().map(c -> toAdventureComponent(c, clientVersion)).collect(Collectors.toList()));
}
}
@@ -42,8 +42,11 @@ public class ViaVersionHook {
}
ProtocolVersion protocol = ProtocolVersion.fromNetworkId(version);
if (protocol == ProtocolVersion.UNKNOWN_CLIENT_VERSION) {
TAB.getInstance().getPlatform().logWarn(new IChatBaseComponent("ViaVersion returned unknown protocol version " + version +
" for player " + playerName + ". This may result in plugin not working correctly for them."));
TAB.getInstance().getPlatform().logWarn(new IChatBaseComponent(String.format(
"ViaVersion returned unknown protocol version %d for player %s. " +
"Latest version recognized by this plugin version is %d (%s). " +
"Did a new MC version come out without you updating the plugin? This may result in plugin not working correctly for them.",
version, playerName, ProtocolVersion.LATEST_KNOWN_VERSION.getNetworkId(), ProtocolVersion.LATEST_KNOWN_VERSION.getFriendlyName())));
} else {
TAB.getInstance().debug("ViaVersion returned protocol version " + version + " for " + playerName);
}
@@ -59,14 +59,16 @@ public abstract class EventListener<T> {
* UUID of the player
* @param server
* New server
* @param callOnLoginPacket
* Whether onLoginPacket method should be called as well or not
*/
public void serverChange(@NotNull T player, @NotNull UUID uuid, @NotNull String server) {
public void serverChange(@NotNull T player, @NotNull UUID uuid, @NotNull String server, boolean callOnLoginPacket) {
if (TAB.getInstance().isPluginDisabled()) return;
TAB.getInstance().getCPUManager().runTask(() -> {
if (TAB.getInstance().getPlayer(uuid) == null) {
TAB.getInstance().getFeatureManager().onJoin(createPlayer(player));
} else {
TAB.getInstance().getFeatureManager().onServerChange(uuid, server);
TAB.getInstance().getFeatureManager().onServerChange(uuid, server, callOnLoginPacket);
}
});
}
@@ -82,7 +84,7 @@ public abstract class EventListener<T> {
public void pluginMessage(@NotNull UUID player, byte[] message) {
TAB.getInstance().getCPUManager().runMeasuredTask("Plugin message handling",
TabConstants.CpuUsageCategory.PLUGIN_MESSAGE, () ->
((ProxyPlatform<?>)TAB.getInstance().getPlatform()).getPluginMessageHandler().onPluginMessage(player, message));
((ProxyPlatform)TAB.getInstance().getPlatform()).getPluginMessageHandler().onPluginMessage(player, message));
}
/**
@@ -18,7 +18,7 @@ import java.io.File;
* An interface with methods that are called in universal code,
* but require platform-specific API calls
*/
public interface Platform<T> {
public interface Platform {
/**
* Detects permission plugin and returns its representing object
@@ -144,15 +144,4 @@ public interface Platform<T> {
* @return plugin's data folder
*/
File getDataFolder();
/**
* Converts internal component class to platform's component class
*
* @param component
* Component to convert
* @param version
* Game version to convert component for
* @return Converted component
*/
T toComponent(@NotNull IChatBaseComponent component, @NotNull ProtocolVersion version);
}
@@ -90,7 +90,7 @@ public abstract class Scoreboard<T extends TabPlayer> {
}
private void error(@NotNull String format, @NotNull Object... args) {
TAB.getInstance().getErrorManager().printError(String.format(format, args) + player.getName());
TAB.getInstance().debug(String.format(format, args) + player.getName());
}
/**
@@ -21,7 +21,7 @@ import java.util.*;
public abstract class TabPlayer implements me.neznamy.tab.api.TabPlayer {
/** Platform reference */
protected final Platform<?> platform;
protected final Platform platform;
/** Platform-specific player object instance */
@Setter protected Object player;
@@ -86,7 +86,7 @@ public abstract class TabPlayer implements me.neznamy.tab.api.TabPlayer {
* @param useRealId
* Whether tablist uses real uuid or offline
*/
protected TabPlayer(@NotNull Platform<?> platform, @NotNull Object player, @NotNull UUID uniqueId, @NotNull String name,
protected TabPlayer(@NotNull Platform platform, @NotNull Object player, @NotNull UUID uniqueId, @NotNull String name,
@NotNull String server, @NotNull String world, int protocolVersion, boolean useRealId) {
this.platform = platform;
this.player = player;
@@ -345,5 +345,5 @@ public abstract class TabPlayer implements me.neznamy.tab.api.TabPlayer {
*
* @return Server platform
*/
public abstract Platform<?> getPlatform();
public abstract Platform getPlatform();
}
@@ -10,23 +10,44 @@ import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.hook.AdventureHook;
import me.neznamy.tab.shared.platform.TabPlayer;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.bossbar.BossBar.Color;
import net.kyori.adventure.bossbar.BossBar.Overlay;
import org.jetbrains.annotations.NotNull;
@AllArgsConstructor
public class AdventureBossBar implements BossBar {
/** Color array for fast access */
private static final Color[] colors = new Color[] {
Color.PINK,
Color.BLUE,
Color.RED,
Color.GREEN,
Color.YELLOW,
Color.PURPLE,
Color.WHITE
};
/** Overlay array for fast access */
private static final Overlay[] overlays = new Overlay[] {
Overlay.PROGRESS,
Overlay.NOTCHED_6,
Overlay.NOTCHED_10,
Overlay.NOTCHED_12,
Overlay.NOTCHED_20
};
private final TabPlayer player;
private final Map<UUID, net.kyori.adventure.bossbar.BossBar> bossBars = new LinkedHashMap<>();
@Override
public void create(@NotNull UUID id, @NotNull String title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
if (bossBars.containsKey(id)) return;
net.kyori.adventure.bossbar.BossBar bar = net.kyori.adventure.bossbar.BossBar.bossBar(
AdventureHook.toAdventureComponent(IChatBaseComponent.optimizedComponent(title), player.getVersion()),
progress,
net.kyori.adventure.bossbar.BossBar.Color.valueOf(color.toString()),
net.kyori.adventure.bossbar.BossBar.Overlay.valueOf(style.toString())
colors[color.ordinal()],
overlays[style.ordinal()]
);
bossBars.put(id, bar);
((Audience)player.getPlayer()).showBossBar(bar);
@@ -44,12 +65,12 @@ public class AdventureBossBar implements BossBar {
@Override
public void update(@NotNull UUID id, @NotNull BarStyle style) {
bossBars.get(id).overlay(net.kyori.adventure.bossbar.BossBar.Overlay.valueOf(style.toString()));
bossBars.get(id).overlay(overlays[style.ordinal()]);
}
@Override
public void update(@NotNull UUID id, @NotNull BarColor color) {
bossBars.get(id).color(net.kyori.adventure.bossbar.BossBar.Color.valueOf(color.toString()));
bossBars.get(id).color(colors[color.ordinal()]);
}
@Override
@@ -56,7 +56,7 @@ public class PluginMessageHandler {
public void placeholder(@NotNull ProxyTabPlayer player, @NotNull ByteArrayDataInput in) {
String identifier = in.readUTF();
if (!TAB.getInstance().getPlaceholderManager().getRegisteredPlaceholders().containsKey(identifier)) return;
if (!TAB.getInstance().getPlaceholderManager().isPlaceholderRegistered(identifier)) return;
Placeholder placeholder = TAB.getInstance().getPlaceholderManager().getPlaceholder(identifier);
if (placeholder instanceof RelationalPlaceholder) {
TabPlayer other = TAB.getInstance().getPlayer(in.readUTF());
@@ -100,6 +100,7 @@ public class PluginMessageHandler {
}
public void playerJoinResponse(@NotNull ProxyTabPlayer player, @NotNull ByteArrayDataInput in) {
TAB.getInstance().debug("Bridge took " + (System.currentTimeMillis()-player.getBridgeRequestTime()) + "ms to respond to join message of " + player.getName());
TAB.getInstance().getFeatureManager().onWorldChange(player.getUniqueId(), in.readUTF());
if (TAB.getInstance().getGroupManager().getPermissionPlugin().contains("Vault") &&
!TAB.getInstance().getGroupManager().isGroupsByPermissions()) player.setGroup(in.readUTF());
@@ -110,22 +111,25 @@ public class PluginMessageHandler {
int placeholderCount = in.readInt();
for (int i=0; i<placeholderCount; i++) {
String identifier = in.readUTF();
boolean registered = TAB.getInstance().getPlaceholderManager().isPlaceholderRegistered(identifier);
if (identifier.startsWith("%rel_")) {
int playerCount = in.readInt();
for (int j=0; j<playerCount; j++) {
TabPlayer other = TAB.getInstance().getPlayer(in.readUTF());
String value = in.readUTF();
if (other != null) { // Backend player did not connect via this proxy if null
if (registered && other != null) { // Backend player did not connect via this proxy if null
((RelationalPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder(identifier))
.updateValue(player, other, value);
}
}
} else {
String value = in.readUTF();
if (!registered) continue;
Placeholder pl = TAB.getInstance().getPlaceholderManager().getPlaceholder(identifier);
if (pl instanceof PlayerPlaceholder) {
((PlayerPlaceholder) pl).updateValue(player, in.readUTF());
((PlayerPlaceholder) pl).updateValue(player, value);
} else {
((ServerPlaceholder) pl).updateValue(in.readUTF());
((ServerPlaceholder) pl).updateValue(value);
}
}
}
@@ -26,7 +26,7 @@ import java.util.concurrent.ConcurrentHashMap;
* Abstract class containing common variables and methods
* shared between proxies.
*/
public abstract class ProxyPlatform<T> implements Platform<T> {
public abstract class ProxyPlatform implements Platform {
/** Plugin message handler for sending and receiving plugin messages */
@Getter protected final PluginMessageHandler pluginMessageHandler = new PluginMessageHandler();
@@ -37,6 +37,9 @@ public abstract class ProxyTabPlayer extends TabPlayer {
/** Player's boat vehicle status for unlimited NameTags */
private boolean onBoat;
/** Timestamp when join plugin message was sent to track how long bridge took to respond */
private long bridgeRequestTime;
/** Flag for marking if this player has received join response from bridge or not */
private boolean bridgeConnected;
@@ -63,7 +66,7 @@ public abstract class ProxyTabPlayer extends TabPlayer {
* @param protocolVersion
* Player's protocol network id
*/
protected ProxyTabPlayer(@NotNull ProxyPlatform<?> platform, @NotNull Object player, @NotNull UUID uniqueId,
protected ProxyTabPlayer(@NotNull ProxyPlatform platform, @NotNull Object player, @NotNull UUID uniqueId,
@NotNull String name, @NotNull String server, int protocolVersion) {
super(platform, player, uniqueId, name, server, "N/A", protocolVersion, TAB.getInstance().getConfiguration().isOnlineUuidInTabList());
sendJoinPluginMessage();
@@ -82,7 +85,7 @@ public abstract class ProxyTabPlayer extends TabPlayer {
TAB.getInstance().getGroupManager().getPermissionPlugin().contains("Vault") &&
!TAB.getInstance().getGroupManager().isGroupsByPermissions(),
true);
Map<String, Integer> placeholders = ((ProxyPlatform<?>) getPlatform()).getBridgePlaceholders();
Map<String, Integer> placeholders = ((ProxyPlatform) getPlatform()).getBridgePlaceholders();
args.add(placeholders.size());
for (Map.Entry<String, Integer> entry : placeholders.entrySet()) {
args.add(entry.getKey());
@@ -115,6 +118,7 @@ public abstract class ProxyTabPlayer extends TabPlayer {
}
sendPluginMessage(args.toArray());
if (expansion instanceof ProxyTabExpansion) ((ProxyTabExpansion) expansion).resendAllValues(this);
bridgeRequestTime = System.currentTimeMillis();
}
/**
@@ -1,12 +1,12 @@
package me.neznamy.tab.platforms.sponge7;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.bossbar.BossBar;
import me.neznamy.tab.api.bossbar.BarColor;
import me.neznamy.tab.api.bossbar.BarStyle;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.api.boss.*;
import org.spongepowered.api.text.Text;
import java.util.HashMap;
import java.util.Map;
@@ -17,12 +17,22 @@ public class SpongeBossBar implements BossBar {
/** Color array for fast access */
private static final BossBarColor[] colors = new BossBarColor[] {
BossBarColors.PINK, BossBarColors.BLUE, BossBarColors.RED, BossBarColors.GREEN, BossBarColors.YELLOW, BossBarColors.PURPLE, BossBarColors.WHITE
BossBarColors.PINK,
BossBarColors.BLUE,
BossBarColors.RED,
BossBarColors.GREEN,
BossBarColors.YELLOW,
BossBarColors.PURPLE,
BossBarColors.WHITE
};
/** Style array for fast access */
private static final BossBarOverlay[] styles = new BossBarOverlay[] {
BossBarOverlays.PROGRESS, BossBarOverlays.NOTCHED_6, BossBarOverlays.NOTCHED_10, BossBarOverlays.NOTCHED_12, BossBarOverlays.NOTCHED_20
BossBarOverlays.PROGRESS,
BossBarOverlays.NOTCHED_6,
BossBarOverlays.NOTCHED_10,
BossBarOverlays.NOTCHED_12,
BossBarOverlays.NOTCHED_20
};
/** Player to send boss bars to */
@@ -35,7 +45,7 @@ public class SpongeBossBar implements BossBar {
@Override
public void create(@NotNull UUID id, @NotNull String title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
ServerBossBar bar = ServerBossBar.builder()
.name(player.getPlatform().toComponent(IChatBaseComponent.optimizedComponent(title), player.getVersion()))
.name(Text.of(title))
.color(colors[color.ordinal()])
.overlay(styles[style.ordinal()])
.percent(progress)
@@ -46,7 +56,7 @@ public class SpongeBossBar implements BossBar {
@Override
public void update(@NotNull UUID id, @NotNull String title) {
bossBars.get(id).setName(player.getPlatform().toComponent(IChatBaseComponent.optimizedComponent(title), player.getVersion()));
bossBars.get(id).setName(Text.of(title));
}
@Override
@@ -26,7 +26,7 @@ import java.io.File;
* Platform implementation for Sponge 7 and lower
*/
@RequiredArgsConstructor
public class SpongePlatform implements BackendPlatform<Text> {
public class SpongePlatform implements BackendPlatform {
/** Plugin reference */
@NotNull
@@ -115,11 +115,6 @@ public class SpongePlatform implements BackendPlatform<Text> {
return plugin.getConfigDir();
}
@Override
public Text toComponent(@NotNull IChatBaseComponent component, @NotNull ProtocolVersion version) {
return Text.of(component.toLegacyText());
}
@Override
public double getTPS() {
return Sponge.getServer().getTicksPerSecond();
@@ -6,6 +6,7 @@ import org.spongepowered.api.scoreboard.*;
import org.spongepowered.api.scoreboard.critieria.Criteria;
import org.spongepowered.api.scoreboard.displayslot.DisplaySlots;
import org.spongepowered.api.scoreboard.objective.Objective;
import org.spongepowered.api.scoreboard.objective.displaymode.ObjectiveDisplayMode;
import org.spongepowered.api.scoreboard.objective.displaymode.ObjectiveDisplayModes;
import org.spongepowered.api.text.Text;
@@ -15,17 +16,31 @@ public class SpongeScoreboard extends Scoreboard<SpongeTabPlayer> {
/** Collision rule array for fast access */
private static final org.spongepowered.api.scoreboard.CollisionRule[] collisionRules = new org.spongepowered.api.scoreboard.CollisionRule[]{
CollisionRules.ALWAYS, CollisionRules.NEVER, CollisionRules.PUSH_OTHER_TEAMS, CollisionRules.PUSH_OWN_TEAM
CollisionRules.ALWAYS,
CollisionRules.NEVER,
CollisionRules.PUSH_OTHER_TEAMS,
CollisionRules.PUSH_OWN_TEAM
};
/** Visibility array for fast access */
private static final Visibility[] visibilities = new Visibility[] {
Visibilities.ALWAYS, Visibilities.NEVER, Visibilities.HIDE_FOR_OTHER_TEAMS, Visibilities.HIDE_FOR_OWN_TEAM
Visibilities.ALWAYS,
Visibilities.NEVER,
Visibilities.HIDE_FOR_OTHER_TEAMS,
Visibilities.HIDE_FOR_OWN_TEAM
};
/** DisplaySlot array for fast access */
private static final org.spongepowered.api.scoreboard.displayslot.DisplaySlot[] displaySlots = new org.spongepowered.api.scoreboard.displayslot.DisplaySlot[] {
DisplaySlots.LIST, DisplaySlots.SIDEBAR, DisplaySlots.BELOW_NAME
DisplaySlots.LIST,
DisplaySlots.SIDEBAR,
DisplaySlots.BELOW_NAME
};
/** Health display array for fast access */
private static final ObjectiveDisplayMode[] healthDisplays = new ObjectiveDisplayMode[] {
ObjectiveDisplayModes.INTEGER,
ObjectiveDisplayModes.HEARTS
};
/** Scoreboard of the player */
@@ -34,7 +49,8 @@ public class SpongeScoreboard extends Scoreboard<SpongeTabPlayer> {
public SpongeScoreboard(@NotNull SpongeTabPlayer player) {
super(player);
// Make sure each player is in different scoreboard for per-player view
// Make sure each player is in a different scoreboard for per-player view
player.getPlayer().setScoreboard(sb);
}
@@ -48,9 +64,10 @@ public class SpongeScoreboard extends Scoreboard<SpongeTabPlayer> {
sb.addObjective(Objective.builder()
.name(objectiveName)
.displayName(Text.of(title))
.objectiveDisplayMode(display == HealthDisplay.HEARTS ? ObjectiveDisplayModes.HEARTS : ObjectiveDisplayModes.INTEGER)
.objectiveDisplayMode(healthDisplays[display.ordinal()])
.criterion(Criteria.DUMMY)
.build());
.build()
);
}
@Override
@@ -62,7 +79,7 @@ public class SpongeScoreboard extends Scoreboard<SpongeTabPlayer> {
public void updateObjective0(@NotNull String objectiveName, @NotNull String title, @NotNull HealthDisplay display) {
sb.getObjective(objectiveName).ifPresent(obj -> {
obj.setDisplayName(Text.of(title));
obj.setDisplayMode(display == HealthDisplay.HEARTS ? ObjectiveDisplayModes.HEARTS : ObjectiveDisplayModes.INTEGER);
obj.setDisplayMode(healthDisplays[display.ordinal()]);
});
}
@@ -42,7 +42,7 @@ public class SpongeTabList implements TabList {
@Override
public void updateDisplayName(@NotNull UUID entry, @Nullable IChatBaseComponent displayName) {
player.getPlayer().getTabList().getEntry(entry).ifPresent(e -> {
Text component = displayName == null ? null : player.getPlatform().toComponent(displayName, player.getVersion());
Text component = displayName == null ? null : Text.of(displayName.toLegacyText());
e.setDisplayName(component);
expectedDisplayNames.put(e, component);
});
@@ -60,7 +60,7 @@ public class SpongeTabList implements TabList {
@Override
public void addEntry(@NotNull Entry entry) {
Text displayName = entry.getDisplayName() == null ? null : player.getPlatform().toComponent(entry.getDisplayName(), player.getVersion());
Text displayName = entry.getDisplayName() == null ? null : Text.of(entry.getDisplayName().toLegacyText());
GameProfile profile = GameProfile.of(entry.getUniqueId(), entry.getName());
if (entry.getSkin() != null) profile.getPropertyMap().put(TEXTURES_PROPERTY, ProfileProperty.of(
TEXTURES_PROPERTY, entry.getSkin().getValue(), entry.getSkin().getSignature()));
@@ -78,8 +78,8 @@ public class SpongeTabList implements TabList {
@Override
public void setPlayerListHeaderFooter(@NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
player.getPlayer().getTabList().setHeaderAndFooter(
player.getPlatform().toComponent(header, player.getVersion()),
player.getPlatform().toComponent(footer, player.getVersion())
Text.of(header.toLegacyText()),
Text.of(footer.toLegacyText())
);
}
@@ -26,7 +26,7 @@ import java.io.File;
* Platform implementation for Sponge 8 and up
*/
@RequiredArgsConstructor
public class SpongePlatform implements BackendPlatform<Component> {
public class SpongePlatform implements BackendPlatform {
/** Main class reference */
@NotNull
@@ -71,13 +71,13 @@ public class SpongePlatform implements BackendPlatform<Component> {
@Override
public void logInfo(@NotNull IChatBaseComponent message) {
Sponge.systemSubject().sendMessage(Component.text("[TAB] ").append(
toComponent(message, TAB.getInstance().getServerVersion())));
AdventureHook.toAdventureComponent(message, TAB.getInstance().getServerVersion())));
}
@Override
public void logWarn(@NotNull IChatBaseComponent message) {
Sponge.systemSubject().sendMessage(Component.text("[TAB] [WARN] ").append(
toComponent(message, TAB.getInstance().getServerVersion()))); // Sponge console does not support colors
AdventureHook.toAdventureComponent(message, TAB.getInstance().getServerVersion()))); // Sponge console does not support colors
}
@Override
@@ -116,11 +116,6 @@ public class SpongePlatform implements BackendPlatform<Component> {
return plugin.getConfigDir().toFile();
}
@Override
public Component toComponent(@NotNull IChatBaseComponent component, @NotNull ProtocolVersion version) {
return AdventureHook.toAdventureComponent(component, version);
}
@Override
public double getTPS() {
return Sponge.server().ticksPerSecond();
@@ -1,6 +1,7 @@
package me.neznamy.tab.platforms.sponge8;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.hook.AdventureHook;
import me.neznamy.tab.shared.platform.Scoreboard;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
@@ -11,6 +12,7 @@ import org.spongepowered.api.scoreboard.Visibility;
import org.spongepowered.api.scoreboard.criteria.Criteria;
import org.spongepowered.api.scoreboard.displayslot.DisplaySlots;
import org.spongepowered.api.scoreboard.objective.Objective;
import org.spongepowered.api.scoreboard.objective.displaymode.ObjectiveDisplayMode;
import org.spongepowered.api.scoreboard.objective.displaymode.ObjectiveDisplayModes;
import java.util.Collection;
@@ -19,17 +21,31 @@ public class SpongeScoreboard extends Scoreboard<SpongeTabPlayer> {
/** Collision rule array for fast access */
private static final org.spongepowered.api.scoreboard.CollisionRule[] collisionRules = new org.spongepowered.api.scoreboard.CollisionRule[]{
CollisionRules.ALWAYS.get(), CollisionRules.NEVER.get(), CollisionRules.PUSH_OTHER_TEAMS.get(), CollisionRules.PUSH_OWN_TEAM.get()
CollisionRules.ALWAYS.get(),
CollisionRules.NEVER.get(),
CollisionRules.PUSH_OTHER_TEAMS.get(),
CollisionRules.PUSH_OWN_TEAM.get()
};
/** Visibility array for fast access */
private static final Visibility[] visibilities = new Visibility[] {
Visibilities.ALWAYS.get(), Visibilities.NEVER.get(), Visibilities.HIDE_FOR_OTHER_TEAMS.get(), Visibilities.HIDE_FOR_OWN_TEAM.get()
Visibilities.ALWAYS.get(),
Visibilities.NEVER.get(),
Visibilities.HIDE_FOR_OTHER_TEAMS.get(),
Visibilities.HIDE_FOR_OWN_TEAM.get()
};
/** DisplaySlot array for fast access */
private static final org.spongepowered.api.scoreboard.displayslot.DisplaySlot[] displaySlots = new org.spongepowered.api.scoreboard.displayslot.DisplaySlot[] {
DisplaySlots.LIST.get(), DisplaySlots.SIDEBAR.get(), DisplaySlots.BELOW_NAME.get()
DisplaySlots.LIST.get(),
DisplaySlots.SIDEBAR.get(),
DisplaySlots.BELOW_NAME.get()
};
/** Health display array for fast access */
private static final ObjectiveDisplayMode[] healthDisplays = new ObjectiveDisplayMode[] {
ObjectiveDisplayModes.INTEGER.get(),
ObjectiveDisplayModes.HEARTS.get()
};
/** Scoreboard of the player */
@@ -38,7 +54,8 @@ public class SpongeScoreboard extends Scoreboard<SpongeTabPlayer> {
public SpongeScoreboard(@NotNull SpongeTabPlayer player) {
super(player);
// Make sure each player is in different scoreboard for per-player view
// Make sure each player is in a different scoreboard for per-player view
player.getPlayer().setScoreboard(sb);
}
@@ -52,9 +69,10 @@ public class SpongeScoreboard extends Scoreboard<SpongeTabPlayer> {
sb.addObjective(Objective.builder()
.name(objectiveName)
.displayName(adventure(title))
.objectiveDisplayMode(display == HealthDisplay.HEARTS ? ObjectiveDisplayModes.HEARTS : ObjectiveDisplayModes.INTEGER)
.objectiveDisplayMode(healthDisplays[display.ordinal()])
.criterion(Criteria.DUMMY)
.build());
.build()
);
}
@Override
@@ -66,7 +84,7 @@ public class SpongeScoreboard extends Scoreboard<SpongeTabPlayer> {
public void updateObjective0(@NotNull String objectiveName, @NotNull String title, @NotNull HealthDisplay display) {
sb.objective(objectiveName).ifPresent(obj -> {
obj.setDisplayName(adventure(title));
obj.setDisplayMode(display == HealthDisplay.HEARTS ? ObjectiveDisplayModes.HEARTS.get() : ObjectiveDisplayModes.INTEGER.get());
obj.setDisplayMode(healthDisplays[display.ordinal()]);
});
}
@@ -128,6 +146,6 @@ public class SpongeScoreboard extends Scoreboard<SpongeTabPlayer> {
*/
@NotNull
private Component adventure(@NotNull String text) {
return player.getPlatform().toComponent(IChatBaseComponent.optimizedComponent(text), player.getVersion());
return AdventureHook.toAdventureComponent(IChatBaseComponent.optimizedComponent(text), player.getVersion());
}
}
@@ -2,6 +2,7 @@ package me.neznamy.tab.platforms.sponge8;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.hook.AdventureHook;
import me.neznamy.tab.shared.platform.TabList;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
@@ -42,7 +43,7 @@ public class SpongeTabList implements TabList {
@Override
public void updateDisplayName(@NotNull UUID entry, @Nullable IChatBaseComponent displayName) {
player.getPlayer().tabList().entry(entry).ifPresent(e -> {
Component component = displayName == null ? null : player.getPlatform().toComponent(displayName, player.getVersion());
Component component = displayName == null ? null : AdventureHook.toAdventureComponent(displayName, player.getVersion());
e.setDisplayName(component);
expectedDisplayNames.put(e, component);
});
@@ -60,7 +61,7 @@ public class SpongeTabList implements TabList {
@Override
public void addEntry(@NotNull Entry entry) {
Component displayName = entry.getDisplayName() == null ? null : player.getPlatform().toComponent(entry.getDisplayName(), player.getVersion());
Component displayName = entry.getDisplayName() == null ? null : AdventureHook.toAdventureComponent(entry.getDisplayName(), player.getVersion());
GameProfile profile = GameProfile.of(entry.getUniqueId(), entry.getName());
if (entry.getSkin() != null) profile = profile.withProperty(ProfileProperty.of(
TEXTURES_PROPERTY, entry.getSkin().getValue(), entry.getSkin().getSignature()));
@@ -78,8 +79,8 @@ public class SpongeTabList implements TabList {
@Override
public void setPlayerListHeaderFooter(@NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
player.getPlayer().tabList().setHeaderAndFooter(
player.getPlatform().toComponent(header, player.getVersion()),
player.getPlatform().toComponent(footer, player.getVersion())
AdventureHook.toAdventureComponent(header, player.getVersion()),
AdventureHook.toAdventureComponent(footer, player.getVersion())
);
}
@@ -4,6 +4,7 @@ import lombok.Getter;
import me.neznamy.tab.shared.backend.BackendTabPlayer;
import me.neznamy.tab.shared.backend.entityview.DummyEntityView;
import me.neznamy.tab.shared.backend.entityview.EntityView;
import me.neznamy.tab.shared.hook.AdventureHook;
import me.neznamy.tab.shared.platform.bossbar.AdventureBossBar;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.TabList;
@@ -53,7 +54,7 @@ public class SpongeTabPlayer extends BackendTabPlayer {
@Override
public void sendMessage(@NotNull IChatBaseComponent message) {
getPlayer().sendMessage(getPlatform().toComponent(message, getVersion()));
getPlayer().sendMessage(AdventureHook.toAdventureComponent(message, getVersion()));
}
@Override
@@ -30,7 +30,8 @@ public class VelocityEventListener extends EventListener<Player> {
public void onConnect(@NotNull ServerPostConnectEvent e) {
serverChange(
e.getPlayer(), e.getPlayer().getUniqueId(),
e.getPlayer().getCurrentServer().map(s -> s.getServerInfo().getName()).orElse("null")
e.getPlayer().getCurrentServer().map(s -> s.getServerInfo().getName()).orElse("null"),
true
);
}
@@ -7,17 +7,14 @@ import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.platforms.velocity.features.VelocityRedisSupport;
import me.neznamy.tab.shared.ProtocolVersion;
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.IChatBaseComponent;
import me.neznamy.tab.shared.features.injection.PipelineInjector;
import me.neznamy.tab.shared.features.redis.RedisSupport;
import me.neznamy.tab.shared.hook.AdventureHook;
import me.neznamy.tab.shared.proxy.ProxyPlatform;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.kyori.adventure.text.Component;
import org.bstats.charts.SimplePie;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -28,7 +25,7 @@ import java.io.File;
* Velocity implementation of Platform
*/
@RequiredArgsConstructor
public class VelocityPlatform extends ProxyPlatform<Component> {
public class VelocityPlatform extends ProxyPlatform {
@NotNull
private final VelocityTAB plugin;
@@ -94,11 +91,6 @@ public class VelocityPlatform extends ProxyPlatform<Component> {
return plugin.getDataFolder().toFile();
}
@Override
public Component toComponent(@NotNull IChatBaseComponent component, @NotNull ProtocolVersion version) {
return AdventureHook.toAdventureComponent(component, version);
}
@Override
@Nullable
public PipelineInjector createPipelineInjector() {
@@ -10,7 +10,6 @@ import com.velocitypowered.api.proxy.ProxyServer;
import lombok.Getter;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import org.bstats.velocity.Metrics;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@@ -52,9 +51,6 @@ public class VelocityTAB {
@Subscribe
public void onProxyInitialization(@Nullable ProxyInitializeEvent event) {
TAB.create(new VelocityPlatform(this));
TAB.getInstance().getPlatform().logWarn(new IChatBaseComponent("Velocity compatibility is very experimental and should not be used in production! " +
"If you use it, you WILL run into issues and they WILL NOT be fixed. Any bug reports featuring Velocity installation " +
"will be immediately closed."));
}
/**
@@ -5,6 +5,7 @@ import com.velocitypowered.api.proxy.player.TabListEntry;
import com.velocitypowered.api.util.GameProfile;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.hook.AdventureHook;
import me.neznamy.tab.shared.platform.TabList;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
@@ -39,7 +40,7 @@ public class VelocityTabList implements TabList {
public void updateDisplayName(@NotNull UUID entry, @Nullable IChatBaseComponent displayName) {
getEntry(entry).ifPresent(e -> {
if (player.getVersion().getMinorVersion() >= 8) {
Component component = displayName == null ? null : player.getPlatform().toComponent(displayName, player.getVersion());
Component component = displayName == null ? null : AdventureHook.toAdventureComponent(displayName, player.getVersion());
e.setDisplayName(component);
expectedDisplayNames.put(e, component);
} else {
@@ -62,7 +63,8 @@ public class VelocityTabList implements TabList {
@Override
public void addEntry(@NotNull Entry entry) {
Component displayName = entry.getDisplayName() == null ? null : player.getPlatform().toComponent(entry.getDisplayName(), player.getVersion());
if (player.getPlayer().getTabList().containsEntry(entry.getUniqueId())) return;
Component displayName = entry.getDisplayName() == null ? null : AdventureHook.toAdventureComponent(entry.getDisplayName(), player.getVersion());
TabListEntry e = TabListEntry.builder()
.tabList(player.getPlayer().getTabList())
.profile(new GameProfile(
@@ -82,8 +84,8 @@ public class VelocityTabList implements TabList {
@Override
public void setPlayerListHeaderFooter(@NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
player.getPlayer().sendPlayerListHeaderAndFooter(
player.getPlatform().toComponent(header, player.getVersion()),
player.getPlatform().toComponent(footer, player.getVersion())
AdventureHook.toAdventureComponent(header, player.getVersion()),
AdventureHook.toAdventureComponent(footer, player.getVersion())
);
}
@@ -3,6 +3,7 @@ package me.neznamy.tab.platforms.velocity;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.util.GameProfile;
import lombok.Getter;
import me.neznamy.tab.shared.hook.AdventureHook;
import me.neznamy.tab.shared.platform.bossbar.AdventureBossBar;
import me.neznamy.tab.shared.platform.bossbar.BossBar;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
@@ -57,7 +58,7 @@ public class VelocityTabPlayer extends ProxyTabPlayer {
@Override
public void sendMessage(@NotNull IChatBaseComponent message) {
getPlayer().sendMessage(getPlatform().toComponent(message, getVersion()));
getPlayer().sendMessage(AdventureHook.toAdventureComponent(message, getVersion()));
}
@Override