Skip to content

Commit

Permalink
[Bukkit] Refactor feature provider loaders
Browse files Browse the repository at this point in the history
  • Loading branch information
NEZNAMY committed Feb 25, 2025
1 parent 181f850 commit 885783c
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 236 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package me.neznamy.tab.platforms.bukkit.header;

import me.neznamy.chat.component.TabComponent;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import org.jetbrains.annotations.NotNull;

/**
* An empty implementation that does not do anything.
*/
public class DummyHeaderFooter extends HeaderFooter {

@Override
public void set(@NotNull BukkitTabPlayer player, @NotNull TabComponent header, @NotNull TabComponent footer) {
// Do nothing
}
}
Original file line number Diff line number Diff line change
@@ -1,51 +1,14 @@
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.chat.component.TabComponent;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
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() {
instance = findInstance0();
}

@Nullable
private static HeaderFooter findInstance0() {
try {
return new PacketHeaderFooter();
} catch (Exception e) {
if (PaperHeaderFooter.isAvailable()) {
BukkitUtils.compatibilityError(e, "sending Header/Footer", "Paper API");
return new PaperHeaderFooter();
}
if (BukkitHeaderFooter.isAvailable()) {
BukkitUtils.compatibilityError(e, "sending Header/Footer", "Bukkit API",
"Header/Footer having drastically increased CPU usage",
"Header/Footer not supporting fonts (1.16+)");
return new BukkitHeaderFooter();
} else {
BukkitUtils.compatibilityError(e, "sending Header/Footer", null,
"Header/Footer feature not working");
return null;
}
}
}

