mirror of
https://github.com/NEZNAMY/TAB.git
synced 2026-11-12 20:22:01 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
827197f916 | ||
|
|
29a35cf1cc | ||
|
|
f26516ecae | ||
|
|
f26a7ad2b1 | ||
|
|
96e0a29573 | ||
|
|
f30d667ca8 | ||
|
|
8db7a1d6d9 | ||
|
|
d285a0512e | ||
|
|
256d0d5c84 | ||
|
|
b808e1f9d1 | ||
|
|
07cd781ec1 | ||
|
|
9115b687f4 | ||
|
|
7c51377203 | ||
|
|
193cd02072 | ||
|
|
dbb6d91f0c | ||
|
|
a82714e811 | ||
|
|
bcd1bbac62 |
No files matched your search
+4
-3
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
allprojects {
|
||||
group = "me.neznamy"
|
||||
version = "5.1.0"
|
||||
version = "5.1.1"
|
||||
description = "An all-in-one solution that works"
|
||||
|
||||
ext.set("id", "tab")
|
||||
@@ -15,11 +15,12 @@ allprojects {
|
||||
val platforms = setOf(
|
||||
projects.bukkit,
|
||||
projects.bukkit.paper,
|
||||
projects.bukkit.v18R3,
|
||||
projects.bukkit.v112R1,
|
||||
projects.bungeecord,
|
||||
projects.component,
|
||||
projects.velocity,
|
||||
projects.sponge7,
|
||||
projects.sponge8,
|
||||
projects.sponge,
|
||||
projects.fabric
|
||||
).map { it.dependencyProject }
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@ repositories {
|
||||
maven("https://oss.sonatype.org/content/repositories/snapshots")
|
||||
}
|
||||
|
||||
val version = "1.21.4-R0.1-SNAPSHOT"
|
||||
val version = "1.21.5-R0.1-SNAPSHOT"
|
||||
|
||||
dependencies {
|
||||
implementation(projects.bukkit)
|
||||
paperweight.paperDevBundle(version)
|
||||
paperweight.paperDevBundle("1.21.5-no-moonrise-SNAPSHOT") // Just temporary
|
||||
compileOnly("io.papermc.paper:paper-api:${version}")
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
/**
|
||||
* Component converter using direct mojang-mapped code.
|
||||
*/
|
||||
@SuppressWarnings("unused") // Used via reflection
|
||||
public class PaperComponentConverter extends ComponentConverter {
|
||||
|
||||
@Override
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package me.neznamy.tab.platforms.paper;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.converter.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.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct Mojang-mapped NMS code (1.20.5+).
|
||||
*/
|
||||
@Getter
|
||||
public class PaperImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@NotNull
|
||||
private final ComponentConverter componentConverter = new PaperComponentConverter();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
return new PaperPacketScoreboard(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabList newTabList(@NotNull BukkitTabPlayer player) {
|
||||
return new PaperPacketTabList(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().connection.connection.channel;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package me.neznamy.tab.platforms.paper;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
|
||||
/**
|
||||
* Loader for Paper to speed up functions by using direct NMS code instead of reflection.
|
||||
*/
|
||||
@SuppressWarnings("unused") // Via reflection
|
||||
public class PaperLoader {
|
||||
|
||||
/** Channel getter using direct NMS code */
|
||||
public static final FunctionWithException<BukkitTabPlayer, Channel> getChannel =
|
||||
player -> ((CraftPlayer)player.getPlayer()).getHandle().connection.connection.channel;
|
||||
}
|
||||
+11
-13
@@ -18,30 +18,21 @@ import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Scoreboard implementation using direct mojang-mapped code.
|
||||
*/
|
||||
@SuppressWarnings("unused") // Used via reflection
|
||||
public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
private static final ChatFormatting[] formats = ChatFormatting.values();
|
||||
private static final net.minecraft.world.scores.Team.CollisionRule[] collisions = net.minecraft.world.scores.Team.CollisionRule.values();
|
||||
private static final net.minecraft.world.scores.Team.Visibility[] visibilities = net.minecraft.world.scores.Team.Visibility.values();
|
||||
private static final Scoreboard dummyScoreboard = new Scoreboard();
|
||||
private static final Field method;
|
||||
private static final Field players;
|
||||
|
||||
static {
|
||||
try {
|
||||
method = ReflectionUtils.setAccessible(ClientboundSetPlayerTeamPacket.class.getDeclaredField("method"));
|
||||
players = ReflectionUtils.setAccessible(ClientboundSetPlayerTeamPacket.class.getDeclaredField("players"));
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
private static final Field method = ReflectionUtils.getInstanceFields(ClientboundSetPlayerTeamPacket.class, int.class).getFirst();
|
||||
private static final Field players = ReflectionUtils.getOnlyField(ClientboundSetPlayerTeamPacket.class, Collection.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance with given player.
|
||||
@@ -66,7 +57,14 @@ public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
);
|
||||
objective.setPlatformObjective(obj);
|
||||
sendPacket(new ClientboundSetObjectivePacket(obj, ObjectiveAction.REGISTER));
|
||||
sendPacket(new ClientboundSetDisplayObjectivePacket(net.minecraft.world.scores.DisplaySlot.values()[objective.getDisplaySlot().ordinal()], obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySlot(@NonNull Objective objective) {
|
||||
sendPacket(new ClientboundSetDisplayObjectivePacket(
|
||||
net.minecraft.world.scores.DisplaySlot.values()[objective.getDisplaySlot().ordinal()],
|
||||
(net.minecraft.world.scores.Objective) objective.getPlatformObjective()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,7 +26,6 @@ import java.util.*;
|
||||
/**
|
||||
* TabList implementation using direct mojang-mapped code.
|
||||
*/
|
||||
@SuppressWarnings("unused") // Used via reflection
|
||||
public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> addPlayer = EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class);
|
||||
|
||||
@@ -3,15 +3,11 @@ package me.neznamy.tab.platforms.bukkit;
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
|
||||
import me.neznamy.tab.shared.features.injection.NettyPipelineInjector;
|
||||
import me.neznamy.tab.shared.platform.TabPlayer;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* Pipeline injection for Bukkit 1.8+.
|
||||
*/
|
||||
@@ -28,33 +24,6 @@ public class BukkitPipelineInjector extends NettyPipelineInjector {
|
||||
super("packet_handler");
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to load required classes, fields and methods and marks class as available.
|
||||
* If something fails, an error message is printed and class is not marked as available.
|
||||
*/
|
||||
public static void tryLoad() {
|
||||
try {
|
||||
Class<?> NetworkManager = BukkitReflection.getClass("network.Connection", "network.NetworkManager", "NetworkManager");
|
||||
Class<?> PlayerConnection = BukkitReflection.getClass("server.network.ServerGamePacketListenerImpl",
|
||||
"server.network.PlayerConnection", "PlayerConnection");
|
||||
Class<?> EntityPlayer = BukkitReflection.getClass("server.level.ServerPlayer", "server.level.EntityPlayer", "EntityPlayer");
|
||||
Field PLAYER_CONNECTION = ReflectionUtils.getOnlyField(EntityPlayer, PlayerConnection);
|
||||
Field NETWORK_MANAGER;
|
||||
if (BukkitReflection.is1_20_2Plus()) {
|
||||
NETWORK_MANAGER = ReflectionUtils.getOnlyField(PlayerConnection.getSuperclass(), NetworkManager);
|
||||
} else {
|
||||
NETWORK_MANAGER = ReflectionUtils.getOnlyField(PlayerConnection, NetworkManager);
|
||||
}
|
||||
Field CHANNEL = ReflectionUtils.getOnlyField(NetworkManager, Channel.class);
|
||||
getChannel = player -> (Channel) CHANNEL.get(NETWORK_MANAGER.get(PLAYER_CONNECTION.get(player.getHandle())));
|
||||
} catch (Exception e) {
|
||||
BukkitUtils.compatibilityError(e, "network channel injection", null,
|
||||
"Anti-override for tablist & nametags not working",
|
||||
"Compatibility with nickname plugins changing player names will not work",
|
||||
"Scoreboard will not be checking for other plugins");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if pipeline injection is available, {@code false} if not.
|
||||
*
|
||||
|
||||
@@ -7,6 +7,7 @@ import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
@@ -23,6 +24,7 @@ public class BukkitReflection {
|
||||
public static final Method CraftPlayer_getHandle = getHandle();
|
||||
|
||||
/** Server version data */
|
||||
@Getter
|
||||
private static final ServerVersion serverVersion = detectServerVersion();
|
||||
|
||||
/** Flag determining whether the server version is at least 1.20.2 or not */
|
||||
@@ -59,11 +61,12 @@ public class BukkitReflection {
|
||||
ClassLoader loader = BukkitReflection.class.getClassLoader();
|
||||
classFunction = name -> loader.loadClass("net.minecraft.server." + serverPackage + "." + name);
|
||||
}
|
||||
return new ServerVersion(classFunction, minorVersion, serverPackage);
|
||||
} else {
|
||||
// Paper without CB relocation
|
||||
minorVersion = Integer.parseInt(Bukkit.getBukkitVersion().split("-")[0].split("\\.")[1]);
|
||||
return new ServerVersion(classFunction, minorVersion, null);
|
||||
}
|
||||
return new ServerVersion(classFunction, minorVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,9 +105,12 @@ public class BukkitReflection {
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
private static class ServerVersion {
|
||||
public static class ServerVersion {
|
||||
|
||||
private final FunctionWithException<String, Class<?>> getClass;
|
||||
private final int minorVersion;
|
||||
|
||||
@Nullable
|
||||
private final String serverPackage;
|
||||
}
|
||||
}
|
||||
+41
-165
@@ -1,7 +1,5 @@
|
||||
package me.neznamy.tab.platforms.bukkit.platform;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
@@ -15,14 +13,9 @@ 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.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.platforms.bukkit.provider.BukkitImplementationProvider;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ViaVersionImplementationProvider;
|
||||
import me.neznamy.tab.shared.GroupManager;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
@@ -33,7 +26,6 @@ import me.neznamy.tab.shared.features.PlaceholderManagerImpl;
|
||||
import me.neznamy.tab.shared.features.injection.PipelineInjector;
|
||||
import me.neznamy.tab.shared.features.types.TabFeature;
|
||||
import me.neznamy.tab.shared.hook.LuckPermsHook;
|
||||
import me.neznamy.tab.shared.hook.ViaVersionHook;
|
||||
import me.neznamy.tab.shared.placeholders.expansion.EmptyTabExpansion;
|
||||
import me.neznamy.tab.shared.placeholders.expansion.TabExpansion;
|
||||
import me.neznamy.tab.shared.placeholders.types.PlayerPlaceholderImpl;
|
||||
@@ -43,10 +35,8 @@ import me.neznamy.tab.shared.platform.TabList;
|
||||
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;
|
||||
@@ -62,10 +52,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -94,25 +82,22 @@ 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, TabList> tablistProvider = findTablistProvider();
|
||||
|
||||
/** Header/footer implementation */
|
||||
@Getter
|
||||
@NotNull
|
||||
private final HeaderFooter headerFooter = findHeaderFooter();
|
||||
private final HeaderFooter headerFooter;
|
||||
|
||||
/** Flag tracking if direct mojang-mapped code can be used or not */
|
||||
private final boolean canUseDirectNMS = BukkitReflection.isMojangMapped() && serverVersion == ProtocolVersion.V1_21_4;
|
||||
private final boolean canUseDirectNMS = BukkitReflection.isMojangMapped() &&
|
||||
(serverVersion == ProtocolVersion.V1_21_4 || serverVersion == ProtocolVersion.V1_21_5);
|
||||
|
||||
/** Implementation for creating new instances using content available on the server */
|
||||
@NotNull
|
||||
private final ImplementationProvider serverImplementationProvider;
|
||||
|
||||
/** Implementation for sending new content to new players on old servers */
|
||||
@NotNull
|
||||
private final ViaVersionImplementationProvider viaVersionImplementationProvider = new ViaVersionImplementationProvider(serverVersion);
|
||||
|
||||
/**
|
||||
* Constructs new instance with given plugin.
|
||||
@@ -121,7 +106,6 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
* Plugin
|
||||
*/
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public BukkitPlatform(@NotNull JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
long time = System.currentTimeMillis();
|
||||
@@ -134,105 +118,27 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("PremiumVanish")) {
|
||||
new BukkitPremiumVanishHook().register();
|
||||
}
|
||||
PingRetriever.tryLoad();
|
||||
if (BukkitReflection.getMinorVersion() >= 8) {
|
||||
if (canUseDirectNMS) {
|
||||
BukkitPipelineInjector.setGetChannel((FunctionWithException<BukkitTabPlayer, Channel>) Class.forName("me.neznamy.tab.platforms.paper.PaperLoader").getDeclaredField("getChannel").get(null));
|
||||
} else {
|
||||
BukkitPipelineInjector.tryLoad();
|
||||
}
|
||||
if (canUseDirectNMS) {
|
||||
serverImplementationProvider = (ImplementationProvider) Class.forName("me.neznamy.tab.platforms.paper.PaperImplementationProvider").getConstructor().newInstance();
|
||||
} else if ("v1_12_R1".equals(BukkitReflection.getServerVersion().getServerPackage())) {
|
||||
serverImplementationProvider = (ImplementationProvider) Class.forName("me.neznamy.tab.platforms.bukkit.v1_12_R1.NMSImplementationProvider").getConstructor().newInstance();
|
||||
} else if ("v1_8_R3".equals(BukkitReflection.getServerVersion().getServerPackage())) {
|
||||
serverImplementationProvider = (ImplementationProvider) Class.forName("me.neznamy.tab.platforms.bukkit.v1_8_R3.NMSImplementationProvider").getConstructor().newInstance();
|
||||
} else {
|
||||
serverImplementationProvider = new BukkitImplementationProvider();
|
||||
}
|
||||
headerFooter = findHeaderFooter();
|
||||
PingRetriever.tryLoad();
|
||||
BukkitPipelineInjector.setGetChannel(serverImplementationProvider.getChannelFunction());
|
||||
BukkitUtils.sendCompatibilityMessage();
|
||||
Bukkit.getConsoleSender().sendMessage("[TAB] §7Loaded NMS hook in " + (System.currentTimeMillis()-time) + "ms");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
private FunctionWithException<BukkitTabPlayer, Scoreboard> findScoreboardProvider() {
|
||||
final FunctionWithException<BukkitTabPlayer, Scoreboard> base;
|
||||
if (ViaVersionHook.getInstance().isInstalled()) {
|
||||
base = ViaVersionPlatform.findScoreboardProvider(serverVersion);
|
||||
} else {
|
||||
base = FunctionWithException.empty();
|
||||
}
|
||||
if (canUseDirectNMS) {
|
||||
Constructor<?> constructor = Class.forName("me.neznamy.tab.platforms.paper.PaperPacketScoreboard").getConstructor(BukkitTabPlayer.class);
|
||||
return base.fallback(player -> (Scoreboard) constructor.newInstance(player));
|
||||
}
|
||||
try {
|
||||
if (BukkitReflection.getMinorVersion() >= 7) Objects.requireNonNull(componentConverter);
|
||||
PacketScoreboard.load();
|
||||
return base.fallback(PacketScoreboard::new);
|
||||
} catch (Exception e) {
|
||||
if (PaperScoreboard.isAvailable()) {
|
||||
BukkitUtils.compatibilityError(e, "Scoreboards", "Paper API", "Compatibility with other plugins being reduced");
|
||||
return base.fallback(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 base.fallback(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 base.fallback(DummyScoreboard::new);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
private FunctionWithException<BukkitTabPlayer, TabList> findTablistProvider() {
|
||||
final FunctionWithException<BukkitTabPlayer, TabList> base;
|
||||
if (ViaVersionHook.getInstance().isInstalled()) {
|
||||
base = ViaVersionPlatform.findTablistProvider(serverVersion);
|
||||
} else {
|
||||
base = FunctionWithException.empty();
|
||||
}
|
||||
if (canUseDirectNMS) {
|
||||
Constructor<?> constructor = Class.forName("me.neznamy.tab.platforms.paper.PaperPacketTabList").getConstructor(BukkitTabPlayer.class);
|
||||
return base.fallback(player -> (TabList) constructor.newInstance(player));
|
||||
}
|
||||
try {
|
||||
if (ReflectionUtils.classExists("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket")) {
|
||||
// 1.19.3+
|
||||
Objects.requireNonNull(componentConverter);
|
||||
PacketTabList1193.loadNew();
|
||||
return base.fallback(PacketTabList1193::new);
|
||||
} else if (BukkitReflection.getMinorVersion() >= 8) {
|
||||
// 1.8 - 1.19.2
|
||||
Objects.requireNonNull(componentConverter);
|
||||
PacketTabList18.load();
|
||||
return base.fallback(PacketTabList18::new);
|
||||
} else {
|
||||
// 1.7.10 and lower
|
||||
PacketTabList17.load();
|
||||
return base.fallback(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 base.fallback(BukkitTabList::new);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private HeaderFooter findHeaderFooter() {
|
||||
if (BukkitReflection.getMinorVersion() >= 8) {
|
||||
try {
|
||||
Objects.requireNonNull(componentConverter);
|
||||
Objects.requireNonNull(serverImplementationProvider.getComponentConverter());
|
||||
return new PacketHeaderFooter();
|
||||
} catch (Exception e) {
|
||||
if (PaperHeaderFooter.isAvailable()) return new PaperHeaderFooter();
|
||||
@@ -250,38 +156,6 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
return new DummyHeaderFooter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to load component converter.
|
||||
*
|
||||
* @return Instance or {@code null} if not available
|
||||
*/
|
||||
@Nullable
|
||||
@SneakyThrows
|
||||
public ComponentConverter findComponentConverter() {
|
||||
if (canUseDirectNMS) {
|
||||
return (ComponentConverter) Class.forName("me.neznamy.tab.platforms.paper.PaperComponentConverter").getConstructor().newInstance();
|
||||
}
|
||||
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 : getOnlinePlayers()) {
|
||||
@@ -338,19 +212,17 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
}
|
||||
PlaceholderManagerImpl pl = TAB.getInstance().getPlaceholderManager();
|
||||
if (identifier.startsWith("%rel_")) {
|
||||
// relational placeholder
|
||||
pl.registerRelationalPlaceholder(identifier, (viewer, target) ->
|
||||
//relational placeholder
|
||||
TAB.getInstance().getPlaceholderManager().registerRelationalPlaceholder(identifier, (viewer, target) ->
|
||||
PlaceholderAPI.setRelationalPlaceholders((Player) viewer.getPlayer(), (Player) target.getPlayer(), identifier));
|
||||
} else if (identifier.startsWith("%sync:")) {
|
||||
registerSyncPlaceholder(identifier);
|
||||
} else if (identifier.contains("{") && identifier.contains("}")) {
|
||||
// has nested bracket placeholders
|
||||
pl.registerPlayerPlaceholder(identifier, p -> PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), PlaceholderAPI.setBracketPlaceholders((Player) p.getPlayer(), identifier)));
|
||||
} else if (identifier.startsWith("%server_")) {
|
||||
// placeholder with the same output for all players, register as server for better performance
|
||||
pl.registerServerPlaceholder(identifier, () -> PlaceholderAPI.setPlaceholders(null, identifier));
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(identifier,
|
||||
() -> PlaceholderAPI.setPlaceholders(null, identifier));
|
||||
} else {
|
||||
pl.registerPlayerPlaceholder(identifier, p -> PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), identifier));
|
||||
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier,
|
||||
p -> PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), identifier));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +238,7 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
ppl[0] = TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier, p -> {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
long time = System.nanoTime();
|
||||
ppl[0].updateValue(p, placeholderAPI ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), PlaceholderAPI.setBracketPlaceholders((Player) p.getPlayer(), syncedPlaceholder)) : identifier);
|
||||
ppl[0].updateValue(p, placeholderAPI ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), syncedPlaceholder) : identifier);
|
||||
TAB.getInstance().getCPUManager().addPlaceholderTime(identifier, System.nanoTime() - time);
|
||||
});
|
||||
return null;
|
||||
@@ -424,8 +296,8 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
@Override
|
||||
@NotNull
|
||||
public Object convertComponent(@NotNull TabComponent component) {
|
||||
if (componentConverter != null) {
|
||||
return componentConverter.convert(component);
|
||||
if (serverImplementationProvider.getComponentConverter() != null) {
|
||||
return serverImplementationProvider.getComponentConverter().convert(component);
|
||||
} else {
|
||||
return component;
|
||||
}
|
||||
@@ -435,7 +307,9 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
public Scoreboard createScoreboard(@NotNull TabPlayer player) {
|
||||
return scoreboardProvider.apply((BukkitTabPlayer) player);
|
||||
Scoreboard scoreboard = viaVersionImplementationProvider.newScoreboard((BukkitTabPlayer) player);
|
||||
if (scoreboard != null) return scoreboard;
|
||||
return serverImplementationProvider.newScoreboard((BukkitTabPlayer) player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -458,7 +332,9 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
public TabList createTabList(@NotNull TabPlayer player) {
|
||||
return tablistProvider.apply((BukkitTabPlayer) player);
|
||||
TabList tabList = viaVersionImplementationProvider.newTabList((BukkitTabPlayer) player);
|
||||
if (tabList != null) return tabList;
|
||||
return serverImplementationProvider.newTabList((BukkitTabPlayer) player);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.platform;
|
||||
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.scoreboard.viaversion.ViaScoreboard1203;
|
||||
import me.neznamy.tab.platforms.bukkit.scoreboard.viaversion.ViaScoreboard113;
|
||||
import me.neznamy.tab.platforms.bukkit.scoreboard.viaversion.ViaScoreboard116;
|
||||
import me.neznamy.tab.platforms.bukkit.tablist.viaversion.ViaTabList1193;
|
||||
import me.neznamy.tab.platforms.bukkit.tablist.viaversion.ViaTabList1212;
|
||||
import me.neznamy.tab.platforms.bukkit.tablist.viaversion.ViaTabList1214;
|
||||
import me.neznamy.tab.platforms.bukkit.tablist.viaversion.ViaTabList116;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ViaVersionPlatform {
|
||||
|
||||
@NotNull
|
||||
public static FunctionWithException<BukkitTabPlayer, Scoreboard> findScoreboardProvider(@NotNull ProtocolVersion serverVersion) {
|
||||
if (!ReflectionUtils.classExists("com.viaversion.viaversion.protocols.v1_20_2to1_20_3.Protocol1_20_2To1_20_3")) {
|
||||
return FunctionWithException.empty();
|
||||
}
|
||||
if (serverVersion.getNetworkId() < ProtocolVersion.V1_13.getNetworkId()) {
|
||||
return player -> {
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_20_3.getNetworkId()) {
|
||||
return new ViaScoreboard1203(player);
|
||||
}
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_16.getNetworkId()) {
|
||||
return new ViaScoreboard116(player);
|
||||
}
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_13.getNetworkId()) {
|
||||
return new ViaScoreboard113(player);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
if (serverVersion.getNetworkId() < ProtocolVersion.V1_16.getNetworkId()) {
|
||||
return player -> {
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_20_3.getNetworkId()) {
|
||||
return new ViaScoreboard1203(player);
|
||||
}
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_16.getNetworkId()) {
|
||||
return new ViaScoreboard116(player);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
if (serverVersion.getNetworkId() < ProtocolVersion.V1_20_3.getNetworkId()) {
|
||||
return player -> {
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_20_3.getNetworkId()) {
|
||||
return new ViaScoreboard1203(player);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
return FunctionWithException.empty();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static FunctionWithException<BukkitTabPlayer, TabList> findTablistProvider(@NotNull ProtocolVersion serverVersion) {
|
||||
if (!ReflectionUtils.classExists("com.viaversion.viaversion.protocols.v1_21_2to1_21_4.Protocol1_21_2To1_21_4")) {
|
||||
return FunctionWithException.empty();
|
||||
}
|
||||
if (serverVersion.getNetworkId() < ProtocolVersion.V1_16.getNetworkId()) {
|
||||
return player -> {
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_21_4.getNetworkId()) {
|
||||
return new ViaTabList1214(player);
|
||||
}
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_21_2.getNetworkId()) {
|
||||
return new ViaTabList1212(player);
|
||||
}
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) {
|
||||
return new ViaTabList1193(player);
|
||||
}
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_16.getNetworkId()) {
|
||||
return new ViaTabList116(player);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
if (serverVersion.getNetworkId() < ProtocolVersion.V1_19_3.getNetworkId()) {
|
||||
return player -> {
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_21_4.getNetworkId()) {
|
||||
return new ViaTabList1214(player);
|
||||
}
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_21_2.getNetworkId()) {
|
||||
return new ViaTabList1212(player);
|
||||
}
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) {
|
||||
return new ViaTabList1193(player);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
if (serverVersion.getNetworkId() < ProtocolVersion.V1_21_2.getNetworkId()) {
|
||||
return player -> {
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_21_4.getNetworkId()) {
|
||||
return new ViaTabList1214(player);
|
||||
}
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_21_2.getNetworkId()) {
|
||||
return new ViaTabList1212(player);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
if (serverVersion.getNetworkId() < ProtocolVersion.V1_21_4.getNetworkId()) {
|
||||
return player -> {
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_21_4.getNetworkId()) {
|
||||
return new ViaTabList1214(player);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
return FunctionWithException.empty();
|
||||
}
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
package me.neznamy.tab.platforms.bukkit.provider;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
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.platforms.bukkit.nms.converter.ComponentConverter;
|
||||
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.BukkitTabList;
|
||||
import me.neznamy.tab.platforms.bukkit.tablist.PacketTabList1193;
|
||||
import me.neznamy.tab.platforms.bukkit.tablist.PacketTabList17;
|
||||
import me.neznamy.tab.platforms.bukkit.tablist.PacketTabList18;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.platform.impl.DummyScoreboard;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Implementation provider using reflection for NMS / Bukkit implementations as fallback options.
|
||||
*/
|
||||
@Getter
|
||||
public class BukkitImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@Nullable
|
||||
private final FunctionWithException<BukkitTabPlayer, Channel> channelFunction = findChannelFunction();
|
||||
|
||||
@Nullable
|
||||
private final ComponentConverter componentConverter = findComponentConverter();
|
||||
|
||||
@NotNull
|
||||
private final Function<BukkitTabPlayer, Scoreboard> scoreboardProvider = findScoreboardProvider();
|
||||
|
||||
@NotNull
|
||||
private final Function<BukkitTabPlayer, TabList> tablistProvider = findTablistProvider();
|
||||
|
||||
@Nullable
|
||||
private FunctionWithException<BukkitTabPlayer, Channel> findChannelFunction() {
|
||||
if (BukkitReflection.getMinorVersion() < 8) return null;
|
||||
try {
|
||||
Class<?> NetworkManager = BukkitReflection.getClass("network.Connection", "network.NetworkManager", "NetworkManager");
|
||||
Class<?> PlayerConnection = BukkitReflection.getClass("server.network.ServerGamePacketListenerImpl",
|
||||
"server.network.PlayerConnection", "PlayerConnection");
|
||||
Class<?> EntityPlayer = BukkitReflection.getClass("server.level.ServerPlayer", "server.level.EntityPlayer", "EntityPlayer");
|
||||
Field PLAYER_CONNECTION = ReflectionUtils.getOnlyField(EntityPlayer, PlayerConnection);
|
||||
Field NETWORK_MANAGER;
|
||||
if (BukkitReflection.is1_20_2Plus()) {
|
||||
NETWORK_MANAGER = ReflectionUtils.getOnlyField(PlayerConnection.getSuperclass(), NetworkManager);
|
||||
} else {
|
||||
NETWORK_MANAGER = ReflectionUtils.getOnlyField(PlayerConnection, NetworkManager);
|
||||
}
|
||||
Field CHANNEL = ReflectionUtils.getOnlyField(NetworkManager, Channel.class);
|
||||
return player -> (Channel) CHANNEL.get(NETWORK_MANAGER.get(PLAYER_CONNECTION.get(player.getHandle())));
|
||||
} catch (Exception e) {
|
||||
BukkitUtils.compatibilityError(e, "network channel injection", null,
|
||||
"Anti-override for tablist & nametags not working",
|
||||
"Compatibility with nickname plugins changing player names will not work",
|
||||
"Scoreboard will not be checking for other plugins");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SneakyThrows
|
||||
private 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;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Function<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
|
||||
@SneakyThrows
|
||||
private Function<BukkitTabPlayer, TabList> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
return scoreboardProvider.apply(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabList newTabList(@NotNull BukkitTabPlayer player) {
|
||||
return tablistProvider.apply(player);
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package me.neznamy.tab.platforms.bukkit.provider;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.converter.ComponentConverter;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface ImplementationProvider {
|
||||
|
||||
@NotNull
|
||||
Scoreboard newScoreboard(@NotNull BukkitTabPlayer player);
|
||||
|
||||
@NotNull
|
||||
TabList newTabList(@NotNull BukkitTabPlayer player);
|
||||
|
||||
@Nullable
|
||||
ComponentConverter getComponentConverter();
|
||||
|
||||
@Nullable
|
||||
FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction();
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package me.neznamy.tab.platforms.bukkit.provider;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.scoreboard.viaversion.ViaScoreboard113;
|
||||
import me.neznamy.tab.platforms.bukkit.scoreboard.viaversion.ViaScoreboard116;
|
||||
import me.neznamy.tab.platforms.bukkit.scoreboard.viaversion.ViaScoreboard1203;
|
||||
import me.neznamy.tab.platforms.bukkit.tablist.viaversion.ViaTabList116;
|
||||
import me.neznamy.tab.platforms.bukkit.tablist.viaversion.ViaTabList1193;
|
||||
import me.neznamy.tab.platforms.bukkit.tablist.viaversion.ViaTabList1212;
|
||||
import me.neznamy.tab.platforms.bukkit.tablist.viaversion.ViaTabList1214;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Implementation for sending new features on old servers using ViaVersion API.
|
||||
* If no implementation is required for a player, {@code null} is returned.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ViaVersionImplementationProvider {
|
||||
|
||||
/** Map of tablist implementations by each new version */
|
||||
private static final Map<ProtocolVersion, Function<BukkitTabPlayer, TabList>> TABLIST_IMPLEMENTATIONS = new LinkedHashMap<>();
|
||||
|
||||
/** Map of scoreboard implementations by each new version */
|
||||
private static final Map<ProtocolVersion, Function<BukkitTabPlayer, Scoreboard>> SCOREBOARD_IMPLEMENTATIONS = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
TABLIST_IMPLEMENTATIONS.put(ProtocolVersion.V1_21_4, ViaTabList1214::new);
|
||||
TABLIST_IMPLEMENTATIONS.put(ProtocolVersion.V1_21_2, ViaTabList1212::new);
|
||||
TABLIST_IMPLEMENTATIONS.put(ProtocolVersion.V1_19_3, ViaTabList1193::new);
|
||||
TABLIST_IMPLEMENTATIONS.put(ProtocolVersion.V1_16, ViaTabList116::new);
|
||||
|
||||
SCOREBOARD_IMPLEMENTATIONS.put(ProtocolVersion.V1_20_3, ViaScoreboard1203::new);
|
||||
SCOREBOARD_IMPLEMENTATIONS.put(ProtocolVersion.V1_16, ViaScoreboard116::new);
|
||||
SCOREBOARD_IMPLEMENTATIONS.put(ProtocolVersion.V1_13, ViaScoreboard113::new);
|
||||
}
|
||||
|
||||
private final ProtocolVersion serverVersion;
|
||||
|
||||
@Nullable
|
||||
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
if (!ReflectionUtils.classExists("com.viaversion.viaversion.protocols.v1_20_2to1_20_3.Protocol1_20_2To1_20_3")) {
|
||||
return null;
|
||||
}
|
||||
int serverVer = serverVersion.getNetworkId();
|
||||
int playerVer = player.getVersion().getNetworkId();
|
||||
for (Map.Entry<ProtocolVersion, Function<BukkitTabPlayer, Scoreboard>> entry : SCOREBOARD_IMPLEMENTATIONS.entrySet()) {
|
||||
if (serverVer < entry.getKey().getNetworkId() && playerVer >= entry.getKey().getNetworkId()) {
|
||||
return entry.getValue().apply(player);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TabList newTabList(@NotNull BukkitTabPlayer player) {
|
||||
if (!ReflectionUtils.classExists("com.viaversion.viaversion.protocols.v1_21_2to1_21_4.Protocol1_21_2To1_21_4")) {
|
||||
return null;
|
||||
}
|
||||
int serverVer = serverVersion.getNetworkId();
|
||||
int playerVer = player.getVersion().getNetworkId();
|
||||
for (Map.Entry<ProtocolVersion, Function<BukkitTabPlayer, TabList>> entry : TABLIST_IMPLEMENTATIONS.entrySet()) {
|
||||
if (serverVer < entry.getKey().getNetworkId() && playerVer >= entry.getKey().getNetworkId()) {
|
||||
return entry.getValue().apply(player);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -41,7 +41,7 @@ import java.util.concurrent.CountDownLatch;
|
||||
* making per-player view of teams, especially sidebar not working.
|
||||
*/
|
||||
// Throw the NPE if something is not as expected, parent class should ensure it anyway
|
||||
@SuppressWarnings({"deprecation", "DataFlowIssue"})
|
||||
@SuppressWarnings("deprecation")
|
||||
public class BukkitScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
/** Version in which team colors were added */
|
||||
@@ -100,10 +100,16 @@ public class BukkitScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
checkPlayerScoreboard();
|
||||
org.bukkit.scoreboard.Objective obj = newObjective(objective.getName(), "dummy", objective.getTitle(), objective.getHealthDisplay());
|
||||
setObjectiveNumberFormat(obj, objective.getNumberFormat());
|
||||
obj.setDisplaySlot(slots[objective.getDisplaySlot().ordinal()]);
|
||||
objective.setPlatformObjective(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySlot(@NonNull Objective objective) {
|
||||
checkPlayerScoreboard();
|
||||
org.bukkit.scoreboard.Objective obj = (org.bukkit.scoreboard.Objective) objective.getPlatformObjective();
|
||||
obj.setDisplaySlot(slots[objective.getDisplaySlot().ordinal()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterObjective(@NonNull Objective objective) {
|
||||
checkPlayerScoreboard();
|
||||
|
||||
+4
@@ -113,6 +113,10 @@ public class PacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
@Override
|
||||
public void registerObjective(@NonNull Objective objective) {
|
||||
packetSender.sendPacket(player, newObjectivePacket(ObjectiveAction.REGISTER, objective));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySlot(@NonNull Objective objective) {
|
||||
packetSender.sendPacket(player, displayPacketData.setDisplaySlot(objective.getDisplaySlot().ordinal(), newObjective(objective)));
|
||||
}
|
||||
|
||||
|
||||
+19
-22
@@ -5,6 +5,7 @@ import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.api.protocol.packet.PacketType;
|
||||
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
|
||||
import com.viaversion.viaversion.api.protocol.packet.State;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.chat.component.SimpleTextComponent;
|
||||
@@ -14,6 +15,7 @@ import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -23,19 +25,19 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
public abstract class ViaScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
private static final long DELAY = 500;
|
||||
private static final long CHECK_DELAY = 50;
|
||||
private static final Object DUMMY = new Object();
|
||||
|
||||
protected final Class<? extends Protocol> protocol;
|
||||
private final PacketType setDisplayObjective;
|
||||
protected final PacketType setDisplayObjective;
|
||||
private final PacketType setObjective;
|
||||
protected final PacketType setScore;
|
||||
private final PacketType setPlayerTeam;
|
||||
/** User connection this tablist belongs to */
|
||||
/** User connection this scoreboard belongs to */
|
||||
protected final UserConnection connection;
|
||||
|
||||
private transient final Queue<Runnable> queue = new ConcurrentLinkedQueue<>();
|
||||
private transient boolean delayed = true;
|
||||
private transient ScheduledFuture<?> task;
|
||||
private transient final Queue<PacketWrapper> queuedPackets = new ConcurrentLinkedQueue<>();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given player.
|
||||
@@ -62,25 +64,22 @@ public abstract class ViaScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
this.setPlayerTeam = setPlayerTeam;
|
||||
this.connection = Via.getManager().getConnectionManager().getConnectedClient(player.getUniqueId());
|
||||
|
||||
// First-time delay
|
||||
connection.getChannel().eventLoop().schedule(() -> {
|
||||
Runnable task;
|
||||
while ((task = queue.poll()) != null) {
|
||||
task.run();
|
||||
// Queue packets until connection is available
|
||||
// Is important to use scheduleWithFixedDelay() to avoid task overlap
|
||||
task = connection.getChannel().eventLoop().scheduleWithFixedDelay(() -> {
|
||||
if (connection.getProtocolInfo().getClientState() == State.PLAY) {
|
||||
PacketWrapper packet;
|
||||
while ((packet = queuedPackets.poll()) != null) {
|
||||
packet.send(protocol);
|
||||
}
|
||||
task.cancel(true);
|
||||
}
|
||||
delayed = false;
|
||||
}, DELAY, TimeUnit.MILLISECONDS);
|
||||
}, 0L, CHECK_DELAY, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerObjective(@NonNull Objective objective) {
|
||||
sendObjectiveUpdate(ObjectiveAction.REGISTER, objective);
|
||||
|
||||
final PacketWrapper packet = PacketWrapper.create(setDisplayObjective, null, connection);
|
||||
|
||||
writeObjective(packet, objective);
|
||||
|
||||
send(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -178,13 +177,11 @@ public abstract class ViaScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
protected abstract void writeComponent(@NonNull PacketWrapper packet, @NonNull TabComponent component);
|
||||
|
||||
protected abstract void writeObjective(@NonNull PacketWrapper packet, @NonNull Objective objective);
|
||||
|
||||
protected abstract void writeObjectiveDisplay(@NonNull PacketWrapper packet, @NonNull Objective objective);
|
||||
|
||||
protected void send(@NonNull PacketWrapper packet) {
|
||||
if (delayed) {
|
||||
queue.add(() -> packet.send(protocol));
|
||||
if (!task.isCancelled()) {
|
||||
queuedPackets.add(packet);
|
||||
} else {
|
||||
packet.scheduleSend(protocol);
|
||||
}
|
||||
|
||||
+10
-6
@@ -30,6 +30,16 @@ public class ViaScoreboard113 extends ViaScoreboard {
|
||||
super(player, protocol, setDisplayObjective, setObjective, setScore, setPlayerTeam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySlot(@NonNull Objective objective) {
|
||||
PacketWrapper packet = PacketWrapper.create(setDisplayObjective, null, connection);
|
||||
|
||||
packet.write(Types.BYTE, (byte) objective.getDisplaySlot().ordinal());
|
||||
packet.write(Types.STRING, objective.getName());
|
||||
|
||||
send(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScore(@NonNull Score score) {
|
||||
sendScoreUpdate(ScoreAction.CHANGE, score);
|
||||
@@ -62,12 +72,6 @@ public class ViaScoreboard113 extends ViaScoreboard {
|
||||
packet.write(Types.COMPONENT, ComponentUtil.legacyToJson(component.toLegacyText()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeObjective(@NonNull PacketWrapper packet, @NonNull Objective objective) {
|
||||
packet.write(Types.BYTE, (byte) objective.getDisplaySlot().ordinal());
|
||||
packet.write(Types.STRING, objective.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeObjectiveDisplay(@NonNull PacketWrapper packet, @NonNull Objective objective) {
|
||||
// Objective value
|
||||
|
||||
+10
-6
@@ -33,6 +33,16 @@ public class ViaScoreboard1203 extends ViaScoreboard116 {
|
||||
this.resetScore = resetScore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySlot(@NonNull Objective objective) {
|
||||
PacketWrapper packet = PacketWrapper.create(setDisplayObjective, null, connection);
|
||||
|
||||
packet.write(Types.VAR_INT, objective.getDisplaySlot().ordinal());
|
||||
packet.write(Types.STRING, objective.getName());
|
||||
|
||||
send(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScore(@NonNull Score score) {
|
||||
final PacketWrapper packet = PacketWrapper.create(setScore, null, connection);
|
||||
@@ -68,12 +78,6 @@ public class ViaScoreboard1203 extends ViaScoreboard116 {
|
||||
packet.write(Types.TAG, component.toViaVersionTag());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeObjective(@NonNull PacketWrapper packet, @NonNull Objective objective) {
|
||||
packet.write(Types.VAR_INT, objective.getDisplaySlot().ordinal());
|
||||
packet.write(Types.STRING, objective.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeObjectiveDisplay(@NonNull PacketWrapper packet, @NonNull Objective objective) {
|
||||
super.writeObjectiveDisplay(packet, objective);
|
||||
|
||||
+17
-12
@@ -5,6 +5,7 @@ import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.api.protocol.packet.PacketType;
|
||||
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
|
||||
import com.viaversion.viaversion.api.protocol.packet.State;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.chat.component.TabComponent;
|
||||
@@ -18,6 +19,7 @@ import java.util.BitSet;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -27,7 +29,7 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
public abstract class ViaTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
private static final long DELAY = 500;
|
||||
private static final long CHECK_DELAY = 50;
|
||||
|
||||
protected final Class<? extends Protocol> protocol;
|
||||
private final PacketType playerInfoUpdate;
|
||||
@@ -35,8 +37,8 @@ public abstract class ViaTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
/** User connection this tablist belongs to */
|
||||
protected final UserConnection connection;
|
||||
|
||||
private transient final Queue<Runnable> queue = new ConcurrentLinkedQueue<>();
|
||||
private transient boolean delayed = true;
|
||||
private transient ScheduledFuture<?> task;
|
||||
private transient final Queue<PacketWrapper> queuedPackets = new ConcurrentLinkedQueue<>();
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -56,14 +58,17 @@ public abstract class ViaTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
this.tabList = tabList;
|
||||
this.connection = Via.getManager().getConnectionManager().getConnectedClient(player.getUniqueId());
|
||||
|
||||
// First-time delay
|
||||
connection.getChannel().eventLoop().schedule(() -> {
|
||||
Runnable task;
|
||||
while ((task = queue.poll()) != null) {
|
||||
task.run();
|
||||
// Queue packets until connection is available
|
||||
// Is important to use scheduleWithFixedDelay() to avoid task overlap
|
||||
task = connection.getChannel().eventLoop().scheduleWithFixedDelay(() -> {
|
||||
if (connection.getProtocolInfo().getClientState() == State.PLAY) {
|
||||
PacketWrapper packet;
|
||||
while ((packet = queuedPackets.poll()) != null) {
|
||||
packet.send(protocol);
|
||||
}
|
||||
task.cancel(true);
|
||||
}
|
||||
delayed = false;
|
||||
}, DELAY, TimeUnit.MILLISECONDS);
|
||||
}, 0L, CHECK_DELAY, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -164,8 +169,8 @@ public abstract class ViaTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
protected void send(@NonNull PacketWrapper packet) {
|
||||
if (delayed) {
|
||||
queue.add(() -> packet.send(protocol));
|
||||
if (!task.isCancelled()) {
|
||||
queuedPackets.add(packet);
|
||||
} else {
|
||||
packet.scheduleSend(protocol);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
dependencies {
|
||||
implementation(projects.bukkit)
|
||||
compileOnly("org.spigotmc:spigot:1.12-R0.1-SNAPSHOT")
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_12_R1;
|
||||
|
||||
import me.neznamy.chat.ChatModifier;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.converter.ComponentConverter;
|
||||
import net.minecraft.server.v1_12_R1.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Component converter using direct NMS code.
|
||||
*/
|
||||
public class NMSComponentConverter extends ComponentConverter {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object newTextComponent(@NotNull String text) {
|
||||
return new ChatComponentText(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object newTranslatableComponent(@NotNull String key) {
|
||||
return new ChatMessage(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object newKeybindComponent(@NotNull String keybind) {
|
||||
return new ChatComponentKeybind(keybind);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyStyle(@NotNull Object nmsComponent, @NotNull ChatModifier modifier) {
|
||||
((IChatBaseComponent)nmsComponent).setChatModifier(
|
||||
new net.minecraft.server.v1_12_R1.ChatModifier()
|
||||
.setColor(modifier.getColor() == null ? null : EnumChatFormat.valueOf(modifier.getColor().getLegacyColor().name()))
|
||||
.setBold(modifier.getBold())
|
||||
.setItalic(modifier.getItalic())
|
||||
.setUnderline(modifier.getUnderlined())
|
||||
.setStrikethrough(modifier.getStrikethrough())
|
||||
.setRandom(modifier.getObfuscated())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSibling(@NotNull Object parent, @NotNull Object child) {
|
||||
((IChatBaseComponent)parent).addSibling((IChatBaseComponent) child);
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_12_R1;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.converter.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.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct NMS code for 1.8.8.
|
||||
*/
|
||||
|
||||
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
|
||||
public @Nullable ComponentConverter getComponentConverter() {
|
||||
return componentConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().playerConnection.networkManager.channel;
|
||||
}
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_12_R1;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.Limitations;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.minecraft.server.v1_12_R1.*;
|
||||
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* 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 Field TeamPacket_NAME = ReflectionUtils.getFields(PacketPlayOutScoreboardTeam.class, String.class).get(0);
|
||||
private static final Field TeamPacket_ACTION = ReflectionUtils.getInstanceFields(PacketPlayOutScoreboardTeam.class, int.class).get(1);
|
||||
private static final Field TeamPacket_PLAYERS = ReflectionUtils.getOnlyField(PacketPlayOutScoreboardTeam.class, Collection.class);
|
||||
|
||||
private static final Field Objective_OBJECTIVE_NAME = ReflectionUtils.getFields(PacketPlayOutScoreboardObjective.class, String.class).get(0);
|
||||
private static final Field Objective_METHOD = ReflectionUtils.getOnlyField(PacketPlayOutScoreboardObjective.class, int.class);
|
||||
|
||||
private static final Field DisplayObjective_OBJECTIVE_NAME = ReflectionUtils.getOnlyField(PacketPlayOutScoreboardDisplayObjective.class, String.class);
|
||||
private static final Field DisplayObjective_POSITION = ReflectionUtils.getOnlyField(PacketPlayOutScoreboardDisplayObjective.class, int.class);
|
||||
|
||||
|
||||
/**
|
||||
* 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.b);
|
||||
obj.setDisplayName(cutTo(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13));
|
||||
obj.a(IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(objective.getHealthDisplay().name()));
|
||||
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();
|
||||
String title = objective.getTitle().toLegacyText();
|
||||
if (player.getVersion().getMinorVersion() < 13 || TAB.getInstance().getConfiguration().getConfig().isPacketEventsCompensation()) {
|
||||
title = cutTo(title, Limitations.SCOREBOARD_TITLE_PRE_1_13);
|
||||
}
|
||||
obj.setDisplayName(title);
|
||||
obj.a(IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(objective.getHealthDisplay().name()));
|
||||
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.UPDATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScore(@NonNull Score score) {
|
||||
ScoreboardScore s = new ScoreboardScore(dummyScoreboard, (ScoreboardObjective) score.getObjective().getPlatformObjective(), score.getHolder());
|
||||
s.setScore(score.getValue());
|
||||
sendPacket(new PacketPlayOutScoreboardScore(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScore(@NonNull Score score) {
|
||||
sendPacket(new PacketPlayOutScoreboardScore(score.getHolder()));
|
||||
}
|
||||
|
||||
@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.getPlayerNameSet().addAll(team.getPlayers());
|
||||
sendPacket(new PacketPlayOutScoreboardTeam(t, TeamAction.CREATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterTeam(@NonNull Team team) {
|
||||
sendPacket(new PacketPlayOutScoreboardTeam((ScoreboardTeam) team.getPlatformTeam(), TeamAction.REMOVE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTeam(@NonNull Team team) {
|
||||
updateTeamProperties(team);
|
||||
sendPacket(new PacketPlayOutScoreboardTeam((ScoreboardTeam) team.getPlatformTeam(), TeamAction.UPDATE));
|
||||
}
|
||||
|
||||
private void updateTeamProperties(@NonNull Team team) {
|
||||
ScoreboardTeam t = (ScoreboardTeam) team.getPlatformTeam();
|
||||
t.setAllowFriendlyFire((team.getOptions() & 0x01) != 0);
|
||||
t.setCanSeeFriendlyInvisibles((team.getOptions() & 0x02) != 0);
|
||||
t.setNameTagVisibility(visibilities[team.getVisibility().ordinal()]);
|
||||
t.setCollisionRule(collisions[team.getCollision().ordinal()]);
|
||||
t.setPrefix(cutTo(team.getPrefix().toLegacyText(), Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13));
|
||||
t.setSuffix(cutTo(team.getSuffix().toLegacyText(), Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (isAntiOverrideScoreboard()) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, DisplayObjective_POSITION.getInt(packet),
|
||||
(String) DisplayObjective_OBJECTIVE_NAME.get(packet));
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardObjective) {
|
||||
TAB.getInstance().getFeatureManager().onObjective(player,
|
||||
Objective_METHOD.getInt(packet), (String) Objective_OBJECTIVE_NAME.get(packet));
|
||||
}
|
||||
}
|
||||
if (isAntiOverrideTeams() && packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = TeamPacket_ACTION.getInt(packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, ((SafeScoreboard<?>)player.getScoreboard()).onTeamPacket(
|
||||
action, (String) TeamPacket_NAME.get(packet), players));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the packet to the player.
|
||||
*
|
||||
* @param packet
|
||||
* Packet to send
|
||||
*/
|
||||
private void sendPacket(@NotNull Packet<?> packet) {
|
||||
((CraftPlayer)player.getPlayer()).getHandle().playerConnection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_12_R1;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.chat.component.TabComponent;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
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.server.v1_12_R1.*;
|
||||
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
*/
|
||||
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
// PlayerInfoData subclass is broken, using reflection to get it
|
||||
private static final Class<?> PlayerInfoData = PacketPlayOutPlayerInfo.class.getDeclaredClasses()[0];
|
||||
private static final Constructor<?> newPlayerInfoData = ReflectionUtils.getOnlyConstructor(PlayerInfoData);
|
||||
|
||||
private static final Field ACTION = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class);
|
||||
private static final Field PLAYERS = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, List.class);
|
||||
|
||||
private static final Field PlayerInfoData_Profile = ReflectionUtils.getOnlyField(PlayerInfoData, GameProfile.class);
|
||||
private static final Field PlayerInfoData_Latency = ReflectionUtils.getFields(PlayerInfoData, int.class).get(0);
|
||||
private static final Field PlayerInfoData_DisplayName = ReflectionUtils.getOnlyField(PlayerInfoData, IChatBaseComponent.class);
|
||||
|
||||
private static final Field HEADER = ReflectionUtils.getFields(PacketPlayOutPlayerListHeaderFooter.class, IChatBaseComponent.class).get(0);
|
||||
private static final Field FOOTER = ReflectionUtils.getFields(PacketPlayOutPlayerListHeaderFooter.class, IChatBaseComponent.class).get(1);
|
||||
|
||||
/**
|
||||
* 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(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, entry, "", null, 0, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
|
||||
sendPacket(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_DISPLAY_NAME, entry, "", null, 0, 0, displayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLatency(@NonNull UUID entry, int latency) {
|
||||
sendPacket(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY, entry, "", null, latency, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGameMode(@NonNull UUID entry, int gameMode) {
|
||||
sendPacket(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_GAME_MODE, 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.20.2
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHat(@NonNull UUID entry, boolean showHat) {
|
||||
// Added in 1.20.4
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEntry0(@NonNull Entry entry) {
|
||||
sendPacket(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, entry.getUniqueId(), entry.getName(), entry.getSkin(), entry.getLatency(),
|
||||
entry.getGameMode(), entry.getDisplayName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
HEADER.set(packet, header.convert());
|
||||
FOOTER.set(packet, footer.convert());
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEntry(@NonNull UUID entry) {
|
||||
return true; // TODO?
|
||||
}
|
||||
|
||||
@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
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
String action = ACTION.get(packet).toString();
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(packet)) {
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
UUID id = profile.getId();
|
||||
if (action.equals(Action.UPDATE_DISPLAY_NAME.name()) || action.equals(Action.ADD_PLAYER.name())) {
|
||||
TabComponent expectedName = getExpectedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
}
|
||||
if (action.equals(Action.UPDATE_LATENCY.name()) || action.equals(Action.ADD_PLAYER.name())) {
|
||||
int oldLatency = PlayerInfoData_Latency.getInt(nmsData);
|
||||
int newLatency = TAB.getInstance().getFeatureManager().onLatencyChange(player, id, oldLatency);
|
||||
if (oldLatency != newLatency) {
|
||||
PlayerInfoData_Latency.set(nmsData, newLatency);
|
||||
}
|
||||
}
|
||||
if (action.equals(Action.ADD_PLAYER.name())) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData.newInstance(
|
||||
packet,
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
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().playerConnection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
dependencies {
|
||||
implementation(projects.bukkit)
|
||||
compileOnly("org.bukkit:craftbukkit:1.8.8-R0.1-SNAPSHOT")
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_8_R3;
|
||||
|
||||
import me.neznamy.chat.ChatModifier;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.converter.ComponentConverter;
|
||||
import net.minecraft.server.v1_8_R3.ChatComponentText;
|
||||
import net.minecraft.server.v1_8_R3.ChatMessage;
|
||||
import net.minecraft.server.v1_8_R3.EnumChatFormat;
|
||||
import net.minecraft.server.v1_8_R3.IChatBaseComponent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Component converter using direct NMS code.
|
||||
*/
|
||||
public class NMSComponentConverter extends ComponentConverter {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object newTextComponent(@NotNull String text) {
|
||||
return new ChatComponentText(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object newTranslatableComponent(@NotNull String key) {
|
||||
return new ChatMessage(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object newKeybindComponent(@NotNull String keybind) {
|
||||
throw new UnsupportedOperationException("Keybind components were added in 1.12");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyStyle(@NotNull Object nmsComponent, @NotNull ChatModifier modifier) {
|
||||
((IChatBaseComponent)nmsComponent).setChatModifier(
|
||||
new net.minecraft.server.v1_8_R3.ChatModifier()
|
||||
.setColor(modifier.getColor() == null ? null : EnumChatFormat.valueOf(modifier.getColor().getLegacyColor().name()))
|
||||
.setBold(modifier.getBold())
|
||||
.setItalic(modifier.getItalic())
|
||||
.setUnderline(modifier.getUnderlined())
|
||||
.setStrikethrough(modifier.getStrikethrough())
|
||||
.setRandom(modifier.getObfuscated())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSibling(@NotNull Object parent, @NotNull Object child) {
|
||||
((IChatBaseComponent)parent).addSibling((IChatBaseComponent) child);
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_8_R3;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.converter.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.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct NMS code for 1.8.8.
|
||||
*/
|
||||
@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 FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().playerConnection.networkManager.channel;
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_8_R3;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.Limitations;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.minecraft.server.v1_8_R3.*;
|
||||
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Scoreboard implementation using direct NMS code.
|
||||
*/
|
||||
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
private static final ScoreboardTeamBase.EnumNameTagVisibility[] visibilities = ScoreboardTeamBase.EnumNameTagVisibility.values();
|
||||
private static final Scoreboard dummyScoreboard = new Scoreboard();
|
||||
|
||||
private static final Field TeamPacket_NAME = ReflectionUtils.getFields(PacketPlayOutScoreboardTeam.class, String.class).get(0);
|
||||
private static final Field TeamPacket_ACTION = ReflectionUtils.getInstanceFields(PacketPlayOutScoreboardTeam.class, int.class).get(1);
|
||||
private static final Field TeamPacket_PLAYERS = ReflectionUtils.getOnlyField(PacketPlayOutScoreboardTeam.class, Collection.class);
|
||||
|
||||
private static final Field Objective_OBJECTIVE_NAME = ReflectionUtils.getFields(PacketPlayOutScoreboardObjective.class, String.class).get(0);
|
||||
private static final Field Objective_METHOD = ReflectionUtils.getOnlyField(PacketPlayOutScoreboardObjective.class, int.class);
|
||||
|
||||
private static final Field DisplayObjective_OBJECTIVE_NAME = ReflectionUtils.getOnlyField(PacketPlayOutScoreboardDisplayObjective.class, String.class);
|
||||
private static final Field DisplayObjective_POSITION = ReflectionUtils.getOnlyField(PacketPlayOutScoreboardDisplayObjective.class, int.class);
|
||||
|
||||
|
||||
/**
|
||||
* 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.b);
|
||||
obj.setDisplayName(cutTo(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13));
|
||||
obj.a(IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(objective.getHealthDisplay().name()));
|
||||
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();
|
||||
String title = objective.getTitle().toLegacyText();
|
||||
if (player.getVersion().getMinorVersion() < 13 || TAB.getInstance().getConfiguration().getConfig().isPacketEventsCompensation()) {
|
||||
title = cutTo(title, Limitations.SCOREBOARD_TITLE_PRE_1_13);
|
||||
}
|
||||
obj.setDisplayName(title);
|
||||
obj.a(IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(objective.getHealthDisplay().name()));
|
||||
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.UPDATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScore(@NonNull Score score) {
|
||||
ScoreboardScore s = new ScoreboardScore(dummyScoreboard, (ScoreboardObjective) score.getObjective().getPlatformObjective(), score.getHolder());
|
||||
s.setScore(score.getValue());
|
||||
sendPacket(new PacketPlayOutScoreboardScore(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScore(@NonNull Score score) {
|
||||
sendPacket(new PacketPlayOutScoreboardScore(score.getHolder()));
|
||||
}
|
||||
|
||||
@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.getPlayerNameSet().addAll(team.getPlayers());
|
||||
sendPacket(new PacketPlayOutScoreboardTeam(t, TeamAction.CREATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterTeam(@NonNull Team team) {
|
||||
sendPacket(new PacketPlayOutScoreboardTeam((ScoreboardTeam) team.getPlatformTeam(), TeamAction.REMOVE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTeam(@NonNull Team team) {
|
||||
updateTeamProperties(team);
|
||||
sendPacket(new PacketPlayOutScoreboardTeam((ScoreboardTeam) team.getPlatformTeam(), TeamAction.UPDATE));
|
||||
}
|
||||
|
||||
private void updateTeamProperties(@NonNull Team team) {
|
||||
ScoreboardTeam t = (ScoreboardTeam) team.getPlatformTeam();
|
||||
t.setAllowFriendlyFire((team.getOptions() & 0x01) != 0);
|
||||
t.setCanSeeFriendlyInvisibles((team.getOptions() & 0x02) != 0);
|
||||
t.setNameTagVisibility(visibilities[team.getVisibility().ordinal()]);
|
||||
t.setPrefix(cutTo(team.getPrefix().toLegacyText(), Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13));
|
||||
t.setSuffix(cutTo(team.getSuffix().toLegacyText(), Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (isAntiOverrideScoreboard()) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, DisplayObjective_POSITION.getInt(packet),
|
||||
(String) DisplayObjective_OBJECTIVE_NAME.get(packet));
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardObjective) {
|
||||
TAB.getInstance().getFeatureManager().onObjective(player,
|
||||
Objective_METHOD.getInt(packet), (String) Objective_OBJECTIVE_NAME.get(packet));
|
||||
}
|
||||
}
|
||||
if (isAntiOverrideTeams() && packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = TeamPacket_ACTION.getInt(packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, ((SafeScoreboard<?>)player.getScoreboard()).onTeamPacket(
|
||||
action, (String) TeamPacket_NAME.get(packet), players));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the packet to the player.
|
||||
*
|
||||
* @param packet
|
||||
* Packet to send
|
||||
*/
|
||||
private void sendPacket(@NotNull Packet<?> packet) {
|
||||
((CraftPlayer)player.getPlayer()).getHandle().playerConnection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_8_R3;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.chat.component.TabComponent;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
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.server.v1_8_R3.*;
|
||||
import net.minecraft.server.v1_8_R3.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import org.bukkit.craftbukkit.v1_8_R3.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 ACTION = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class);
|
||||
private static final Field PLAYERS = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, List.class);
|
||||
|
||||
private static final Field PlayerInfoData_Latency = ReflectionUtils.getFields(PlayerInfoData.class, int.class).get(0);
|
||||
private static final Field PlayerInfoData_DisplayName = ReflectionUtils.getOnlyField(PlayerInfoData.class, IChatBaseComponent.class);
|
||||
|
||||
private static final Field FOOTER = ReflectionUtils.getFields(PacketPlayOutPlayerListHeaderFooter.class, IChatBaseComponent.class).get(1);
|
||||
|
||||
/**
|
||||
* 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(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, entry, "", null, 0, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
|
||||
sendPacket(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_DISPLAY_NAME, entry, "", null, 0, 0, displayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLatency(@NonNull UUID entry, int latency) {
|
||||
sendPacket(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY, entry, "", null, latency, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGameMode(@NonNull UUID entry, int gameMode) {
|
||||
sendPacket(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_GAME_MODE, 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.20.2
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHat(@NonNull UUID entry, boolean showHat) {
|
||||
// Added in 1.20.4
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEntry0(@NonNull Entry entry) {
|
||||
sendPacket(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, entry.getUniqueId(), entry.getName(), entry.getSkin(), entry.getLatency(),
|
||||
entry.getGameMode(), entry.getDisplayName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter(header.convert());
|
||||
FOOTER.set(packet, footer.convert());
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEntry(@NonNull UUID entry) {
|
||||
return true; // TODO?
|
||||
}
|
||||
|
||||
@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
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
String action = ACTION.get(packet).toString();
|
||||
for (PlayerInfoData nmsData : (List<PlayerInfoData>) PLAYERS.get(packet)) {
|
||||
GameProfile profile = nmsData.a();
|
||||
UUID id = profile.getId();
|
||||
if (action.equals(Action.UPDATE_DISPLAY_NAME.name()) || action.equals(Action.ADD_PLAYER.name())) {
|
||||
TabComponent expectedName = getExpectedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
}
|
||||
if (action.equals(Action.UPDATE_LATENCY.name()) || action.equals(Action.ADD_PLAYER.name())) {
|
||||
int oldLatency = PlayerInfoData_Latency.getInt(nmsData);
|
||||
int newLatency = TAB.getInstance().getFeatureManager().onLatencyChange(player, id, oldLatency);
|
||||
if (oldLatency != newLatency) {
|
||||
PlayerInfoData_Latency.set(nmsData, newLatency);
|
||||
}
|
||||
}
|
||||
if (action.equals(Action.ADD_PLAYER.name())) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
PLAYERS.set(packet, Collections.singletonList(packet.new PlayerInfoData(
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
WorldSettings.EnumGamemode.values()[gameMode],
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
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().playerConnection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,10 @@ public class BungeeScoreboard extends SafeScoreboard<BungeeTabPlayer> {
|
||||
@Override
|
||||
public void registerObjective(@NonNull Objective objective) {
|
||||
sendObjectivePacket(objective, (byte) ObjectiveAction.REGISTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySlot(@NonNull Objective objective) {
|
||||
player.sendPacket(new ScoreboardDisplay(objective.getDisplaySlot().ordinal(), objective.getName()));
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -53,7 +54,8 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
|
||||
@Override
|
||||
public void sendPluginMessage(byte[] message) {
|
||||
getPlayer().getServer().sendData(TabConstants.PLUGIN_MESSAGE_CHANNEL_NAME, message);
|
||||
Server server = getPlayer().getServer();
|
||||
if (server != null) server.sendData(TabConstants.PLUGIN_MESSAGE_CHANNEL_NAME, message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,7 +48,11 @@ public class FabricScoreboard extends SafeScoreboard<FabricTabPlayer> {
|
||||
);
|
||||
objective.setPlatformObjective(obj);
|
||||
sendPacket(new ClientboundSetObjectivePacket(obj, ObjectiveAction.REGISTER));
|
||||
sendPacket(new ClientboundSetDisplayObjectivePacket(net.minecraft.world.scores.DisplaySlot.values()[objective.getDisplaySlot().ordinal()], obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySlot(@NonNull Objective objective) {
|
||||
sendPacket(new ClientboundSetDisplayObjectivePacket(net.minecraft.world.scores.DisplaySlot.values()[objective.getDisplaySlot().ordinal()], (net.minecraft.world.scores.Objective) objective.getPlatformObjective()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -7,10 +7,11 @@ plugins {
|
||||
val platforms = setOf(
|
||||
rootProject.projects.bukkit,
|
||||
rootProject.projects.bukkit.paper,
|
||||
rootProject.projects.bukkit.v18R3,
|
||||
rootProject.projects.bukkit.v112R1,
|
||||
rootProject.projects.bungeecord,
|
||||
rootProject.projects.velocity,
|
||||
rootProject.projects.sponge7,
|
||||
rootProject.projects.sponge8
|
||||
rootProject.projects.sponge
|
||||
).map { it.dependencyProject }
|
||||
|
||||
tasks {
|
||||
|
||||
+4
-2
@@ -12,6 +12,7 @@ dependencyResolutionManagement {
|
||||
maven("https://repo.spongepowered.org/repository/maven-public/") // Sponge
|
||||
maven("https://jitpack.io") // PremiumVanish, Vault, YamlAssist, RedisBungee
|
||||
maven("https://repo.md-5.net/content/groups/public/") // LibsDisguises
|
||||
maven("https://repo.codemc.org/repository/nms/") // CraftBukkit + NMS
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +32,10 @@ include(":shared")
|
||||
include(":velocity")
|
||||
include(":bukkit")
|
||||
include(":bukkit:paper")
|
||||
include(":bukkit:v1_8_R3")
|
||||
include(":bukkit:v1_12_R1")
|
||||
include(":bungeecord")
|
||||
include(":component")
|
||||
include(":sponge7")
|
||||
include(":sponge8")
|
||||
include(":sponge")
|
||||
include(":fabric")
|
||||
include(":jar")
|
||||
@@ -569,20 +569,18 @@ public class FeatureManager {
|
||||
|
||||
// Load the feature first, because it will be processed in main thread (to make it run before feature threads)
|
||||
if (config.isEnableProxySupport()) {
|
||||
String type = config.getConfig().getString("proxy-support.type");
|
||||
ProxySupport proxy;
|
||||
String type = config.getConfig().getString("proxy-support.type", "PLUGIN");
|
||||
ProxySupport proxy = null;
|
||||
if (type.equalsIgnoreCase("PLUGIN")) {
|
||||
String plugin = config.getConfig().getString("proxy-support.plugin.name", "RedisBungee");
|
||||
proxy = TAB.getInstance().getPlatform().getProxySupport(plugin);
|
||||
} else if (type.equalsIgnoreCase("REDIS")) {
|
||||
String url = config.getConfig().getString("proxy-support.redis.url");
|
||||
proxy = new ProxyMessengerSupport(() -> RedisBroker.of(url));
|
||||
String url = config.getConfig().getString("proxy-support.redis.url", "redis://:password@localhost:6379/0");
|
||||
proxy = new ProxyMessengerSupport("Redis", () -> RedisBroker.of(url));
|
||||
} else if (type.equalsIgnoreCase("RABBITMQ")) {
|
||||
String exchange = config.getConfig().getString("proxy-support.rabbitmq.exchange");
|
||||
String url = config.getConfig().getString("proxy-support.rabbitmq.url");
|
||||
proxy = new ProxyMessengerSupport(() -> RabbitMQBroker.of(url, exchange));
|
||||
} else {
|
||||
proxy = null;
|
||||
String exchange = config.getConfig().getString("proxy-support.rabbitmq.exchange", "plugin");
|
||||
String url = config.getConfig().getString("proxy-support.rabbitmq.url", "amqp://guest:guest@localhost:5672/%2F");
|
||||
proxy = new ProxyMessengerSupport("RabbitMQ", () -> RabbitMQBroker.of(url, exchange));
|
||||
}
|
||||
if (proxy != null) TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.PROXY_SUPPORT, proxy);
|
||||
}
|
||||
|
||||
@@ -249,8 +249,8 @@ public class Converter {
|
||||
|
||||
private void convertHeaderFooter(@NotNull ConfigurationFile oldConfig, @NotNull ConfigurationFile newConfig) {
|
||||
newConfig.set("header-footer.enabled", oldConfig.getBoolean("enable-header-footer", true));
|
||||
newConfig.set("header-footer.header", oldConfig.getStringList("header"));
|
||||
newConfig.set("header-footer.footer", oldConfig.getStringList("footer"));
|
||||
newConfig.set("header-footer.header", oldConfig.getStringList("header", Collections.emptyList()));
|
||||
newConfig.set("header-footer.footer", oldConfig.getStringList("footer", Collections.emptyList()));
|
||||
newConfig.set("header-footer.disable-in-worlds", oldConfig.getStringList("disable-features-in-worlds.header-footer", Collections.singletonList("disabledworld")));
|
||||
if (TAB.getInstance().getPlatform().isProxy())
|
||||
newConfig.set("header-footer.disable-in-servers", oldConfig.getStringList("disable-features-in-servers.header-footer", Collections.singletonList("disabledserver")));
|
||||
@@ -337,7 +337,7 @@ public class Converter {
|
||||
}
|
||||
|
||||
private String translateWorldGroup(@NotNull ConfigurationFile oldConfig, @NotNull String group) {
|
||||
String oldSeparator = oldConfig.hasConfigOption("multi-world-separator") ? oldConfig.getString("multi-world-separator") : "-";
|
||||
String oldSeparator = oldConfig.getString("multi-world-separator", "-");
|
||||
return group.replace(oldSeparator, ";");
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -17,17 +18,17 @@ import java.util.Map.Entry;
|
||||
/**
|
||||
* Abstract class for configuration file
|
||||
*/
|
||||
@Getter
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract class ConfigurationFile {
|
||||
|
||||
/** Comments on top of the file */
|
||||
protected List<String> header;
|
||||
|
||||
/** Configuration file content */
|
||||
@Getter @Setter protected Map<Object, Object> values;
|
||||
@Setter
|
||||
protected Map<Object, Object> values;
|
||||
|
||||
/** File to use */
|
||||
@Getter protected final File file;
|
||||
@NotNull
|
||||
protected final File file;
|
||||
|
||||
/**
|
||||
* Constructs new instance and attempts to load specified configuration file.
|
||||
@@ -69,6 +70,7 @@ public abstract class ConfigurationFile {
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file
|
||||
*/
|
||||
@Nullable
|
||||
public Object getObject(@NonNull String path, @Nullable Object defaultValue) {
|
||||
if (path.isEmpty()) return values;
|
||||
Object value = values;
|
||||
@@ -108,10 +110,10 @@ public abstract class ConfigurationFile {
|
||||
* @return Value at specified path
|
||||
*/
|
||||
@Nullable
|
||||
public Object getObject(String[] path) {
|
||||
public Object getObject(@NonNull String[] path) {
|
||||
Object value = values;
|
||||
for (String section : path) {
|
||||
if (section == null || !(value instanceof Map)) {
|
||||
if (!(value instanceof Map)) {
|
||||
return null;
|
||||
}
|
||||
value = getIgnoreCase((Map<Object, Object>) value, section);
|
||||
@@ -129,6 +131,7 @@ public abstract class ConfigurationFile {
|
||||
* case-insensitive key name
|
||||
* @return map value from case-insensitive key
|
||||
*/
|
||||
@Nullable
|
||||
private Object getIgnoreCase(@NonNull Map<Object, Object> map, @NonNull String key) {
|
||||
try {
|
||||
for (Entry<Object, Object> entry : map.entrySet()) {
|
||||
@@ -141,18 +144,6 @@ public abstract class ConfigurationFile {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code String}. Returns {@code null} if
|
||||
* option is not present.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return value from file or null if not present
|
||||
*/
|
||||
public String getString(@NonNull String path) {
|
||||
return getString(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code String}. If the option is not present
|
||||
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
|
||||
@@ -164,25 +155,13 @@ public abstract class ConfigurationFile {
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file as {@code String}
|
||||
*/
|
||||
@Contract("_, !null -> !null")
|
||||
public String getString(@NonNull String path, @Nullable String defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code List<String>}. Returns {@code null} if
|
||||
* option is not present.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return value from file or null if not present
|
||||
*/
|
||||
@Nullable
|
||||
public List<String> getStringList(@NonNull String path) {
|
||||
return getStringList(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code List<String>}. If the option is not present
|
||||
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
|
||||
@@ -194,6 +173,7 @@ public abstract class ConfigurationFile {
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file as {@code List<String>}
|
||||
*/
|
||||
@Contract("_, !null -> !null")
|
||||
public List<String> getStringList(@NonNull String path, @Nullable List<String> defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
@@ -207,18 +187,6 @@ public abstract class ConfigurationFile {
|
||||
return fixedList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code Integer}. Returns {@code null} if
|
||||
* option is not present.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return value from file or null if not present
|
||||
*/
|
||||
public Integer getInt(@NonNull String path) {
|
||||
return getInt(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code Integer}. If the option is not present
|
||||
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
|
||||
@@ -230,6 +198,7 @@ public abstract class ConfigurationFile {
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file as {@code Integer}
|
||||
*/
|
||||
@Contract("_, !null -> !null")
|
||||
public Integer getInt(@NonNull String path, @Nullable Integer defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
@@ -257,27 +226,6 @@ public abstract class ConfigurationFile {
|
||||
return Boolean.parseBoolean(value.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code Double}. If the option is not present
|
||||
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
|
||||
* and {@code defaultValue} returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param defaultValue
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file as {@code Double}
|
||||
*/
|
||||
public Double getDouble(@NonNull String path, double defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
try {
|
||||
return Double.parseDouble(value.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code Map<K, V>}. If the option
|
||||
* is not present or value is not a {@code Map}, new empty {@code Map} is returned.
|
||||
@@ -337,6 +285,7 @@ public abstract class ConfigurationFile {
|
||||
* Value to save
|
||||
* @return the first argument to allow chaining
|
||||
*/
|
||||
@NotNull
|
||||
private Map<Object, Object> set(@NonNull Map<Object, Object> map, @NonNull String path, @Nullable Object value) {
|
||||
if (path.contains(".")) {
|
||||
String keyWord = getRealKey(map, path.split("\\.")[0]);
|
||||
@@ -365,6 +314,7 @@ public abstract class ConfigurationFile {
|
||||
* Key to find
|
||||
* @return The real key name
|
||||
*/
|
||||
@NotNull
|
||||
private String getRealKey(@NonNull Map<?, ?> map, @NonNull String key) {
|
||||
for (Object mapKey : map.keySet()) {
|
||||
if (mapKey.toString().equalsIgnoreCase(key)) return mapKey.toString();
|
||||
|
||||
@@ -208,12 +208,12 @@ public class BelowName extends RefreshableFeature implements JoinListener, QuitL
|
||||
|
||||
private void register(@NotNull TabPlayer player) {
|
||||
player.getScoreboard().registerObjective(
|
||||
Scoreboard.DisplaySlot.BELOW_NAME,
|
||||
OBJECTIVE_NAME,
|
||||
cache.get(player.belowNameData.text.updateAndGet()),
|
||||
Scoreboard.HealthDisplay.INTEGER,
|
||||
cache.get(player.belowNameData.defaultNumberFormat.updateAndGet())
|
||||
);
|
||||
player.getScoreboard().setDisplaySlot(OBJECTIVE_NAME, Scoreboard.DisplaySlot.BELOW_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -208,12 +208,12 @@ public class YellowNumber extends RefreshableFeature implements JoinListener, Qu
|
||||
|
||||
private void register(@NotNull TabPlayer player) {
|
||||
player.getScoreboard().registerObjective(
|
||||
Scoreboard.DisplaySlot.PLAYER_LIST,
|
||||
OBJECTIVE_NAME,
|
||||
cache.get(player.playerlistObjectiveData.title.updateAndGet()),
|
||||
configuration.getHealthDisplay(),
|
||||
SimpleTextComponent.EMPTY
|
||||
);
|
||||
player.getScoreboard().setDisplaySlot(OBJECTIVE_NAME, Scoreboard.DisplaySlot.PLAYER_LIST);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+28
-10
@@ -3,38 +3,56 @@ package me.neznamy.tab.shared.features.proxy;
|
||||
import com.saicone.delivery4j.AbstractMessenger;
|
||||
import com.saicone.delivery4j.Broker;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.chat.TextColor;
|
||||
import me.neznamy.chat.component.TextComponent;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ProxyMessengerSupport extends ProxySupport {
|
||||
|
||||
@NotNull
|
||||
private final String messengerName;
|
||||
|
||||
@NotNull
|
||||
private final Supplier<Broker> brokerSupplier;
|
||||
|
||||
@Nullable
|
||||
private AbstractMessenger messenger;
|
||||
|
||||
@Override
|
||||
public void sendMessage(@NotNull String message) {
|
||||
if (messenger == null) return;
|
||||
messenger.send(TabConstants.PROXY_CHANNEL_NAME, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register() {
|
||||
messenger = new AbstractMessenger() {
|
||||
@Override
|
||||
protected @NotNull Broker loadBroker() {
|
||||
return brokerSupplier.get();
|
||||
}
|
||||
};
|
||||
messenger.subscribe(TabConstants.PROXY_CHANNEL_NAME).consume((channel, lines) -> {
|
||||
processMessage(lines[0]);
|
||||
}).cache(true);
|
||||
messenger.start();
|
||||
try {
|
||||
Broker broker = brokerSupplier.get();
|
||||
messenger = new AbstractMessenger() {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected Broker loadBroker() {
|
||||
return broker;
|
||||
}
|
||||
};
|
||||
messenger.subscribe(TabConstants.PROXY_CHANNEL_NAME).consume((channel, lines) -> processMessage(lines[0])).cache(true);
|
||||
messenger.start();
|
||||
TAB.getInstance().getPlatform().logInfo(new TextComponent("Successfully connected to " + messengerName, TextColor.GREEN));
|
||||
} catch (Exception e) {
|
||||
TAB.getInstance().getErrorManager().criticalError("Failed to connect to " + messengerName + ": " + e.getClass().getName() + ": " + e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister() {
|
||||
if (messenger == null) return;
|
||||
messenger.close();
|
||||
messenger.clear();
|
||||
}
|
||||
|
||||
@@ -150,12 +150,12 @@ public class ScoreboardImpl extends RefreshableFeature implements me.neznamy.tab
|
||||
if (p.scoreboardData.activeScoreboard == this) return; // already registered
|
||||
p.scoreboardData.titleProperty = new Property(this, p, title);
|
||||
p.getScoreboard().registerObjective(
|
||||
Scoreboard.DisplaySlot.SIDEBAR,
|
||||
ScoreboardManagerImpl.OBJECTIVE_NAME,
|
||||
manager.getCache().get(p.scoreboardData.titleProperty.get()),
|
||||
Scoreboard.HealthDisplay.INTEGER,
|
||||
SimpleTextComponent.EMPTY
|
||||
);
|
||||
p.getScoreboard().setDisplaySlot(ScoreboardManagerImpl.OBJECTIVE_NAME, Scoreboard.DisplaySlot.SIDEBAR);
|
||||
for (Line s : lines) {
|
||||
((ScoreboardLine)s).register(p);
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ public interface Scoreboard {
|
||||
/**
|
||||
* Registers new scoreboard objective.
|
||||
*
|
||||
* @param displaySlot
|
||||
* Display slot
|
||||
* @param objectiveName
|
||||
* Objective name
|
||||
* @param title
|
||||
@@ -31,8 +29,16 @@ public interface Scoreboard {
|
||||
* @param numberFormat
|
||||
* Default number format for all scores in this objective (1.20.3+)
|
||||
*/
|
||||
void registerObjective(@NonNull DisplaySlot displaySlot, @NonNull String objectiveName, @NonNull TabComponent title,
|
||||
@NonNull HealthDisplay display, @Nullable TabComponent numberFormat);
|
||||
void registerObjective(@NonNull String objectiveName, @NonNull TabComponent title, @NonNull HealthDisplay display, @Nullable TabComponent numberFormat);
|
||||
|
||||
/**
|
||||
* Sets display slot of an objective.
|
||||
* @param objectiveName
|
||||
* Objective name
|
||||
* @param displaySlot
|
||||
* Display slot
|
||||
*/
|
||||
void setDisplaySlot(@NonNull String objectiveName, @NonNull DisplaySlot displaySlot);
|
||||
|
||||
/**
|
||||
* Unregisters scoreboard objective.
|
||||
|
||||
@@ -57,9 +57,9 @@ public abstract class SafeScoreboard<T extends TabPlayer> implements Scoreboard
|
||||
private boolean antiOverrideScoreboard;
|
||||
|
||||
@Override
|
||||
public synchronized void registerObjective(@NonNull DisplaySlot displaySlot, @NonNull String objectiveName, @NonNull TabComponent title,
|
||||
public synchronized void registerObjective(@NonNull String objectiveName, @NonNull TabComponent title,
|
||||
@NonNull HealthDisplay display, @Nullable TabComponent numberFormat) {
|
||||
Objective objective = new Objective(displaySlot, objectiveName, title, display, numberFormat, null);
|
||||
Objective objective = new Objective(objectiveName, title, display, numberFormat, null);
|
||||
if (objectives.put(objectiveName, objective) != null) {
|
||||
error("Tried to register duplicated objective %s to player ", objectiveName);
|
||||
return;
|
||||
@@ -68,6 +68,18 @@ public abstract class SafeScoreboard<T extends TabPlayer> implements Scoreboard
|
||||
registerObjective(objective);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setDisplaySlot(@NonNull String objectiveName, @NonNull DisplaySlot displaySlot) {
|
||||
Objective objective = objectives.get(objectiveName);
|
||||
if (objective == null) {
|
||||
error("Tried to set display slot for non-existing objective %s to player ", objectiveName);
|
||||
return;
|
||||
}
|
||||
objective.setDisplaySlot(displaySlot);
|
||||
if (frozen) return;
|
||||
setDisplaySlot(objective);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void unregisterObjective(@NonNull String objectiveName) {
|
||||
Objective objective = objectives.remove(objectiveName);
|
||||
@@ -202,6 +214,9 @@ public abstract class SafeScoreboard<T extends TabPlayer> implements Scoreboard
|
||||
public synchronized void resend() {
|
||||
for (Objective objective : objectives.values()) {
|
||||
registerObjective(objective);
|
||||
if (objective.getDisplaySlot() != null) {
|
||||
setDisplaySlot(objective);
|
||||
}
|
||||
for (Score score : objective.getScores().values()) {
|
||||
setScore(score);
|
||||
}
|
||||
@@ -370,6 +385,14 @@ public abstract class SafeScoreboard<T extends TabPlayer> implements Scoreboard
|
||||
*/
|
||||
public abstract void registerObjective(@NonNull Objective objective);
|
||||
|
||||
/**
|
||||
* Sets display slot for an objective
|
||||
*
|
||||
* @param objective
|
||||
* Objective to set display slot for
|
||||
*/
|
||||
public abstract void setDisplaySlot(@NonNull Objective objective);
|
||||
|
||||
/**
|
||||
* Unregisters an objective
|
||||
*
|
||||
@@ -439,12 +462,11 @@ public abstract class SafeScoreboard<T extends TabPlayer> implements Scoreboard
|
||||
/**
|
||||
* Structure holding objective data.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Objective {
|
||||
|
||||
@NonNull private final DisplaySlot displaySlot;
|
||||
@Nullable private DisplaySlot displaySlot;
|
||||
@NonNull private final String name;
|
||||
@NonNull private TabComponent title;
|
||||
@NonNull private HealthDisplay healthDisplay;
|
||||
@@ -454,6 +476,15 @@ public abstract class SafeScoreboard<T extends TabPlayer> implements Scoreboard
|
||||
/** Platform's objective object (if it has one) for fast access */
|
||||
@Nullable private Object platformObjective;
|
||||
|
||||
private Objective(@NonNull String name, @NonNull TabComponent title,
|
||||
@NonNull HealthDisplay healthDisplay, @Nullable TabComponent numberFormat, @Nullable Object platformObjective) {
|
||||
this.name = name;
|
||||
this.title = title;
|
||||
this.healthDisplay = healthDisplay;
|
||||
this.numberFormat = numberFormat;
|
||||
this.platformObjective = platformObjective;
|
||||
}
|
||||
|
||||
private void update(@NonNull TabComponent title, @NonNull HealthDisplay healthDisplay, @Nullable TabComponent numberFormat) {
|
||||
this.title = title;
|
||||
this.healthDisplay = healthDisplay;
|
||||
|
||||
@@ -51,7 +51,7 @@ public abstract class TrackedTabList<P extends TabPlayer> implements TabList {
|
||||
* Not needed for platforms which support pipeline injection.
|
||||
*/
|
||||
public void checkDisplayNames() {
|
||||
// Empty by default, overridden by Sponge7, Sponge8 and Velocity
|
||||
// Empty by default, overridden Sponge and Velocity
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,11 @@ public class DummyScoreboard extends SafeScoreboard<TabPlayer> {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySlot(@NonNull Objective objective) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterObjective(@NonNull Objective objective) {
|
||||
// Do nothing
|
||||
|
||||
@@ -57,6 +57,7 @@ playerlist-objective:
|
||||
value: "%ping%"
|
||||
fancy-value: "&7Ping: %ping%"
|
||||
title: "Java Edition is better" # Only visible on Bedrock Edition
|
||||
render-type: INTEGER
|
||||
disable-condition: '%world%=disabledworld'
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Belowname
|
||||
|
||||
@@ -17,8 +17,12 @@ dependencies {
|
||||
implementation("org.bstats:bstats-sponge:3.1.0")
|
||||
}
|
||||
|
||||
tasks.compileJava {
|
||||
options.release.set(21)
|
||||
}
|
||||
|
||||
sponge {
|
||||
apiVersion("8.0.0")
|
||||
apiVersion("12.0.0")
|
||||
loader {
|
||||
name(PluginLoaders.JAVA_PLAIN)
|
||||
version("1.0")
|
||||
@@ -28,7 +32,7 @@ sponge {
|
||||
displayName(rootProject.name)
|
||||
version(project.version.toString())
|
||||
description(project.description)
|
||||
entrypoint("me.neznamy.tab.platforms.sponge8.Sponge8TAB")
|
||||
entrypoint("me.neznamy.tab.platforms.sponge.SpongeTAB")
|
||||
links {
|
||||
val website = rootProject.ext.get("website")!!.toString()
|
||||
homepage(website)
|
||||
+6
-3
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.sponge8;
|
||||
package me.neznamy.tab.platforms.sponge;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
@@ -14,9 +14,12 @@ import org.spongepowered.api.event.entity.ChangeEntityWorldEvent;
|
||||
import org.spongepowered.api.event.entity.living.player.RespawnPlayerEvent;
|
||||
import org.spongepowered.api.event.filter.cause.First;
|
||||
import org.spongepowered.api.event.network.ServerSideConnectionEvent;
|
||||
import org.spongepowered.api.profile.GameProfile;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Event listener for Sponge 8 to forward events to enabled features.
|
||||
* Event listener for Sponge to forward events to enabled features.
|
||||
*/
|
||||
public class SpongeEventListener implements EventListener<ServerPlayer> {
|
||||
|
||||
@@ -29,7 +32,7 @@ public class SpongeEventListener implements EventListener<ServerPlayer> {
|
||||
@Listener
|
||||
@SneakyThrows
|
||||
public void onQuit(ServerSideConnectionEvent.Disconnect event) {
|
||||
quit(SpongeMultiVersion.getUniqueId.apply(event));
|
||||
quit(event.profile().map(GameProfile::uuid).orElse(new UUID(0, 0)));
|
||||
}
|
||||
|
||||
/**
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.sponge8;
|
||||
package me.neznamy.tab.platforms.sponge;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -28,14 +28,14 @@ import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Platform implementation for Sponge 8 and up.
|
||||
* Platform implementation for Sponge.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class SpongePlatform implements BackendPlatform {
|
||||
|
||||
/** Main class reference */
|
||||
@NotNull
|
||||
private final Sponge8TAB plugin;
|
||||
private final SpongeTAB plugin;
|
||||
|
||||
/** Server version */
|
||||
@Getter
|
||||
+9
-5
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.sponge8;
|
||||
package me.neznamy.tab.platforms.sponge;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
|
||||
@@ -14,7 +14,7 @@ import org.spongepowered.api.scoreboard.objective.displaymode.ObjectiveDisplayMo
|
||||
import org.spongepowered.api.scoreboard.objective.displaymode.ObjectiveDisplayModes;
|
||||
|
||||
/**
|
||||
* Scoreboard implementation for Sponge 8 using its API.
|
||||
* Scoreboard implementation for Sponge using its API.
|
||||
*/
|
||||
public class SpongeScoreboard extends SafeScoreboard<SpongeTabPlayer> {
|
||||
|
||||
@@ -72,10 +72,14 @@ public class SpongeScoreboard extends SafeScoreboard<SpongeTabPlayer> {
|
||||
.criterion(Criteria.DUMMY)
|
||||
.build();
|
||||
sb.addObjective(obj);
|
||||
sb.updateDisplaySlot(obj, displaySlots[objective.getDisplaySlot().ordinal()]);
|
||||
objective.setPlatformObjective(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySlot(@NonNull Objective objective) {
|
||||
sb.updateDisplaySlot((org.spongepowered.api.scoreboard.objective.Objective) objective.getPlatformObjective(), displaySlots[objective.getDisplaySlot().ordinal()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterObjective(@NonNull Objective objective) {
|
||||
sb.removeObjective((org.spongepowered.api.scoreboard.objective.Objective) objective.getPlatformObjective());
|
||||
@@ -91,13 +95,13 @@ public class SpongeScoreboard extends SafeScoreboard<SpongeTabPlayer> {
|
||||
@Override
|
||||
public void setScore(@NonNull Score score) {
|
||||
org.spongepowered.api.scoreboard.objective.Objective obj = (org.spongepowered.api.scoreboard.objective.Objective) score.getObjective().getPlatformObjective();
|
||||
SpongeMultiVersion.findOrCreateScore(obj, score.getHolder()).setScore(score.getValue());
|
||||
obj.findOrCreateScore(score.getHolder()).setScore(score.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScore(@NonNull Score score) {
|
||||
org.spongepowered.api.scoreboard.objective.Objective obj = (org.spongepowered.api.scoreboard.objective.Objective) score.getObjective().getPlatformObjective();
|
||||
obj.removeScore(SpongeMultiVersion.findOrCreateScore(obj, score.getHolder()));
|
||||
obj.removeScore(score.getHolder());
|
||||
}
|
||||
|
||||
@Override
|
||||
+15
-4
@@ -1,13 +1,16 @@
|
||||
package me.neznamy.tab.platforms.sponge8;
|
||||
package me.neznamy.tab.platforms.sponge;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bstats.sponge.Metrics;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.api.Server;
|
||||
import org.spongepowered.api.Sponge;
|
||||
import org.spongepowered.api.command.Command;
|
||||
import org.spongepowered.api.config.ConfigDir;
|
||||
import org.spongepowered.api.event.Listener;
|
||||
@@ -20,15 +23,16 @@ import org.spongepowered.plugin.builtin.jvm.Plugin;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Main class for Sponge 8.
|
||||
* Main class for Sponge.
|
||||
*/
|
||||
@Getter
|
||||
@Plugin(TabConstants.PLUGIN_ID)
|
||||
public class Sponge8TAB {
|
||||
public class SpongeTAB {
|
||||
|
||||
@Inject @ConfigDir(sharedRoot = false) private Path configDir;
|
||||
@Inject private PluginContainer container;
|
||||
@Inject private Metrics.Factory metricsFactory;
|
||||
@Inject private Logger logger;
|
||||
|
||||
/**
|
||||
* Enables the plugin.
|
||||
@@ -38,6 +42,13 @@ public class Sponge8TAB {
|
||||
*/
|
||||
@Listener
|
||||
public void onServerStart(@Nullable StartingEngineEvent<Server> event) {
|
||||
ProtocolVersion serverVersion = ProtocolVersion.fromNetworkId(Sponge.platform().minecraftVersion().protocolVersion());
|
||||
if (serverVersion.getNetworkId() < ProtocolVersion.V1_20_6.getNetworkId()) {
|
||||
logger.warn("====================================================================================================");
|
||||
logger.warn("This plugin version was made for Minecraft version 1.20.6 and above.");
|
||||
logger.warn("====================================================================================================");
|
||||
return;
|
||||
}
|
||||
TAB.create(new SpongePlatform(this));
|
||||
}
|
||||
|
||||
@@ -60,6 +71,6 @@ public class Sponge8TAB {
|
||||
*/
|
||||
@Listener
|
||||
public void onServerStop(@Nullable StoppingEngineEvent<Server> event) {
|
||||
TAB.getInstance().unload();
|
||||
if (TAB.getInstance() != null) TAB.getInstance().unload();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.sponge8;
|
||||
package me.neznamy.tab.platforms.sponge;
|
||||
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
@@ -22,7 +22,7 @@ import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Command processor for plugin's command for Sponge 8.
|
||||
* Command processor for plugin's command for Sponge.
|
||||
*/
|
||||
public class SpongeTabCommand implements Command.Raw {
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.sponge8;
|
||||
package me.neznamy.tab.platforms.sponge;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.chat.component.TabComponent;
|
||||
@@ -15,7 +15,7 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* TabList implementation for Sponge 8 and up
|
||||
* TabList implementation for Sponge.
|
||||
*/
|
||||
public class SpongeTabList extends TrackedTabList<SpongeTabPlayer> {
|
||||
|
||||
+4
-3
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.sponge8;
|
||||
package me.neznamy.tab.platforms.sponge;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.chat.component.TabComponent;
|
||||
@@ -10,11 +10,12 @@ import org.spongepowered.api.effect.potion.PotionEffect;
|
||||
import org.spongepowered.api.effect.potion.PotionEffectTypes;
|
||||
import org.spongepowered.api.entity.living.player.gamemode.GameModes;
|
||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
||||
import org.spongepowered.api.network.ServerConnectionState;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* TabPlayer implementation for Sponge 8+.
|
||||
* TabPlayer implementation for Sponge.
|
||||
*/
|
||||
public class SpongeTabPlayer extends BackendTabPlayer {
|
||||
|
||||
@@ -38,7 +39,7 @@ public class SpongeTabPlayer extends BackendTabPlayer {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public int getPing() {
|
||||
return SpongeMultiVersion.getPing.apply(getPlayer());
|
||||
return getPlayer().connection().state().map(c -> ((ServerConnectionState.Game)c).latency()).orElse(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1,4 +0,0 @@
|
||||
dependencies {
|
||||
implementation(projects.shared)
|
||||
compileOnly("org.spongepowered:spongeapi:5.0.0")
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package me.neznamy.tab.platforms.sponge7;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.api.config.ConfigDir;
|
||||
import org.spongepowered.api.event.Listener;
|
||||
import org.spongepowered.api.event.game.state.GameStartedServerEvent;
|
||||
import org.spongepowered.api.event.game.state.GameStoppedServerEvent;
|
||||
import org.spongepowered.api.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Main class for Sponge 7.
|
||||
*/
|
||||
@Plugin(
|
||||
id = TabConstants.PLUGIN_ID,
|
||||
name = TabConstants.PLUGIN_NAME,
|
||||
version = TabConstants.PLUGIN_VERSION,
|
||||
description = TabConstants.PLUGIN_DESCRIPTION,
|
||||
url = TabConstants.PLUGIN_WEBSITE,
|
||||
authors = TabConstants.PLUGIN_AUTHOR
|
||||
)
|
||||
@Getter
|
||||
public class Sponge7TAB {
|
||||
|
||||
@Inject @ConfigDir(sharedRoot = false) private File configDir;
|
||||
@Inject private Logger logger;
|
||||
|
||||
/**
|
||||
* Enables the plugin.
|
||||
*
|
||||
* @param event
|
||||
* Server start event
|
||||
*/
|
||||
@Listener
|
||||
public void onServerStart(@Nullable GameStartedServerEvent event) {
|
||||
TAB.create(new SpongePlatform(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the plugin.
|
||||
*
|
||||
* @param event
|
||||
* Server stop event
|
||||
*/
|
||||
@Listener
|
||||
public void onServerStop(@Nullable GameStoppedServerEvent event) {
|
||||
TAB.getInstance().unload();
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package me.neznamy.tab.platforms.sponge7;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.bossbar.BarColor;
|
||||
import me.neznamy.tab.api.bossbar.BarStyle;
|
||||
import me.neznamy.chat.component.TabComponent;
|
||||
import me.neznamy.tab.shared.platform.decorators.SafeBossBar;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.api.boss.*;
|
||||
|
||||
/**
|
||||
* BossBar implementation for Sponge 7 using its API.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class SpongeBossBar extends SafeBossBar<ServerBossBar> {
|
||||
|
||||
/** Color array for fast access */
|
||||
private static final BossBarColor[] colors = {
|
||||
BossBarColors.PINK,
|
||||
BossBarColors.BLUE,
|
||||
BossBarColors.RED,
|
||||
BossBarColors.GREEN,
|
||||
BossBarColors.YELLOW,
|
||||
BossBarColors.PURPLE,
|
||||
BossBarColors.WHITE
|
||||
};
|
||||
|
||||
/** Style array for fast access */
|
||||
private static final BossBarOverlay[] styles = {
|
||||
BossBarOverlays.PROGRESS,
|
||||
BossBarOverlays.NOTCHED_6,
|
||||
BossBarOverlays.NOTCHED_10,
|
||||
BossBarOverlays.NOTCHED_12,
|
||||
BossBarOverlays.NOTCHED_20
|
||||
};
|
||||
|
||||
/** Player to send boss bars to */
|
||||
@NotNull
|
||||
private final SpongeTabPlayer player;
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public ServerBossBar constructBossBar(@NotNull TabComponent title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
|
||||
return ServerBossBar.builder()
|
||||
.name(title.convert())
|
||||
.color(colors[color.ordinal()])
|
||||
.overlay(styles[style.ordinal()])
|
||||
.percent(progress)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(@NotNull BossBarInfo bar) {
|
||||
bar.getBossBar().addPlayer(player.getPlayer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTitle(@NotNull BossBarInfo bar) {
|
||||
bar.getBossBar().setName(bar.getTitle().convert());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProgress(@NotNull BossBarInfo bar) {
|
||||
bar.getBossBar().setPercent(bar.getProgress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStyle(@NotNull BossBarInfo bar) {
|
||||
bar.getBossBar().setOverlay(styles[bar.getStyle().ordinal()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateColor(@NotNull BossBarInfo bar) {
|
||||
bar.getBossBar().setColor(colors[bar.getColor().ordinal()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull BossBarInfo bar) {
|
||||
bar.getBossBar().removePlayer(player.getPlayer());
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package me.neznamy.tab.platforms.sponge7;
|
||||
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.platform.EventListener;
|
||||
import me.neznamy.tab.shared.platform.TabPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.api.entity.living.player.Player;
|
||||
import org.spongepowered.api.event.Listener;
|
||||
import org.spongepowered.api.event.Order;
|
||||
import org.spongepowered.api.event.command.SendCommandEvent;
|
||||
import org.spongepowered.api.event.entity.MoveEntityEvent;
|
||||
import org.spongepowered.api.event.entity.living.humanoid.player.RespawnPlayerEvent;
|
||||
import org.spongepowered.api.event.filter.cause.First;
|
||||
import org.spongepowered.api.event.network.ClientConnectionEvent;
|
||||
|
||||
/**
|
||||
* Event listener for Sponge 7 to forward events to enabled features.
|
||||
*/
|
||||
public class SpongeEventListener implements EventListener<Player> {
|
||||
|
||||
/**
|
||||
* Listens to player quit event.
|
||||
*
|
||||
* @param event
|
||||
* Disconnect event
|
||||
*/
|
||||
@Listener
|
||||
public void onQuit(ClientConnectionEvent.Disconnect event) {
|
||||
quit(event.getTargetEntity().getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to player join event.
|
||||
*
|
||||
* @param event
|
||||
* Join event
|
||||
*/
|
||||
@Listener
|
||||
public void onJoin(ClientConnectionEvent.Join event) {
|
||||
join(event.getTargetEntity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to world change event.
|
||||
*
|
||||
* @param event
|
||||
* World change event
|
||||
* @param player
|
||||
* Player who changed world
|
||||
*/
|
||||
@Listener
|
||||
public void onWorldChange(MoveEntityEvent.Teleport event, @First Player player) {
|
||||
worldChange(event.getTargetEntity().getUniqueId(), event.getTargetEntity().getWorld().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to command event to potentially cancel it.
|
||||
*
|
||||
* @param event
|
||||
* Command event
|
||||
* @param player
|
||||
* Player who executed the command
|
||||
*/
|
||||
@Listener
|
||||
public void onCommand(SendCommandEvent event, @First Player player) {
|
||||
if (command(player.getUniqueId(), event.getCommand())) event.setCancelled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to player respawn event.
|
||||
*
|
||||
* @param event
|
||||
* Player respawn event
|
||||
*/
|
||||
@Listener(order = Order.PRE)
|
||||
public void onRespawn(RespawnPlayerEvent event) {
|
||||
replacePlayer(event.getTargetEntity().getUniqueId(), event.getTargetEntity());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabPlayer createPlayer(@NotNull Player player) {
|
||||
return new SpongeTabPlayer((SpongePlatform) TAB.getInstance().getPlatform(), player);
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
package me.neznamy.tab.platforms.sponge7;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.backend.BackendPlatform;
|
||||
import me.neznamy.chat.component.TabComponent;
|
||||
import me.neznamy.tab.shared.features.PerWorldPlayerListConfiguration;
|
||||
import me.neznamy.tab.shared.features.injection.PipelineInjector;
|
||||
import me.neznamy.tab.shared.features.types.TabFeature;
|
||||
import me.neznamy.tab.shared.placeholders.expansion.EmptyTabExpansion;
|
||||
import me.neznamy.tab.shared.placeholders.expansion.TabExpansion;
|
||||
import me.neznamy.tab.shared.platform.BossBar;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.platform.TabPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.api.Sponge;
|
||||
import org.spongepowered.api.command.args.GenericArguments;
|
||||
import org.spongepowered.api.command.spec.CommandSpec;
|
||||
import org.spongepowered.api.entity.living.player.Player;
|
||||
import org.spongepowered.api.text.Text;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Platform implementation for Sponge 7 and lower
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class SpongePlatform implements BackendPlatform {
|
||||
|
||||
/** Plugin reference */
|
||||
@NotNull
|
||||
private final Sponge7TAB plugin;
|
||||
|
||||
/** Server version */
|
||||
@Getter
|
||||
private final ProtocolVersion serverVersion = ProtocolVersion.fromFriendlyName(Sponge.getGame().getPlatform().getMinecraftVersion().getName());
|
||||
|
||||
@Override
|
||||
public void registerUnknownPlaceholder(@NotNull String identifier) {
|
||||
registerDummyPlaceholder(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadPlayers() {
|
||||
for (Player player : Sponge.getServer().getOnlinePlayers()) {
|
||||
TAB.getInstance().addPlayer(new SpongeTabPlayer(this, player));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PipelineInjector createPipelineInjector() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabExpansion createTabExpansion() {
|
||||
return new EmptyTabExpansion();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public TabFeature getPerWorldPlayerList(@NotNull PerWorldPlayerListConfiguration configuration) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logInfo(@NotNull TabComponent message) {
|
||||
plugin.getLogger().info(message.toLegacyText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logWarn(@NotNull TabComponent message) {
|
||||
plugin.getLogger().warn("§c" + message.toLegacyText());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getServerVersionInfo() {
|
||||
return "[Sponge] " + Sponge.getPlatform().getMinecraftVersion().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerListener() {
|
||||
Sponge.getGame().getEventManager().registerListeners(plugin, new SpongeEventListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCommand() {
|
||||
SpongeTabCommand cmd = new SpongeTabCommand();
|
||||
Sponge.getGame().getCommandManager().register(plugin, CommandSpec.builder()
|
||||
.arguments(cmd, GenericArguments.remainingJoinedStrings(Text.of("arguments"))) // GenericArguments.none() doesn't work, so rip no-arg
|
||||
.executor(cmd)
|
||||
.build(), getCommand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startMetrics() {
|
||||
// Not available
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public File getDataFolder() {
|
||||
return plugin.getConfigDir();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Text convertComponent(@NotNull TabComponent component) {
|
||||
return Text.of(component.toLegacyText());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Scoreboard createScoreboard(@NotNull TabPlayer player) {
|
||||
return new SpongeScoreboard((SpongeTabPlayer) player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public BossBar createBossBar(@NotNull TabPlayer player) {
|
||||
return new SpongeBossBar((SpongeTabPlayer) player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabList createTabList(@NotNull TabPlayer player) {
|
||||
return new SpongeTabList((SpongeTabPlayer) player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsScoreboards() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTPS() {
|
||||
return Sponge.getServer().getTicksPerSecond();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getMSPT() {
|
||||
return -1; // Not available
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package me.neznamy.tab.platforms.sponge7;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.shared.Limitations;
|
||||
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.api.scoreboard.CollisionRules;
|
||||
import org.spongepowered.api.scoreboard.Visibilities;
|
||||
import org.spongepowered.api.scoreboard.Visibility;
|
||||
import org.spongepowered.api.scoreboard.critieria.Criteria;
|
||||
import org.spongepowered.api.scoreboard.displayslot.DisplaySlots;
|
||||
import org.spongepowered.api.scoreboard.objective.displaymode.ObjectiveDisplayMode;
|
||||
import org.spongepowered.api.scoreboard.objective.displaymode.ObjectiveDisplayModes;
|
||||
import org.spongepowered.api.text.Text;
|
||||
|
||||
/**
|
||||
* Scoreboard implementation for Sponge 7 using its API.
|
||||
*/
|
||||
public class SpongeScoreboard extends SafeScoreboard<SpongeTabPlayer> {
|
||||
|
||||
/** Collision rule array for fast access */
|
||||
private static final org.spongepowered.api.scoreboard.CollisionRule[] collisionRules = {
|
||||
CollisionRules.ALWAYS,
|
||||
CollisionRules.NEVER,
|
||||
CollisionRules.PUSH_OTHER_TEAMS,
|
||||
CollisionRules.PUSH_OWN_TEAM
|
||||
};
|
||||
|
||||
/** Visibility array for fast access */
|
||||
private static final Visibility[] visibilities = {
|
||||
Visibilities.ALWAYS,
|
||||
Visibilities.NEVER,
|
||||
Visibilities.HIDE_FOR_OTHER_TEAMS,
|
||||
Visibilities.HIDE_FOR_OWN_TEAM
|
||||
};
|
||||
|
||||
/** DisplaySlot array for fast access */
|
||||
private static final org.spongepowered.api.scoreboard.displayslot.DisplaySlot[] displaySlots = {
|
||||
DisplaySlots.LIST,
|
||||
DisplaySlots.SIDEBAR,
|
||||
DisplaySlots.BELOW_NAME
|
||||
};
|
||||
|
||||
/** Health display array for fast access */
|
||||
private static final ObjectiveDisplayMode[] healthDisplays = {
|
||||
ObjectiveDisplayModes.INTEGER,
|
||||
ObjectiveDisplayModes.HEARTS
|
||||
};
|
||||
|
||||
/** Scoreboard of the player */
|
||||
private final org.spongepowered.api.scoreboard.Scoreboard sb = org.spongepowered.api.scoreboard.Scoreboard.builder().build();
|
||||
|
||||
/**
|
||||
* Constructs new instance and puts player into new scoreboard.
|
||||
*
|
||||
* @param player
|
||||
* Player this scoreboard will belong to
|
||||
*/
|
||||
public SpongeScoreboard(@NonNull SpongeTabPlayer player) {
|
||||
super(player);
|
||||
|
||||
// Make sure each player is in a different scoreboard for per-player view
|
||||
player.getPlayer().setScoreboard(sb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerObjective(@NonNull Objective objective) {
|
||||
org.spongepowered.api.scoreboard.objective.Objective obj = org.spongepowered.api.scoreboard.objective.Objective.builder()
|
||||
.name(objective.getName())
|
||||
.displayName(Text.of(cutTo(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13)))
|
||||
.objectiveDisplayMode(healthDisplays[objective.getHealthDisplay().ordinal()])
|
||||
.criterion(Criteria.DUMMY)
|
||||
.build();
|
||||
sb.addObjective(obj);
|
||||
sb.updateDisplaySlot(obj, displaySlots[objective.getDisplaySlot().ordinal()]);
|
||||
objective.setPlatformObjective(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterObjective(@NonNull Objective objective) {
|
||||
sb.removeObjective(((org.spongepowered.api.scoreboard.objective.Objective)objective.getPlatformObjective()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateObjective(@NonNull Objective objective) {
|
||||
org.spongepowered.api.scoreboard.objective.Objective obj = ((org.spongepowered.api.scoreboard.objective.Objective)objective.getPlatformObjective());
|
||||
obj.setDisplayName(Text.of(cutTo(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13)));
|
||||
obj.setDisplayMode(healthDisplays[objective.getHealthDisplay().ordinal()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScore(@NonNull Score score) {
|
||||
((org.spongepowered.api.scoreboard.objective.Objective)score.getObjective().getPlatformObjective())
|
||||
.getOrCreateScore(Text.of(score.getHolder())).setScore(score.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScore(@NonNull Score score) {
|
||||
((org.spongepowered.api.scoreboard.objective.Objective)score.getObjective().getPlatformObjective()).removeScore(Text.of(score.getHolder()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object createTeam(@NonNull String name) {
|
||||
return new Object(); // This implementation does not need teams tracked
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerTeam(@NonNull Team team) {
|
||||
org.spongepowered.api.scoreboard.Team spongeTeam = org.spongepowered.api.scoreboard.Team.builder()
|
||||
.name(team.getName())
|
||||
.displayName(Text.of(team.getName()))
|
||||
.prefix(Text.of(cutTo(team.getPrefix().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13)))
|
||||
.suffix(Text.of(cutTo(team.getSuffix().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13)))
|
||||
.allowFriendlyFire((team.getOptions() & 0x01) != 0)
|
||||
.canSeeFriendlyInvisibles((team.getOptions() & 0x02) != 0)
|
||||
.collisionRule(collisionRules[team.getCollision().ordinal()])
|
||||
.nameTagVisibility(visibilities[team.getVisibility().ordinal()])
|
||||
.build();
|
||||
for (String member : team.getPlayers()) {
|
||||
spongeTeam.addMember(Text.of(member));
|
||||
}
|
||||
sb.registerTeam(spongeTeam);
|
||||
team.setPlatformTeam(sb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterTeam(@NonNull Team team) {
|
||||
((org.spongepowered.api.scoreboard.Team)team.getPlatformTeam()).unregister();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTeam(@NonNull Team team) {
|
||||
org.spongepowered.api.scoreboard.Team spongeTeam = (org.spongepowered.api.scoreboard.Team) team.getPlatformTeam();
|
||||
spongeTeam.setDisplayName(Text.of(team.getName()));
|
||||
spongeTeam.setPrefix(Text.of(cutTo(team.getPrefix().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13)));
|
||||
spongeTeam.setSuffix(Text.of(cutTo(team.getSuffix().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13)));
|
||||
spongeTeam.setAllowFriendlyFire((team.getOptions() & 0x01) != 0);
|
||||
spongeTeam.setCanSeeFriendlyInvisibles((team.getOptions() & 0x02) != 0);
|
||||
spongeTeam.setCollisionRule(collisionRules[team.getCollision().ordinal()]);
|
||||
spongeTeam.setNameTagVisibility(visibilities[team.getVisibility().ordinal()]);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package me.neznamy.tab.platforms.sponge7;
|
||||
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.platform.TabPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.api.command.CommandResult;
|
||||
import org.spongepowered.api.command.CommandSource;
|
||||
import org.spongepowered.api.command.args.CommandArgs;
|
||||
import org.spongepowered.api.command.args.CommandContext;
|
||||
import org.spongepowered.api.command.args.CommandElement;
|
||||
import org.spongepowered.api.command.spec.CommandExecutor;
|
||||
import org.spongepowered.api.entity.living.player.Player;
|
||||
import org.spongepowered.api.text.Text;
|
||||
import org.spongepowered.api.text.serializer.TextSerializers;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command processor for plugin's command for Sponge 7.
|
||||
*/
|
||||
public class SpongeTabCommand extends CommandElement implements CommandExecutor {
|
||||
|
||||
protected SpongeTabCommand() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CommandResult execute(@NotNull CommandSource source, @NotNull CommandContext context) {
|
||||
String[] args = context.<String>getOne(Text.of("arguments")).orElse("").split(" ");
|
||||
|
||||
if (TAB.getInstance().isPluginDisabled()) {
|
||||
boolean hasReloadPermission = source.hasPermission(TabConstants.Permission.COMMAND_RELOAD);
|
||||
boolean hasAdminPermission = source.hasPermission(TabConstants.Permission.COMMAND_ALL);
|
||||
List<String> messages = TAB.getInstance().getDisabledCommand().execute(args, hasReloadPermission, hasAdminPermission);
|
||||
for (String message : messages) {
|
||||
source.sendMessage(TextSerializers.FORMATTING_CODE.deserialize(message));
|
||||
}
|
||||
return CommandResult.success();
|
||||
}
|
||||
TabPlayer player = null;
|
||||
if (source instanceof Player) {
|
||||
player = TAB.getInstance().getPlayer(((Player) source).getUniqueId());
|
||||
if (player == null) return CommandResult.success(); // Player not loaded correctly
|
||||
}
|
||||
TAB.getInstance().getCommand().execute(player, args);
|
||||
return CommandResult.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object parseValue(@NotNull CommandSource source, @NotNull CommandArgs args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<String> complete(@NotNull CommandSource source, @NotNull CommandArgs commandArgs, @NotNull CommandContext context) {
|
||||
TabPlayer player = null;
|
||||
if (source instanceof Player) {
|
||||
player = TAB.getInstance().getPlayer(((Player)source).getUniqueId());
|
||||
if (player == null) return Collections.emptyList(); // Player not loaded correctly
|
||||
}
|
||||
String[] args = commandArgs.getRaw().split(" ");
|
||||
if (commandArgs.getRaw().endsWith(" ")) {
|
||||
args = Arrays.copyOf(args, args.length+1);
|
||||
args[args.length-1] = "";
|
||||
}
|
||||
return TAB.getInstance().getCommand().complete(player, args);
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
package me.neznamy.tab.platforms.sponge7;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.chat.component.TabComponent;
|
||||
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.api.entity.living.player.gamemode.GameMode;
|
||||
import org.spongepowered.api.entity.living.player.gamemode.GameModes;
|
||||
import org.spongepowered.api.entity.living.player.tab.TabListEntry;
|
||||
import org.spongepowered.api.profile.GameProfile;
|
||||
import org.spongepowered.api.profile.property.ProfileProperty;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* TabList implementation for Sponge 7 and lower
|
||||
*/
|
||||
public class SpongeTabList extends TrackedTabList<SpongeTabPlayer> {
|
||||
|
||||
/** Gamemode array for fast access */
|
||||
private static final GameMode[] gameModes = {
|
||||
GameModes.SURVIVAL, GameModes.CREATIVE, GameModes.ADVENTURE, GameModes.SPECTATOR
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
* @param player
|
||||
* Player this tablist will belong to
|
||||
*/
|
||||
public SpongeTabList(@NotNull SpongeTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntry(@NonNull UUID entry) {
|
||||
player.getPlayer().getTabList().removeEntry(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
|
||||
player.getPlayer().getTabList().getEntry(entry).ifPresent(e -> e.setDisplayName(displayName == null ? null : displayName.convert()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLatency(@NonNull UUID entry, int latency) {
|
||||
player.getPlayer().getTabList().getEntry(entry).ifPresent(e -> e.setLatency(latency));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGameMode(@NonNull UUID entry, int gameMode) {
|
||||
player.getPlayer().getTabList().getEntry(entry).ifPresent(e -> e.setGameMode(gameModes[gameMode]));
|
||||
}
|
||||
|
||||
@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) {
|
||||
GameProfile profile = GameProfile.of(entry.getUniqueId(), entry.getName());
|
||||
if (entry.getSkin() != null) profile.getPropertyMap().put(TEXTURES_PROPERTY, ProfileProperty.of(
|
||||
TEXTURES_PROPERTY, entry.getSkin().getValue(), entry.getSkin().getSignature()));
|
||||
TabListEntry tabListEntry = TabListEntry.builder()
|
||||
.list(player.getPlayer().getTabList())
|
||||
.profile(profile)
|
||||
.latency(entry.getLatency())
|
||||
.gameMode(gameModes[entry.getGameMode()])
|
||||
.displayName(entry.getDisplayName() == null ? null : entry.getDisplayName().convert())
|
||||
.build();
|
||||
player.getPlayer().getTabList().addEntry(tabListEntry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerListHeaderFooter(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
player.getPlayer().getTabList().setHeaderAndFooter(header.convert(), footer.convert());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEntry(@NonNull UUID entry) {
|
||||
return player.getPlayer().getTabList().getEntry(entry).isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Skin getSkin() {
|
||||
Collection<ProfileProperty> properties = player.getPlayer().getProfile().getPropertyMap().get(TEXTURES_PROPERTY);
|
||||
if (properties.isEmpty()) return null; // Offline mode
|
||||
ProfileProperty property = properties.iterator().next();
|
||||
return new Skin(property.getValue(), property.getSignature().orElse(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkDisplayNames() {
|
||||
for (TabListEntry entry : player.getPlayer().getTabList().getEntries()) {
|
||||
TabComponent expectedComponent = getExpectedDisplayNames().get(entry.getProfile().getUniqueId());
|
||||
if (expectedComponent != null && entry.getDisplayName().orElse(null) != expectedComponent.convert()) {
|
||||
entry.setDisplayName(expectedComponent.convert());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package me.neznamy.tab.platforms.sponge7;
|
||||
|
||||
import me.neznamy.chat.component.TabComponent;
|
||||
import me.neznamy.tab.shared.backend.BackendTabPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.api.data.key.Keys;
|
||||
import org.spongepowered.api.data.manipulator.mutable.PotionEffectData;
|
||||
import org.spongepowered.api.effect.potion.PotionEffectTypes;
|
||||
import org.spongepowered.api.entity.living.player.Player;
|
||||
import org.spongepowered.api.entity.living.player.gamemode.GameMode;
|
||||
import org.spongepowered.api.entity.living.player.gamemode.GameModes;
|
||||
import org.spongepowered.api.text.Text;
|
||||
|
||||
/**
|
||||
* TabPlayer implementation for Sponge 7.
|
||||
*/
|
||||
public class SpongeTabPlayer extends BackendTabPlayer {
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param platform
|
||||
* Server platform
|
||||
* @param player
|
||||
* Platform's player object
|
||||
*/
|
||||
public SpongeTabPlayer(@NotNull SpongePlatform platform, @NotNull Player player) {
|
||||
super(platform, player, player.getUniqueId(), player.getName(), player.getWorld().getName(), platform.getServerVersion().getNetworkId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NotNull String permission) {
|
||||
return getPlayer().hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing() {
|
||||
return getPlayer().getConnection().getLatency();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(@NotNull TabComponent message) {
|
||||
getPlayer().sendMessage((Text) message.convert());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasInvisibilityPotion() {
|
||||
PotionEffectData potionEffects = getPlayer().get(PotionEffectData.class).orElse(null);
|
||||
if (potionEffects == null) return false;
|
||||
return potionEffects.asList().stream().anyMatch(effect -> effect.getType().equals(PotionEffectTypes.INVISIBILITY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisguised() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Player getPlayer() {
|
||||
return (Player) player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpongePlatform getPlatform() {
|
||||
return (SpongePlatform) platform;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVanished0() {
|
||||
return getPlayer().get(Keys.VANISH).orElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGamemode() {
|
||||
GameMode gameMode = getPlayer().getGameModeData().type().get();
|
||||
if (gameMode.equals(GameModes.CREATIVE)) return 1;
|
||||
if (gameMode.equals(GameModes.ADVENTURE)) return 2;
|
||||
if (gameMode.equals(GameModes.SPECTATOR)) return 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealth() {
|
||||
return getPlayer().health().get();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return getPlayer().getDisplayNameData().displayName().get().toPlain();
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package me.neznamy.tab.platforms.sponge8;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.shared.util.function.BiFunctionWithException;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import me.neznamy.tab.shared.util.function.ToIntFunction;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
||||
import org.spongepowered.api.event.network.ServerSideConnectionEvent;
|
||||
import org.spongepowered.api.profile.GameProfile;
|
||||
import org.spongepowered.api.scoreboard.Score;
|
||||
import org.spongepowered.api.scoreboard.objective.Objective;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Class managing code that varies based on server version.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class SpongeMultiVersion {
|
||||
|
||||
/** Function for getting score from an objective */
|
||||
private static final BiFunctionWithException<Objective, String, Score> findOrCreateScore;
|
||||
|
||||
/** Function for getting player's ping*/
|
||||
public static final ToIntFunction<ServerPlayer> getPing;
|
||||
|
||||
/** Function for getting UUID from disconnect event */
|
||||
public static final FunctionWithException<ServerSideConnectionEvent.Disconnect, UUID> getUniqueId;
|
||||
|
||||
static {
|
||||
if (ReflectionUtils.methodExists(Objective.class, "findOrCreateScore", String.class)) {
|
||||
// Sponge 11+ (1.20.4+)
|
||||
findOrCreateScore = (objective, holder) -> (Score) Objective.class.getMethod("findOrCreateScore", String.class).invoke(objective, holder);
|
||||
} else {
|
||||
// Sponge 8 - 10 (and early 11) (1.16.5 - 1.20.2)
|
||||
findOrCreateScore = (objective, holder) -> objective.findOrCreateScore(Component.text(holder));
|
||||
}
|
||||
|
||||
if (!ReflectionUtils.classExists("org.spongepowered.api.network.ServerPlayerConnection")) {
|
||||
// Sponge 11+ (1.20.6+)
|
||||
getPing = player -> {
|
||||
Object connection = player.getClass().getMethod("connection").invoke(player);
|
||||
Optional<Object> state = (Optional<Object>) connection.getClass().getMethod("state").invoke(connection);
|
||||
return (int) state.get().getClass().getMethod("latency").invoke(state.get());
|
||||
};
|
||||
} else {
|
||||
// Sponge 8 - 10 (and early 11) (1.16.5 - 1.20.4)
|
||||
getPing = player -> player.connection().latency();
|
||||
}
|
||||
|
||||
if (!ReflectionUtils.methodExists(ServerSideConnectionEvent.Disconnect.class, "player")) {
|
||||
// Sponge 11+ (1.20.6+)
|
||||
getUniqueId = event -> ((Optional<GameProfile>) ReflectionUtils.setAccessible(event.getClass().getMethod("profile")).invoke(event)).get().uuid();
|
||||
} else {
|
||||
// Sponge 8 - 10 (and early 11) (1.16.5 - 1.20.4)
|
||||
getUniqueId = event -> event.player().uniqueId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns score of holder in given objective. If it does not exist, it is created.
|
||||
*
|
||||
* @param objective
|
||||
* Objective to get score from
|
||||
* @param holder
|
||||
* Score holder
|
||||
* @return Score of given holder in objective
|
||||
*/
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
public static Score findOrCreateScore(@NotNull Objective objective, @NotNull String holder) {
|
||||
return findOrCreateScore.apply(objective, holder);
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,6 @@ public class VelocityScoreboard extends SafeScoreboard<VelocityTabPlayer> {
|
||||
public void registerObjective(@NonNull Objective objective) {
|
||||
try {
|
||||
ProxyObjective.Builder builder = scoreboard.objectiveBuilder(objective.getName())
|
||||
.displaySlot(com.velocitypowered.api.scoreboard.DisplaySlot.valueOf(objective.getDisplaySlot().name()))
|
||||
.healthDisplay(com.velocitypowered.api.scoreboard.HealthDisplay.valueOf(objective.getHealthDisplay().name()))
|
||||
.title(objective.getTitle().toTextHolder(textHolderFunction))
|
||||
.numberFormat(objective.getNumberFormat() == null ? null : NumberFormat.fixed(objective.getNumberFormat().toAdventure()));
|
||||
@@ -51,6 +50,11 @@ public class VelocityScoreboard extends SafeScoreboard<VelocityTabPlayer> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySlot(@NonNull Objective objective) {
|
||||
((ProxyObjective)objective.getPlatformObjective()).setDisplaySlot(com.velocitypowered.api.scoreboard.DisplaySlot.valueOf(objective.getDisplaySlot().name()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterObjective(@NonNull Objective objective) {
|
||||
try {
|
||||
|
||||
Reference in new issue
Block a user