Compare commits

..
17 Commits
Author SHA1 Message Date
NEZNAMY f6359d2f77 6.2.0 2026-09-17 16:26:18 +02:00
NEZNAMY 727c500b17 [Bukkit] Update supported versions message 2026-09-16 20:40:47 +02:00
NEZNAMY bebf2988fb [Bukkit] Mark Spigot 26.3 as compatible 2026-09-16 20:37:36 +02:00
NEZNAMY e3cde1968b [NeoForge] Update to support 26.3 as well 2026-09-16 20:26:49 +02:00
NEZNAMY ef6b668c70 [Fabric] Change "players" field access to support both 26.2 and 26.3 2026-09-16 20:04:56 +02:00
NEZNAMY fef62e796b [Bukkit] Drop support for 1.19.1 - 1.19.3 2026-09-16 18:04:28 +02:00
NEZNAMY 0d4b1dbce2 [Bukkit] Mark Paper 26.3 as compatible 2026-09-16 18:01:42 +02:00
NEZNAMY 43d210dbf8 [Bukkit] Update to new bStats chart 2026-09-15 19:03:33 +02:00
NEZNAMY e0d2209e97 [API] Mark redundant method as deprecated 2026-09-15 18:30:02 +02:00
NEZNAMY 720cae3e94 [Fabric] Update to 26.3 2026-09-15 17:56:00 +02:00
NEZNAMY f2ea34a717 [Conditions] Fix NPE during config conversion 2026-09-15 17:51:27 +02:00
NEZNAMY 2c33747e1b [Placeholders] Make method synchronized to avoid concurrency issues (#1724, #1725) 2026-09-12 11:32:36 +02:00
NEZNAMY 5f9d72eb90 Include forgotten change 2026-09-12 10:47:42 +02:00
NEZNAMY 3abcc1d96f [Fabric] Update to 26.3-rc1 2026-09-12 10:35:59 +02:00
NEZNAMY 7d458422c7 [Belowname / Playerlist objective] Only create the features if they are actually enabled (got broken by a PR) 2026-09-12 10:28:24 +02:00
NEZNAMY 0feee14bfb 6.2.0-SNAPSHOT 2026-09-10 18:53:28 +02:00
NEZNAMY 0023cfcafb [Description] Update badges 2026-09-06 17:57:35 +02:00
40 changed files with 54 additions and 1233 deletions

No files matched your search

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="92" height="20" role="img" aria-label="Release: 6.1.2"><title>Release: 6.1.2</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="92" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="53" height="20" fill="#555"/><rect x="53" width="39" height="20" fill="#007ec6"/><rect width="92" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="275" y="150" fill-opacity=".8" filter="url(#blur)" textLength="430">Release</text><text x="275" y="150" fill-opacity=".3" textLength="430">Release</text></g><text x="275" y="140" textLength="430">Release</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="715" y="150" fill-opacity=".8" filter="url(#blur)" textLength="290">6.1.2</text><text x="715" y="150" fill-opacity=".3" textLength="290">6.1.2</text></g><text x="715" y="140" textLength="290">6.1.2</text></g></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="92" height="20" role="img" aria-label="Release: 6.1.3"><title>Release: 6.1.3</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="92" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="53" height="20" fill="#555"/><rect x="53" width="39" height="20" fill="#007ec6"/><rect width="92" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="275" y="150" fill-opacity=".8" filter="url(#blur)" textLength="430">Release</text><text x="275" y="150" fill-opacity=".3" textLength="430">Release</text></g><text x="275" y="140" textLength="430">Release</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="715" y="150" fill-opacity=".8" filter="url(#blur)" textLength="290">6.1.3</text><text x="715" y="150" fill-opacity=".3" textLength="290">6.1.3</text></g><text x="715" y="140" textLength="290">6.1.3</text></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

+1 -1
View File
@@ -274,7 +274,7 @@ if __name__ == "__main__":
]
)
release = "6.1.2"
release = "6.1.3"
download_image(f"https://img.shields.io/badge/Release-{release}-blue.svg", "Badge_Release.svg")
java = "8+"
@@ -48,14 +48,12 @@ public interface LayoutManager {
Layout getLayout(@NonNull String name);
/**
* Sends layout to player. Set to {@code null} to make player not see
* any layout, but default TabList instead.
* Sends layout to player. Set to {@code null} to reset layout back to original one based on configuration.
*
* @param player
* Player to send layout to
* @param layout
* Layout to send
* @see #resetLayout(TabPlayer)
*/
void sendLayout(@NonNull TabPlayer player, @Nullable Layout layout);
@@ -65,6 +63,8 @@ public interface LayoutManager {
* @param player
* Player to reset layout for
* @see #sendLayout(TabPlayer, Layout)
* @deprecated Use {@link #sendLayout(TabPlayer, Layout)} with {@code null} instead
*/
@Deprecated
void resetLayout(@NonNull TabPlayer player);
}
+1 -3
View File
@@ -4,7 +4,7 @@ plugins {
allprojects {
group = "me.neznamy"
version = "6.1.3"
version = "6.2.0"
description = "An all-in-one solution that works"
ext.set("id", "tab")
@@ -27,8 +27,6 @@ val platformPaths = setOf(
":bukkit:v1_16_R3",
":bukkit:v1_17_R1",
":bukkit:v1_18_R2",
":bukkit:v1_19_R1",
":bukkit:v1_19_R2",
":bukkit:v1_19_R3",
":bukkit:v1_20_R1",
":bukkit:v1_20_R2",
@@ -28,7 +28,7 @@ public class BukkitTAB extends JavaPlugin {
Bukkit.getConsoleSender().sendMessage("§c[TAB] Use the other jar in the release instead.");
} else {
Bukkit.getConsoleSender().sendMessage("§c[TAB] Your server version (" + Bukkit.getBukkitVersion() + ") is not supported.");
Bukkit.getConsoleSender().sendMessage("§c[TAB] This jar only supports 1.7.10, 1.8.8, 1.12.2, 1.16.5, 1.17.1 and 1.18.2 - 26.2");
Bukkit.getConsoleSender().sendMessage("§c[TAB] This jar only supports 1.7.10, 1.8.8, 1.12.2, 1.16.5, 1.17.1, 1.18.2 and 1.19.4 - 26.3");
Bukkit.getConsoleSender().sendMessage("§c[TAB] If you just updated to a new Minecraft version, check for TAB updates.");
Bukkit.getConsoleSender().sendMessage("§c[TAB] If you are using an unsupported 1.x version, use an older version of TAB (latest TAB 5.x supports all MC 1.x versions).");
Bukkit.getConsoleSender().sendMessage("§c[TAB] Thrown error message: " + e.getMessage());
@@ -96,6 +96,7 @@ public class ServerVersionInfo {
spigotVersions.put(ProtocolVersion.V26_1_1, "v26_1");
spigotVersions.put(ProtocolVersion.V26_1_2, "v26_1");
spigotVersions.put(ProtocolVersion.V26_2, "v26_2");
spigotVersions.put(ProtocolVersion.V26_3, "v26_2");
Map<ProtocolVersion, String> paperVersions = new LinkedHashMap<>();
paperVersions.put(ProtocolVersion.V1_20_5, "paper_1_20_5");
@@ -116,6 +117,7 @@ public class ServerVersionInfo {
paperVersions.put(ProtocolVersion.V26_1_1, "paper_1_21_11"); // v26_1 works too
paperVersions.put(ProtocolVersion.V26_1_2, "paper_1_21_11"); // v26_1 works too
paperVersions.put(ProtocolVersion.V26_2, "paper_26_2"); // v26_2 works too
paperVersions.put(ProtocolVersion.V26_3, "paper_26_2");
if (serverVersion == ProtocolVersion.UNKNOWN) {
throw new IllegalStateException(String.format(
@@ -238,9 +238,9 @@ public class BukkitPlatform implements BackendPlatform {
Metrics metrics = new Metrics(plugin, TabConstants.BSTATS_PLUGIN_ID_BUKKIT);
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PERMISSION_SYSTEM,
() -> TAB.getInstance().getGroupManager().getPermissionPlugin()));
metrics.addCustomChart(new SimplePie("tab_6_1_0_servers",
metrics.addCustomChart(new SimplePie("tab_6_2_x_servers",
() -> serverVersionInfo.getServerName() + " " + serverVersionInfo.getServerVersion().getFriendlyName()));
metrics.addCustomChart(new SimplePie("tab_6_1_0_package", serverVersionInfo::getImplementationPackage));
metrics.addCustomChart(new SimplePie("tab_6_2_x_package", serverVersionInfo::getImplementationPackage));
}
@Override
-10
View File
@@ -1,10 +0,0 @@
dependencies {
implementation(projects.bukkit)
compileOnly("org.spigotmc:spigot:1.19.2-R0.1-SNAPSHOT") {
// Broken dependencies
exclude("com.mojang", "authlib")
exclude("com.mojang", "logging")
exclude("com.mojang", "datafixerupper")
}
compileOnly("com.mojang:authlib:1.5.25")
}
@@ -1,64 +0,0 @@
package me.neznamy.tab.platforms.bukkit.v1_19_R1;
import me.neznamy.tab.shared.chat.TabStyle;
import me.neznamy.tab.shared.chat.component.object.TabAtlasSprite;
import me.neznamy.tab.shared.chat.component.object.TabObjectComponent;
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
import me.neznamy.tab.shared.chat.component.object.TabPlayerSprite;
import net.minecraft.network.chat.*;
import net.minecraft.resources.MinecraftKey;
import org.jetbrains.annotations.NotNull;
/**
* Component converter using direct NMS code.
*/
public class NMSComponentConverter extends ComponentConverter<IChatBaseComponent> {
@Override
@NotNull
public IChatBaseComponent newTextComponent(@NotNull String text) {
return IChatBaseComponent.b(text);
}
@Override
@NotNull
public IChatBaseComponent newTranslatableComponent(@NotNull String key) {
return IChatBaseComponent.c(key);
}
@Override
@NotNull
public IChatBaseComponent newKeybindComponent(@NotNull String keybind) {
return IChatBaseComponent.d(keybind);
}
@Override
@NotNull
public IChatBaseComponent newObjectComponent(@NotNull TabAtlasSprite sprite) {
return IChatBaseComponent.b(TabObjectComponent.ERROR_MESSAGE);
}
@Override
@NotNull
public IChatBaseComponent newObjectComponent(@NotNull TabPlayerSprite sprite) {
return IChatBaseComponent.b(TabObjectComponent.ERROR_MESSAGE);
}
@Override
public void applyStyle(@NotNull IChatBaseComponent nmsComponent, @NotNull TabStyle modifier) {
((IChatMutableComponent)nmsComponent).c(ChatModifier.a
.a(modifier.getColor() == null ? null : ChatHexColor.a(modifier.getColor().getRgb()))
.a(modifier.getBold())
.b(modifier.getItalic())
.c(modifier.getUnderlined())
.d(modifier.getStrikethrough())
.e(modifier.getObfuscated())
.a(modifier.getFont() == null ? null : MinecraftKey.a(modifier.getFont()))
);
}
@Override
public void addSibling(@NotNull IChatBaseComponent parent, @NotNull IChatBaseComponent child) {
((IChatMutableComponent)parent).b(child);
}
}
@@ -1,52 +0,0 @@
package me.neznamy.tab.platforms.bukkit.v1_19_R1;
import io.netty.channel.Channel;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabListEntryTracker;
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Implementation provider using direct NMS code for 1.19.1 - 1.19.2.
*/
@Getter
public class NMSImplementationProvider implements ImplementationProvider {
@NotNull
private final ComponentConverter<?> componentConverter = new NMSComponentConverter();
@Override
@NotNull
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
return new NMSPacketScoreboard(player);
}
@Override
@NotNull
public TabList newTabList(@NotNull BukkitTabPlayer player) {
return new NMSPacketTabList(player);
}
@Override
@NotNull
public Channel getChannel(@NotNull Player player) {
return ((CraftPlayer)player).getHandle().b.b.m;
}
@Override
@NotNull
public TabListEntryTracker newTabListEntryTracker(@NotNull Player player) {
return new NMSTabListEntryTracker(getChannel(player));
}
@Override
public int getPing(@NotNull BukkitTabPlayer player) {
return player.getPlayer().getPing();
}
}
@@ -1,207 +0,0 @@
package me.neznamy.tab.platforms.bukkit.v1_19_R1;
import lombok.NonNull;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
import net.minecraft.EnumChatFormat;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardDisplayObjective;
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective;
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardScore;
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam;
import net.minecraft.server.ScoreboardServer;
import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.scores.ScoreboardObjective;
import net.minecraft.world.scores.ScoreboardTeam;
import net.minecraft.world.scores.ScoreboardTeamBase;
import net.minecraft.world.scores.criteria.IScoreboardCriteria;
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Optional;
/**
* Scoreboard implementation using direct NMS code.
*/
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
private static final ScoreboardTeamBase.EnumNameTagVisibility[] visibilities = ScoreboardTeamBase.EnumNameTagVisibility.values();
private static final ScoreboardTeamBase.EnumTeamPush[] collisions = ScoreboardTeamBase.EnumTeamPush.values();
private static final Scoreboard dummyScoreboard = new Scoreboard();
private static final Constructor<PacketPlayOutScoreboardTeam> teamConstructor;
static {
try {
teamConstructor = PacketPlayOutScoreboardTeam.class.getDeclaredConstructor(String.class, int.class, Optional.class, Collection.class);
teamConstructor.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
/**
* Constructs new instance with given player.
*
* @param player
* Player this scoreboard will belong to
*/
public NMSPacketScoreboard(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
public void registerObjective(@NonNull Objective objective) {
ScoreboardObjective obj = new ScoreboardObjective(
dummyScoreboard,
objective.getName(),
IScoreboardCriteria.a,
objective.getTitle().convert(),
IScoreboardCriteria.EnumScoreboardHealthDisplay.values()[objective.getHealthDisplay().ordinal()]
);
objective.setPlatformObjective(obj);
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.REGISTER));
}
@Override
public void setDisplaySlot(@NonNull Objective objective) {
sendPacket(new PacketPlayOutScoreboardDisplayObjective(
objective.getDisplaySlot().ordinal(),
(ScoreboardObjective) objective.getPlatformObjective()
));
}
@Override
public void unregisterObjective(@NonNull Objective objective) {
sendPacket(new PacketPlayOutScoreboardObjective((ScoreboardObjective) objective.getPlatformObjective(), ObjectiveAction.UNREGISTER));
}
@Override
public void updateObjective(@NonNull Objective objective) {
ScoreboardObjective obj = (ScoreboardObjective) objective.getPlatformObjective();
obj.a((IChatBaseComponent) objective.getTitle().convert());
obj.a(IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(objective.getHealthDisplay().name()));
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.UPDATE));
}
@Override
public void setScore(@NonNull Score score) {
sendPacket(new PacketPlayOutScoreboardScore(
ScoreboardServer.Action.a,
score.getObjective().getName(),
score.getHolder(),
score.getValue()
));
}
@Override
public void removeScore(@NonNull Score score) {
sendPacket(new PacketPlayOutScoreboardScore(
ScoreboardServer.Action.b,
score.getObjective().getName(),
score.getHolder(),
score.getValue()
));
}
@Override
@NotNull
public Object createTeam(@NonNull String name) {
return new ScoreboardTeam(dummyScoreboard, name);
}
@Override
public void registerTeam(@NonNull Team team) {
updateTeamProperties(team);
ScoreboardTeam t = (ScoreboardTeam) team.getPlatformTeam();
t.g().addAll(team.getPlayers());
sendPacket(PacketPlayOutScoreboardTeam.a(t, true));
}
@Override
public void unregisterTeam(@NonNull Team team) {
sendPacket(PacketPlayOutScoreboardTeam.a((ScoreboardTeam) team.getPlatformTeam()));
}
@Override
public void updateTeam(@NonNull Team team) {
updateTeamProperties(team);
sendPacket(PacketPlayOutScoreboardTeam.a((ScoreboardTeam) team.getPlatformTeam(), false));
}
private void updateTeamProperties(@NonNull Team team) {
ScoreboardTeam t = (ScoreboardTeam) team.getPlatformTeam();
t.a((team.getOptions() & 0x01) != 0);
t.b((team.getOptions() & 0x02) != 0);
t.a(visibilities[team.getVisibility().ordinal()]);
t.a(collisions[team.getCollision().ordinal()]);
t.b((IChatBaseComponent) team.getPrefix().convert());
t.c(team.getSuffix().convert());
t.a(EnumChatFormat.valueOf(team.getColor().name()));
}
@Override
@SneakyThrows
@NotNull
public Object onPacketSend(@NonNull Object packet) {
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
TAB.getInstance().getFeatureManager().onDisplayObjective(
player,
((PacketPlayOutScoreboardDisplayObjective)packet).b(),
((PacketPlayOutScoreboardDisplayObjective)packet).c()
);
}
if (packet instanceof PacketPlayOutScoreboardObjective) {
TAB.getInstance().getFeatureManager().onObjective(
player,
((PacketPlayOutScoreboardObjective)packet).d(),
((PacketPlayOutScoreboardObjective)packet).b()
);
}
if (packet instanceof PacketPlayOutScoreboardTeam) {
int action = getMethod((PacketPlayOutScoreboardTeam) packet);
if (action != TeamAction.UPDATE) {
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
if (players != null) {
String name = ((PacketPlayOutScoreboardTeam) packet).d();
return teamConstructor.newInstance(
name,
action,
((PacketPlayOutScoreboardTeam) packet).f(),
onTeamPacket(action, name, players)
);
}
}
}
return packet;
}
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
if (team.c() == PacketPlayOutScoreboardTeam.a.a) {
return 0;
} else if (team.c() == PacketPlayOutScoreboardTeam.a.b) {
return 1;
} else if (team.b() == PacketPlayOutScoreboardTeam.a.a) {
return 3;
} else if (team.b() == PacketPlayOutScoreboardTeam.a.b) {
return 4;
} else {
return 2;
}
}
/**
* Sends the packet to the player.
*
* @param packet
* Packet to send
*/
private void sendPacket(@NotNull Packet<?> packet) {
((CraftPlayer)player.getPlayer()).getHandle().b.a(packet);
}
}
@@ -1,210 +0,0 @@
package me.neznamy.tab.platforms.bukkit.v1_19_R1;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import lombok.NonNull;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.component.TabComponent;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo;
import net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
import net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo.PlayerInfoData;
import net.minecraft.network.protocol.game.PacketPlayOutPlayerListHeaderFooter;
import net.minecraft.world.entity.player.ProfilePublicKey;
import net.minecraft.world.level.EnumGamemode;
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* TabList implementation using direct NMS code.
*/
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
private static final EnumPlayerInfoAction ADD_PLAYER = EnumPlayerInfoAction.a;
private static final EnumPlayerInfoAction UPDATE_GAME_MODE = EnumPlayerInfoAction.b;
private static final EnumPlayerInfoAction UPDATE_LATENCY = EnumPlayerInfoAction.c;
private static final EnumPlayerInfoAction UPDATE_DISPLAY_NAME = EnumPlayerInfoAction.d;
private static final EnumPlayerInfoAction REMOVE_PLAYER = EnumPlayerInfoAction.e;
/**
* Constructs new instance.
*
* @param player
* Player this tablist will belong to
*/
public NMSPacketTabList(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
@SneakyThrows
public void removeEntry(@NonNull UUID entry) {
sendPacket(EnumPlayerInfoAction.e, entry, "", null, 0, 0, null);
}
@Override
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
sendPacket(EnumPlayerInfoAction.d, entry, "", null, 0, 0, displayName);
}
@Override
public void updateLatency(@NonNull UUID entry, int latency) {
sendPacket(EnumPlayerInfoAction.c, entry, "", null, latency, 0, null);
}
@Override
public void updateGameMode(@NonNull UUID entry, int gameMode) {
sendPacket(EnumPlayerInfoAction.b, entry, "", null, 0, gameMode, null);
}
@Override
public void updateListed(@NonNull UUID entry, boolean listed) {
// Added in 1.19.3
}
@Override
public void updateListOrder(@NonNull UUID entry, int listOrder) {
// Added in 1.21.2
}
@Override
public void updateHat(@NonNull UUID entry, boolean showHat) {
// Added in 1.21.4
}
@Override
public void addEntry0(@NonNull Entry entry) {
sendPacket(EnumPlayerInfoAction.a, entry.getUniqueId(), entry.getName(), entry.getSkin(), entry.getLatency(),
entry.getGameMode(), entry.getDisplayName());
}
@Override
@SneakyThrows
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
sendPacket(new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert()));
}
@Override
@Nullable
public Skin getSkin() {
Collection<Property> properties = ((CraftPlayer)player.getPlayer()).getProfile().getProperties().get(TEXTURES_PROPERTY);
if (properties.isEmpty()) return null; // Offline mode
Property property = properties.iterator().next();
return new Skin(property.getValue(), property.getSignature());
}
@Override
@SneakyThrows
@NotNull
public Object onPacketSend(@NonNull Object packet) {
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
if (header == null || footer == null) return packet;
if (tablist.a != header.convert() || tablist.b != footer.convert()) {
return new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert());
}
}
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
EnumPlayerInfoAction action = info.c();
List<PlayerInfoData> updatedList = new ArrayList<>();
boolean rewritePacket = false;
for (PlayerInfoData nmsData : info.b()) {
boolean rewriteEntry = false;
GameProfile profile = nmsData.a();
UUID id = profile.getId();
IChatBaseComponent displayName = nmsData.d();
int latency = nmsData.b();
int gameMode = nmsData.c().a();
ProfilePublicKey.a publicKey = nmsData.e();
if (action == UPDATE_DISPLAY_NAME || action == ADD_PLAYER) {
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
displayName = forcedDisplayName.convert();
rewriteEntry = rewritePacket = true;
}
}
if (action == UPDATE_GAME_MODE || action == ADD_PLAYER) {
if (getBlockedSpectators().contains(id) && gameMode == 3) {
gameMode = 0;
rewriteEntry = rewritePacket = true;
}
}
if (action == UPDATE_LATENCY || action == ADD_PLAYER) {
if (getForcedLatency() != null) {
latency = getForcedLatency();
rewriteEntry = rewritePacket = true;
}
}
if (action == ADD_PLAYER) {
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
}
updatedList.add(rewriteEntry ? new PlayerInfoData(
profile,
latency,
EnumGamemode.a(gameMode),
displayName,
publicKey
) : nmsData);
}
if (rewritePacket) {
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
newPacket.b().clear();
newPacket.b().addAll(updatedList);
return newPacket;
}
return packet;
}
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
packet.b().add(new PlayerInfoData(
createProfile(id, name, skin),
latency,
EnumGamemode.a(gameMode),
displayName == null ? null : displayName.convert(),
null
));
sendPacket(packet);
}
/**
* Creates GameProfile from given parameters.
*
* @param id
* Profile ID
* @param name
* Profile name
* @param skin
* Player skin
* @return GameProfile from given parameters
*/
@NotNull
private GameProfile createProfile(@NonNull UUID id, @NonNull String name, @Nullable Skin skin) {
GameProfile profile = new GameProfile(id, name);
if (skin != null) {
profile.getProperties().put(TabList.TEXTURES_PROPERTY,
new Property(TabList.TEXTURES_PROPERTY, skin.getValue(), skin.getSignature()));
}
return profile;
}
/**
* Sends the packet to the player.
*
* @param packet
* Packet to send
*/
private void sendPacket(@NotNull Packet<?> packet) {
((CraftPlayer)player.getPlayer()).getHandle().b.a(packet);
}
}
@@ -1,35 +0,0 @@
package me.neznamy.tab.platforms.bukkit.v1_19_R1;
import io.netty.channel.Channel;
import me.neznamy.tab.shared.platform.NettyTabListEntryTracker;
import net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo;
import net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
import org.jetbrains.annotations.NotNull;
/**
* Implementation of TabListEntryTracker.
*/
public class NMSTabListEntryTracker extends NettyTabListEntryTracker {
private static final EnumPlayerInfoAction ADD_PLAYER = EnumPlayerInfoAction.valueOf("ADD_PLAYER");
private static final EnumPlayerInfoAction REMOVE_PLAYER = EnumPlayerInfoAction.valueOf("REMOVE_PLAYER");
public NMSTabListEntryTracker(@NotNull Channel channel) {
super(channel);
}
@Override
public void onPacketSend(@NotNull Object packet) {
if (packet instanceof PacketPlayOutPlayerInfo) {
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
for (PacketPlayOutPlayerInfo.PlayerInfoData nmsData : info.b()) {
if (info.c() == ADD_PLAYER) {
tablistEntries.add(nmsData.a().getId());
}
if (info.c() == REMOVE_PLAYER) {
tablistEntries.remove(nmsData.a().getId());
}
}
}
}
}
-14
View File
@@ -1,14 +0,0 @@
dependencies {
implementation(projects.bukkit)
compileOnly("org.spigotmc:spigot:1.19.3-R0.1-SNAPSHOT") {
// Broken dependencies
exclude("com.mojang", "authlib")
exclude("com.mojang", "logging")
exclude("com.mojang", "datafixerupper")
}
compileOnly("com.mojang:authlib:1.5.25")
}
tasks.compileJava {
options.release.set(14) // Records
}
@@ -1,64 +0,0 @@
package me.neznamy.tab.platforms.bukkit.v1_19_R2;
import me.neznamy.tab.shared.chat.TabStyle;
import me.neznamy.tab.shared.chat.component.object.TabAtlasSprite;
import me.neznamy.tab.shared.chat.component.object.TabObjectComponent;
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
import me.neznamy.tab.shared.chat.component.object.TabPlayerSprite;
import net.minecraft.network.chat.*;
import net.minecraft.resources.MinecraftKey;
import org.jetbrains.annotations.NotNull;
/**
* Component converter using direct NMS code.
*/
public class NMSComponentConverter extends ComponentConverter<IChatBaseComponent> {
@Override
@NotNull
public IChatBaseComponent newTextComponent(@NotNull String text) {
return IChatBaseComponent.b(text);
}
@Override
@NotNull
public IChatBaseComponent newTranslatableComponent(@NotNull String key) {
return IChatBaseComponent.c(key);
}
@Override
@NotNull
public IChatBaseComponent newKeybindComponent(@NotNull String keybind) {
return IChatBaseComponent.d(keybind);
}
@Override
@NotNull
public IChatBaseComponent newObjectComponent(@NotNull TabAtlasSprite sprite) {
return IChatBaseComponent.b(TabObjectComponent.ERROR_MESSAGE);
}
@Override
@NotNull
public IChatBaseComponent newObjectComponent(@NotNull TabPlayerSprite sprite) {
return IChatBaseComponent.b(TabObjectComponent.ERROR_MESSAGE);
}
@Override
public void applyStyle(@NotNull IChatBaseComponent nmsComponent, @NotNull TabStyle modifier) {
((IChatMutableComponent)nmsComponent).c(ChatModifier.a
.a(modifier.getColor() == null ? null : ChatHexColor.a(modifier.getColor().getRgb()))
.a(modifier.getBold())
.b(modifier.getItalic())
.c(modifier.getUnderlined())
.d(modifier.getStrikethrough())
.e(modifier.getObfuscated())
.a(modifier.getFont() == null ? null : MinecraftKey.a(modifier.getFont()))
);
}
@Override
public void addSibling(@NotNull IChatBaseComponent parent, @NotNull IChatBaseComponent child) {
((IChatMutableComponent)parent).b(child);
}
}
@@ -1,52 +0,0 @@
package me.neznamy.tab.platforms.bukkit.v1_19_R2;
import io.netty.channel.Channel;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabListEntryTracker;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Implementation provider using direct NMS code for 1.19.3.
*/
@Getter
public class NMSImplementationProvider implements ImplementationProvider {
@NotNull
private final ComponentConverter<?> componentConverter = new NMSComponentConverter();
@Override
@NotNull
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
return new NMSPacketScoreboard(player);
}
@Override
@NotNull
public TabList newTabList(@NotNull BukkitTabPlayer player) {
return new NMSPacketTabList(player);
}
@Override
@NotNull
public Channel getChannel(@NotNull Player player) {
return ((CraftPlayer)player).getHandle().b.b.m;
}
@Override
@NotNull
public TabListEntryTracker newTabListEntryTracker(@NotNull Player player) {
return new NMSTabListEntryTracker(getChannel(player));
}
@Override
public int getPing(@NotNull BukkitTabPlayer player) {
return player.getPlayer().getPing();
}
}
@@ -1,206 +0,0 @@
package me.neznamy.tab.platforms.bukkit.v1_19_R2;
import lombok.NonNull;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
import net.minecraft.EnumChatFormat;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardDisplayObjective;
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective;
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardScore;
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam;
import net.minecraft.server.ScoreboardServer;
import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.scores.ScoreboardObjective;
import net.minecraft.world.scores.ScoreboardTeam;
import net.minecraft.world.scores.ScoreboardTeamBase;
import net.minecraft.world.scores.criteria.IScoreboardCriteria;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Optional;
/**
* Scoreboard implementation using direct NMS code.
*/
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
private static final ScoreboardTeamBase.EnumNameTagVisibility[] visibilities = ScoreboardTeamBase.EnumNameTagVisibility.values();
private static final ScoreboardTeamBase.EnumTeamPush[] collisions = ScoreboardTeamBase.EnumTeamPush.values();
private static final Scoreboard dummyScoreboard = new Scoreboard();
private static final Constructor<PacketPlayOutScoreboardTeam> teamConstructor;
static {
try {
teamConstructor = PacketPlayOutScoreboardTeam.class.getDeclaredConstructor(String.class, int.class, Optional.class, Collection.class);
teamConstructor.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
/**
* Constructs new instance with given player.
*
* @param player
* Player this scoreboard will belong to
*/
public NMSPacketScoreboard(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
public void registerObjective(@NonNull Objective objective) {
ScoreboardObjective obj = new ScoreboardObjective(
dummyScoreboard,
objective.getName(),
IScoreboardCriteria.a,
objective.getTitle().convert(),
IScoreboardCriteria.EnumScoreboardHealthDisplay.values()[objective.getHealthDisplay().ordinal()]
);
objective.setPlatformObjective(obj);
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.REGISTER));
}
@Override
public void setDisplaySlot(@NonNull Objective objective) {
sendPacket(new PacketPlayOutScoreboardDisplayObjective(
objective.getDisplaySlot().ordinal(),
(ScoreboardObjective) objective.getPlatformObjective()
));
}
@Override
public void unregisterObjective(@NonNull Objective objective) {
sendPacket(new PacketPlayOutScoreboardObjective((ScoreboardObjective) objective.getPlatformObjective(), ObjectiveAction.UNREGISTER));
}
@Override
public void updateObjective(@NonNull Objective objective) {
ScoreboardObjective obj = (ScoreboardObjective) objective.getPlatformObjective();
obj.a((IChatBaseComponent) objective.getTitle().convert());
obj.a(IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(objective.getHealthDisplay().name()));
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.UPDATE));
}
@Override
public void setScore(@NonNull Score score) {
sendPacket(new PacketPlayOutScoreboardScore(
ScoreboardServer.Action.a,
score.getObjective().getName(),
score.getHolder(),
score.getValue()
));
}
@Override
public void removeScore(@NonNull Score score) {
sendPacket(new PacketPlayOutScoreboardScore(
ScoreboardServer.Action.b,
score.getObjective().getName(),
score.getHolder(),
score.getValue()
));
}
@Override
@NotNull
public Object createTeam(@NonNull String name) {
return new ScoreboardTeam(dummyScoreboard, name);
}
@Override
public void registerTeam(@NonNull Team team) {
updateTeamProperties(team);
ScoreboardTeam t = (ScoreboardTeam) team.getPlatformTeam();
t.g().addAll(team.getPlayers());
sendPacket(PacketPlayOutScoreboardTeam.a(t, true));
}
@Override
public void unregisterTeam(@NonNull Team team) {
sendPacket(PacketPlayOutScoreboardTeam.a((ScoreboardTeam) team.getPlatformTeam()));
}
@Override
public void updateTeam(@NonNull Team team) {
updateTeamProperties(team);
sendPacket(PacketPlayOutScoreboardTeam.a((ScoreboardTeam) team.getPlatformTeam(), false));
}
private void updateTeamProperties(@NonNull Team team) {
ScoreboardTeam t = (ScoreboardTeam) team.getPlatformTeam();
t.a((team.getOptions() & 0x01) != 0);
t.b((team.getOptions() & 0x02) != 0);
t.a(visibilities[team.getVisibility().ordinal()]);
t.a(collisions[team.getCollision().ordinal()]);
t.b((IChatBaseComponent) team.getPrefix().convert());
t.c(team.getSuffix().convert());
t.a(EnumChatFormat.valueOf(team.getColor().name()));
}
@Override
@SneakyThrows
@NotNull
public Object onPacketSend(@NonNull Object packet) {
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
TAB.getInstance().getFeatureManager().onDisplayObjective(
player,
((PacketPlayOutScoreboardDisplayObjective)packet).b(),
((PacketPlayOutScoreboardDisplayObjective)packet).c()
);
}
if (packet instanceof PacketPlayOutScoreboardObjective) {
TAB.getInstance().getFeatureManager().onObjective(
player,
((PacketPlayOutScoreboardObjective)packet).d(),
((PacketPlayOutScoreboardObjective)packet).b()
);
}
if (packet instanceof PacketPlayOutScoreboardTeam) {
int action = getMethod((PacketPlayOutScoreboardTeam) packet);
if (action != TeamAction.UPDATE) {
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
if (players != null) {
String name = ((PacketPlayOutScoreboardTeam) packet).d();
return teamConstructor.newInstance(
name,
action,
((PacketPlayOutScoreboardTeam) packet).f(),
onTeamPacket(action, name, players)
);
}
}
}
return packet;
}
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
if (team.c() == PacketPlayOutScoreboardTeam.a.a) {
return 0;
} else if (team.c() == PacketPlayOutScoreboardTeam.a.b) {
return 1;
} else if (team.b() == PacketPlayOutScoreboardTeam.a.a) {
return 3;
} else if (team.b() == PacketPlayOutScoreboardTeam.a.b) {
return 4;
} else {
return 2;
}
}
/**
* Sends the packet to the player.
*
* @param packet
* Packet to send
*/
private void sendPacket(@NotNull Packet<?> packet) {
((CraftPlayer)player.getPlayer()).getHandle().b.a(packet);
}
}
@@ -1,223 +0,0 @@
package me.neznamy.tab.platforms.bukkit.v1_19_R2;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import lombok.NonNull;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.component.TabComponent;
import me.neznamy.tab.shared.features.layout.LayoutManagerImpl;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.minecraft.network.chat.IChatBaseComponent;
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.PacketPlayOutPlayerListHeaderFooter;
import net.minecraft.world.level.EnumGamemode;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.*;
/**
* TabList implementation using direct NMS code.
*/
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
private static final Field PLAYERS = ReflectionUtils.getOnlyField(ClientboundPlayerInfoUpdatePacket.class, List.class);
private static final ClientboundPlayerInfoUpdatePacket.a ADD_PLAYER = ClientboundPlayerInfoUpdatePacket.a.valueOf("ADD_PLAYER");
private static final ClientboundPlayerInfoUpdatePacket.a UPDATE_GAME_MODE = ClientboundPlayerInfoUpdatePacket.a.valueOf("UPDATE_GAME_MODE");
private static final ClientboundPlayerInfoUpdatePacket.a UPDATE_LATENCY = ClientboundPlayerInfoUpdatePacket.a.valueOf("UPDATE_LATENCY");
private static final ClientboundPlayerInfoUpdatePacket.a UPDATE_DISPLAY_NAME = ClientboundPlayerInfoUpdatePacket.a.valueOf("UPDATE_DISPLAY_NAME");
private static final ClientboundPlayerInfoUpdatePacket.a UPDATE_LISTED = ClientboundPlayerInfoUpdatePacket.a.valueOf("UPDATE_LISTED");
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> ADD_PLAYER_SET = EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.a.class);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_GAME_MODE_SET = EnumSet.of(UPDATE_GAME_MODE);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_DISPLAY_NAME_SET = EnumSet.of(UPDATE_DISPLAY_NAME);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LATENCY_SET = EnumSet.of(UPDATE_LATENCY);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LISTED_SET = EnumSet.of(UPDATE_LISTED);
/**
* Constructs new instance.
*
* @param player
* Player this tablist will belong to
*/
public NMSPacketTabList(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
@SneakyThrows
public void removeEntry(@NonNull UUID entry) {
sendPacket(new ClientboundPlayerInfoRemovePacket(Collections.singletonList(entry)));
}
@Override
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
sendPacket(UPDATE_DISPLAY_NAME_SET, entry, "", null, false, 0, 0, displayName);
}
@Override
public void updateLatency(@NonNull UUID entry, int latency) {
sendPacket(UPDATE_LATENCY_SET, entry, "", null, false, latency, 0, null);
}
@Override
public void updateGameMode(@NonNull UUID entry, int gameMode) {
sendPacket(UPDATE_GAME_MODE_SET, entry, "", null, false, 0, gameMode, null);
}
@Override
public void updateListed(@NonNull UUID entry, boolean listed) {
sendPacket(UPDATE_LISTED_SET, entry, "", null, listed, 0, 0, null);
}
@Override
public void updateListOrder(@NonNull UUID entry, int listOrder) {
// Added in 1.21.2
}
@Override
public void updateHat(@NonNull UUID entry, boolean showHat) {
// Added in 1.21.4
}
@Override
public void addEntry0(@NonNull Entry entry) {
sendPacket(ADD_PLAYER_SET, entry.getUniqueId(), entry.getName(), entry.getSkin(), entry.isListed(), entry.getLatency(),
entry.getGameMode(), entry.getDisplayName());
}
@Override
@SneakyThrows
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
sendPacket(new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert()));
}
@Override
@Nullable
public Skin getSkin() {
Collection<Property> properties = ((CraftPlayer)player.getPlayer()).getProfile().getProperties().get(TEXTURES_PROPERTY);
if (properties.isEmpty()) return null; // Offline mode
Property property = properties.iterator().next();
return new Skin(property.getValue(), property.getSignature());
}
@SneakyThrows
@Override
@NotNull
public Object onPacketSend(@NonNull Object packet) {
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
if (header == null || footer == null) return packet;
if (tablist.a != header.convert() || tablist.b != footer.convert()) {
return new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert());
}
}
if (!(packet instanceof ClientboundPlayerInfoUpdatePacket)) return packet;
ClientboundPlayerInfoUpdatePacket info = (ClientboundPlayerInfoUpdatePacket) packet;
EnumSet<ClientboundPlayerInfoUpdatePacket.a> actions = info.b();
List<ClientboundPlayerInfoUpdatePacket.b> updatedList = new ArrayList<>();
boolean rewritePacket = false;
for (ClientboundPlayerInfoUpdatePacket.b nmsData : info.c()) {
UUID profileId = nmsData.a();
boolean rewriteEntry = false;
IChatBaseComponent displayName = nmsData.f();
int latency = nmsData.d();
int gameMode = nmsData.e().a();
boolean listed = nmsData.c();
if (actions.contains(UPDATE_DISPLAY_NAME)) {
TabComponent forcedDisplayName = getForcedDisplayNames().get(profileId);
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
displayName = forcedDisplayName.convert();
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(UPDATE_GAME_MODE)) {
if (getBlockedSpectators().contains(profileId) && gameMode == 3) {
gameMode = 0;
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(UPDATE_LATENCY)) {
if (getForcedLatency() != null) {
latency = getForcedLatency();
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(UPDATE_LISTED)) {
if (allPlayersHidden && !LayoutManagerImpl.UUIDS_SET.contains(nmsData.a())) { // Filter out layout entries
listed = false;
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ADD_PLAYER)) {
TAB.getInstance().getFeatureManager().onEntryAdd(player, profileId, nmsData.b().getName());
}
updatedList.add(rewriteEntry ? new ClientboundPlayerInfoUpdatePacket.b(
profileId, nmsData.b(), listed, latency, EnumGamemode.a(gameMode), displayName,
nmsData.g()
) : nmsData);
}
if (rewritePacket) {
ClientboundPlayerInfoUpdatePacket newPacket = new ClientboundPlayerInfoUpdatePacket(actions, Collections.emptyList());
PLAYERS.set(newPacket, updatedList);
return newPacket;
}
return packet;
}
@SneakyThrows
private void sendPacket(@NonNull EnumSet<ClientboundPlayerInfoUpdatePacket.a> action, @NonNull UUID id, @NonNull String name,
@Nullable Skin skin, boolean listed, int latency, int gameMode, @Nullable TabComponent displayName) {
ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(action, Collections.emptyList());
PLAYERS.set(packet, Collections.singletonList(new ClientboundPlayerInfoUpdatePacket.b(
id,
createProfile(id, name, skin),
listed,
latency,
EnumGamemode.a(gameMode),
displayName == null ? null : displayName.convert(),
null
)));
sendPacket(packet);
}
/**
* Creates GameProfile from given parameters.
*
* @param id
* Profile ID
* @param name
* Profile name
* @param skin
* Player skin
* @return GameProfile from given parameters
*/
@NotNull
private GameProfile createProfile(@NonNull UUID id, @NonNull String name, @Nullable Skin skin) {
GameProfile profile = new GameProfile(id, name);
if (skin != null) {
profile.getProperties().put(TabList.TEXTURES_PROPERTY,
new Property(TabList.TEXTURES_PROPERTY, skin.getValue(), skin.getSignature()));
}
return profile;
}
/**
* Sends the packet to the player.
*
* @param packet
* Packet to send
*/
private void sendPacket(@NotNull Packet<?> packet) {
((CraftPlayer)player.getPlayer()).getHandle().b.a(packet);
}
}
@@ -1,40 +0,0 @@
package me.neznamy.tab.platforms.bukkit.v1_19_R2;
import io.netty.channel.Channel;
import me.neznamy.tab.shared.platform.NettyTabListEntryTracker;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket.a;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Implementation of TabListEntryTracker.
*/
public class NMSTabListEntryTracker extends NettyTabListEntryTracker {
private static final a ADD_PLAYER = a.valueOf("ADD_PLAYER");
public NMSTabListEntryTracker(@NotNull Channel channel) {
super(channel);
}
@Override
public void onPacketSend(@NotNull Object packet) {
if (packet instanceof ClientboundPlayerInfoRemovePacket) {
ClientboundPlayerInfoRemovePacket remove = (ClientboundPlayerInfoRemovePacket) packet;
for (UUID id : remove.b()) {
tablistEntries.remove(id);
}
}
if (packet instanceof ClientboundPlayerInfoUpdatePacket) {
ClientboundPlayerInfoUpdatePacket update = (ClientboundPlayerInfoUpdatePacket) packet;
if (update.b().contains(ADD_PLAYER)) {
for (ClientboundPlayerInfoUpdatePacket.b nmsData : update.c()) {
tablistEntries.add(nmsData.a());
}
}
}
}
}
+2 -1
View File
@@ -14,11 +14,12 @@ repositories {
maven("https://repo.papermc.io/repository/maven-public/") // Adventure
}
val minecraftVersion = "26.2"
val minecraftVersion = "26.3"
// Fabric API versions for each Minecraft version for easier backporting
// Official website (for updating in the future): https://fabricmc.net/develop/
val fabricApiVersions = mapOf(
"26.3" to "0.160.5+26.3",
"26.2" to "0.150.3+26.2",
"26.1.2" to "0.150.0+26.1.2"
)
@@ -133,7 +133,12 @@ public class FabricScoreboard extends SafeScoreboard<FabricTabPlayer> {
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
int method = getMethod(team);
if (method != TeamAction.UPDATE) {
team.players = onTeamPacket(method, team.getName(), team.getPlayers());
return new ClientboundSetPlayerTeamPacket(
team.getName(),
method,
team.getParameters(),
onTeamPacket(method, team.getName(), team.getPlayers())
);
}
}
return packet;
+2 -1
View File
@@ -14,7 +14,8 @@
"fabric-api-base": "*",
"java": ">=25",
"minecraft": [
"26.2"
"26.2",
"26.3"
]
},
"recommends": {
@@ -5,8 +5,7 @@ accessible field net/minecraft/server/network/ServerCommonPacketListenerImpl con
accessible field net/minecraft/network/Connection channel Lio/netty/channel/Channel;
# Scoreboard team
accessible field net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket players Ljava/util/Collection;
mutable field net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket players Ljava/util/Collection;
accessible method net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket <init> (Ljava/lang/String;ILjava/util/Optional;Ljava/util/Collection;)V
# Player info
accessible field net/minecraft/network/protocol/game/ClientboundPlayerInfoUpdatePacket entries Ljava/util/List;
-2
View File
@@ -27,8 +27,6 @@ val platformPaths = setOf(
":bukkit:v1_16_R3",
":bukkit:v1_17_R1",
":bukkit:v1_18_R2",
":bukkit:v1_19_R1",
":bukkit:v1_19_R2",
":bukkit:v1_19_R3",
":bukkit:v1_20_R1",
":bukkit:v1_20_R2",
+4 -3
View File
@@ -15,13 +15,14 @@ repositories {
maven("https://repo.papermc.io/repository/maven-public/") // Adventure
}
val minecraftVersion = "26.2"
val minecraftVersion = "26.3"
// NeoForge API versions for each Minecraft version for easier backporting
// Official website (for updating in the future): https://projects.neoforged.net/neoforged/neoforge
val neoforgeApiVersions = mapOf(
"26.2" to "26.2.0.1-beta",
"26.1.2" to "26.1.2.17-beta"
"26.3" to "26.3.0.1-beta",
"26.2" to "26.2.0.88",
"26.1.2" to "26.1.2.109"
)
dependencies {
@@ -133,7 +133,12 @@ public class NeoForgeScoreboard extends SafeScoreboard<NeoForgeTabPlayer> {
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
int method = getMethod(team);
if (method != TeamAction.UPDATE) {
team.players = onTeamPacket(method, team.getName(), team.getPlayers());
return new ClientboundSetPlayerTeamPacket(
team.getName(),
method,
team.getParameters(),
onTeamPacket(method, team.getName(), team.getPlayers())
);
}
}
return packet;
@@ -1,5 +1,5 @@
# Scoreboard team
public-f net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket players
public net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket <init>(Ljava/lang/String;ILjava/util/Optional;Ljava/util/Collection;)V
# Player info
public-f net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket entries
@@ -15,6 +15,6 @@ description = '''@description@'''
[[dependencies.tab]]
modId = "minecraft"
type = "required"
versionRange = "[26.2]"
versionRange = "[26.2],[26.3]"
ordering = "NONE"
side = "SERVER"
-2
View File
@@ -52,8 +52,6 @@ include(":bukkit:v1_12_R1")
include(":bukkit:v1_16_R3")
include(":bukkit:v1_17_R1")
include(":bukkit:v1_18_R2")
include(":bukkit:v1_19_R1")
include(":bukkit:v1_19_R2")
include(":bukkit:v1_19_R3")
include(":bukkit:v1_20_R1")
include(":bukkit:v1_20_R2")
@@ -596,8 +596,12 @@ public class FeatureManager {
if (config.getScoreboard() != null) {
registerFeature(TabConstants.Feature.SCOREBOARD, new ScoreboardManagerImpl(config.getScoreboard()));
}
registerFeature(TabConstants.Feature.YELLOW_NUMBER, new YellowNumber(config.getPlayerlistObjective()));
registerFeature(TabConstants.Feature.BELOW_NAME, new BelowName(config.getBelowname()));
if (config.getPlayerlistObjective() != null) {
registerFeature(TabConstants.Feature.YELLOW_NUMBER, new YellowNumber(config.getPlayerlistObjective()));
}
if (config.getBelowname() != null) {
registerFeature(TabConstants.Feature.BELOW_NAME, new BelowName(config.getBelowname()));
}
if (config.getSorting() != null) {
registerFeature(TabConstants.Feature.SORTING, new Sorting(config.getSorting()));
}
@@ -11,6 +11,7 @@ import org.jetbrains.annotations.NotNull;
public enum ProtocolVersion {
UNKNOWN,
V26_3 (777),
V26_2 (776),
V26_1_2 (775),
V26_1_1 (775),
@@ -40,7 +40,7 @@ public class Config {
@NotNull private final ConfigurationFile config = new YamlConfigurationFile(getClass().getClassLoader().getResourceAsStream("config/config.yml"),
new File(TAB.getInstance().getDataFolder(), "config.yml"));
@NotNull private final BelowNameConfiguration belowname;
@Nullable private BelowNameConfiguration belowname;
@Nullable private BossBarConfiguration bossbar;
@NotNull private final ConditionsSection conditions;
@Nullable private GlobalPlayerListConfiguration globalPlayerList;
@@ -52,7 +52,7 @@ public class Config {
@NotNull private final PlaceholderRefreshConfiguration refresh;
@NotNull private final PlaceholderReplacementsConfiguration replacements;
@NotNull private final PlaceholdersConfiguration placeholders;
@NotNull private final PlayerListObjectiveConfiguration playerlistObjective;
@Nullable private PlayerListObjectiveConfiguration playerlistObjective;
@Nullable private ScoreboardConfiguration scoreboard;
@Nullable private SortingConfiguration sorting;
@Nullable private TablistFormattingConfiguration tablistFormatting;
@@ -95,10 +95,10 @@ public class Config {
replacements = PlaceholderReplacementsConfiguration.fromSection(config.getConfigurationSection("placeholder-output-replacements"));
placeholders = PlaceholdersConfiguration.fromSection(config.getConfigurationSection("placeholders"));
components = ComponentConfiguration.fromSection(config.getConfigurationSection("components"));
belowname = BelowNameConfiguration.fromSection(config.getConfigurationSection("belowname-objective"));
playerlistObjective = PlayerListObjectiveConfiguration.fromSection(config.getConfigurationSection("playerlist-objective"));
if (config.getBoolean("bossbar.enabled", false)) bossbar = BossBarConfiguration.fromSection(config.getConfigurationSection("bossbar"));
if (config.getBoolean("belowname-objective.enabled", false)) belowname = BelowNameConfiguration.fromSection(config.getConfigurationSection("belowname-objective"));
if (config.getBoolean("playerlist-objective.enabled", true)) playerlistObjective = PlayerListObjectiveConfiguration.fromSection(config.getConfigurationSection("playerlist-objective"));
if (config.getBoolean("global-playerlist.enabled", false)) globalPlayerList = GlobalPlayerListConfiguration.fromSection(config.getConfigurationSection("global-playerlist"));
if (config.getBoolean("header-footer.enabled", true)) headerFooter = HeaderFooterConfiguration.fromSection(config.getConfigurationSection("header-footer"));
if (config.getBoolean("layout.enabled", false)) layout = LayoutConfiguration.fromSection(config.getConfigurationSection("layout"));
@@ -118,7 +118,7 @@ public class Config {
" fake players, making per world playerlist completely useless as real players are pushed out of the playerlist. " +
"Disable per world playerlist for the same result, but with better performance.");
}
if (playerlistObjective.isEnabled()) {
if (playerlistObjective != null) {
TAB.getInstance().getConfigHelper().startup().startupWarn(config.getFile(), "Layout feature breaks playerlist-objective feature, because it replaces real player with fake slots " +
"with different usernames for more reliable functionality. Disable playerlist-objective feature, as it will only look bad " +
"and consume resources.");
@@ -150,15 +150,11 @@ public class BelowName extends RefreshableFeature implements JoinListener, QuitL
p.belowNameData.disabled.set(!forced);
return;
}
if (!configuration.isEnabled()) {
p.belowNameData.disabled.set(true);
return;
}
updateVisibility(p, disabledNow);
}
private boolean configuredDisabled(@NotNull TabPlayer p) {
return !configuration.isEnabled() || disableChecker.isDisableConditionMet(p);
return disableChecker.isDisableConditionMet(p);
}
private void applyDisabledState(@NotNull TabPlayer p, boolean disabled) {
@@ -17,7 +17,6 @@ import java.util.Arrays;
public class BelowNameConfiguration {
@NotNull private final ConfigurationSection section;
private final boolean enabled;
@NotNull private final String value;
@NotNull private final String title;
@NotNull private final String fancyValue;
@@ -71,7 +70,6 @@ public class BelowNameConfiguration {
return new BelowNameConfiguration(
section,
section.getBoolean("enabled", false),
value,
title,
section.getString("fancy-value", "&c" + TabConstants.Placeholder.HEALTH),
@@ -210,11 +210,7 @@ public class LayoutManagerImpl extends RefreshableFeature implements LayoutManag
@Override
public void resetLayout(@NonNull me.neznamy.tab.api.TabPlayer player) {
ensureActive();
TabPlayer p = (TabPlayer) player;
p.ensureLoaded();
p.layoutData.forcedLayout = null;
refresh(p, false);
sendLayout(player, null);
}
@NotNull
@@ -18,7 +18,6 @@ import java.util.Arrays;
public class PlayerListObjectiveConfiguration {
@NotNull private final ConfigurationSection section;
private final boolean enabled;
@NotNull private final String value;
@NotNull private final String fancyValue;
@NotNull private final String title;
@@ -72,7 +71,6 @@ public class PlayerListObjectiveConfiguration {
return new PlayerListObjectiveConfiguration(
section,
section.getBoolean("enabled", true),
value,
section.getString("fancy-value", "&7Ping: " + Placeholder.PING),
section.getString("title", "TAB"),
@@ -138,15 +138,11 @@ public class YellowNumber extends RefreshableFeature implements JoinListener, Qu
p.playerlistObjectiveData.disabled.set(!forced);
return;
}
if (!configuration.isEnabled()) {
p.playerlistObjectiveData.disabled.set(true);
return;
}
updateVisibility(p, disabledNow);
}
private boolean configuredDisabled(@NotNull TabPlayer p) {
return !configuration.isEnabled() || disableChecker.isDisableConditionMet(p);
return disableChecker.isDisableConditionMet(p);
}
private void applyDisabledState(@NotNull TabPlayer p, boolean disabled) {
@@ -78,6 +78,7 @@ public class Condition {
List<PlaceholderReference> placeholdersInConditions = new ArrayList<>();
PlaceholderManagerImpl manager = TAB.getInstance().getPlaceholderManager();
if (manager == null) return; // Header / footer config conversion, the placeholder manager is not initialized yet
placeholdersInConditions.addAll(manager.detectPlaceholders(yes).stream().map(manager::getPlaceholderReference).collect(Collectors.toList()));
placeholdersInConditions.addAll(manager.detectPlaceholders(no).stream().map(manager::getPlaceholderReference).collect(Collectors.toList()));
for (ConditionalExpression expression : expressions) {
@@ -138,7 +138,7 @@ public class PlayerPlaceholderImpl extends TabPlaceholder implements PlayerPlace
@Override
@NotNull
public String getLastReturnedValue(@Nullable TabPlayer player) {
public synchronized String getLastReturnedValue(@Nullable TabPlayer player) {
if (player == null) return identifier;
String value = player.lastPlaceholderReturnedValues.get(this);
if (value != null) return value;
@@ -324,11 +324,11 @@ public abstract class SafeScoreboard<T extends TabPlayer> implements Scoreboard
* Team name in the packet
* @param players
* Players in the packet
* @return Modified collection of players
* @return Modified list of players
*/
@NotNull
public Collection<String> onTeamPacket(int action, @NonNull String teamName, @NonNull Collection<String> players) {
Collection<String> newList = new ArrayList<>();
public List<String> onTeamPacket(int action, @NonNull String teamName, @NonNull Collection<String> players) {
List<String> newList = new ArrayList<>();
if (action == TeamAction.CREATE || action == TeamAction.ADD_PLAYER) {
for (String entry : players) {
Team expectedTeam = getExpectedTeam(entry);