mirror of
https://github.com/NEZNAMY/TAB.git
synced 2026-11-12 20:22:01 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9da0db5f74 | ||
|
|
562a973c84 | ||
|
|
967980ddf7 | ||
|
|
81d3675a87 | ||
|
|
7be786bbaf | ||
|
|
f41c9be1f7 | ||
|
|
e88062d028 | ||
|
|
edb2828643 | ||
|
|
58c189cd0e | ||
|
|
012994847e | ||
|
|
d87801be5f | ||
|
|
6d78c0c104 | ||
|
|
3a83519122 | ||
|
|
a9f940ebed | ||
|
|
db73d9ef81 | ||
|
|
e2bd21c717 | ||
|
|
871215b7b5 | ||
|
|
a6153ef46f | ||
|
|
35475a6103 | ||
|
|
bf77ffc1a5 | ||
|
|
7a64a9a534 | ||
|
|
68e057b99c | ||
|
|
7338afff96 | ||
|
|
97ae7813aa | ||
|
|
c0f3667ac1 | ||
|
|
d5c5502abe | ||
|
|
194e8ffbd9 | ||
|
|
ef39458abd | ||
|
|
6a6907fb0a | ||
|
|
8bb7c4a4a6 | ||
|
|
91700e7ed0 | ||
|
|
661c2e6fc3 | ||
|
|
3d053a6149 | ||
|
|
34179e4f62 | ||
|
|
a23e5d9f50 | ||
|
|
1cbcbf70d9 | ||
|
|
0a7371aaec | ||
|
|
da13e2d861 | ||
|
|
5b18fa8c25 | ||
|
|
347231850a | ||
|
|
b1f9bdc26c | ||
|
|
03c9b95041 | ||
|
|
6e7e1d783b | ||
|
|
cfb9b0a029 | ||
|
|
b7ea6e5af6 | ||
|
|
44104df712 | ||
|
|
5de20d1c58 | ||
|
|
d156303740 | ||
|
|
6e36eb3554 | ||
|
|
f7f3891817 | ||
|
|
1ec28856ff | ||
|
|
9658e0b74d | ||
|
|
86cd8d4151 | ||
|
|
2418eb206e | ||
|
|
08fa715d26 | ||
|
|
52426de283 | ||
|
|
64b3da14c4 | ||
|
|
bf7341c52c | ||
|
|
e3490564a2 | ||
|
|
6218f0ed16 | ||
|
|
54e8e1ea98 | ||
|
|
4cd50fb3d5 | ||
|
|
590286a6c2 | ||
|
|
6c96650dcc | ||
|
|
7c83a74fd8 | ||
|
|
7b3f60cb3b | ||
|
|
bfc845088a | ||
|
|
e67d93a592 | ||
|
|
b518b980ae | ||
|
|
744bc1403a |
No files matched your search
@@ -60,4 +60,6 @@ body:
|
||||
- label: I ticked all of the boxes without actually reading them
|
||||
required: false
|
||||
- label: I have read the Compatibility wiki page and am not trying to run the plugin on an unsupported server version / platform
|
||||
required: true
|
||||
- label: I am willing to cooperate to get this issue resolved faster, including providing any additional information requested or testing new builds
|
||||
required: true
|
||||
@@ -0,0 +1,38 @@
|
||||
name: Compatibility issue
|
||||
description: A compatibility issue with another plugin/mod or a specific version of a platform marked as supported
|
||||
labels: ["Type: Compatibility issue"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: TAB version
|
||||
description: Full version in numbers, "latest" is not a version.
|
||||
placeholder: 4.0.0
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Software causing compatibility issue
|
||||
description: plugin/mod/platform version causing the issue
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional info
|
||||
description: Anything else you want to provide that will help in resolving the issue faster.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Let's make sure this report is valid
|
||||
options:
|
||||
- label: I am running latest version of the plugin
|
||||
required: true
|
||||
- label: I ticked all of the boxes without actually reading them
|
||||
required: false
|
||||
- label: The compatibility wiki page does not mention this incompatibility
|
||||
required: true
|
||||
- label: I am willing to cooperate to get this issue resolved faster, including providing any additional information requested or testing new builds
|
||||
required: true
|
||||
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
@@ -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
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
allprojects {
|
||||
group = "me.neznamy"
|
||||
version = "4.0.4"
|
||||
version = "4.0.8-SNAPSHOT"
|
||||
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
|
||||
|
||||
@@ -62,11 +62,7 @@ public class BukkitUtils {
|
||||
sb.append(component.getModifier().getColor().getLegacyColor().getFormat());
|
||||
}
|
||||
}
|
||||
if (component.getModifier().isBold()) sb.append(EnumChatFormat.BOLD.getFormat());
|
||||
if (component.getModifier().isStrikethrough()) sb.append(EnumChatFormat.STRIKETHROUGH.getFormat());
|
||||
if (component.getModifier().isItalic()) sb.append(EnumChatFormat.ITALIC.getFormat());
|
||||
if (component.getModifier().isObfuscated()) sb.append(EnumChatFormat.OBFUSCATED.getFormat());
|
||||
if (component.getModifier().isUnderlined()) sb.append(EnumChatFormat.UNDERLINE.getFormat());
|
||||
sb.append(component.getModifier().getMagicCodes());
|
||||
if (component.getText() != null) sb.append(component.getText());
|
||||
for (IChatBaseComponent extra : component.getExtra()) {
|
||||
sb.append(toBukkitFormat(extra, rgbClient));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,6 +7,7 @@ import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.platform.TabPlayer;
|
||||
import me.neznamy.tab.shared.placeholders.expansion.TabExpansion;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -112,4 +113,15 @@ public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpan
|
||||
public void setValue(@NotNull TabPlayer player, @NotNull String key, @NotNull String value) {
|
||||
values.computeIfAbsent(player, p -> new HashMap<>()).put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"UnstableApiUsage", "deprecation"})
|
||||
public void unregisterExpansion() {
|
||||
if (ReflectionUtils.methodExists(PlaceholderExpansion.class, "unregister")) {
|
||||
// Added in 2.10.7 (Jul 28, 2020)
|
||||
unregister();
|
||||
} else {
|
||||
PlaceholderAPI.unregisterExpansion(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,55 @@
|
||||
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 (PaperHeaderFooter.isAvailable()) {
|
||||
instance = new PaperHeaderFooter();
|
||||
} else if (PacketHeaderFooter.isAvailable()) {
|
||||
instance = new PacketHeaderFooter();
|
||||
} 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,47 +96,46 @@ public class DataWatcher implements EntityData {
|
||||
DataWatcherSerializer = BukkitReflection.getLegacyClass("DataWatcherSerializer");
|
||||
}
|
||||
}
|
||||
newDataWatcher = ReflectionUtils.getOnlyConstructor(DataWatcher);
|
||||
if (BukkitReflection.isMojangMapped()) {
|
||||
DataWatcher_register = DataWatcher.getMethod("define", DataWatcherObject, Object.class);
|
||||
} else if (minorVersion >= 9) {
|
||||
DataWatcher_register = ReflectionUtils.getMethod(DataWatcher, new String[]{"register", "a", "m_135372_"}, DataWatcherObject, Object.class); // {Bukkit, Bukkit 1.18+, Mohist 1.18.2}
|
||||
if (minorVersion >= 7) {
|
||||
newDataWatcher = DataWatcher.getConstructor(Entity);
|
||||
} else {
|
||||
newDataWatcher = DataWatcher.getConstructor();
|
||||
}
|
||||
if (minorVersion >= 9) {
|
||||
DataWatcher_register = ReflectionUtils.getMethod(
|
||||
DataWatcher,
|
||||
new String[]{"define", "register", "a", "m_135372_"}, // {Bukkit 1.20.2+, Bukkit, Bukkit 1.18+, Mohist 1.18.2}
|
||||
DataWatcherObject, Object.class
|
||||
);
|
||||
} else {
|
||||
DataWatcher_register = ReflectionUtils.getMethod(DataWatcher, new String[]{"func_75682_a", "a"}, int.class, Object.class); // {Thermos 1.7.10, Bukkit}
|
||||
}
|
||||
if (minorVersion >= 19) {
|
||||
if (BukkitReflection.isMojangMapped()) {
|
||||
DataWatcher_packDirty = DataWatcher.getMethod("packDirty");
|
||||
} else {
|
||||
DataWatcher_packDirty = DataWatcher.getMethod("b");
|
||||
}
|
||||
DataWatcher_packDirty = ReflectionUtils.getMethod(DataWatcher, new String[] {"packDirty", "b"}); // {Mojang | 1.20.2+, 1.20.2-}
|
||||
}
|
||||
if (BukkitReflection.is1_19_3Plus()) {
|
||||
DataWatcher_markDirty = ReflectionUtils.getOnlyMethod(DataWatcher, void.class, DataWatcherObject);
|
||||
DataWatcher_markDirty = ReflectionUtils.getMethods(DataWatcher, void.class, DataWatcherObject).get(0);
|
||||
}
|
||||
if (minorVersion >= 9) {
|
||||
newDataWatcherObject = DataWatcherObject.getConstructor(int.class, DataWatcherSerializer);
|
||||
DataWatcherSerializer_BYTE = ReflectionUtils.getField(DataWatcherRegistry, "BYTE", "a", "f_135027_").get(null); // Mohist 1.18.2
|
||||
DataWatcherSerializer_FLOAT = ReflectionUtils.getField(DataWatcherRegistry, "FLOAT", "c", "f_135029_").get(null); // Mohist 1.18.2
|
||||
DataWatcherSerializer_STRING = ReflectionUtils.getField(DataWatcherRegistry, "STRING", "d", "f_135030_").get(null); // Mohist 1.18.2
|
||||
if (BukkitReflection.isMojangMapped()) {
|
||||
DataWatcherSerializer_OPTIONAL_COMPONENT = DataWatcherRegistry.getDeclaredField("OPTIONAL_COMPONENT").get(null);
|
||||
DataWatcherSerializer_BOOLEAN = DataWatcherRegistry.getDeclaredField("BOOLEAN").get(null);
|
||||
} else {
|
||||
if (BukkitReflection.is1_19_3Plus()) {
|
||||
DataWatcherSerializer_OPTIONAL_COMPONENT = DataWatcherRegistry.getDeclaredField("g").get(null);
|
||||
if (BukkitReflection.is1_19_4Plus()) {
|
||||
DataWatcherSerializer_BOOLEAN = DataWatcherRegistry.getDeclaredField("k").get(null);
|
||||
} else {
|
||||
DataWatcherSerializer_BOOLEAN = DataWatcherRegistry.getDeclaredField("j").get(null);
|
||||
}
|
||||
if (BukkitReflection.is1_19_3Plus()) {
|
||||
DataWatcherSerializer_OPTIONAL_COMPONENT = ReflectionUtils.getField(DataWatcherRegistry, "OPTIONAL_COMPONENT", "g").get(null);
|
||||
if (BukkitReflection.is1_19_4Plus()) {
|
||||
DataWatcherSerializer_BOOLEAN = ReflectionUtils.getField(DataWatcherRegistry, "BOOLEAN", "k").get(null);
|
||||
} else {
|
||||
if (minorVersion >= 13) {
|
||||
DataWatcherSerializer_OPTIONAL_COMPONENT = ReflectionUtils.getField(DataWatcherRegistry, "f", "f_135032_").get(null); // Mohist 1.18.2
|
||||
DataWatcherSerializer_BOOLEAN = ReflectionUtils.getField(DataWatcherRegistry, "i", "f_135035_").get(null); // Mohist 1.18.2
|
||||
} else {
|
||||
DataWatcherSerializer_BOOLEAN = DataWatcherRegistry.getDeclaredField("h").get(null);
|
||||
}
|
||||
DataWatcherSerializer_BOOLEAN = ReflectionUtils.getField(DataWatcherRegistry, "BOOLEAN", "j").get(null);
|
||||
}
|
||||
} else {
|
||||
if (minorVersion >= 13) {
|
||||
DataWatcherSerializer_OPTIONAL_COMPONENT = ReflectionUtils.getField(DataWatcherRegistry,
|
||||
"OPTIONAL_COMPONENT", "f", "f_135032_").get(null); // Mohist 1.18.2
|
||||
DataWatcherSerializer_BOOLEAN = ReflectionUtils.getField(DataWatcherRegistry,
|
||||
"BOOLEAN", "i", "f_135035_").get(null); // Mohist 1.18.2
|
||||
} else {
|
||||
DataWatcherSerializer_BOOLEAN = DataWatcherRegistry.getDeclaredField("h").get(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,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 {
|
||||
@@ -285,4 +289,4 @@ public class DataWatcher implements EntityData {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -66,6 +66,9 @@ public class PacketEntityView implements EntityView {
|
||||
private static Class<?> PacketPlayOutNamedEntitySpawn;
|
||||
private static Field PacketPlayOutNamedEntitySpawn_ENTITYID;
|
||||
|
||||
private static Class<?> ClientboundBundlePacket;
|
||||
private static Field ClientboundBundlePacket_packets;
|
||||
|
||||
private static Object dummyEntity;
|
||||
|
||||
@Getter
|
||||
@@ -86,6 +89,10 @@ public class PacketEntityView implements EntityView {
|
||||
loadEntityTeleport();
|
||||
loadEntityMove();
|
||||
loadEntitySpawn();
|
||||
if (BukkitReflection.is1_19_4Plus()) {
|
||||
ClientboundBundlePacket = Class.forName("net.minecraft.network.protocol.game.ClientboundBundlePacket");
|
||||
ClientboundBundlePacket_packets = ReflectionUtils.getOnlyField(ClientboundBundlePacket.getSuperclass(), Iterable.class);
|
||||
}
|
||||
available = true;
|
||||
}
|
||||
|
||||
@@ -174,12 +181,10 @@ public class PacketEntityView implements EntityView {
|
||||
Class<?> Vec3D = getClass("world.phys.Vec3", "world.phys.Vec3D");
|
||||
Vec3D_Empty = ReflectionUtils.getOnlyField(Vec3D, Vec3D).get(null);
|
||||
Class<?> EntityTypes = getClass("world.entity.EntityType", "world.entity.EntityTypes");
|
||||
if (BukkitReflection.isMojangMapped()) {
|
||||
EntityTypes_ARMOR_STAND = EntityTypes.getDeclaredField("ARMOR_STAND").get(null);
|
||||
} else if (BukkitReflection.getMinorVersion() >= 19) {
|
||||
EntityTypes_ARMOR_STAND = EntityTypes.getDeclaredField("d").get(null);
|
||||
if (BukkitReflection.getMinorVersion() >= 19) {
|
||||
EntityTypes_ARMOR_STAND = ReflectionUtils.getField(EntityTypes, "ARMOR_STAND", "d").get(null);
|
||||
} else {
|
||||
EntityTypes_ARMOR_STAND = ReflectionUtils.getField(EntityTypes, "c", "f_20529_").get(null); // Mohist 1.18.2
|
||||
EntityTypes_ARMOR_STAND = ReflectionUtils.getField(EntityTypes, "ARMOR_STAND", "c", "f_20529_").get(null); // Mohist 1.18.2
|
||||
}
|
||||
if (BukkitReflection.getMinorVersion() >= 19) {
|
||||
newSpawnEntity = SpawnEntityClass.getConstructor(int.class, UUID.class, double.class, double.class, double.class, float.class, float.class, EntityTypes, int.class, Vec3D, double.class);
|
||||
@@ -368,6 +373,17 @@ public class PacketEntityView implements EntityView {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBundlePacket(@NotNull Object packet) {
|
||||
return BukkitReflection.is1_19_4Plus() && ClientboundBundlePacket.isInstance(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public Iterable<Object> getPackets(@NotNull Object bundlePacket) {
|
||||
return (Iterable<Object>) ClientboundBundlePacket_packets.get(bundlePacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns class from given potential names. Supports modded servers, such as 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
|
||||
@@ -142,7 +142,7 @@ public class BukkitPlatform implements BackendPlatform<Object> {
|
||||
@Override
|
||||
public void registerUnknownPlaceholder(@NotNull String identifier) {
|
||||
if (!placeholderAPI) {
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(identifier, -1, () -> identifier);
|
||||
registerDummyPlaceholder(identifier);
|
||||
return;
|
||||
}
|
||||
PlaceholderManagerImpl pl = TAB.getInstance().getPlaceholderManager();
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+5
-4
@@ -4,6 +4,7 @@ import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitUtils;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
|
||||
import me.neznamy.tab.shared.Limitations;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.chat.EnumChatFormat;
|
||||
@@ -155,22 +156,22 @@ public class BukkitScoreboard extends Scoreboard<BukkitTabPlayer> {
|
||||
|
||||
public void newObjective(String objectiveName, String criteria, String title, @NotNull HealthDisplay display) {
|
||||
if (serverMinorVersion >= 14) {
|
||||
scoreboard.registerNewObjective(objectiveName, criteria, transform(title, 128, 32), RenderType.valueOf(display.name()));
|
||||
scoreboard.registerNewObjective(objectiveName, criteria, transform(title, 128, Limitations.SCOREBOARD_TITLE_PRE_1_13), RenderType.valueOf(display.name()));
|
||||
} else {
|
||||
setDisplayName(scoreboard.registerNewObjective(objectiveName, display == HealthDisplay.HEARTS ? "health" : "dummy"), title);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDisplayName(@NotNull Objective objective, @NotNull String displayName) {
|
||||
objective.setDisplayName(transform(displayName, 128, 32));
|
||||
objective.setDisplayName(transform(displayName, 128, Limitations.SCOREBOARD_TITLE_PRE_1_13));
|
||||
}
|
||||
|
||||
public void setPrefix(@NotNull Team team, @NotNull String prefix) {
|
||||
team.setPrefix(transform(prefix, 64, 16));
|
||||
team.setPrefix(transform(prefix, 64, Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13));
|
||||
}
|
||||
|
||||
public void setSuffix(@NotNull Team team, @NotNull String suffix) {
|
||||
team.setSuffix(transform(suffix, 64, 16));
|
||||
team.setSuffix(transform(suffix, 64, Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+95
-42
@@ -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);
|
||||
@@ -189,27 +213,50 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
|
||||
} else {
|
||||
newTeamPacket = TeamPacketClass.getConstructor(scoreboardTeam, int.class);
|
||||
}
|
||||
if (BukkitReflection.isMojangMapped()) {
|
||||
ScoreboardScore_setScore = scoreboardScoreClass.getMethod("setScore", int.class);
|
||||
ScoreboardTeam_setAllowFriendlyFire = scoreboardTeam.getMethod("setAllowFriendlyFire", boolean.class);
|
||||
ScoreboardTeam_setCanSeeFriendlyInvisibles = scoreboardTeam.getMethod("setSeeFriendlyInvisibles", boolean.class);
|
||||
ScoreboardTeam_setPrefix = scoreboardTeam.getMethod("setPlayerPrefix", IChatBaseComponent);
|
||||
ScoreboardTeam_setSuffix = scoreboardTeam.getMethod("setPlayerSuffix", IChatBaseComponent);
|
||||
ScoreboardTeam_setNameTagVisibility = scoreboardTeam.getMethod("setNameTagVisibility", EnumNameTagVisibility);
|
||||
ScoreboardScore_setScore = ReflectionUtils.getMethod(
|
||||
scoreboardScoreClass,
|
||||
new String[] {"func_96647_c", "setScore", "b", "c", "m_83402_"}, // {Thermos, 1.5.1 - 1.17.1 & 1.20.2+, 1.18 - 1.20.1, 1.5, Mohist 1.18.2}
|
||||
int.class
|
||||
);
|
||||
ScoreboardTeam_setAllowFriendlyFire = ReflectionUtils.getMethod(
|
||||
scoreboardTeam,
|
||||
new String[] {"func_96660_a", "setAllowFriendlyFire", "a", "m_83355_"}, // {Thermos, 1.5.1+, 1.5 & 1.18+, Mohist 1.18.2}
|
||||
boolean.class
|
||||
);
|
||||
ScoreboardTeam_setCanSeeFriendlyInvisibles = ReflectionUtils.getMethod(
|
||||
scoreboardTeam,
|
||||
new String[] {"func_98300_b", "setCanSeeFriendlyInvisibles", "b", "m_83362_", "setSeeFriendlyInvisibles"}, // {Thermos, 1.5.1+, 1.5 & 1.18+, Mohist 1.18.2, 1.20.2+}
|
||||
boolean.class
|
||||
);
|
||||
if (minorVersion >= 13) {
|
||||
ScoreboardTeam_setPrefix = ReflectionUtils.getMethod(
|
||||
scoreboardTeam,
|
||||
new String[]{"setPrefix", "b", "m_83360_", "setPlayerPrefix"}, // {1.17.1-, 1.18 - 1.20.1, Mohist 1.18.2, 1.20.2+}
|
||||
IChatBaseComponent
|
||||
);
|
||||
ScoreboardTeam_setSuffix = ReflectionUtils.getMethod(
|
||||
scoreboardTeam,
|
||||
new String[]{"setSuffix", "c", "m_83365_", "setPlayerSuffix"}, // {1.17.1-, 1.18 - 1.20.1, Mohist 1.18.2, 1.20.2+}
|
||||
IChatBaseComponent
|
||||
);
|
||||
} else {
|
||||
ScoreboardScore_setScore = ReflectionUtils.getMethod(scoreboardScoreClass, new String[] {"func_96647_c", "setScore", "b", "c", "m_83402_"}, int.class); // {Thermos, 1.5.1 - 1.17.1, 1.18+, 1.5, Mohist 1.18.2}
|
||||
ScoreboardTeam_setAllowFriendlyFire = ReflectionUtils.getMethod(scoreboardTeam, new String[] {"func_96660_a", "setAllowFriendlyFire", "a", "m_83355_"}, boolean.class); // {Thermos, 1.5.1+, 1.5 & 1.18+, Mohist 1.18.2}
|
||||
ScoreboardTeam_setCanSeeFriendlyInvisibles = ReflectionUtils.getMethod(scoreboardTeam, new String[] {"func_98300_b", "setCanSeeFriendlyInvisibles", "b", "m_83362_"}, boolean.class); // {Thermos, 1.5.1+, 1.5 & 1.18+, Mohist 1.18.2}
|
||||
if (minorVersion >= 13) {
|
||||
ScoreboardTeam_setPrefix = ReflectionUtils.getMethod(scoreboardTeam, new String[]{"setPrefix", "b", "m_83360_"}, IChatBaseComponent); // {1.17.1-, 1.18+, Mohist 1.18.2}
|
||||
ScoreboardTeam_setSuffix = ReflectionUtils.getMethod(scoreboardTeam, new String[]{"setSuffix", "c", "m_83365_"}, IChatBaseComponent); // {1.17.1-, 1.18+, Mohist 1.18.2}
|
||||
} else {
|
||||
ScoreboardTeam_setPrefix = ReflectionUtils.getMethod(scoreboardTeam, new String[] {"func_96666_b", "setPrefix", "b"}, String.class); // {Thermos, 1.5.1+, 1.5}
|
||||
ScoreboardTeam_setSuffix = ReflectionUtils.getMethod(scoreboardTeam, new String[] {"func_96662_c", "setSuffix", "c"}, String.class); // {Thermos, 1.5.1+, 1.5}
|
||||
}
|
||||
if (minorVersion >= 8) {
|
||||
ScoreboardTeam_setNameTagVisibility = ReflectionUtils.getMethod(scoreboardTeam, new String[] {"setNameTagVisibility", "a", "m_83346_"}, EnumNameTagVisibility); // {1.8.1+, 1.8 & 1.18+, Mohist 1.18.2}
|
||||
}
|
||||
ScoreboardTeam_setPrefix = ReflectionUtils.getMethod(
|
||||
scoreboardTeam,
|
||||
new String[] {"func_96666_b", "setPrefix", "b"}, // {Thermos, 1.5.1+, 1.5}
|
||||
String.class
|
||||
);
|
||||
ScoreboardTeam_setSuffix = ReflectionUtils.getMethod(
|
||||
scoreboardTeam,
|
||||
new String[] {"func_96662_c", "setSuffix", "c"}, // {Thermos, 1.5.1+, 1.5}
|
||||
String.class
|
||||
);
|
||||
}
|
||||
if (minorVersion >= 8) {
|
||||
ScoreboardTeam_setNameTagVisibility = ReflectionUtils.getMethod(
|
||||
scoreboardTeam,
|
||||
new String[] {"setNameTagVisibility", "a", "m_83346_"}, // {1.8.1+, 1.8 & 1.18+, Mohist 1.18.2}
|
||||
EnumNameTagVisibility
|
||||
);
|
||||
}
|
||||
available = true;
|
||||
}
|
||||
@@ -223,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();
|
||||
}
|
||||
@@ -250,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
|
||||
);
|
||||
}
|
||||
@@ -260,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;
|
||||
@@ -310,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;
|
||||
}
|
||||
|
||||
@@ -326,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);
|
||||
@@ -342,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));
|
||||
}
|
||||
@@ -362,7 +415,7 @@ public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
|
||||
null,
|
||||
objectiveName,
|
||||
null,
|
||||
toComponent(new IChatBaseComponent("")),
|
||||
toComponent(""),
|
||||
null
|
||||
);
|
||||
}
|
||||
@@ -373,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());
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.shared.platform.bossbar.BossBar;
|
||||
import me.neznamy.tab.api.bossbar.BarColor;
|
||||
import me.neznamy.tab.api.bossbar.BarStyle;
|
||||
import me.neznamy.tab.shared.chat.IChatBaseComponent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -35,10 +34,10 @@ public class BungeeBossBar implements BossBar {
|
||||
|
||||
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 0);
|
||||
packet.setHealth(progress);
|
||||
packet.setTitle(IChatBaseComponent.optimizedComponent(title).toString(player.getVersion()));
|
||||
BungeeMultiVersion.setTitle(packet, title, player.getVersion());
|
||||
packet.setColor(color.ordinal());
|
||||
packet.setDivision(style.ordinal());
|
||||
player.getPlayer().unsafe().sendPacket(packet);
|
||||
player.sendPacket(packet);
|
||||
|
||||
colors.put(id, color.ordinal());
|
||||
styles.put(id, style.ordinal());
|
||||
@@ -49,8 +48,8 @@ public class BungeeBossBar implements BossBar {
|
||||
if (player.getVersion().getMinorVersion() < 9) return;
|
||||
|
||||
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 3);
|
||||
packet.setTitle(IChatBaseComponent.optimizedComponent(title).toString(player.getVersion()));
|
||||
player.getPlayer().unsafe().sendPacket(packet);
|
||||
BungeeMultiVersion.setTitle(packet, title, player.getVersion());
|
||||
player.sendPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,7 +58,7 @@ public class BungeeBossBar implements BossBar {
|
||||
|
||||
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 2);
|
||||
packet.setHealth(progress);
|
||||
player.getPlayer().unsafe().sendPacket(packet);
|
||||
player.sendPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,7 +68,7 @@ public class BungeeBossBar implements BossBar {
|
||||
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 4);
|
||||
packet.setDivision(style.ordinal());
|
||||
packet.setColor(colors.get(id));
|
||||
player.getPlayer().unsafe().sendPacket(packet);
|
||||
player.sendPacket(packet);
|
||||
|
||||
styles.put(id, style.ordinal());
|
||||
}
|
||||
@@ -81,7 +80,7 @@ public class BungeeBossBar implements BossBar {
|
||||
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 4);
|
||||
packet.setDivision(styles.get(id));
|
||||
packet.setColor(color.ordinal());
|
||||
player.getPlayer().unsafe().sendPacket(packet);
|
||||
player.sendPacket(packet);
|
||||
|
||||
colors.put(id, color.ordinal());
|
||||
}
|
||||
@@ -90,7 +89,7 @@ public class BungeeBossBar implements BossBar {
|
||||
public void remove(@NotNull UUID id) {
|
||||
if (player.getVersion().getMinorVersion() < 9) return;
|
||||
|
||||
player.getPlayer().unsafe().sendPacket(new net.md_5.bungee.protocol.packet.BossBar(id, 1));
|
||||
player.sendPacket(new net.md_5.bungee.protocol.packet.BossBar(id, 1));
|
||||
|
||||
colors.remove(id);
|
||||
styles.remove(id);
|
||||
|
||||
+24
-1
@@ -1,5 +1,6 @@
|
||||
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;
|
||||
@@ -25,7 +26,29 @@ public class BungeeEventListener extends EventListener<ProxiedPlayer> implements
|
||||
|
||||
@EventHandler
|
||||
public void onSwitch(ServerSwitchEvent e) {
|
||||
serverChange(e.getPlayer(), e.getPlayer().getUniqueId(), e.getPlayer().getServer().getInfo().getName());
|
||||
TAB tab = TAB.getInstance();
|
||||
if (tab.isPluginDisabled()) return;
|
||||
tab.getCPUManager().runTask(() -> {
|
||||
TabPlayer player = tab.getPlayer(e.getPlayer().getUniqueId());
|
||||
if (player == null) {
|
||||
player = createPlayer(e.getPlayer());
|
||||
|
||||
// I don't think this is actually needed, but someone said it fixed geyser warn in console
|
||||
// Doing this won't hurt, so whatever
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) {
|
||||
player.getScoreboard().freeze();
|
||||
}
|
||||
|
||||
tab.getFeatureManager().onJoin(player);
|
||||
} else {
|
||||
tab.getFeatureManager().onServerChange(player.getUniqueId(), e.getPlayer().getServer().getInfo().getName());
|
||||
if (player.getVersion().getNetworkId() < ProtocolVersion.V1_20_2.getNetworkId()) {
|
||||
// For versions below 1.20.2 the tablist is already clean when this event is called
|
||||
// For 1.20.2+ this event is called before, so we listen to Login packet instead
|
||||
tab.getFeatureManager().onTabListClear(player);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.chat.ChatModifier;
|
||||
import me.neznamy.tab.shared.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.packet.*;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItem.Item;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BungeeMultiVersion {
|
||||
|
||||
/**
|
||||
* Flag tracking if the current BungeeCord version contains changes made in build 1732.
|
||||
* This build changes "ScoreboardDisplay" field "position" from "byte" to "int".
|
||||
*/
|
||||
private static final boolean b1732Plus = ReflectionUtils.getFields(ScoreboardDisplay.class, int.class).size() > 0;
|
||||
|
||||
/**
|
||||
* Flag tracking if the current BungeeCord version contains changes made in build 1738.
|
||||
* This build adds "sendPacketQueued" method to UserConnection class.
|
||||
*/
|
||||
private static final boolean b1738Plus = ReflectionUtils.methodExists(UserConnection.class, "sendPacketQueued", DefinedPacket.class);
|
||||
|
||||
/**
|
||||
* Flag tracking if the current BungeeCord version contains changes made in build 1760.
|
||||
* This build changes all packet fields for displaying information from String to BaseComponent.
|
||||
*/
|
||||
private static final boolean b1760Plus = ReflectionUtils.methodExists(BossBar.class, "setTitle", BaseComponent.class);
|
||||
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
public static ScoreboardDisplay newScoreboardDisplay(int position, @NotNull String name) {
|
||||
if (b1732Plus) {
|
||||
return ScoreboardDisplay.class.getConstructor(int.class, String.class).newInstance(position, name);
|
||||
} else {
|
||||
return new ScoreboardDisplay((byte) position, name);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void sendPacket(@NotNull BungeeTabPlayer player, @NotNull DefinedPacket packet) {
|
||||
if (b1738Plus) {
|
||||
try {
|
||||
UserConnection.class.getDeclaredMethod("sendPacketQueued", DefinedPacket.class).invoke(player.getPlayer(), 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 " + player.getName() +
|
||||
" (online = " + player.getPlayer().isConnected() + "): " + packet, BungeeCordBug);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
player.getPlayer().unsafe().sendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void setDisplayName(@NotNull Item item, @NotNull IChatBaseComponent displayName, @NotNull ProtocolVersion version) {
|
||||
if (b1760Plus) {
|
||||
Item.class.getMethod("setDisplayName", BaseComponent.class).invoke(item, toComponent(displayName, version));
|
||||
} else {
|
||||
item.setDisplayName(displayName.toString(version));
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
public static PlayerListHeaderFooter newPlayerListHeaderFooter(@NotNull IChatBaseComponent header,
|
||||
@NotNull IChatBaseComponent footer, ProtocolVersion version) {
|
||||
if (b1760Plus) {
|
||||
return PlayerListHeaderFooter.class.getConstructor(BaseComponent.class, BaseComponent.class).newInstance(
|
||||
toComponent(header, version),
|
||||
toComponent(footer, version)
|
||||
);
|
||||
} else {
|
||||
return new PlayerListHeaderFooter(
|
||||
header.toString(version),
|
||||
footer.toString(version)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void setTitle(@NotNull BossBar bossBar, @NotNull String title, @NotNull ProtocolVersion version) {
|
||||
if (b1760Plus) {
|
||||
BossBar.class.getMethod("setTitle", BaseComponent.class).invoke(bossBar, toComponent(IChatBaseComponent.optimizedComponent(title), version));
|
||||
} else {
|
||||
bossBar.setTitle(IChatBaseComponent.optimizedComponent(title).toString(version));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
public static ScoreboardObjective newScoreboardObjective(@NotNull String objective, @NotNull IChatBaseComponent title,
|
||||
@Nullable ScoreboardObjective.HealthDisplay healthDisplay, byte action,
|
||||
@NotNull ProtocolVersion version) {
|
||||
if (b1760Plus) {
|
||||
return ScoreboardObjective.class
|
||||
.getConstructor(String.class, BaseComponent.class, ScoreboardObjective.HealthDisplay.class, byte.class)
|
||||
.newInstance(objective, toComponent(title, version), healthDisplay, action);
|
||||
} else {
|
||||
return new ScoreboardObjective(objective, title.toString(version), healthDisplay, action);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
public static Team newTeam(@NotNull String name, byte mode, @NotNull String prefix, @NotNull String suffix,
|
||||
@NotNull String nameTagVisibility, @NotNull String collisionRule, int color,
|
||||
byte options, @Nullable String[] players, @NotNull ProtocolVersion version) {
|
||||
if (b1760Plus) {
|
||||
return Team.class.getConstructor(String.class, byte.class, BaseComponent.class, BaseComponent.class,
|
||||
BaseComponent.class, String.class, String.class, int.class, byte.class, String[].class)
|
||||
.newInstance(
|
||||
name,
|
||||
mode,
|
||||
toComponent(IChatBaseComponent.optimizedComponent(name), version),
|
||||
toComponent(IChatBaseComponent.optimizedComponent(prefix), version),
|
||||
toComponent(IChatBaseComponent.optimizedComponent(suffix), version),
|
||||
nameTagVisibility,
|
||||
collisionRule,
|
||||
color,
|
||||
options,
|
||||
players
|
||||
);
|
||||
} else {
|
||||
return new Team(
|
||||
name,
|
||||
mode,
|
||||
jsonOrRaw(name, version),
|
||||
jsonOrRaw(prefix, version),
|
||||
jsonOrRaw(suffix, version),
|
||||
nameTagVisibility,
|
||||
collisionRule,
|
||||
color,
|
||||
options,
|
||||
players
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If player's version is 1.13+, creates a component from given text and returns
|
||||
* it as a serialized component, which BungeeCord uses.
|
||||
* <p>
|
||||
* If player's version is 1.12-, the text is returned
|
||||
*
|
||||
* @param text
|
||||
* Text to convert
|
||||
* @param version
|
||||
* Protocol version to create json string for
|
||||
* @return serialized component for 1.13+ clients, cut string for 1.12-
|
||||
*/
|
||||
@NotNull
|
||||
private static String jsonOrRaw(@NotNull String text, @NotNull ProtocolVersion version) {
|
||||
if (version.getMinorVersion() >= 13) {
|
||||
return IChatBaseComponent.optimizedComponent(text).toString(version);
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private static BaseComponent toComponent(@NotNull IChatBaseComponent component, @NotNull ProtocolVersion version) {
|
||||
TextComponent textComponent = new TextComponent(component.getText());
|
||||
ChatModifier modifier = component.getModifier();
|
||||
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 (!component.getExtra().isEmpty()) textComponent.setExtra(
|
||||
component.getExtra().stream().map(c -> toComponent(c, version)).collect(Collectors.toList()));
|
||||
return textComponent;
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -163,7 +163,7 @@ public class BungeePipelineInjector extends NettyPipelineInjector {
|
||||
for (PlayerListItem.Item item : update.getItems()) {
|
||||
if (update.getActions().contains(PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME)) {
|
||||
IChatBaseComponent newDisplayName = TAB.getInstance().getFeatureManager().onDisplayNameChange(receiver, item.getUuid());
|
||||
if (newDisplayName != null) item.setDisplayName(newDisplayName.toString(receiver.getVersion()));
|
||||
if (newDisplayName != null) BungeeMultiVersion.setDisplayName(item, newDisplayName, receiver.getVersion());
|
||||
}
|
||||
if (update.getActions().contains(PlayerListItemUpdate.Action.UPDATE_LATENCY)) {
|
||||
item.setPing(TAB.getInstance().getFeatureManager().onLatencyChange(receiver, item.getUuid(), item.getPing()));
|
||||
@@ -177,7 +177,7 @@ public class BungeePipelineInjector extends NettyPipelineInjector {
|
||||
for (PlayerListItem.Item item : listItem.getItems()) {
|
||||
if (listItem.getAction() == PlayerListItem.Action.UPDATE_DISPLAY_NAME || listItem.getAction() == PlayerListItem.Action.ADD_PLAYER) {
|
||||
IChatBaseComponent newDisplayName = TAB.getInstance().getFeatureManager().onDisplayNameChange(receiver, item.getUuid());
|
||||
if (newDisplayName != null) item.setDisplayName(newDisplayName.toString(receiver.getVersion()));
|
||||
if (newDisplayName != null) BungeeMultiVersion.setDisplayName(item, newDisplayName, receiver.getVersion());
|
||||
}
|
||||
if (listItem.getAction() == PlayerListItem.Action.UPDATE_LATENCY || listItem.getAction() == PlayerListItem.Action.ADD_PLAYER) {
|
||||
item.setPing(TAB.getInstance().getFeatureManager().onLatencyChange(receiver, item.getUuid(), item.getPing()));
|
||||
@@ -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() {
|
||||
|
||||
+40
-44
@@ -4,14 +4,11 @@ import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.shared.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.shared.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import net.md_5.bungee.protocol.packet.ScoreboardDisplay;
|
||||
import net.md_5.bungee.protocol.packet.ScoreboardObjective;
|
||||
import net.md_5.bungee.protocol.packet.ScoreboardScore;
|
||||
import net.md_5.bungee.protocol.packet.Team;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
@@ -21,11 +18,6 @@ import java.util.Collection;
|
||||
*/
|
||||
public class BungeeScoreboard extends Scoreboard<BungeeTabPlayer> {
|
||||
|
||||
/** Constructor to support both <1.20.2 and 1.20.2+ builds */
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final Constructor<ScoreboardDisplay> newScoreboardDisplay = (Constructor<ScoreboardDisplay>)
|
||||
Arrays.stream(ScoreboardDisplay.class.getConstructors()).filter(c -> c.getParameterCount() == 2).findAny().orElse(null);
|
||||
|
||||
public BungeeScoreboard(@NotNull BungeeTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
@@ -33,36 +25,39 @@ public class BungeeScoreboard extends Scoreboard<BungeeTabPlayer> {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setDisplaySlot(@NotNull DisplaySlot slot, @NotNull String objective) {
|
||||
player.getPlayer().unsafe().sendPacket(newScoreboardDisplay.newInstance((byte)slot.ordinal(), objective));
|
||||
player.sendPacket(BungeeMultiVersion.newScoreboardDisplay(slot.ordinal(), objective));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerObjective0(@NotNull String objectiveName, @NotNull String title, @NotNull HealthDisplay display) {
|
||||
player.getPlayer().unsafe().sendPacket(new ScoreboardObjective(
|
||||
player.sendPacket(BungeeMultiVersion.newScoreboardObjective(
|
||||
objectiveName,
|
||||
jsonOrRaw(title),
|
||||
IChatBaseComponent.optimizedComponent(title),
|
||||
ScoreboardObjective.HealthDisplay.valueOf(display.name()),
|
||||
(byte) 0
|
||||
(byte) 0,
|
||||
player.getVersion()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterObjective0(@NotNull String objectiveName) {
|
||||
player.getPlayer().unsafe().sendPacket(new ScoreboardObjective(
|
||||
player.sendPacket(BungeeMultiVersion.newScoreboardObjective(
|
||||
objectiveName,
|
||||
"", // Empty string to prevent kick on 1.7
|
||||
new IChatBaseComponent(), // Empty value instead of null to prevent NPE kick on 1.7
|
||||
null,
|
||||
(byte) 1
|
||||
(byte) 1,
|
||||
player.getVersion()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateObjective0(@NotNull String objectiveName, @NotNull String title, @NotNull HealthDisplay display) {
|
||||
player.getPlayer().unsafe().sendPacket(new ScoreboardObjective(
|
||||
player.sendPacket(BungeeMultiVersion.newScoreboardObjective(
|
||||
objectiveName,
|
||||
jsonOrRaw(title),
|
||||
IChatBaseComponent.optimizedComponent(title),
|
||||
ScoreboardObjective.HealthDisplay.valueOf(display.name()),
|
||||
(byte) 2
|
||||
(byte) 2,
|
||||
player.getVersion()
|
||||
));
|
||||
}
|
||||
|
||||
@@ -74,13 +69,23 @@ public class BungeeScoreboard extends Scoreboard<BungeeTabPlayer> {
|
||||
if (player.getVersion().getMinorVersion() >= 13) {
|
||||
color = EnumChatFormat.lastColorsOf(prefix).ordinal();
|
||||
}
|
||||
player.getPlayer().unsafe().sendPacket(new Team(name, (byte) 0, jsonOrRaw(name), jsonOrRaw(prefix), jsonOrRaw(suffix),
|
||||
visibility.toString(), collision.toString(), color, (byte)options, players.toArray(new String[0])));
|
||||
player.sendPacket(BungeeMultiVersion.newTeam(
|
||||
name,
|
||||
(byte) 0,
|
||||
prefix,
|
||||
suffix,
|
||||
visibility.toString(),
|
||||
collision.toString(),
|
||||
color,
|
||||
(byte)options,
|
||||
players.toArray(new String[0]),
|
||||
player.getVersion()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterTeam0(@NotNull String name) {
|
||||
player.getPlayer().unsafe().sendPacket(new Team(name));
|
||||
player.sendPacket(new Team(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -90,36 +95,27 @@ public class BungeeScoreboard extends Scoreboard<BungeeTabPlayer> {
|
||||
if (player.getVersion().getMinorVersion() >= 13) {
|
||||
color = EnumChatFormat.lastColorsOf(prefix).ordinal();
|
||||
}
|
||||
player.getPlayer().unsafe().sendPacket(new Team(name, (byte) 2, jsonOrRaw(name), jsonOrRaw(prefix),
|
||||
jsonOrRaw(suffix), visibility.toString(), collision.toString(), color, (byte)options, null));
|
||||
player.sendPacket(BungeeMultiVersion.newTeam(
|
||||
name,
|
||||
(byte) 2,
|
||||
prefix,
|
||||
suffix,
|
||||
visibility.toString(),
|
||||
collision.toString(),
|
||||
color,
|
||||
(byte)options,
|
||||
null,
|
||||
player.getVersion()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScore0(@NotNull String objective, @NotNull String playerName, int score) {
|
||||
player.getPlayer().unsafe().sendPacket(new ScoreboardScore(playerName, (byte) 0, objective, score));
|
||||
player.sendPacket(new ScoreboardScore(playerName, (byte) 0, objective, score));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScore0(@NotNull String objective, @NotNull String playerName) {
|
||||
player.getPlayer().unsafe().sendPacket(new ScoreboardScore(playerName, (byte) 1, objective, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* If player's version is 1.13+, creates a component from given text and returns
|
||||
* it as a serialized component, which BungeeCord uses.
|
||||
* <p>
|
||||
* If player's version is 1.12-, the text is returned
|
||||
*
|
||||
* @param text
|
||||
* Text to convert
|
||||
* @return serialized component for 1.13+ clients, cut string for 1.12-
|
||||
*/
|
||||
@NotNull
|
||||
private String jsonOrRaw(@NotNull String text) {
|
||||
if (player.getVersion().getMinorVersion() >= 13) {
|
||||
return IChatBaseComponent.optimizedComponent(text).toString(player.getVersion());
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
player.sendPacket(new ScoreboardScore(playerName, (byte) 1, objective, 0));
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ public class BungeeTAB extends Plugin {
|
||||
public void onEnable() {
|
||||
if (!ReflectionUtils.classExists("net.md_5.bungee.protocol.packet.PlayerListItemUpdate")) {
|
||||
getLogger().info(EnumChatFormat.color("&cThe plugin requires BungeeCord build #1671 " +
|
||||
"(with 1.19.3 support) and up (or an equivalent fork) to work."));
|
||||
"(released on December 7th, 2022) and up (or an equivalent fork) to work."));
|
||||
return;
|
||||
}
|
||||
TAB.create(new BungeePlatform(this));
|
||||
|
||||
@@ -2,10 +2,12 @@ package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import de.myzelyam.api.vanish.BungeeVanishAPI;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
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;
|
||||
@@ -14,8 +16,10 @@ import me.neznamy.tab.shared.platform.bossbar.BossBar;
|
||||
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
|
||||
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;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -71,7 +75,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
|
||||
@@ -102,11 +106,19 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
|
||||
@Override
|
||||
public void sendPluginMessage(byte[] message) {
|
||||
if (getPlayer().getServer() == null) {
|
||||
errorNoServer(message);
|
||||
return;
|
||||
if (!getPlayer().isConnected()) 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,4 +153,17 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
if (version >= ProtocolVersion.V1_8.getNetworkId()) return tabList1_8;
|
||||
return tabList1_7;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends packet to the player. If BungeeCord supports 1.20.2+, new packet queue method is used
|
||||
* to avoid error when sending packet in configuration phase.
|
||||
*
|
||||
* @param packet
|
||||
* Packet to send
|
||||
*/
|
||||
@SneakyThrows
|
||||
public void sendPacket(@NotNull DefinedPacket packet) {
|
||||
if (!getPlayer().isConnected()) return;
|
||||
BungeeMultiVersion.sendPacket(this, packet);
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package me.neznamy.tab.platforms.bungeecord.tablist;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.platforms.bungeecord.BungeeMultiVersion;
|
||||
import me.neznamy.tab.platforms.bungeecord.BungeeTabPlayer;
|
||||
import me.neznamy.tab.shared.chat.IChatBaseComponent;
|
||||
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.PlayerListItem.Item;
|
||||
import net.md_5.bungee.tab.ServerUnique;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class BungeeTabList implements TabList {
|
||||
|
||||
/** Player this TabList belongs to */
|
||||
@NotNull
|
||||
protected final BungeeTabPlayer player;
|
||||
|
||||
/** Pointer to UUIDs in player's TabList */
|
||||
private final Collection<UUID> uuids;
|
||||
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public BungeeTabList(@NotNull BungeeTabPlayer player) {
|
||||
this.player = player;
|
||||
uuids = (Collection<UUID>) ReflectionUtils.getField(ServerUnique.class, "uuids").get(((UserConnection)player.getPlayer()).getTabListHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerListHeaderFooter(@NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
|
||||
player.sendPacket(BungeeMultiVersion.newPlayerListHeaderFooter(header, footer, player.getVersion()));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Item item(@NotNull UUID id) {
|
||||
Item item = new Item();
|
||||
item.setUuid(id);
|
||||
return item;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Item entryToItem(Entry entry) {
|
||||
Item item = item(entry.getUniqueId());
|
||||
if (entry.getDisplayName() != null) {
|
||||
BungeeMultiVersion.setDisplayName(item, entry.getDisplayName(), player.getVersion());
|
||||
}
|
||||
item.setGamemode(entry.getGameMode());
|
||||
item.setListed(true);
|
||||
item.setPing(entry.getLatency());
|
||||
if (entry.getSkin() != null) {
|
||||
item.setProperties(new Property[]{new Property(TEXTURES_PROPERTY, entry.getSkin().getValue(), entry.getSkin().getSignature())});
|
||||
} else {
|
||||
item.setProperties(new Property[0]);
|
||||
}
|
||||
item.setUsername(entry.getName());
|
||||
return item;
|
||||
}
|
||||
|
||||
public void addUuid(UUID id) {
|
||||
uuids.add(id);
|
||||
}
|
||||
|
||||
public void removeUuid(UUID id) {
|
||||
uuids.remove(id);
|
||||
}
|
||||
}
|
||||
+11
-41
@@ -1,11 +1,8 @@
|
||||
package me.neznamy.tab.platforms.bungeecord.tablist;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.platforms.bungeecord.BungeeMultiVersion;
|
||||
import me.neznamy.tab.platforms.bungeecord.BungeeTabPlayer;
|
||||
import me.neznamy.tab.shared.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItem.Item;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItemRemove;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItemUpdate;
|
||||
@@ -18,27 +15,25 @@ import java.util.UUID;
|
||||
/**
|
||||
* TabList handler for 1.19.3+ players using the new tab list packets.
|
||||
* Because BungeeCord does not have a TabList API, we need to use packets.
|
||||
* They are sent using an internal BungeeCord method that keeps track of them,
|
||||
* so they are removed on server switch to secure parity with Velocity.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class BungeeTabList1193 implements TabList {
|
||||
public class BungeeTabList1193 extends BungeeTabList {
|
||||
|
||||
/** Player this TabList belongs to */
|
||||
@NotNull
|
||||
private final BungeeTabPlayer player;
|
||||
public BungeeTabList1193(@NotNull BungeeTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntry(@NotNull UUID entry) {
|
||||
removeUuid(entry);
|
||||
PlayerListItemRemove remove = new PlayerListItemRemove();
|
||||
remove.setUuids(new UUID[]{entry});
|
||||
((UserConnection)player.getPlayer()).getTabListHandler().onUpdate(remove);
|
||||
player.sendPacket(remove);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayName(@NotNull UUID entry, @Nullable IChatBaseComponent displayName) {
|
||||
Item item = item(entry);
|
||||
item.setDisplayName(displayName == null ? null : displayName.toString(player.getVersion()));
|
||||
if (displayName != null) BungeeMultiVersion.setDisplayName(item, displayName, player.getVersion());
|
||||
sendPacket(EnumSet.of(PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME), item);
|
||||
}
|
||||
|
||||
@@ -58,39 +53,14 @@ public class BungeeTabList1193 implements TabList {
|
||||
|
||||
@Override
|
||||
public void addEntry(@NotNull Entry entry) {
|
||||
Item item = item(entry.getUniqueId());
|
||||
if (entry.getDisplayName() != null) item.setDisplayName(entry.getDisplayName().toString(player.getVersion()));
|
||||
item.setGamemode(entry.getGameMode());
|
||||
item.setListed(true);
|
||||
item.setPing(entry.getLatency());
|
||||
if (entry.getSkin() != null) {
|
||||
item.setProperties(new Property[]{new Property(TEXTURES_PROPERTY, entry.getSkin().getValue(), entry.getSkin().getSignature())});
|
||||
} else {
|
||||
item.setProperties(new Property[0]);
|
||||
}
|
||||
item.setUsername(entry.getName());
|
||||
sendPacket(EnumSet.allOf(PlayerListItemUpdate.Action.class), item);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Item item(@NotNull UUID id) {
|
||||
Item item = new Item();
|
||||
item.setUuid(id);
|
||||
return item;
|
||||
addUuid(entry.getUniqueId());
|
||||
sendPacket(EnumSet.allOf(PlayerListItemUpdate.Action.class), entryToItem(entry));
|
||||
}
|
||||
|
||||
private void sendPacket(@NotNull EnumSet<PlayerListItemUpdate.Action> actions, @NotNull Item item) {
|
||||
PlayerListItemUpdate packet = new PlayerListItemUpdate();
|
||||
packet.setActions(actions);
|
||||
packet.setItems(new Item[]{item});
|
||||
((UserConnection)player.getPlayer()).getTabListHandler().onUpdate(packet);
|
||||
}
|
||||
|
||||
@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(packet);
|
||||
}
|
||||
}
|
||||
+13
-19
@@ -1,10 +1,9 @@
|
||||
package me.neznamy.tab.platforms.bungeecord.tablist;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.platforms.bungeecord.BungeeMultiVersion;
|
||||
import me.neznamy.tab.platforms.bungeecord.BungeeTabPlayer;
|
||||
import me.neznamy.tab.shared.Limitations;
|
||||
import me.neznamy.tab.shared.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItem;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -19,17 +18,10 @@ import java.util.UUID;
|
||||
* names are changed by removing old entry and adding a new one, which
|
||||
* requires further tracking than just the UUID.<p>
|
||||
* Because BungeeCord does not have a TabList API, we need to use packets.
|
||||
* They are sent using an internal BungeeCord method that keeps track of them,
|
||||
* so they are removed on server switch to secure parity with Velocity.
|
||||
* While BungeeCord itself does not support 1.7, some of its forks do.
|
||||
* This was tested on FlameCord fork.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class BungeeTabList17 implements TabList {
|
||||
|
||||
/** Player this TabList belongs to */
|
||||
@NotNull
|
||||
private final BungeeTabPlayer player;
|
||||
public class BungeeTabList17 extends BungeeTabList {
|
||||
|
||||
/** Because entries are identified by names and not uuids on 1.7 */
|
||||
@NotNull
|
||||
@@ -38,9 +30,14 @@ public class BungeeTabList17 implements TabList {
|
||||
@NotNull
|
||||
private final Map<UUID, String> displayNames = new HashMap<>();
|
||||
|
||||
public BungeeTabList17(@NotNull BungeeTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntry(@NotNull UUID entry) {
|
||||
if (!displayNames.containsKey(entry)) return; // Entry not tracked by TAB
|
||||
removeUuid(entry);
|
||||
update(PlayerListItem.Action.REMOVE_PLAYER, createItem(null, displayNames.get(entry), 0));
|
||||
|
||||
// Remove from map
|
||||
@@ -66,8 +63,10 @@ public class BungeeTabList17 implements TabList {
|
||||
|
||||
@Override
|
||||
public void addEntry(@NotNull Entry entry) {
|
||||
addUuid(entry.getUniqueId());
|
||||
String displayNameString = entry.getDisplayName() == null ? String.valueOf(entry.getName()) : entry.getDisplayName().toLegacyText();
|
||||
if (displayNameString.length() > 16) displayNameString = displayNameString.substring(0, 16); // 16 character limit
|
||||
if (displayNameString.length() > Limitations.MAX_DISPLAY_NAME_LENGTH_1_7)
|
||||
displayNameString = displayNameString.substring(0, Limitations.MAX_DISPLAY_NAME_LENGTH_1_7);
|
||||
update(PlayerListItem.Action.ADD_PLAYER, createItem(entry.getName(), displayNameString, entry.getLatency()));
|
||||
|
||||
// Add to map
|
||||
@@ -79,19 +78,14 @@ public class BungeeTabList17 implements TabList {
|
||||
PlayerListItem packet = new PlayerListItem();
|
||||
packet.setAction(action);
|
||||
packet.setItems(new PlayerListItem.Item[]{item});
|
||||
((UserConnection)player.getPlayer()).getTabListHandler().onUpdate(packet);
|
||||
player.sendPacket(packet);
|
||||
}
|
||||
|
||||
private PlayerListItem.Item createItem(@Nullable String username, @NotNull String displayName, int latency) {
|
||||
PlayerListItem.Item item = new PlayerListItem.Item();
|
||||
item.setUsername(username);
|
||||
item.setDisplayName(displayName);
|
||||
BungeeMultiVersion.setDisplayName(item, IChatBaseComponent.optimizedComponent(displayName), player.getVersion());
|
||||
item.setPing(latency);
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerListHeaderFooter(@NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
|
||||
// Added in 1.8
|
||||
}
|
||||
}
|
||||
+10
-39
@@ -1,11 +1,8 @@
|
||||
package me.neznamy.tab.platforms.bungeecord.tablist;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.platforms.bungeecord.BungeeMultiVersion;
|
||||
import me.neznamy.tab.platforms.bungeecord.BungeeTabPlayer;
|
||||
import me.neznamy.tab.shared.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItem;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItem.Item;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -16,25 +13,23 @@ import java.util.UUID;
|
||||
/**
|
||||
* TabList handler for players between 1.8 and 1.19.2.<p>
|
||||
* Because BungeeCord does not have a TabList API, we need to use packets.
|
||||
* They are sent using an internal BungeeCord method that keeps track of them,
|
||||
* so they are removed on server switch to secure parity with Velocity.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class BungeeTabList18 implements TabList {
|
||||
public class BungeeTabList18 extends BungeeTabList {
|
||||
|
||||
/** Player this TabList belongs to */
|
||||
@NotNull
|
||||
private final BungeeTabPlayer player;
|
||||
public BungeeTabList18(@NotNull BungeeTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntry(@NotNull UUID entry) {
|
||||
removeUuid(entry);
|
||||
sendPacket(PlayerListItem.Action.REMOVE_PLAYER, item(entry));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayName(@NotNull UUID entry, @Nullable IChatBaseComponent displayName) {
|
||||
Item item = item(entry);
|
||||
item.setDisplayName(displayName == null ? null : displayName.toString(player.getVersion()));
|
||||
if (displayName != null) BungeeMultiVersion.setDisplayName(item, displayName, player.getVersion());
|
||||
sendPacket(PlayerListItem.Action.UPDATE_DISPLAY_NAME, item);
|
||||
}
|
||||
|
||||
@@ -54,38 +49,14 @@ public class BungeeTabList18 implements TabList {
|
||||
|
||||
@Override
|
||||
public void addEntry(@NotNull Entry entry) {
|
||||
Item item = item(entry.getUniqueId());
|
||||
if (entry.getDisplayName() != null) item.setDisplayName(entry.getDisplayName().toString(player.getVersion()));
|
||||
item.setGamemode(entry.getGameMode());
|
||||
item.setPing(entry.getLatency());
|
||||
if (entry.getSkin() != null) {
|
||||
item.setProperties(new Property[]{new Property(TEXTURES_PROPERTY, entry.getSkin().getValue(), entry.getSkin().getSignature())});
|
||||
} else {
|
||||
item.setProperties(new Property[0]);
|
||||
}
|
||||
item.setUsername(entry.getName());
|
||||
sendPacket(PlayerListItem.Action.ADD_PLAYER, item);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Item item(@NotNull UUID id) {
|
||||
Item item = new Item();
|
||||
item.setUuid(id);
|
||||
return item;
|
||||
addUuid(entry.getUniqueId());
|
||||
sendPacket(PlayerListItem.Action.ADD_PLAYER, entryToItem(entry));
|
||||
}
|
||||
|
||||
private void sendPacket(@NotNull PlayerListItem.Action action, @NotNull Item item) {
|
||||
PlayerListItem packet = new PlayerListItem();
|
||||
packet.setAction(action);
|
||||
packet.setItems(new Item[]{item});
|
||||
((UserConnection)player.getPlayer()).getTabListHandler().onUpdate(packet);
|
||||
}
|
||||
|
||||
@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(packet);
|
||||
}
|
||||
}
|
||||
@@ -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,17 @@ public record FabricEntityView(FabricTabPlayer player) implements EntityView {
|
||||
|
||||
@Override
|
||||
public int[] getDestroyedEntities(@NotNull Object destroyPacket) {
|
||||
return ((ClientboundRemoveEntitiesPacket) destroyPacket).getEntityIds().toIntArray();
|
||||
return FabricMultiVersion.getDestroyedEntities(destroyPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBundlePacket(@NotNull Object packet) {
|
||||
return FabricMultiVersion.isBundlePacket(packet);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Iterable<Object> getPackets(@NotNull Object bundlePacket) {
|
||||
return (Iterable<Object>) (Object) FabricMultiVersion.getPackets(bundlePacket);
|
||||
}
|
||||
}
|
||||
@@ -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, this::worldChange);
|
||||
//TODO command preprocess
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
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.level.Level;
|
||||
import net.minecraft.world.level.storage.ServerLevelData;
|
||||
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 String getWorldName(Level level) {
|
||||
// 1.15.2-
|
||||
// return level.getLevelData().getLevelName() + level.dimension.getType().getFileSuffix();
|
||||
|
||||
// 1.16+
|
||||
String path = level.dimension().location().getPath();
|
||||
String dimensionSuffix = switch (path) {
|
||||
case "overworld" -> ""; // No suffix for overworld
|
||||
case "the_nether" -> "_nether";
|
||||
default -> "_" + path; // End + default behavior for other dimensions created by mods
|
||||
};
|
||||
return ((ServerLevelData)level.getLevelData()).getLevelName() + dimensionSuffix;
|
||||
}
|
||||
|
||||
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> respawnAction, BiConsumer<UUID, String> worldChangeAction) {
|
||||
// 1.16+
|
||||
net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents.AFTER_RESPAWN.register(
|
||||
(oldPlayer, newPlayer, alive) -> {
|
||||
respawnAction.accept(newPlayer.getUUID(), newPlayer);
|
||||
// respawning from death & taking end portal in the end do not call world change event
|
||||
worldChangeAction.accept(newPlayer.getUUID(), getWorldName(newPlayer.level));
|
||||
});
|
||||
net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register(
|
||||
(player, origin, destination) -> worldChangeAction.accept(player.getUUID(), getWorldName(destination)));
|
||||
}
|
||||
|
||||
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 boolean isBundlePacket(Object packet) {
|
||||
// 1.19.4+
|
||||
return packet instanceof ClientboundBundlePacket;
|
||||
}
|
||||
|
||||
public static Iterable<Packet<ClientGamePacketListener>> getPackets(Object bundlePacket) {
|
||||
// 1.19.4+
|
||||
return ((ClientboundBundlePacket)bundlePacket).subPackets();
|
||||
}
|
||||
|
||||
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,30 +13,23 @@ 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) {
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(identifier, -1, () -> identifier);
|
||||
registerDummyPlaceholder(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -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(), FabricMultiVersion.getWorldName(player.level));
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
@@ -69,7 +69,7 @@ public class FeatureManager {
|
||||
((UnLoadable) f).unload();
|
||||
TAB.getInstance().debug("Feature " + f.getClass().getSimpleName() + " processed unload in " + (System.currentTimeMillis()-time) + "ms");
|
||||
}
|
||||
TAB.getInstance().getPlaceholderManager().getTabExpansion().unregister();
|
||||
TAB.getInstance().getPlaceholderManager().getTabExpansion().unregisterExpansion();
|
||||
if (TAB.getInstance().getPlatform() instanceof ProxyPlatform) {
|
||||
for (TabPlayer player : TAB.getInstance().getOnlinePlayers()) {
|
||||
((ProxyTabPlayer)player).sendPluginMessage("Unload");
|
||||
@@ -164,7 +164,6 @@ public class FeatureManager {
|
||||
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;
|
||||
@@ -271,6 +270,25 @@ public class FeatureManager {
|
||||
return newLatency;
|
||||
}
|
||||
|
||||
public void onLoginPacket(TabPlayer packetReceiver) {
|
||||
packetReceiver.getScoreboard().unfreeze();
|
||||
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 onTabListClear(TabPlayer packetReceiver) {
|
||||
for (TabFeature f : values) {
|
||||
if (!(f instanceof TabListClearListener)) continue;
|
||||
long time = System.nanoTime();
|
||||
((TabListClearListener)f).onTabListClear(packetReceiver);
|
||||
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.TABLIST_CLEAR, 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 +322,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 +378,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 +390,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
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package me.neznamy.tab.shared;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Class containing constants representing value length
|
||||
* limits on different versions.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class Limitations {
|
||||
|
||||
/** Player name length limit for scoreboard score on 1.7 and lower */
|
||||
public static final int SCOREBOARD_SCORE_LENGTH_1_7 = 16;
|
||||
|
||||
/** Player name length limit for scoreboard score on 1.8 and higher */
|
||||
public static final int SCOREBOARD_SCORE_LENGTH_1_8 = 40;
|
||||
|
||||
/** Max scoreboard title length for versions below 1.13 */
|
||||
public static final int SCOREBOARD_TITLE_PRE_1_13 = 32;
|
||||
|
||||
/** Max team prefix/suffix length for versions below 1.13 */
|
||||
public static final int TEAM_PREFIX_SUFFIX_PRE_1_13 = 16;
|
||||
|
||||
/** Max tablist entry length for 1.7 and lower */
|
||||
public static final int MAX_DISPLAY_NAME_LENGTH_1_7 = 16;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ public class TAB extends TabAPI {
|
||||
kill();
|
||||
return (configuration == null ? "&4Failed to reload, file %file% has broken syntax. Check console for more info."
|
||||
: configuration.getMessages().getReloadFailBrokenFile()).replace("%file%", brokenFile);
|
||||
} catch (Exception e) {
|
||||
} catch (Throwable e) {
|
||||
errorManager.criticalError("Failed to enable. Did you just invent a new way to break the plugin by misconfiguring it?", e);
|
||||
kill();
|
||||
return "&cFailed to enable due to an internal plugin error. Check console for more info.";
|
||||
@@ -199,7 +199,7 @@ public class TAB extends TabAPI {
|
||||
if (configuration.getMysql() != null) configuration.getMysql().closeConnection();
|
||||
featureManager.unload();
|
||||
platform.logInfo(IChatBaseComponent.fromColoredText("&aDisabled in " + (System.currentTimeMillis()-time) + "ms"));
|
||||
} catch (Exception | NoClassDefFoundError e) {
|
||||
} catch (Throwable e) {
|
||||
errorManager.criticalError("Failed to disable", e);
|
||||
}
|
||||
kill();
|
||||
|
||||
@@ -52,6 +52,8 @@ 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 TABLIST_CLEAR = "TabList entry re-add";
|
||||
|
||||
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() {
|
||||
@@ -37,6 +37,21 @@ public interface BackendPlatform<T> extends Platform<T> {
|
||||
registry.registerPlaceholders(manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a dummy placeholder implementation for specified identifier in case
|
||||
* no placeholder plugin was found.
|
||||
*
|
||||
* @param identifier
|
||||
* Placeholder identifier to register
|
||||
*/
|
||||
default void registerDummyPlaceholder(@NotNull String identifier) {
|
||||
if (identifier.startsWith("%rel_")) { // To prevent placeholder identifier check from throwing
|
||||
TAB.getInstance().getPlaceholderManager().registerRelationalPlaceholder(identifier, -1, (viewer, target) -> identifier);
|
||||
} else {
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(identifier, -1, () -> identifier);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server's TPS for {@link TabConstants.Placeholder#TPS} placeholder
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -72,4 +72,14 @@ public class DummyEntityView implements EntityView {
|
||||
public int[] getDestroyedEntities(Object destroyPacket) {
|
||||
throw new UnsupportedOperationException("Not available on this platform");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBundlePacket(Object packet) {
|
||||
throw new UnsupportedOperationException("Not available on this platform");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Object> getPackets(Object bundlePacket) {
|
||||
throw new UnsupportedOperationException("Not available on this platform");
|
||||
}
|
||||
}
|
||||
@@ -33,4 +33,8 @@ public interface EntityView {
|
||||
int getSpawnedPlayer(Object playerSpawnPacket);
|
||||
|
||||
int[] getDestroyedEntities(Object destroyPacket);
|
||||
|
||||
boolean isBundlePacket(Object packet);
|
||||
|
||||
Iterable<Object> getPackets(Object bundlePacket);
|
||||
}
|
||||
+10
@@ -163,6 +163,16 @@ public abstract class BackendNameTagX extends NameTagX implements GameModeListen
|
||||
if (receiver.getVersion().getMinorVersion() < 8) return;
|
||||
if (!receiver.isLoaded() || getDisableChecker().isDisabledPlayer(receiver) || getUnlimitedDisableChecker().isDisabledPlayer(receiver)) return;
|
||||
BackendTabPlayer player = (BackendTabPlayer) receiver;
|
||||
if (player.getEntityView().isBundlePacket(packet)) {
|
||||
for (Object wrappedPacket : player.getEntityView().getPackets(packet)) {
|
||||
checkPacket(player, wrappedPacket);
|
||||
}
|
||||
} else {
|
||||
checkPacket(player, packet);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPacket(@NotNull BackendTabPlayer player, @NotNull Object packet) {
|
||||
if (player.getEntityView().isMovePacket(packet) && !player.getEntityView().isLookPacket(packet)) { //ignoring head rotation only packets
|
||||
packetListener.onEntityMove(player, player.getEntityView().getMoveEntityId(packet));
|
||||
} else if (player.getEntityView().isTeleportPacket(packet)) {
|
||||
|
||||
@@ -43,14 +43,20 @@ public class ChatModifier {
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String consisting of magic codes (color symbol + character) of
|
||||
* each magic code used. If none are used, empty String is returned.
|
||||
*
|
||||
* @return Magic codes of this modifier as String
|
||||
*/
|
||||
@NotNull
|
||||
public String getMagicCodes() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (isBold()) builder.append(EnumChatFormat.BOLD.getFormat());
|
||||
if (isItalic()) builder.append(EnumChatFormat.ITALIC.getFormat());
|
||||
if (isUnderlined()) builder.append(EnumChatFormat.UNDERLINE.getFormat());
|
||||
if (isStrikethrough()) builder.append(EnumChatFormat.STRIKETHROUGH.getFormat());
|
||||
if (isObfuscated()) builder.append(EnumChatFormat.OBFUSCATED.getFormat());
|
||||
if (bold) builder.append(EnumChatFormat.BOLD.getFormat());
|
||||
if (italic) builder.append(EnumChatFormat.ITALIC.getFormat());
|
||||
if (underlined) builder.append(EnumChatFormat.UNDERLINE.getFormat());
|
||||
if (strikethrough) builder.append(EnumChatFormat.STRIKETHROUGH.getFormat());
|
||||
if (obfuscated) builder.append(EnumChatFormat.OBFUSCATED.getFormat());
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -232,6 +232,8 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
|
||||
tabExpansion.setPlaceholderValue(connectedPlayer, p.getIdentifier(), ((ServerPlaceholderImpl) p).getLastValue());
|
||||
}
|
||||
}
|
||||
// Initialize to avoid onVanishStatusChange being called in the loop after joining because previous value was null
|
||||
((PlayerPlaceholderImpl)registeredPlaceholders.get(TabConstants.Placeholder.VANISHED)).update(connectedPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -295,4 +297,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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(),
|
||||
|
||||
+14
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-9
@@ -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, TabListClearListener {
|
||||
|
||||
// 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,16 @@ public class GlobalPlayerList extends TabFeature implements JoinListener, QuitLi
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabListClear(TabPlayer player) {
|
||||
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)) {
|
||||
|
||||
+16
@@ -9,6 +9,7 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.platform.TabPlayer;
|
||||
@@ -84,6 +85,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 +113,19 @@ public abstract class NettyPipelineInjector extends PipelineInjector {
|
||||
modifyPlayers(packet);
|
||||
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
|
||||
}
|
||||
if (isLogin(packet)) {
|
||||
player.getScoreboard().freeze();
|
||||
super.write(context, packet, channelPromise);
|
||||
TAB.getInstance().getCPUManager().runTaskLater(200, getFeatureName(),
|
||||
TabConstants.CpuUsageCategory.PACKET_LOGIN, () -> {
|
||||
TAB.getInstance().getFeatureManager().onLoginPacket(player);
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) {
|
||||
// For 1.20.2+ we need to do this, because server switch event is called before tablist is cleared
|
||||
TAB.getInstance().getFeatureManager().onTabListClear(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, TabListClearListener {
|
||||
|
||||
/** 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 onTabListClear(TabPlayer player) {
|
||||
LayoutView view = views.get(player);
|
||||
if (view != null) view.send();
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public enum Direction {
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ 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,
|
||||
VanishListener {
|
||||
|
||||
@Getter private final String featureName = "NameTags";
|
||||
@Getter private final String refreshDisplayName = "Updating prefix/suffix";
|
||||
@@ -77,7 +78,7 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
|
||||
public void unload() {
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer target : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (hasTeamHandlingPaused(target) || disableChecker.isDisabledPlayer(target)) return;
|
||||
if (hasTeamHandlingPaused(target) || disableChecker.isDisabledPlayer(target)) continue;
|
||||
viewer.getScoreboard().unregisterTeam(sorting.getShortTeamName(target));
|
||||
}
|
||||
}
|
||||
@@ -100,7 +101,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()) {
|
||||
@@ -122,6 +122,7 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
|
||||
if (!disableChecker.isDisabledPlayer(disconnectedPlayer) && !hasTeamHandlingPaused(disconnectedPlayer)) {
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (viewer == disconnectedPlayer) continue; //player who just disconnected
|
||||
if (disconnectedPlayer.isVanished() && !viewer.hasPermission(TabConstants.Permission.SEE_VANISHED)) continue;
|
||||
viewer.getScoreboard().unregisterTeam(sorting.getShortTeamName(disconnectedPlayer));
|
||||
}
|
||||
}
|
||||
@@ -135,9 +136,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
|
||||
@@ -231,6 +229,7 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
|
||||
}
|
||||
|
||||
public void updateTeamData(@NonNull TabPlayer p, @NonNull TabPlayer viewer) {
|
||||
if (p.isVanished() && !viewer.hasPermission(TabConstants.Permission.SEE_VANISHED)) return;
|
||||
boolean visible = getTeamVisibility(p, viewer);
|
||||
String currentPrefix = p.getProperty(TabConstants.Property.TAGPREFIX).getFormat(viewer);
|
||||
String currentSuffix = p.getProperty(TabConstants.Property.TAGSUFFIX).getFormat(viewer);
|
||||
@@ -247,6 +246,7 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
|
||||
public void unregisterTeam(@NonNull TabPlayer p, @NonNull String teamName) {
|
||||
if (hasTeamHandlingPaused(p)) return;
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (p.isVanished() && !viewer.hasPermission(TabConstants.Permission.SEE_VANISHED)) continue;
|
||||
viewer.getScoreboard().unregisterTeam(teamName);
|
||||
}
|
||||
}
|
||||
@@ -259,6 +259,7 @@ public class NameTag extends TabFeature implements NameTagManager, JoinListener,
|
||||
|
||||
private void registerTeam(@NonNull TabPlayer p, @NonNull TabPlayer viewer) {
|
||||
if (hasTeamHandlingPaused(p)) return;
|
||||
if (p.isVanished() && !viewer.hasPermission(TabConstants.Permission.SEE_VANISHED)) return;
|
||||
String replacedPrefix = p.getProperty(TabConstants.Property.TAGPREFIX).getFormat(viewer);
|
||||
String replacedSuffix = p.getProperty(TabConstants.Property.TAGSUFFIX).getFormat(viewer);
|
||||
viewer.getScoreboard().registerTeam(
|
||||
@@ -341,4 +342,24 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVanishStatusChange(@NotNull TabPlayer player) {
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (viewer.hasPermission(TabConstants.Permission.SEE_VANISHED)) continue;
|
||||
if (player.isVanished()) {
|
||||
viewer.getScoreboard().unregisterTeam(sorting.getShortTeamName(player));
|
||||
} else {
|
||||
registerTeam(player, viewer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,8 @@ 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,
|
||||
VanishListener, TabListClearListener {
|
||||
|
||||
@NotNull private final String featureName = "RedisSupport";
|
||||
|
||||
@@ -56,6 +57,7 @@ public abstract class RedisSupport extends TabFeature implements JoinListener, Q
|
||||
registerMessage("join", PlayerJoin.class, PlayerJoin::new);
|
||||
registerMessage("quit", PlayerQuit.class, PlayerQuit::new);
|
||||
registerMessage("server", ServerSwitch.class, ServerSwitch::new);
|
||||
registerMessage("vanish", UpdateVanishStatus.class, UpdateVanishStatus::new);
|
||||
}
|
||||
|
||||
public void updateTabFormat(@NotNull TabPlayer p, @NotNull String format) {
|
||||
@@ -207,4 +209,19 @@ 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));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabListClear(TabPlayer player) {
|
||||
features.forEach(f -> f.onTabListClear(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVanishStatusChange(@NotNull TabPlayer player) {
|
||||
sendMessage(new UpdateVanishStatus(player.getUniqueId(), player.isVanished()));
|
||||
}
|
||||
}
|
||||
+5
-15
@@ -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,19 @@ 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 */}
|
||||
|
||||
public void onTabListClear(@NotNull TabPlayer player) {/* Do nothing by default */}
|
||||
|
||||
public void onVanishStatusChange(@NotNull RedisPlayer player) {/* Do nothing by default */}
|
||||
}
|
||||
+22
-5
@@ -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 onTabListClear(@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;
|
||||
@@ -104,4 +104,21 @@ public class RedisGlobalPlayerList extends RedisFeature {
|
||||
redisSupport.getRedisPlayerList() == null ? null :
|
||||
IChatBaseComponent.optimizedComponent(redisSupport.getRedisPlayerList().getFormat(player)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVanishStatusChange(@NotNull RedisPlayer player) {
|
||||
if (player.isVanished()) {
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (!shouldSee(all, player)) {
|
||||
all.getTabList().removeEntry(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (shouldSee(viewer, player)) {
|
||||
viewer.getTabList().addEntry(getEntry(player));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-10
@@ -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() +
|
||||
@@ -76,6 +66,15 @@ public class RedisPlayerList extends RedisFeature {
|
||||
return values.get(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVanishStatusChange(@NotNull RedisPlayer player) {
|
||||
if (player.isVanished()) return;
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (viewer.getVersion().getMinorVersion() < 8) continue;
|
||||
viewer.getTabList().updateDisplayName(player.getUniqueId(), IChatBaseComponent.optimizedComponent(getFormat(player)));
|
||||
}
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Update extends RedisMessage {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
+5
-15
@@ -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 {
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package me.neznamy.tab.shared.features.redis.message;
|
||||
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import me.neznamy.tab.shared.features.redis.RedisPlayer;
|
||||
import me.neznamy.tab.shared.features.redis.RedisSupport;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UpdateVanishStatus extends RedisMessage {
|
||||
|
||||
private UUID playerId;
|
||||
private boolean vanished;
|
||||
|
||||
@Override
|
||||
public void write(@NotNull ByteArrayDataOutput out) {
|
||||
writeUUID(out, playerId);
|
||||
out.writeBoolean(vanished);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull ByteArrayDataInput in) {
|
||||
playerId = readUUID(in);
|
||||
vanished = in.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(@NotNull RedisSupport redisSupport) {
|
||||
RedisPlayer target = redisSupport.getRedisPlayers().get(playerId);
|
||||
if (target == null) return; // Print warn?
|
||||
target.setVanished(vanished);
|
||||
redisSupport.getFeatures().forEach(f -> f.onVanishStatusChange(target));
|
||||
}
|
||||
}
|
||||
+15
-6
@@ -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, LoginPacketListener {
|
||||
|
||||
public static final String OBJECTIVE_NAME = "TAB-Scoreboard";
|
||||
|
||||
@@ -306,12 +306,21 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServerChange(@NotNull TabPlayer changed, @NotNull String from, @NotNull String to) {
|
||||
otherPluginScoreboards.remove(changed);
|
||||
ScoreboardImpl scoreboard = activeScoreboards.get(changed);
|
||||
public void onQuit(@NotNull TabPlayer disconnectedPlayer) {
|
||||
ScoreboardImpl sb = activeScoreboards.get(disconnectedPlayer);
|
||||
if (sb != null) {
|
||||
sb.removePlayerFromSet(disconnectedPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoginPacket(TabPlayer player) {
|
||||
if (!player.isLoaded()) return;
|
||||
otherPluginScoreboards.remove(player);
|
||||
ScoreboardImpl scoreboard = activeScoreboards.get(player);
|
||||
if (scoreboard != null) {
|
||||
scoreboard.removePlayerFromSet(changed);
|
||||
scoreboard.addPlayer(changed);
|
||||
scoreboard.removePlayerFromSet(player);
|
||||
scoreboard.addPlayer(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package me.neznamy.tab.shared.features.scoreboard.lines;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.shared.Limitations;
|
||||
import me.neznamy.tab.shared.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabPlayer;
|
||||
@@ -46,7 +47,11 @@ public class LongLine extends ScoreboardLine implements Refreshable {
|
||||
"", Scoreboard.NameVisibility.ALWAYS, Scoreboard.CollisionRule.ALWAYS, 0);
|
||||
} else {
|
||||
removeLine(refreshed, refreshed.getProperty(nameProperty).get());
|
||||
String[] values = splitText(getPlayerName(lineNumber), RGBUtils.getInstance().convertRGBtoLegacy(refreshed.getProperty(textProperty).get()), refreshed.getVersion().getMinorVersion() >= 8 ? 40 : 16);
|
||||
String[] values = splitText(
|
||||
getPlayerName(lineNumber),
|
||||
RGBUtils.getInstance().convertRGBtoLegacy(refreshed.getProperty(textProperty).get()),
|
||||
refreshed.getVersion().getMinorVersion() >= 8 ? Limitations.SCOREBOARD_SCORE_LENGTH_1_8 : Limitations.SCOREBOARD_SCORE_LENGTH_1_7
|
||||
);
|
||||
addLine(refreshed, values[1], values[0], values[2]);
|
||||
refreshed.setProperty(this, nameProperty, values[1]);
|
||||
}
|
||||
@@ -61,7 +66,11 @@ public class LongLine extends ScoreboardLine implements Refreshable {
|
||||
addLine(p, playerName, value, "");
|
||||
p.setProperty(this, nameProperty, playerName);
|
||||
} else {
|
||||
String[] values = splitText(playerName, RGBUtils.getInstance().convertRGBtoLegacy(value), p.getVersion().getMinorVersion() >= 8 ? 40 : 16);
|
||||
String[] values = splitText(
|
||||
playerName,
|
||||
RGBUtils.getInstance().convertRGBtoLegacy(value),
|
||||
p.getVersion().getMinorVersion() >= 8 ? Limitations.SCOREBOARD_SCORE_LENGTH_1_8 : Limitations.SCOREBOARD_SCORE_LENGTH_1_7
|
||||
);
|
||||
addLine(p, values[1], values[0], values[2]);
|
||||
p.setProperty(this, nameProperty, values[1]);
|
||||
}
|
||||
|
||||
+3
-2
@@ -2,6 +2,7 @@ package me.neznamy.tab.shared.features.scoreboard.lines;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.shared.Limitations;
|
||||
import me.neznamy.tab.shared.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.scoreboard.Line;
|
||||
import me.neznamy.tab.shared.features.types.TabFeature;
|
||||
@@ -164,7 +165,7 @@ public abstract class ScoreboardLine extends TabFeature implements Line {
|
||||
* @param text
|
||||
* text to display
|
||||
* @param maxNameLength
|
||||
* maximum length of name field, used values are 16 characters for <1.8 and 40 for 1.8+
|
||||
* maximum length of name field
|
||||
* @return Split text as an array of 3 elements
|
||||
*/
|
||||
protected String[] splitText(@NonNull String playerNameStart, @NonNull String text, int maxNameLength) {
|
||||
@@ -176,7 +177,7 @@ public abstract class ScoreboardLine extends TabFeature implements Line {
|
||||
nameValue = playerNameStart + text;
|
||||
suffixValue = "";
|
||||
} else {
|
||||
String[] prefixOther = split(text, 16);
|
||||
String[] prefixOther = split(text, Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13);
|
||||
prefixValue = prefixOther[0];
|
||||
String other = prefixOther[1];
|
||||
if (playerNameStart.length() > 0) {
|
||||
|
||||
+5
-4
@@ -1,6 +1,7 @@
|
||||
package me.neznamy.tab.shared.features.scoreboard.lines;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.shared.Limitations;
|
||||
import me.neznamy.tab.shared.platform.TabPlayer;
|
||||
import me.neznamy.tab.shared.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.shared.chat.rgb.RGBUtils;
|
||||
@@ -15,12 +16,12 @@ import me.neznamy.tab.shared.features.scoreboard.ScoreboardImpl;
|
||||
*/
|
||||
public class StaticLine extends ScoreboardLine {
|
||||
|
||||
//values for 1.7 clients with 16-character limit for player name
|
||||
//values for 1.7 clients
|
||||
protected String prefix17;
|
||||
protected String name17;
|
||||
protected String suffix17;
|
||||
|
||||
//values for 1.8-1.12 clients with 40-character limit for player name
|
||||
//values for 1.8-1.12 clients
|
||||
protected String prefix;
|
||||
protected String name;
|
||||
protected String suffix;
|
||||
@@ -35,12 +36,12 @@ public class StaticLine extends ScoreboardLine {
|
||||
super.text = text;
|
||||
String legacy = RGBUtils.getInstance().convertRGBtoLegacy(this.text);
|
||||
//1.8+
|
||||
String[] v18 = splitText(getPlayerName(lineNumber), legacy, 40);
|
||||
String[] v18 = splitText(getPlayerName(lineNumber), legacy, Limitations.SCOREBOARD_SCORE_LENGTH_1_8);
|
||||
prefix = v18[0];
|
||||
name = v18[1];
|
||||
suffix = v18[2];
|
||||
//1.7-
|
||||
String[] v17 = splitText(getPlayerName(lineNumber), legacy, 16);
|
||||
String[] v17 = splitText(getPlayerName(lineNumber), legacy, Limitations.SCOREBOARD_SCORE_LENGTH_1_7);
|
||||
prefix17 = v17[0];
|
||||
name17 = v17[1];
|
||||
suffix17 = v17[2];
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package me.neznamy.tab.shared.features.types;
|
||||
|
||||
import me.neznamy.tab.shared.platform.TabPlayer;
|
||||
|
||||
/**
|
||||
* Interface for features listening to tablist entry clear
|
||||
* on server switch to re-add entries.
|
||||
*/
|
||||
public interface TabListClearListener {
|
||||
|
||||
/**
|
||||
* Called after player's TabList is cleared by the proxy
|
||||
* on server switch.
|
||||
*
|
||||
* @param player
|
||||
* Player who got their TabList entries cleared
|
||||
*/
|
||||
void onTabListClear(TabPlayer player);
|
||||
}
|
||||
@@ -3,8 +3,10 @@ package me.neznamy.tab.shared.hook;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.chat.ChatModifier;
|
||||
import me.neznamy.tab.shared.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.shared.util.ComponentCache;
|
||||
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;
|
||||
|
||||
@@ -14,6 +16,10 @@ import java.util.stream.Collectors;
|
||||
|
||||
public class AdventureHook {
|
||||
|
||||
/** Component cache for adventure components */
|
||||
private static final ComponentCache<IChatBaseComponent, Component> cache =
|
||||
new ComponentCache<>(1000, AdventureHook::toAdventureComponent0);
|
||||
|
||||
/**
|
||||
* Converts component to adventure component
|
||||
*
|
||||
@@ -25,13 +31,27 @@ public class AdventureHook {
|
||||
*/
|
||||
@NotNull
|
||||
public static Component toAdventureComponent(@NotNull IChatBaseComponent component, @NotNull ProtocolVersion clientVersion) {
|
||||
return cache.get(component, clientVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts component to adventure component
|
||||
*
|
||||
* @param component
|
||||
* Component to convert
|
||||
* @param clientVersion
|
||||
* Version to create component for
|
||||
* @return Adventure component from this component.
|
||||
*/
|
||||
@NotNull
|
||||
private static Component toAdventureComponent0(@NotNull IChatBaseComponent component, @NotNull ProtocolVersion clientVersion) {
|
||||
ChatModifier modifier = component.getModifier();
|
||||
net.kyori.adventure.text.format.TextColor color = null;
|
||||
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 +60,14 @@ 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()));
|
||||
Component adventureComponent = Component.text(component.getText(), color, decorations);
|
||||
if (modifier.getFont() != null) {
|
||||
adventureComponent = adventureComponent.font(Key.key(modifier.getFont()));
|
||||
}
|
||||
if (!component.getExtra().isEmpty()) {
|
||||
adventureComponent = adventureComponent.children(component.getExtra().stream().map(
|
||||
c -> toAdventureComponent(c, clientVersion)).collect(Collectors.toList()));
|
||||
}
|
||||
return adventureComponent;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+4
-3
@@ -129,11 +129,12 @@ public class RelationalPlaceholderImpl extends TabPlaceholder implements Relatio
|
||||
* @return last known value for entered player duo
|
||||
*/
|
||||
public String getLastValue(@NonNull TabPlayer viewer, @NonNull TabPlayer target) {
|
||||
if (!lastValues.computeIfAbsent(viewer, v -> new WeakHashMap<>()).containsKey(target)) {
|
||||
lastValues.get(viewer).put(target, getReplacements().findReplacement(identifier));
|
||||
WeakHashMap<me.neznamy.tab.api.TabPlayer, String> viewerMap = lastValues.computeIfAbsent(viewer, v -> new WeakHashMap<>());
|
||||
if (!viewerMap.containsKey(target)) {
|
||||
viewerMap.put(target, getReplacements().findReplacement(identifier));
|
||||
update(viewer, target);
|
||||
}
|
||||
return setPlaceholders(EnumChatFormat.color(lastValues.computeIfAbsent(viewer, v -> new WeakHashMap<>()).get(target)), target);
|
||||
return setPlaceholders(EnumChatFormat.color(viewerMap.get(target)), target);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -12,5 +12,5 @@ public class EmptyTabExpansion implements TabExpansion {
|
||||
public void setValue(@NonNull TabPlayer player, @NonNull String key, @NonNull String value) {/*Do nothing*/}
|
||||
|
||||
@Override
|
||||
public boolean unregister() { return false; }
|
||||
public void unregisterExpansion() {/* Do nothing */}
|
||||
}
|
||||
@@ -39,5 +39,5 @@ public interface TabExpansion {
|
||||
|
||||
void setValue(@NotNull TabPlayer player, @NotNull String key, @NotNull String value);
|
||||
|
||||
boolean unregister();
|
||||
void unregisterExpansion();
|
||||
}
|
||||
@@ -50,27 +50,6 @@ public abstract class EventListener<T> {
|
||||
TAB.getInstance().getFeatureManager().onWorldChange(player, world));
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes server change by forwarding it to all features.
|
||||
*
|
||||
* @param player
|
||||
* Player who switched server or joined
|
||||
* @param uuid
|
||||
* UUID of the player
|
||||
* @param server
|
||||
* New server
|
||||
*/
|
||||
public void serverChange(@NotNull T player, @NotNull UUID uuid, @NotNull String server) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes plugin message.
|
||||
*
|
||||
@@ -82,7 +61,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);
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package me.neznamy.tab.shared.platform;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.shared.Limitations;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.shared.chat.rgb.RGBUtils;
|
||||
@@ -23,7 +24,11 @@ public abstract class Scoreboard<T extends TabPlayer> {
|
||||
/** Scoreboard objectives player has registered */
|
||||
private final Set<String> registeredObjectives = new HashSet<>();
|
||||
|
||||
/** Flag tracking time between Login packet send and its processing */
|
||||
private boolean frozen;
|
||||
|
||||
public void setScore(@NotNull String objective, @NotNull String playerName, int score) {
|
||||
if (frozen) return;
|
||||
if (!registeredObjectives.contains(objective)) {
|
||||
error("Tried to update score (%s) without the existence of its requested objective '%s' to player ", playerName, objective);
|
||||
return;
|
||||
@@ -32,6 +37,7 @@ public abstract class Scoreboard<T extends TabPlayer> {
|
||||
}
|
||||
|
||||
public void removeScore(@NotNull String objective, @NotNull String playerName) {
|
||||
if (frozen) return;
|
||||
if (!registeredObjectives.contains(objective)) {
|
||||
error("Tried to remove score (%s) without the existence of its requested objective '%s' to player ", playerName, objective);
|
||||
return;
|
||||
@@ -40,14 +46,16 @@ public abstract class Scoreboard<T extends TabPlayer> {
|
||||
}
|
||||
|
||||
public void registerObjective(@NotNull String objectiveName, @NotNull String title, @NotNull HealthDisplay display) {
|
||||
if (frozen) return;
|
||||
if (!registeredObjectives.add(objectiveName)) {
|
||||
error("Tried to register duplicated objective %s to player ", objectiveName);
|
||||
return;
|
||||
}
|
||||
registerObjective0(objectiveName, cutTo(title, 32), display);
|
||||
registerObjective0(objectiveName, cutTo(title, Limitations.SCOREBOARD_TITLE_PRE_1_13), display);
|
||||
}
|
||||
|
||||
public void unregisterObjective(@NotNull String objectiveName) {
|
||||
if (frozen) return;
|
||||
if (!registeredObjectives.remove(objectiveName)) {
|
||||
error("Tried to unregister non-existing objective %s for player ", objectiveName);
|
||||
return;
|
||||
@@ -56,23 +64,34 @@ public abstract class Scoreboard<T extends TabPlayer> {
|
||||
}
|
||||
|
||||
public void updateObjective(@NotNull String objectiveName, @NotNull String title, @NotNull HealthDisplay display) {
|
||||
if (frozen) return;
|
||||
if (!registeredObjectives.contains(objectiveName)) {
|
||||
error("Tried to modify non-existing objective %s for player ", objectiveName);
|
||||
return;
|
||||
}
|
||||
updateObjective0(objectiveName, cutTo(title, 32), display);
|
||||
updateObjective0(objectiveName, cutTo(title, Limitations.SCOREBOARD_TITLE_PRE_1_13), display);
|
||||
}
|
||||
|
||||
public void registerTeam(@NotNull String name, @NotNull String prefix, @NotNull String suffix, @NotNull NameVisibility visibility,
|
||||
@NotNull CollisionRule collision, @NotNull Collection<String> players, int options) {
|
||||
if (frozen) return;
|
||||
if (!registeredTeams.add(name)) {
|
||||
error("Tried to register duplicated team %s to player ", name);
|
||||
return;
|
||||
}
|
||||
registerTeam0(name, cutTo(prefix, 16), cutTo(suffix, 16), visibility, collision, players, options);
|
||||
registerTeam0(
|
||||
name,
|
||||
cutTo(prefix, Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13),
|
||||
cutTo(suffix, Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13),
|
||||
visibility,
|
||||
collision,
|
||||
players,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
public void unregisterTeam(@NotNull String name) {
|
||||
if (frozen) return;
|
||||
if (!registeredTeams.remove(name)) {
|
||||
error("Tried to unregister non-existing team %s for player ", name);
|
||||
return;
|
||||
@@ -82,23 +101,39 @@ public abstract class Scoreboard<T extends TabPlayer> {
|
||||
|
||||
public void updateTeam(@NotNull String name, @NotNull String prefix, @NotNull String suffix, @NotNull NameVisibility visibility,
|
||||
@NotNull CollisionRule collision, int options) {
|
||||
if (frozen) return;
|
||||
if (!registeredTeams.contains(name)) {
|
||||
error("Tried to modify non-existing team %s for player ", name);
|
||||
return;
|
||||
}
|
||||
updateTeam0(name, cutTo(prefix, 16), cutTo(suffix, 16), visibility, collision, options);
|
||||
updateTeam0(
|
||||
name,
|
||||
cutTo(prefix, Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13),
|
||||
cutTo(suffix, Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13),
|
||||
visibility,
|
||||
collision,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears maps of registered teams and objectives on server switch, as proxy sends Login packet
|
||||
* Marks for freeze.
|
||||
*/
|
||||
public void clearRegisteredObjectives() {
|
||||
public void freeze() {
|
||||
frozen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears frozen flag and clears maps of registered teams and objectives.
|
||||
*/
|
||||
public void unfreeze() {
|
||||
registeredTeams.clear();
|
||||
registeredObjectives.clear();
|
||||
frozen = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
Loaded 100 of 118 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user