/**
* Sends header/footer to player.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package me.neznamy.tab.platforms.bukkit.header;

import lombok.SneakyThrows;
import me.neznamy.chat.component.TabComponent;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.platforms.bukkit.nms.converter.ComponentConverter;
import me.neznamy.tab.platforms.bukkit.nms.PacketSender;
import me.neznamy.chat.component.TabComponent;
import me.neznamy.tab.shared.util.function.BiFunctionWithException;
import me.neznamy.tab.shared.util.ReflectionUtils;
import me.neznamy.tab.shared.util.function.BiFunctionWithException;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Constructor;
Expand All @@ -32,7 +31,6 @@ public PacketHeaderFooter() throws ReflectiveOperationException {
Class<?> Component = BukkitReflection.getClass("network.chat.Component", "network.chat.IChatBaseComponent", "IChatBaseComponent");
Class<?> HeaderFooterClass = BukkitReflection.getClass("network.protocol.game.ClientboundTabListPacket",
"network.protocol.game.PacketPlayOutPlayerListHeaderFooter", "PacketPlayOutPlayerListHeaderFooter");
if (ComponentConverter.INSTANCE == null) throw new IllegalStateException("Component converter is not available");
if (BukkitReflection.getMinorVersion() >= 17) {
Constructor<?> newHeaderFooter = HeaderFooterClass.getConstructor(Component, Component);
createPacket = newHeaderFooter::newInstance;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,13 @@
import me.neznamy.chat.component.TabComponent;
import me.neznamy.chat.component.TextComponent;
import me.neznamy.chat.component.TranslatableComponent;
import me.neznamy.tab.platforms.bukkit.BukkitUtils;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* Interface for converting TAB components into NMS components (1.7+).
*/
public abstract class ComponentConverter {

/** Instance of this class */
@Nullable
public static ComponentConverter INSTANCE;

/**
* Converts TAB component to NMS component.
*
Expand Down Expand Up @@ -100,26 +93,4 @@ public Object convert(@NotNull TabComponent component) {
* Child component to append
*/
public abstract void addSibling(@NotNull Object parent, @NotNull Object child);

/**
* Attempts to load component converter.
*/
public static void tryLoad() {
try {
if (BukkitReflection.getMinorVersion() >= 19) {
// 1.19+
INSTANCE = new ModernComponentConverter();
} else if (BukkitReflection.getMinorVersion() >= 16) {
// 1.16 - 1.18.2
INSTANCE = new ModerateComponentConverter();
} else {
// 1.7 - 1.15.2
INSTANCE = new LegacyComponentConverter();
}
} catch (Exception e) {
if (BukkitUtils.PRINT_EXCEPTIONS) {
e.printStackTrace();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package me.neznamy.tab.platforms.bukkit.platform;

import com.google.common.collect.Lists;
import lombok.Getter;
import lombok.SneakyThrows;
import me.clip.placeholderapi.PlaceholderAPI;
Expand All @@ -9,13 +10,18 @@
import me.neznamy.tab.platforms.bukkit.bossbar.ViaBossBar;
import me.neznamy.tab.platforms.bukkit.features.BukkitTabExpansion;
import me.neznamy.tab.platforms.bukkit.features.PerWorldPlayerList;
import me.neznamy.tab.platforms.bukkit.header.HeaderFooter;
import me.neznamy.tab.platforms.bukkit.header.*;
import me.neznamy.tab.platforms.bukkit.hook.BukkitPremiumVanishHook;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.platforms.bukkit.nms.PingRetriever;
import me.neznamy.tab.platforms.bukkit.nms.converter.ComponentConverter;
import me.neznamy.tab.platforms.bukkit.scoreboard.ScoreboardLoader;
import me.neznamy.tab.platforms.bukkit.tablist.TabListBase;
import me.neznamy.tab.platforms.bukkit.nms.converter.LegacyComponentConverter;
import me.neznamy.tab.platforms.bukkit.nms.converter.ModerateComponentConverter;
import me.neznamy.tab.platforms.bukkit.nms.converter.ModernComponentConverter;
import me.neznamy.tab.platforms.bukkit.scoreboard.BukkitScoreboard;
import me.neznamy.tab.platforms.bukkit.scoreboard.PaperScoreboard;
import me.neznamy.tab.platforms.bukkit.scoreboard.packet.PacketScoreboard;
import me.neznamy.tab.platforms.bukkit.tablist.*;
import me.neznamy.tab.shared.GroupManager;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
Expand All @@ -35,8 +41,10 @@
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.platform.impl.AdventureBossBar;
import me.neznamy.tab.shared.platform.impl.DummyBossBar;
import me.neznamy.tab.shared.platform.impl.DummyScoreboard;
import me.neznamy.tab.shared.util.PerformanceUtil;
import me.neznamy.tab.shared.util.ReflectionUtils;
import me.neznamy.tab.shared.util.function.FunctionWithException;
import net.kyori.adventure.audience.Audience;
import net.milkbowl.vault.chat.Chat;
import net.milkbowl.vault.permission.Permission;
Expand All @@ -52,6 +60,8 @@
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.util.List;
import java.util.Objects;

/**
* Implementation of Platform interface for Bukkit platform
Expand Down Expand Up @@ -79,6 +89,23 @@ public class BukkitPlatform implements BackendPlatform {
/** Detection for presence of Paper's MSPT getter */
private final boolean paperMspt = ReflectionUtils.methodExists(Bukkit.class, "getAverageTickTime");

/** Component converter from TAB to minecraft components */
@Nullable
private final ComponentConverter componentConverter = findComponentConverter();

/** Provider for scoreboard implementation */
@NotNull
private final FunctionWithException<BukkitTabPlayer, Scoreboard> scoreboardProvider = findScoreboardProvider();

/** Provider for tablist implementation */
@NotNull
private final FunctionWithException<BukkitTabPlayer, TabListBase> tablistProvider = findTablistProvider();

/** Header/footer implementation */
@Getter
@NotNull
private final HeaderFooter headerFooter = findHeaderFooter();

/**
* Constructs new instance with given plugin.
*
Expand All @@ -98,17 +125,123 @@ public BukkitPlatform(@NotNull JavaPlugin plugin) {
new BukkitPremiumVanishHook().register();
}
PingRetriever.tryLoad();
ComponentConverter.tryLoad();
ScoreboardLoader.findInstance();
TabListBase.findInstance();
if (BukkitReflection.getMinorVersion() >= 8) {
HeaderFooter.findInstance();
BukkitPipelineInjector.tryLoad();
}
BukkitUtils.sendCompatibilityMessage();
Bukkit.getConsoleSender().sendMessage("[TAB] §7Loaded NMS hook in " + (System.currentTimeMillis()-time) + "ms");
}

@NotNull
private FunctionWithException<BukkitTabPlayer, Scoreboard> findScoreboardProvider() {
try {
if (BukkitReflection.getMinorVersion() >= 7) Objects.requireNonNull(componentConverter);
PacketScoreboard.load();
return PacketScoreboard::new;
} catch (Exception e) {
if (PaperScoreboard.isAvailable()) {
BukkitUtils.compatibilityError(e, "Scoreboards", "Paper API", "Compatibility with other plugins being reduced");
return PaperScoreboard::new;
} else if (BukkitScoreboard.isAvailable()) {
List<String> missingFeatures = Lists.newArrayList(
"Compatibility with other plugins being reduced",
"Features receiving new artificial character limits"
);
if (BukkitReflection.is1_20_3Plus()) {
missingFeatures.add("1.20.3+ visuals not working due to lack of API"); // soontm?
}
BukkitUtils.compatibilityError(e, "Scoreboards", "Bukkit API", missingFeatures.toArray(new String[0]));
return BukkitScoreboard::new;
} else if (BukkitReflection.getMinorVersion() >= 5) {
BukkitUtils.compatibilityError(e, "Scoreboards", null,
"Scoreboard feature will not work",
"Belowname feature will not work",
"Player objective feature will not work",
"Scoreboard teams feature will not work (nametags & sorting)");
}
}
return DummyScoreboard::new;
}

@NotNull
private FunctionWithException<BukkitTabPlayer, TabListBase> findTablistProvider() {
try {
if (ReflectionUtils.classExists("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket")) {
// 1.19.3+
Objects.requireNonNull(componentConverter);
PacketTabList1193.loadNew();
return PacketTabList1193::new;
} else if (BukkitReflection.getMinorVersion() >= 8) {
// 1.8 - 1.19.2
Objects.requireNonNull(componentConverter);
PacketTabList18.load();
return PacketTabList18::new;
} else {
// 1.7.10 and lower
PacketTabList17.load();
return PacketTabList17::new;
}
} catch (Exception e) {
BukkitUtils.compatibilityError(e, "tablist entry management", "Bukkit API",
"Layout feature will not work",
"Prevent-spectator-effect feature will not work",
"Ping spoof feature will not work",
"Tablist formatting missing anti-override",
"Tablist formatting not supporting relational placeholders");
return BukkitTabList::new;
}
}

@NotNull
private HeaderFooter findHeaderFooter() {
if (BukkitReflection.getMinorVersion() >= 8) {
try {
Objects.requireNonNull(componentConverter);
return new PacketHeaderFooter();
} catch (Exception e) {
if (PaperHeaderFooter.isAvailable()) return new PaperHeaderFooter();
if (BukkitHeaderFooter.isAvailable()) {
BukkitUtils.compatibilityError(e, "sending Header/Footer", "Bukkit API",
"Header/Footer having drastically increased CPU usage",
"Header/Footer not supporting fonts (1.16+)");
return new BukkitHeaderFooter();
} else {
BukkitUtils.compatibilityError(e, "sending Header/Footer", null,
"Header/Footer feature not working");
}
}
}
return new DummyHeaderFooter();
}

/**
* Attempts to load component converter.
*
* @return Instance or {@code null} if not available
*/
@Nullable
public static ComponentConverter findComponentConverter() {
try {
if (BukkitReflection.getMinorVersion() >= 19) {
// 1.19+
return new ModernComponentConverter();
} else if (BukkitReflection.getMinorVersion() >= 16) {
// 1.16 - 1.18.2
return new ModerateComponentConverter();
} else {
// 1.7 - 1.15.2
return new LegacyComponentConverter();
}
} catch (Exception e) {
Bukkit.getConsoleSender().sendMessage("§c[TAB] Failed to initialize converter from TAB components to Minecraft components. " +
"This will negatively impact most features, see below.");
if (BukkitUtils.PRINT_EXCEPTIONS) {
e.printStackTrace();
}
return null;
}
}

@Override
public void loadPlayers() {
for (Player p : BukkitUtils.getOnlinePlayers()) {
Expand Down Expand Up @@ -254,8 +387,8 @@ public File getDataFolder() {
@Override
@NotNull
public Object convertComponent(@NotNull TabComponent component) {
if (ComponentConverter.INSTANCE != null) {
return ComponentConverter.INSTANCE.convert(component);
if (componentConverter != null) {
return componentConverter.convert(component);
} else {
return component;
}
Expand All @@ -265,7 +398,7 @@ public Object convertComponent(@NotNull TabComponent component) {
@NotNull
@SneakyThrows
public Scoreboard createScoreboard(@NotNull TabPlayer player) {
return ScoreboardLoader.getInstance().apply((BukkitTabPlayer) player);
return scoreboardProvider.apply((BukkitTabPlayer) player);
}

@Override
Expand All @@ -288,7 +421,7 @@ public BossBar createBossBar(@NotNull TabPlayer player) {
@NotNull
@SneakyThrows
public TabList createTabList(@NotNull TabPlayer player) {
return TabListBase.getInstance().apply((BukkitTabPlayer) player);
return tablistProvider.apply((BukkitTabPlayer) player);
}

@Override
Expand Down
Loading

0 comments on commit 885783c

Please sign in to comment.